mirror of
https://github.com/felipegcoutinho/openmonetis.git
synced 2026-05-09 11:01:45 +00:00
fix(financeiro): alinhar saldo, métricas e relatórios
This commit is contained in:
@@ -1,36 +1,48 @@
|
||||
import { RiInformationLine } from "@remixicon/react";
|
||||
import {
|
||||
Card,
|
||||
CardFooter,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/shared/components/ui/card";
|
||||
import { Separator } from "@/shared/components/ui/separator";
|
||||
import { Skeleton } from "@/shared/components/ui/skeleton";
|
||||
|
||||
export function DashboardMetricsCardsSkeleton() {
|
||||
return (
|
||||
<div className="grid grid-cols-1 gap-3 @xl/main:grid-cols-2 @5xl/main:grid-cols-4">
|
||||
{Array.from({ length: 4 }).map((_, index) => (
|
||||
<Card
|
||||
key={index}
|
||||
className="@container/card min-h-36 justify-between gap-0"
|
||||
>
|
||||
<CardHeader className="gap-4 pb-3">
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
<Skeleton className="size-8 rounded-md bg-foreground/10" />
|
||||
<Skeleton className="h-4 w-24 rounded-md bg-foreground/10" />
|
||||
</CardTitle>
|
||||
<div className="flex flex-wrap items-end justify-between gap-3">
|
||||
<Card key={index} className="gap-2 overflow-hidden">
|
||||
<CardHeader>
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="w-full">
|
||||
<CardTitle className="flex items-center gap-1.5 tracking-tight">
|
||||
<Skeleton className="size-4 rounded-sm bg-foreground/10" />
|
||||
<Skeleton className="h-4 w-24 rounded-md bg-foreground/10" />
|
||||
<RiInformationLine
|
||||
className="size-4 text-muted-foreground/40"
|
||||
aria-hidden
|
||||
/>
|
||||
</CardTitle>
|
||||
<CardDescription className="mt-1.5 tracking-tight">
|
||||
<Skeleton className="h-3 w-32 rounded-md bg-foreground/10" />
|
||||
</CardDescription>
|
||||
</div>
|
||||
</div>
|
||||
<Separator className="mt-1" />
|
||||
</CardHeader>
|
||||
|
||||
<CardContent className="flex flex-col gap-3">
|
||||
<div className="mt-1 flex flex-wrap items-center justify-between gap-2">
|
||||
<Skeleton className="h-10 w-36 rounded-md bg-foreground/10" />
|
||||
<Skeleton className="h-7 w-20 rounded-full bg-foreground/10" />
|
||||
</div>
|
||||
</CardHeader>
|
||||
|
||||
<CardFooter className="items-start pt-0">
|
||||
<div className="flex flex-col items-start gap-1.5">
|
||||
<div className="flex flex-col gap-1.5">
|
||||
<Skeleton className="h-4 w-28 rounded-md bg-foreground/10" />
|
||||
<Skeleton className="h-3 w-24 rounded-md bg-foreground/10" />
|
||||
<Skeleton className="h-4 w-20 rounded-md bg-foreground/10" />
|
||||
</div>
|
||||
</CardFooter>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
|
||||
9
src/shared/lib/accounts/query-filters.ts
Normal file
9
src/shared/lib/accounts/query-filters.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { eq, isNull, or, sql } from "drizzle-orm";
|
||||
import { financialAccounts, transactions } from "@/db/schema";
|
||||
|
||||
export const excludeTransactionsFromExcludedAccounts = () =>
|
||||
or(
|
||||
isNull(transactions.accountId),
|
||||
isNull(financialAccounts.excludeFromBalance),
|
||||
eq(financialAccounts.excludeFromBalance, false),
|
||||
) ?? sql`true`;
|
||||
@@ -1,6 +1,9 @@
|
||||
import {
|
||||
buildDateOnlyStringFromPeriodDay,
|
||||
formatDateOnlyLabel,
|
||||
getBusinessDateString,
|
||||
parseUtcDateString,
|
||||
toDateOnlyString,
|
||||
} from "@/shared/utils/date";
|
||||
|
||||
type FinancialStatusLabelInput = {
|
||||
@@ -16,6 +19,8 @@ type FinancialDueDateInfo = {
|
||||
date: string | null;
|
||||
};
|
||||
|
||||
type RelativeFinancialDateContext = "due" | "paid";
|
||||
|
||||
export function formatFinancialDateLabel(
|
||||
value: string | null,
|
||||
prefix?: string,
|
||||
@@ -24,6 +29,63 @@ export function formatFinancialDateLabel(
|
||||
return formatDateOnlyLabel(value, prefix, options);
|
||||
}
|
||||
|
||||
function getOffsetDateString(
|
||||
referenceDate: string,
|
||||
offset: number,
|
||||
): string | null {
|
||||
const parsedReference = parseUtcDateString(referenceDate);
|
||||
if (!parsedReference) {
|
||||
return null;
|
||||
}
|
||||
|
||||
parsedReference.setUTCDate(parsedReference.getUTCDate() + offset);
|
||||
return toDateOnlyString(parsedReference);
|
||||
}
|
||||
|
||||
export function formatRelativeFinancialDateLabel(
|
||||
value: string | null,
|
||||
context: RelativeFinancialDateContext,
|
||||
options?: {
|
||||
referenceDate?: string | Date | null;
|
||||
},
|
||||
): string | null {
|
||||
const normalizedValue = toDateOnlyString(value);
|
||||
if (!normalizedValue) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const referenceDate =
|
||||
toDateOnlyString(options?.referenceDate) ?? getBusinessDateString();
|
||||
const yesterday = getOffsetDateString(referenceDate, -1);
|
||||
const tomorrow = getOffsetDateString(referenceDate, 1);
|
||||
|
||||
if (context === "due") {
|
||||
if (normalizedValue === referenceDate) {
|
||||
return "Vence hoje";
|
||||
}
|
||||
|
||||
if (normalizedValue === tomorrow) {
|
||||
return "Vence amanhã";
|
||||
}
|
||||
|
||||
if (normalizedValue === yesterday) {
|
||||
return "Venceu ontem";
|
||||
}
|
||||
|
||||
return formatFinancialDateLabel(normalizedValue, "Vence em");
|
||||
}
|
||||
|
||||
if (normalizedValue === referenceDate) {
|
||||
return "Pago hoje";
|
||||
}
|
||||
|
||||
if (normalizedValue === yesterday) {
|
||||
return "Pago ontem";
|
||||
}
|
||||
|
||||
return formatFinancialDateLabel(normalizedValue, "Pago em");
|
||||
}
|
||||
|
||||
export function buildFinancialStatusLabel({
|
||||
isSettled,
|
||||
dueDate,
|
||||
@@ -38,6 +100,18 @@ export function buildFinancialStatusLabel({
|
||||
return formatFinancialDateLabel(dueDate, duePrefix);
|
||||
}
|
||||
|
||||
export function buildRelativeFinancialStatusLabel({
|
||||
isSettled,
|
||||
dueDate,
|
||||
paidAt,
|
||||
}: FinancialStatusLabelInput): string | null {
|
||||
if (isSettled) {
|
||||
return formatRelativeFinancialDateLabel(paidAt, "paid");
|
||||
}
|
||||
|
||||
return formatRelativeFinancialDateLabel(dueDate, "due");
|
||||
}
|
||||
|
||||
export function buildDueDateInfoFromPeriodDay(
|
||||
period: string,
|
||||
dueDay: string,
|
||||
@@ -64,3 +138,28 @@ export function buildDueDateInfoFromPeriodDay(
|
||||
date: dueDate,
|
||||
};
|
||||
}
|
||||
|
||||
export function buildRelativeDueDateInfoFromPeriodDay(
|
||||
period: string,
|
||||
dueDay: string,
|
||||
options?: {
|
||||
fallbackPrefix?: string;
|
||||
},
|
||||
): FinancialDueDateInfo {
|
||||
const fallbackPrefix = options?.fallbackPrefix ?? "Vence dia";
|
||||
const dueDate = buildDateOnlyStringFromPeriodDay(period, dueDay);
|
||||
|
||||
if (!dueDate) {
|
||||
return {
|
||||
label: `${fallbackPrefix} ${dueDay}`,
|
||||
date: null,
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
label:
|
||||
formatRelativeFinancialDateLabel(dueDate, "due") ??
|
||||
`${fallbackPrefix} ${dueDay}`,
|
||||
date: dueDate,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user