fix: corrige tipagem compartilhada e compatibilidade do typecheck

This commit is contained in:
Felipe Coutinho
2026-03-16 01:24:04 +00:00
parent 132f98c0f8
commit 2cb5033486
10 changed files with 55 additions and 32 deletions

View File

@@ -231,7 +231,7 @@ export default async function Page() {
<div className="mx-auto flex max-w-5xl flex-col items-center text-center gap-5 md:gap-6"> <div className="mx-auto flex max-w-5xl flex-col items-center text-center gap-5 md:gap-6">
<Logo variant="small" className="h-12 w-12 mb-1" /> <Logo variant="small" className="h-12 w-12 mb-1" />
<Badge variant="primary"> <Badge variant="default">
<RiGithubFill size={14} className="mr-1" /> <RiGithubFill size={14} className="mr-1" />
Projeto Open Source Projeto Open Source
</Badge> </Badge>
@@ -378,7 +378,7 @@ export default async function Page() {
<div className="mx-auto max-w-6xl"> <div className="mx-auto max-w-6xl">
<AnimateOnScroll> <AnimateOnScroll>
<div className="text-center mb-8 md:mb-12"> <div className="text-center mb-8 md:mb-12">
<Badge variant="primary" className="mb-4"> <Badge variant="default" className="mb-4">
Conheça as telas Conheça as telas
</Badge> </Badge>
<h2 className="text-2xl sm:text-3xl md:text-4xl font-bold tracking-tight mb-3 md:mb-4"> <h2 className="text-2xl sm:text-3xl md:text-4xl font-bold tracking-tight mb-3 md:mb-4">
@@ -430,7 +430,7 @@ export default async function Page() {
<div className="mx-auto max-w-5xl"> <div className="mx-auto max-w-5xl">
<AnimateOnScroll> <AnimateOnScroll>
<div className="text-center mb-8 md:mb-12"> <div className="text-center mb-8 md:mb-12">
<Badge variant="primary" className="mb-4"> <Badge variant="default" className="mb-4">
O que tem aqui O que tem aqui
</Badge> </Badge>
<h2 className="text-2xl sm:text-3xl md:text-4xl font-bold tracking-tight mb-3 md:mb-4"> <h2 className="text-2xl sm:text-3xl md:text-4xl font-bold tracking-tight mb-3 md:mb-4">
@@ -514,7 +514,7 @@ export default async function Page() {
<div className="grid gap-8 md:gap-12 md:grid-cols-2 items-center"> <div className="grid gap-8 md:gap-12 md:grid-cols-2 items-center">
{/* Text content */} {/* Text content */}
<div className="order-2 md:order-1"> <div className="order-2 md:order-1">
<Badge variant="primary" className="mb-4"> <Badge variant="default" className="mb-4">
<RiSmartphoneLine size={14} className="mr-1" /> <RiSmartphoneLine size={14} className="mr-1" />
App Android App Android
</Badge> </Badge>
@@ -625,7 +625,7 @@ export default async function Page() {
<div className="mx-auto max-w-5xl"> <div className="mx-auto max-w-5xl">
<AnimateOnScroll> <AnimateOnScroll>
<div className="text-center mb-8 md:mb-12"> <div className="text-center mb-8 md:mb-12">
<Badge variant="primary" className="mb-4"> <Badge variant="default" className="mb-4">
Stack técnica Stack técnica
</Badge> </Badge>
<h2 className="text-2xl sm:text-3xl md:text-4xl font-bold tracking-tight mb-3 md:mb-4"> <h2 className="text-2xl sm:text-3xl md:text-4xl font-bold tracking-tight mb-3 md:mb-4">
@@ -748,7 +748,7 @@ export default async function Page() {
<div className="mx-auto max-w-3xl"> <div className="mx-auto max-w-3xl">
<AnimateOnScroll> <AnimateOnScroll>
<div className="text-center mb-8 md:mb-12"> <div className="text-center mb-8 md:mb-12">
<Badge variant="primary" className="mb-4"> <Badge variant="default" className="mb-4">
Como usar Como usar
</Badge> </Badge>
<h2 className="text-2xl sm:text-3xl md:text-4xl font-bold tracking-tight mb-3 md:mb-4"> <h2 className="text-2xl sm:text-3xl md:text-4xl font-bold tracking-tight mb-3 md:mb-4">

View File

@@ -1,5 +1,6 @@
import { relations, sql } from "drizzle-orm"; import { relations, sql } from "drizzle-orm";
import { import {
type AnyPgColumn,
boolean, boolean,
date, date,
index, index,
@@ -536,7 +537,7 @@ export const installmentAnticipations = pgTable(
.default("0"), .default("0"),
transactionId: uuid("lancamento_id") transactionId: uuid("lancamento_id")
.notNull() .notNull()
.references(() => transactions.id, { onDelete: "cascade" }), .references((): AnyPgColumn => transactions.id, { onDelete: "cascade" }),
payerId: uuid("pagador_id").references(() => payers.id, { payerId: uuid("pagador_id").references(() => payers.id, {
onDelete: "cascade", onDelete: "cascade",
}), }),
@@ -585,7 +586,7 @@ export const transactions = pgTable(
isDivided: boolean("dividido").default(false), isDivided: boolean("dividido").default(false),
isAnticipated: boolean("antecipado").default(false), isAnticipated: boolean("antecipado").default(false),
anticipationId: uuid("antecipacao_id").references( anticipationId: uuid("antecipacao_id").references(
() => installmentAnticipations.id, (): AnyPgColumn => installmentAnticipations.id,
{ onDelete: "set null" }, { onDelete: "set null" },
), ),
createdAt: timestamp("created_at", { createdAt: timestamp("created_at", {

View File

@@ -387,7 +387,7 @@ export function CategoryHistoryWidget({ data }: CategoryHistoryWidgetProps) {
return ( return (
<div <div
key={entry.dataKey} key={String(entry.dataKey ?? entry.name)}
className="flex items-center justify-between gap-4" className="flex items-center justify-between gap-4"
> >
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">

View File

@@ -91,7 +91,7 @@ export function IncomeExpenseBalanceWidget({
return ( return (
<div <div
key={entry.dataKey} key={String(entry.dataKey ?? entry.name)}
className="flex items-center gap-2" className="flex items-center gap-2"
> >
<div <div

View File

@@ -6,7 +6,9 @@ import {
Area, Area,
AreaChart, AreaChart,
CartesianGrid, CartesianGrid,
type TooltipProps, type TooltipContentProps,
type TooltipPayloadEntry,
type TooltipValueType,
XAxis, XAxis,
} from "recharts"; } from "recharts";
import type { CategoryChartData } from "@/features/reports/category-chart-queries"; import type { CategoryChartData } from "@/features/reports/category-chart-queries";
@@ -35,12 +37,19 @@ import {
import { CATEGORY_COLORS } from "@/shared/utils/category-colors"; import { CATEGORY_COLORS } from "@/shared/utils/category-colors";
import { currencyFormatter } from "@/shared/utils/currency"; import { currencyFormatter } from "@/shared/utils/currency";
function AreaTooltip({ active, payload, label }: TooltipProps<number, string>) { function AreaTooltip({
active,
payload,
label,
}: Partial<TooltipContentProps<TooltipValueType, string>>) {
if (!active || !payload?.length) return null; if (!active || !payload?.length) return null;
const items = payload const items = payload
.filter((entry) => Number(entry.value) > 0) .filter((entry: TooltipPayloadEntry) => Number(entry.value) > 0)
.sort((a, b) => Number(b.value) - Number(a.value)); .sort(
(a: TooltipPayloadEntry, b: TooltipPayloadEntry) =>
Number(b.value) - Number(a.value),
);
if (items.length === 0) return null; if (items.length === 0) return null;
@@ -50,9 +59,9 @@ function AreaTooltip({ active, payload, label }: TooltipProps<number, string>) {
{label} {label}
</p> </p>
<div className="space-y-1.5"> <div className="space-y-1.5">
{items.map((entry) => ( {items.map((entry: TooltipPayloadEntry) => (
<div <div
key={entry.dataKey} key={String(entry.dataKey ?? entry.name)}
className="flex items-center justify-between gap-6" className="flex items-center justify-between gap-6"
> >
<div className="flex min-w-0 items-center gap-1.5"> <div className="flex min-w-0 items-center gap-1.5">

View File

@@ -34,7 +34,7 @@ export function AnticipationCard({
}: AnticipationCardProps) { }: AnticipationCardProps) {
const [isPending, startTransition] = useTransition(); const [isPending, startTransition] = useTransition();
const isSettled = anticipation.transaction.isSettled === true; const isSettled = anticipation.transaction?.isSettled === true;
const canCancel = !isSettled; const canCancel = !isSettled;
const formatDate = (date: Date) => { const formatDate = (date: Date) => {

View File

@@ -1,6 +1,13 @@
"use client"; "use client";
import * as React from "react"; import * as React from "react";
import type {
LegendPayload as RechartsLegendPayload,
LegendProps as RechartsLegendProps,
TooltipContentProps as RechartsTooltipContentProps,
TooltipPayloadEntry,
TooltipValueType,
} from "recharts";
import * as RechartsPrimitive from "recharts"; import * as RechartsPrimitive from "recharts";
import { cn } from "@/shared/utils/ui"; import { cn } from "@/shared/utils/ui";
@@ -134,7 +141,7 @@ function ChartTooltipContent({
color, color,
nameKey, nameKey,
labelKey, labelKey,
}: React.ComponentProps<typeof RechartsPrimitive.Tooltip> & }: Partial<RechartsTooltipContentProps<TooltipValueType, string | number>> &
React.ComponentProps<"div"> & { React.ComponentProps<"div"> & {
hideLabel?: boolean; hideLabel?: boolean;
hideIndicator?: boolean; hideIndicator?: boolean;
@@ -197,21 +204,21 @@ function ChartTooltipContent({
<div className="grid gap-1.5"> <div className="grid gap-1.5">
{payload {payload
.filter((item) => item.type !== "none") .filter((item) => item.type !== "none")
.map((item, index) => { .map((item: TooltipPayloadEntry, index) => {
const key = `${nameKey || item.name || item.dataKey || "value"}`; const key = `${nameKey || item.name || item.dataKey || "value"}`;
const itemConfig = getPayloadConfigFromPayload(config, item, key); const itemConfig = getPayloadConfigFromPayload(config, item, key);
const indicatorColor = color || item.payload.fill || item.color; const indicatorColor = color || item.payload.fill || item.color;
return ( return (
<div <div
key={item.dataKey} key={String(item.dataKey ?? item.name ?? index)}
className={cn( className={cn(
"[&>svg]:text-muted-foreground flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5", "[&>svg]:text-muted-foreground flex w-full flex-wrap items-stretch gap-2 [&>svg]:h-2.5 [&>svg]:w-2.5",
indicator === "dot" && "items-center", indicator === "dot" && "items-center",
)} )}
> >
{formatter && item?.value !== undefined && item.name ? ( {formatter && item?.value !== undefined && item.name ? (
formatter(item.value, item.name, item, index, item.payload) formatter(item.value, item.name, item, index, payload)
) : ( ) : (
<> <>
{itemConfig?.icon ? ( {itemConfig?.icon ? (
@@ -250,7 +257,7 @@ function ChartTooltipContent({
{itemConfig?.label || item.name} {itemConfig?.label || item.name}
</span> </span>
</div> </div>
{item.value && ( {item.value !== undefined && item.value !== null && (
<span className="text-foreground font-mono font-medium tabular-nums"> <span className="text-foreground font-mono font-medium tabular-nums">
{item.value.toLocaleString()} {item.value.toLocaleString()}
</span> </span>
@@ -274,12 +281,14 @@ function ChartLegendContent({
payload, payload,
verticalAlign = "bottom", verticalAlign = "bottom",
nameKey, nameKey,
}: React.ComponentProps<"div"> & }: React.ComponentProps<"div"> & {
Pick<RechartsPrimitive.LegendProps, "payload" | "verticalAlign"> & { payload?: ReadonlyArray<RechartsLegendPayload>;
hideIcon?: boolean; verticalAlign?: RechartsLegendProps["verticalAlign"];
nameKey?: string; hideIcon?: boolean;
}) { nameKey?: string;
}) {
const { config } = useChart(); const { config } = useChart();
type LegendPayloadEntry = RechartsLegendPayload;
if (!payload?.length) { if (!payload?.length) {
return null; return null;
@@ -294,14 +303,14 @@ function ChartLegendContent({
)} )}
> >
{payload {payload
.filter((item) => item.type !== "none") .filter((item: LegendPayloadEntry) => item.type !== "none")
.map((item) => { .map((item: LegendPayloadEntry) => {
const key = `${nameKey || item.dataKey || "value"}`; const key = `${nameKey || item.dataKey || "value"}`;
const itemConfig = getPayloadConfigFromPayload(config, item, key); const itemConfig = getPayloadConfigFromPayload(config, item, key);
return ( return (
<div <div
key={item.value} key={String(item.dataKey ?? item.value)}
className={cn( className={cn(
"[&>svg]:text-muted-foreground flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3", "[&>svg]:text-muted-foreground flex items-center gap-1.5 [&>svg]:h-3 [&>svg]:w-3",
)} )}

View File

@@ -63,6 +63,7 @@ export interface CurrencyInputProps
> { > {
value: string; value: string;
onValueChange: (value: string) => void; onValueChange: (value: string) => void;
onChange?: React.ComponentProps<typeof Input>["onChange"];
} }
export const CurrencyInput = React.forwardRef< export const CurrencyInput = React.forwardRef<

View File

@@ -1,7 +1,10 @@
import { RiLoader4Line } from "@remixicon/react"; import { RiLoader4Line } from "@remixicon/react";
import { cn } from "@/shared/utils/ui"; import { cn } from "@/shared/utils/ui";
function Spinner({ className, ...props }: React.ComponentProps<"svg">) { function Spinner({
className,
...props
}: React.ComponentProps<typeof RiLoader4Line>) {
return ( return (
<RiLoader4Line <RiLoader4Line
role="status" role="status"

View File

@@ -26,7 +26,7 @@ export type EligibleInstallment = {
* Antecipação com dados completos * Antecipação com dados completos
*/ */
export type InstallmentAnticipationWithRelations = InstallmentAnticipation & { export type InstallmentAnticipationWithRelations = InstallmentAnticipation & {
transaction: Transaction; transaction: Transaction | null;
payer: Payer | null; payer: Payer | null;
category: Category | null; category: Category | null;
}; };