Files
openmonetis/src/features/dashboard/invoices/invoices-helpers.ts
2026-05-31 15:18:43 -03:00

154 lines
3.9 KiB
TypeScript

import type { DashboardInvoice } from "@/features/dashboard/invoices/invoices-queries";
import type { PaymentDialogState } from "@/features/dashboard/payments/use-payment-dialog-controller";
import {
INVOICE_PAYMENT_STATUS,
type InvoicePaymentStatus,
} from "@/shared/lib/invoices";
import {
getBusinessDateString,
parseUtcDateString,
toDateOnlyString,
} from "@/shared/utils/date";
import {
buildDueDateInfoFromPeriodDay,
buildRelativeDueDateInfoFromPeriodDay,
formatFinancialDateLabel,
formatRelativeFinancialDateLabel,
} from "@/shared/utils/financial-dates";
import { formatPercentage } from "@/shared/utils/percentage";
import { formatPeriodForUrl } from "@/shared/utils/period";
export type InvoiceDialogState = PaymentDialogState;
export type InvoiceLogoTone = "muted" | "accent";
type InvoicePaymentDateInfo = {
label: string;
};
type InvoiceDueDateInfo = {
label: string;
date: string | null;
};
export const buildInvoiceInitials = (value: string) => {
const parts = value.trim().split(/\s+/).filter(Boolean);
if (parts.length === 0) {
return "CC";
}
if (parts.length === 1) {
const firstPart = parts[0];
return firstPart ? firstPart.slice(0, 2).toUpperCase() : "CC";
}
const firstChar = parts[0]?.[0] ?? "";
const secondChar = parts[1]?.[0] ?? "";
return `${firstChar}${secondChar}`.toUpperCase() || "CC";
};
export const parseInvoiceDueDate = (
period: string,
dueDay: string,
): InvoiceDueDateInfo => {
return buildDueDateInfoFromPeriodDay(period, dueDay);
};
export const parseInvoiceWidgetDueDate = (
period: string,
dueDay: string,
): InvoiceDueDateInfo => {
return buildRelativeDueDateInfoFromPeriodDay(period, dueDay);
};
export const formatInvoicePaymentDate = (
value: string | null,
): InvoicePaymentDateInfo | null => {
const label = formatFinancialDateLabel(value, "Pago em");
if (!label) {
return null;
}
return {
label,
};
};
export const formatInvoiceWidgetPaymentDate = (
value: string | null,
): InvoicePaymentDateInfo | null => {
const label = formatRelativeFinancialDateLabel(value, "paid");
if (!label) {
return null;
}
return {
label,
};
};
export const formatInvoiceWidgetOverdueLabel = (
value: string | null,
): string | null => {
const dueDateValue = toDateOnlyString(value);
const todayValue = getBusinessDateString();
if (!dueDateValue || dueDateValue >= todayValue) {
return null;
}
const dueDate = parseUtcDateString(dueDateValue);
const today = parseUtcDateString(todayValue);
if (!dueDate || !today) {
return null;
}
const overdueDays = Math.round(
(today.getTime() - dueDate.getTime()) / (24 * 60 * 60 * 1000),
);
return overdueDays === 1
? "Atrasada · venceu ontem"
: `Atrasada · venceu há ${overdueDays} dias`;
};
export const getCurrentDateString = () => getBusinessDateString();
const formatInvoiceSharePercentage = (value: number) => {
if (!Number.isFinite(value) || value <= 0) {
return "0%";
}
const digits = value >= 10 ? 0 : value >= 1 ? 1 : 2;
return formatPercentage(value, {
minimumFractionDigits: digits,
maximumFractionDigits: digits,
});
};
export const getInvoiceShareLabel = (amount: number, total: number) => {
if (total <= 0) {
return "0% do total";
}
const percentage = (amount / total) * 100;
return `${formatInvoiceSharePercentage(percentage)} do total`;
};
export const getInvoiceStatusBadgeVariant = (
statusLabel: string,
): "success" | "info" => {
if (statusLabel.toLowerCase() === "em aberto") {
return "info";
}
return "success";
};
export const buildInvoiceDetailsHref = (cardId: string, period: string) =>
`/cards/${cardId}/invoice?periodo=${formatPeriodForUrl(period)}`;
export const markInvoiceAsPaid = (
invoice: DashboardInvoice,
paidAt: string,
): DashboardInvoice => ({
...invoice,
paymentStatus: INVOICE_PAYMENT_STATUS.PAID,
paidAt,
});
export const isInvoicePaid = (status: InvoicePaymentStatus) =>
status === INVOICE_PAYMENT_STATUS.PAID;