import { RiBankCard2Line, RiBarcodeLine, RiWallet3Line, } from "@remixicon/react"; import { notFound } from "next/navigation"; import { connection } from "next/server"; import { PayerCardUsageCard } from "@/features/payers/components/details/payer-card-usage-card"; import { PayerHeaderCard } from "@/features/payers/components/details/payer-header-card"; import { PayerHistoryCard } from "@/features/payers/components/details/payer-history-card"; import { PagadorInfoCard } from "@/features/payers/components/details/payer-info-card"; import { PayerLeaveShareCard } from "@/features/payers/components/details/payer-leave-share-card"; import { PayerMonthlySummaryCard } from "@/features/payers/components/details/payer-monthly-summary-card"; import { PayerBoletoCard, PayerPaymentStatusCard, } from "@/features/payers/components/details/payer-payment-method-cards"; import { PayerSharingCard } from "@/features/payers/components/details/payer-sharing-card"; import { fetchCurrentUserShare, fetchPagadorLancamentos, fetchPayerShares, } from "@/features/payers/detail-queries"; import { buildReadOnlyOptionSets } from "@/features/payers/lib/build-readonly-option-sets"; import { fetchUserPreferences } from "@/features/settings/queries"; import { TransactionsPage as LancamentosSection } from "@/features/transactions/components/page/transactions-page"; import { buildOptionSets, buildSluggedFilters, buildSlugMaps, buildTransactionWhere, extractTransactionSearchFilters, getSingleParam, mapTransactionsData, type ResolvedSearchParams, type SluggedFilters, type SlugMaps, type TransactionSearchFilters, } from "@/features/transactions/page-helpers"; import { fetchRecentEstablishments, fetchTransactionFilterSources, } from "@/features/transactions/queries"; import { ExpandableWidgetCard } from "@/shared/components/expandable-widget-card"; import MonthNavigation from "@/shared/components/month-picker/month-navigation"; import { Tabs, TabsContent, TabsList, TabsTrigger, } from "@/shared/components/ui/tabs"; import { getUserId } from "@/shared/lib/auth/server"; import { getPayerAccess } from "@/shared/lib/payers/access"; import { fetchPagadorBoletoItems, fetchPagadorBoletoStats, fetchPagadorCardUsage, fetchPagadorPaymentStatus, fetchPayerHistory, fetchPayerMonthlyBreakdown, type PayerCardUsageItem, } from "@/shared/lib/payers/details"; import { parsePeriodParam } from "@/shared/utils/period"; type PageSearchParams = Promise; type PageProps = { params: Promise<{ payerId: string }>; searchParams?: PageSearchParams; }; const capitalize = (value: string) => value.length ? value.charAt(0).toUpperCase().concat(value.slice(1)) : value; const EMPTY_FILTERS: TransactionSearchFilters = { transactionFilter: null, conditionFilter: null, paymentFilter: null, payerFilter: null, categoryFilter: null, accountCardFilter: null, searchFilter: null, }; const createEmptySlugMaps = (): SlugMaps => ({ payer: new Map(), category: new Map(), financialAccount: new Map(), card: new Map(), }); type OptionSet = ReturnType; export default async function Page({ params, searchParams }: PageProps) { await connection(); const { payerId } = await params; const userId = await getUserId(); const resolvedSearchParams = searchParams ? await searchParams : undefined; const access = await getPayerAccess(userId, payerId); if (!access) { notFound(); } const { pagador, canEdit } = access; const dataOwnerId = pagador.userId; const periodoParamRaw = getSingleParam(resolvedSearchParams, "periodo"); const { period: selectedPeriod, monthName, year, } = parsePeriodParam(periodoParamRaw); const periodLabel = `${capitalize(monthName)} de ${year}`; const allSearchFilters = extractTransactionSearchFilters(resolvedSearchParams); const searchFilters = canEdit ? allSearchFilters : { ...EMPTY_FILTERS, searchFilter: allSearchFilters.searchFilter, // Permitir busca mesmo em modo read-only }; let filterSources: Awaited< ReturnType > | null = null; let loggedUserFilterSources: Awaited< ReturnType > | null = null; let sluggedFilters: SluggedFilters; let slugMaps: SlugMaps; if (canEdit) { filterSources = await fetchTransactionFilterSources(dataOwnerId); sluggedFilters = buildSluggedFilters(filterSources); slugMaps = buildSlugMaps(sluggedFilters); } else { // Buscar opções do usuário logado para usar ao importar loggedUserFilterSources = await fetchTransactionFilterSources(userId); sluggedFilters = { payerFiltersRaw: [], categoryFiltersRaw: [], accountFiltersRaw: [], cardFiltersRaw: [], }; slugMaps = createEmptySlugMaps(); } const filters = buildTransactionWhere({ userId: dataOwnerId, period: selectedPeriod, filters: searchFilters, slugMaps, payerId: pagador.id, }); const sharesPromise = canEdit ? fetchPayerShares(pagador.id) : Promise.resolve([]); const currentUserSharePromise = !canEdit ? fetchCurrentUserShare(pagador.id, userId) : Promise.resolve(null); const [ transactionRows, monthlyBreakdown, historyData, cardUsage, boletoStats, boletoItems, paymentStatus, shareRows, currentUserShare, estabelecimentos, userPreferences, ] = await Promise.all([ fetchPagadorLancamentos(filters), fetchPayerMonthlyBreakdown({ userId: dataOwnerId, payerId: pagador.id, period: selectedPeriod, }), fetchPayerHistory({ userId: dataOwnerId, payerId: pagador.id, period: selectedPeriod, }), fetchPagadorCardUsage({ userId: dataOwnerId, payerId: pagador.id, period: selectedPeriod, }), fetchPagadorBoletoStats({ userId: dataOwnerId, payerId: pagador.id, period: selectedPeriod, }), fetchPagadorBoletoItems({ userId: dataOwnerId, payerId: pagador.id, period: selectedPeriod, }), fetchPagadorPaymentStatus({ userId: dataOwnerId, payerId: pagador.id, period: selectedPeriod, }), sharesPromise, currentUserSharePromise, fetchRecentEstablishments(userId), fetchUserPreferences(userId), ]); const mappedTransactions = mapTransactionsData(transactionRows); const transactionData = canEdit ? mappedTransactions : mappedTransactions.map((item) => ({ ...item, readonly: true })); const payerSharesData = shareRows; let optionSets: OptionSet; let loggedUserOptionSets: OptionSet | null = null; let effectiveSluggedFilters = sluggedFilters; if (canEdit && filterSources) { optionSets = buildOptionSets({ ...sluggedFilters, payerRows: filterSources.payerRows, }); } else { effectiveSluggedFilters = { payerFiltersRaw: [ { id: pagador.id, label: pagador.name, slug: pagador.id, role: pagador.role, avatarUrl: pagador.avatarUrl, }, ], categoryFiltersRaw: [], accountFiltersRaw: [], cardFiltersRaw: [], }; optionSets = buildReadOnlyOptionSets(transactionData, pagador); // Construir opções do usuário logado para usar ao importar if (loggedUserFilterSources) { const loggedUserSluggedFilters = buildSluggedFilters( loggedUserFilterSources, ); loggedUserOptionSets = buildOptionSets({ ...loggedUserSluggedFilters, payerRows: loggedUserFilterSources.payerRows, }); } } const payerSlug = effectiveSluggedFilters.payerFiltersRaw.find( (item) => item.id === pagador.id, )?.slug ?? null; const payerFilterOptions = payerSlug ? optionSets.payerFilterOptions.filter( (option) => option.slug === payerSlug, ) : optionSets.payerFilterOptions; const payerData = { id: pagador.id, name: pagador.name, email: pagador.email ?? null, avatarUrl: pagador.avatarUrl ?? null, status: pagador.status, note: pagador.note ?? null, role: pagador.role ?? null, isAutoSend: pagador.isAutoSend ?? false, createdAt: pagador.createdAt ? pagador.createdAt.toISOString() : new Date().toISOString(), lastMailAt: pagador.lastMailAt ? pagador.lastMailAt.toISOString() : null, shareCode: canEdit ? pagador.shareCode : null, canEdit, }; const summaryPreview = { periodLabel, totalExpenses: monthlyBreakdown.totalExpenses, paymentSplits: monthlyBreakdown.paymentSplits, cardUsage: cardUsage.slice(0, 3).map((item: PayerCardUsageItem) => ({ name: item.name, amount: item.amount, })), boletoStats: { totalAmount: boletoStats.totalAmount, paidAmount: boletoStats.paidAmount, pendingAmount: boletoStats.pendingAmount, paidCount: boletoStats.paidCount, pendingCount: boletoStats.pendingCount, }, lancamentoCount: transactionData.length, }; return (
Perfil Painel Lançamentos {canEdit && payerData.shareCode ? ( ) : null} {!canEdit && currentUserShare ? ( ) : null}
} > } > } >
); }