"use client"; import { Button } from "@/shared/components/ui/button"; import { Checkbox } from "@/shared/components/ui/checkbox"; import { CurrencyInput } from "@/shared/components/ui/currency-input"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/shared/components/ui/dialog"; import { Input } from "@/shared/components/ui/input"; import { formatCurrency } from "@/shared/utils/currency"; import { safeToNumber } from "@/shared/utils/number"; import { cn } from "@/shared/utils/ui"; import { PayerSelectContent } from "../../select-items"; import type { FormState } from "./transaction-dialog-types"; const splitRowClassName = "grid min-h-[2rem] items-center gap-2 rounded-lg border p-1.5 transition-colors sm:grid-cols-[minmax(0,1fr)_7rem_5.5rem]"; const splitDisabledFieldClassName = "hidden h-9 rounded-md border border-transparent sm:block"; type SplitConfigDialogProps = { open: boolean; onOpenChange: (open: boolean) => void; formState: FormState; onFieldChange: ( key: Key, value: FormState[Key], ) => void; payerOptions: Array<{ value: string; label: string; role?: string | null; avatarUrl?: string | null; }>; splitPayerOptions: Array<{ value: string; label: string; avatarUrl?: string | null; }>; totalAmount: number; }; const getPercentValue = (amount: string, totalAmount: number) => { if (totalAmount <= 0) return "0%"; const percentage = (safeToNumber(amount) / totalAmount) * 100; return percentage.toLocaleString("pt-BR", { maximumFractionDigits: 1, }); }; const percentToAmount = (percent: string, totalAmount: number) => { const normalized = percent.replace(/[^\d.,]/g, "").replace(",", "."); const percentage = Number(normalized); if (!Number.isFinite(percentage) || totalAmount <= 0) return "0.00"; const clamped = Math.min(100, Math.max(0, percentage)); return ((totalAmount * clamped) / 100).toFixed(2); }; const getEqualAmounts = (count: number, totalAmount: number) => { if (count <= 0 || totalAmount <= 0) return []; const centsTotal = Math.round(totalAmount * 100); const baseCents = Math.floor(centsTotal / count); let remainder = centsTotal - baseCents * count; return Array.from({ length: count }, () => { const cents = baseCents + (remainder > 0 ? 1 : 0); remainder -= 1; return (cents / 100).toFixed(2); }); }; type SplitSummaryPayerOption = { value: string; label: string; avatarUrl?: string | null; }; export function getSplitSummaryData( formState: FormState, payerOptions: SplitSummaryPayerOption[], totalAmount: number, ) { if (!formState.isSplit) { return { type: "text" as const, label: "Atribuir partes do valor a outras pessoas.", }; } const participants = [ formState.payerId, ...formState.splitShares.map((share) => share.payerId), ].filter(Boolean); if (participants.length <= 1) { return { type: "text" as const, label: "Configure as pessoas e os valores da divisão.", }; } const total = safeToNumber(formState.primarySplitAmount) + formState.splitShares.reduce( (sum, share) => sum + safeToNumber(share.amount), 0, ); const displayedParticipants = participants .slice(0, 3) .map((payerId) => payerOptions.find((option) => option.value === payerId)) .filter(Boolean) .map((option) => ({ label: option?.label ?? "", firstName: option?.label.split(/\s+/)[0] ?? "", avatarUrl: option?.avatarUrl ?? null, })); const remainingCount = Math.max(0, participants.length - 3); const totalLabel = Math.abs(total - totalAmount) <= 0.01 ? formatCurrency(totalAmount) : `${formatCurrency(total)} de ${formatCurrency(totalAmount)}`; return { type: "split" as const, count: participants.length, participants: displayedParticipants, remainingCount, totalLabel, }; } export function SplitConfigDialog({ open, onOpenChange, formState, onFieldChange, payerOptions, splitPayerOptions, totalAmount, }: SplitConfigDialogProps) { const selectedSplitIds = new Set( formState.splitShares.map((share) => share.payerId), ); const availableSplitOptions = splitPayerOptions.filter( (option) => option.value !== formState.payerId, ); const primaryPayerOption = payerOptions.find((option) => option.value === formState.payerId) ?? payerOptions.find((option) => option.role === "admin") ?? null; const splitTotal = safeToNumber(formState.primarySplitAmount) + formState.splitShares.reduce( (total, share) => total + safeToNumber(share.amount), 0, ); const splitDifference = totalAmount - splitTotal; const hasSplitDifference = Math.abs(splitDifference) > 0.01; const splitDifferenceLabel = splitDifference > 0 ? `Faltam ${formatCurrency(splitDifference)}` : `Sobram ${formatCurrency(Math.abs(splitDifference))}`; const applyEqualSplit = (shares = formState.splitShares) => { const participantCount = (formState.payerId ? 1 : 0) + shares.length; const amounts = getEqualAmounts(participantCount, totalAmount); if (amounts.length === 0) return; onFieldChange("primarySplitAmount", amounts[0] ?? "0.00"); onFieldChange( "splitShares", shares.map((share, index) => ({ ...share, amount: amounts[index + 1] ?? "0.00", })), ); }; const toggleSplitPayer = (payerId: string, checked: boolean) => { const nextShares = checked ? [...formState.splitShares, { payerId, amount: "0.00" }] : formState.splitShares.filter((share) => share.payerId !== payerId); applyEqualSplit(nextShares); }; const handleSecondaryAmountChange = (payerId: string, value: string) => { const nextShares = formState.splitShares.map((share) => share.payerId === payerId ? { ...share, amount: value } : share, ); const othersTotal = nextShares.reduce( (total, share) => total + safeToNumber(share.amount), 0, ); onFieldChange("splitShares", nextShares); onFieldChange( "primarySplitAmount", Math.max(0, totalAmount - othersTotal).toFixed(2), ); }; const handleSecondaryPercentChange = (payerId: string, percent: string) => { handleSecondaryAmountChange(payerId, percentToAmount(percent, totalAmount)); }; const handleDisableSplit = () => { onFieldChange("isSplit", false); onOpenChange(false); }; const renderPercentInput = ( amount: string, onPercentChange: (percent: string) => void, ariaLabel: string, ) => (
onPercentChange(event.target.value)} placeholder="0" aria-label={ariaLabel} className="h-9 pr-7 text-sm" /> %
); return ( Dividir lançamento Marque as pessoas e ajuste os valores se precisar.

{formatCurrency(splitTotal)} de {formatCurrency(totalAmount)}

{hasSplitDifference ? splitDifferenceLabel : "Tudo certo"}

{primaryPayerOption ? (
onFieldChange("primarySplitAmount", value) } placeholder="R$ 0,00" aria-label={`Valor de ${primaryPayerOption.label}`} className="h-9 text-sm" /> {renderPercentInput( formState.primarySplitAmount, (percent) => onFieldChange( "primarySplitAmount", percentToAmount(percent, totalAmount), ), `Percentual de ${primaryPayerOption.label}`, )}
) : null} {availableSplitOptions.map((option) => { const isSelected = selectedSplitIds.has(option.value); const share = formState.splitShares.find( (item) => item.payerId === option.value, ); return (
{isSelected && share ? ( <> handleSecondaryAmountChange(option.value, value) } placeholder="R$ 0,00" aria-label={`Valor de ${option.label}`} className="h-9 text-sm" /> {renderPercentInput( share.amount, (percent) => handleSecondaryPercentChange(option.value, percent), `Percentual de ${option.label}`, )} ) : ( <>
)}
); })}
); }