frontend phase 11

This commit is contained in:
hamid
2026-07-10 13:55:42 +03:30
parent ccfa27aff6
commit 67c028562e
43 changed files with 3275 additions and 24 deletions
@@ -0,0 +1,68 @@
'use client';
import { FunctionComponent } from 'react';
import { useLocale, useTranslations } from 'next-intl';
import { Paper, Stack, Typography } from '@mui/material';
import StatusChip from '../StatusChip';
import { formatIrrToToman, formatShamsiDate } from '@/utils';
import { installmentStatusKind, type BnplInstallmentRow, type BnplInstallmentStatus } from '@/services/bnpl/types';
export interface InstallmentScheduleRowProps {
row: BnplInstallmentRow;
/** Render the provider-reported status chip (D5 Wallet). The D4 schedule preview omits it. */
showStatus?: boolean;
}
/** i18n key for each provider-reported installment status (labels are keys off the code, never derived). */
const STATUS_LABEL_KEY: Record<BnplInstallmentStatus, string> = {
paid: 'status_paid',
due_soon: 'status_due_soon',
upcoming: 'status_upcoming',
overdue: 'status_overdue',
};
/**
* One repayment row — the down-payment (due «امروز») or an installment (Shamsi due date), with the
* **served** amount (Toman via the money util). Reused by the D4 schedule table and the D5 Wallet due list;
* `showStatus` adds the provider-reported status chip (D5). No money is computed here — display only.
* @component InstallmentScheduleRow
*/
const InstallmentScheduleRow: FunctionComponent<InstallmentScheduleRowProps> = ({ row, showStatus = false }) => {
const t = useTranslations('bnpl');
const locale = useLocale();
const label =
row.kind === 'down_payment' ? t('down_payment') : t('installment_n', { n: row.sequence });
const dueLabel = row.kind === 'down_payment' ? t('today') : formatShamsiDate(`${row.dueDate}T00:00:00`, locale);
return (
<Paper
elevation={0}
data-row-kind={row.kind}
sx={{ p: 1.5, borderRadius: 2, border: '1px solid', borderColor: 'divider' }}
>
<Stack direction="row" sx={{ justifyContent: 'space-between', alignItems: 'center', gap: 1.5 }}>
<Stack sx={{ gap: 0.25 }}>
<Typography variant="body2" sx={{ fontWeight: 700 }}>
{label}
</Typography>
<Typography variant="caption" sx={{ color: 'text.secondary' }}>
{dueLabel}
</Typography>
</Stack>
<Stack sx={{ alignItems: 'flex-end', gap: 0.5 }}>
<Typography
variant="body2"
sx={{ fontWeight: 700, color: row.kind === 'down_payment' ? 'var(--bal-secondary)' : undefined }}
>
{formatIrrToToman(row.amountIrr, locale)}
</Typography>
{showStatus && row.status ? (
<StatusChip status={installmentStatusKind(row.status)} label={t(STATUS_LABEL_KEY[row.status])} />
) : null}
</Stack>
</Stack>
</Paper>
);
};
export default InstallmentScheduleRow;