130 lines
5.4 KiB
TypeScript
130 lines
5.4 KiB
TypeScript
'use client';
|
|
import { FunctionComponent } from 'react';
|
|
import { useLocale, useTranslations } from 'next-intl';
|
|
import { Box, Paper, Skeleton, Stack, Typography } from '@mui/material';
|
|
import { AppButton, AppIcon, InstallmentScheduleRow, Money, PlaceholderScreen } from '@/components';
|
|
import { formatShamsiDate } from '@/utils';
|
|
import { useWalletInstallments } from '@/services/bnpl';
|
|
import type { WalletInstallmentPlan } from '@/services/bnpl/types';
|
|
|
|
/**
|
|
* D5 · پیگیری اقساط — the Wallet «اقساط» section (active installment plans). It reads
|
|
* `useWalletInstallments` and renders **provider-reported** status: an outstanding-balance card
|
|
* (terracotta), the next-installment date + a provider hand-off «پرداخت زودهنگام» (early-pay is a
|
|
* *provider* action, never a Balinyaar transaction), the per-installment due list with status chips, and
|
|
* the ownership note (Balinyaar displays, it does not manage, this schedule). Section body only — the
|
|
* page-level heading + tab strip live in `WalletScreen`.
|
|
*/
|
|
const WalletInstallments: FunctionComponent = () => {
|
|
const t = useTranslations('bnpl');
|
|
const tc = useTranslations('common');
|
|
const { data: plans, isLoading, isError, refetch } = useWalletInstallments();
|
|
|
|
return (
|
|
<Stack sx={{ gap: 2, width: '100%' }}>
|
|
{isLoading ? (
|
|
<Stack sx={{ gap: 1.5 }}>
|
|
<Skeleton variant="rounded" height={128} />
|
|
<Skeleton variant="rounded" height={56} />
|
|
<Skeleton variant="rounded" height={56} />
|
|
</Stack>
|
|
) : isError ? (
|
|
<Paper elevation={0} sx={{ p: 3, borderRadius: 'var(--bal-radius-md)', border: '1px solid', borderColor: 'divider', textAlign: 'center' }}>
|
|
<Stack sx={{ gap: 1, alignItems: 'center' }}>
|
|
<AppIcon icon="warning" size={36} color="var(--bal-warning)" />
|
|
<Typography variant="subtitle2" sx={{ fontWeight: 700 }}>
|
|
{t('wallet_error_title')}
|
|
</Typography>
|
|
<Typography variant="body2" sx={{ color: 'text.secondary' }}>
|
|
{t('wallet_error_body')}
|
|
</Typography>
|
|
<AppButton variant="outlined" color="secondary" onClick={() => refetch()} sx={{ mt: 1 }}>
|
|
{tc('retry')}
|
|
</AppButton>
|
|
</Stack>
|
|
</Paper>
|
|
) : !plans || plans.length === 0 ? (
|
|
<PlaceholderScreen icon="installments" title={t('wallet_empty_title')} description={t('wallet_empty_body')} />
|
|
) : (
|
|
<Stack sx={{ gap: 3 }}>
|
|
{plans.map((plan) => (
|
|
<InstallmentPlanSection key={plan.bnplTransactionId} plan={plan} />
|
|
))}
|
|
</Stack>
|
|
)}
|
|
</Stack>
|
|
);
|
|
};
|
|
|
|
function InstallmentPlanSection({ plan }: { plan: WalletInstallmentPlan }) {
|
|
const t = useTranslations('bnpl');
|
|
const locale = useLocale();
|
|
const providerName = t(`provider_${plan.providerCode}`);
|
|
|
|
const handleEarlyPay = () => {
|
|
// Early-pay is a PROVIDER action — hand off to the provider, never a Balinyaar payment.
|
|
if (plan.earlyPayUrl) window.open(plan.earlyPayUrl, '_blank', 'noopener,noreferrer');
|
|
};
|
|
|
|
return (
|
|
<Stack sx={{ gap: 1.5 }}>
|
|
{/* Outstanding-balance card — terracotta financial accent; contrast text is scheme-stable. */}
|
|
<Paper
|
|
elevation={0}
|
|
sx={{ p: 2.25, borderRadius: 'var(--bal-radius-lg)', backgroundColor: 'var(--bal-secondary)', color: 'var(--bal-secondary-contrast)' }}
|
|
>
|
|
<Typography variant="caption" sx={{ opacity: 0.85 }}>
|
|
{t('outstanding_balance')}
|
|
</Typography>
|
|
<Money amountIrr={plan.outstandingBalanceIrr} size="lg" sx={{ fontWeight: 800, mt: 0.25 }} />
|
|
<Typography variant="caption" sx={{ opacity: 0.85, display: 'block', mt: 0.5 }}>
|
|
{plan.serviceLabel}
|
|
</Typography>
|
|
|
|
{plan.nextDueDate ? (
|
|
<Stack direction="row" sx={{ justifyContent: 'space-between', alignItems: 'flex-end', mt: 1.5, gap: 1.5 }}>
|
|
<Box>
|
|
<Typography variant="caption" sx={{ opacity: 0.85 }}>
|
|
{t('next_installment')} · {formatShamsiDate(`${plan.nextDueDate}T00:00:00`, locale)}
|
|
</Typography>
|
|
{plan.nextAmountIrr ? (
|
|
<Money amountIrr={plan.nextAmountIrr} size="sm" sx={{ fontWeight: 800 }} />
|
|
) : null}
|
|
</Box>
|
|
{plan.earlyPayUrl ? (
|
|
<AppButton
|
|
color="primary"
|
|
variant="contained"
|
|
size="small"
|
|
onClick={handleEarlyPay}
|
|
sx={{ flex: 'none' }}
|
|
>
|
|
{t('early_pay')}
|
|
</AppButton>
|
|
) : null}
|
|
</Stack>
|
|
) : null}
|
|
</Paper>
|
|
|
|
<Typography variant="subtitle2" sx={{ fontWeight: 700 }}>
|
|
{t('due_dates')}
|
|
</Typography>
|
|
<Stack sx={{ gap: 1 }}>
|
|
{plan.installments.map((row) => (
|
|
<InstallmentScheduleRow key={`${row.kind}-${row.sequence}`} row={row} showStatus />
|
|
))}
|
|
</Stack>
|
|
|
|
{/* Ownership note: Balinyaar displays, it does not manage, this provider-owned schedule. */}
|
|
<Stack direction="row" sx={{ gap: 0.75, alignItems: 'flex-start', px: 0.5 }}>
|
|
<AppIcon icon="info" size={16} color="var(--bal-text-secondary)" />
|
|
<Typography variant="caption" sx={{ color: 'text.secondary' }}>
|
|
{t('provider_owned_note', { provider: providerName })}
|
|
</Typography>
|
|
</Stack>
|
|
</Stack>
|
|
);
|
|
}
|
|
|
|
export default WalletInstallments;
|