ui phase 11
This commit is contained in:
@@ -17,7 +17,7 @@ import {
|
||||
TextField,
|
||||
Typography,
|
||||
} from '@mui/material';
|
||||
import { AppButton, Money, StatusChip } from '@/components';
|
||||
import { AppButton, JalaliDateField, Money, StatusChip } from '@/components';
|
||||
import type { StatusKind } from '@/components';
|
||||
import {
|
||||
AdminDataTable,
|
||||
@@ -30,7 +30,7 @@ import {
|
||||
import type { AdminTableColumn } from '@/components/admin';
|
||||
import { useAdminCapabilities } from '@/hooks';
|
||||
import { adminPayoutBatchPath } from '@/constants';
|
||||
import { formatShamsiDate } from '@/utils';
|
||||
import { formatNumber, formatShamsiDate } from '@/utils';
|
||||
import { PAYOUTS_PAGE_SIZE } from '@/services/payouts/constants';
|
||||
import { usePayoutBatches, usePreviewPayoutBatch, useRunPayoutBatch } from '@/services/payouts';
|
||||
import type { PayoutBatchStatus, PayoutBatchSummary } from '@/services/payouts/types';
|
||||
@@ -52,8 +52,17 @@ const BATCH_STATUSES: readonly PayoutBatchStatus[] = [
|
||||
'failed',
|
||||
];
|
||||
|
||||
/** UTC ISO date (`YYYY-MM-DD`) — the wire shape for the batch window. */
|
||||
const isoDate = (d: Date): string => d.toISOString().slice(0, 10);
|
||||
/**
|
||||
* ISO date (`YYYY-MM-DD`) — the wire shape for the batch window. Formats using the browser's **local**
|
||||
* date fields, never `toISOString()` (which converts to UTC first): near Tehran local midnight that would
|
||||
* silently roll the date back/forward a day from the admin's actual wall-clock date.
|
||||
*/
|
||||
const isoDate = (d: Date): string => {
|
||||
const y = d.getFullYear();
|
||||
const m = String(d.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(d.getDate()).padStart(2, '0');
|
||||
return `${y}-${m}-${day}`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Admin payout-batch dashboard (f15) — the reconciliation list of weekly `nurse_payout_batches` and the
|
||||
@@ -173,7 +182,7 @@ export default function AdminPayoutsPage() {
|
||||
onNext={() => setPage((p) => Math.min(pageCount, p + 1))}
|
||||
prevLabel={t('prev_page')}
|
||||
nextLabel={t('next_page')}
|
||||
indicator={t('page_indicator', { page })}
|
||||
indicator={t('page_indicator', { page, total: pageCount })}
|
||||
/>
|
||||
|
||||
{previewOpen ? <PreviewBatchDialog canPayout={caps.canPayout} onClose={() => setPreviewOpen(false)} /> : null}
|
||||
@@ -230,22 +239,13 @@ function PreviewBatchDialog({ canPayout, onClose }: { canPayout: boolean; onClos
|
||||
<DialogTitle sx={{ fontWeight: 800 }}>{t('payout_preview_title')}</DialogTitle>
|
||||
<DialogContent>
|
||||
<Stack direction="row" sx={{ gap: 1.5, mt: 1, flexWrap: 'wrap', alignItems: 'center' }}>
|
||||
<TextField
|
||||
type="date"
|
||||
<JalaliDateField
|
||||
size="small"
|
||||
label={t('payout_period_start')}
|
||||
value={periodStart}
|
||||
onChange={(e) => setPeriodStart(e.target.value)}
|
||||
slotProps={{ inputLabel: { shrink: true } }}
|
||||
/>
|
||||
<TextField
|
||||
type="date"
|
||||
size="small"
|
||||
label={t('payout_period_end')}
|
||||
value={periodEnd}
|
||||
onChange={(e) => setPeriodEnd(e.target.value)}
|
||||
slotProps={{ inputLabel: { shrink: true } }}
|
||||
onChange={setPeriodStart}
|
||||
/>
|
||||
<JalaliDateField size="small" label={t('payout_period_end')} value={periodEnd} onChange={setPeriodEnd} />
|
||||
<AppButton
|
||||
variant="outlined"
|
||||
color="primary"
|
||||
@@ -361,12 +361,41 @@ function PreviewBatchDialog({ canPayout, onClose }: { canPayout: boolean; onClos
|
||||
<ConfirmDialog
|
||||
open={runConfirmOpen}
|
||||
title={t('payout_run_confirm_title')}
|
||||
body={t('payout_run_confirm_body')}
|
||||
body={
|
||||
result ? (
|
||||
<Stack sx={{ gap: 1.5, mt: 0.5 }}>
|
||||
<Typography variant="body2" sx={{ color: 'text.secondary' }}>
|
||||
{t('payout_run_summary_intro')}
|
||||
</Typography>
|
||||
<Money amountIrr={result.totalNetIrr} size="lg" tone="emphasis" />
|
||||
<Stack sx={{ gap: 0.5 }}>
|
||||
<Stack direction="row" sx={{ justifyContent: 'space-between', gap: 1 }}>
|
||||
<Typography variant="body2" sx={{ color: 'text.secondary' }}>
|
||||
{t('payout_run_count_label')}
|
||||
</Typography>
|
||||
<Typography variant="body2" sx={{ fontWeight: 700 }}>
|
||||
{formatNumber(result.eligible.length, locale)}
|
||||
</Typography>
|
||||
</Stack>
|
||||
<Stack direction="row" sx={{ justifyContent: 'space-between', gap: 1 }}>
|
||||
<Typography variant="body2" sx={{ color: 'text.secondary' }}>
|
||||
{t('payout_col_processing')}
|
||||
</Typography>
|
||||
<Typography variant="body2" sx={{ fontWeight: 700 }}>
|
||||
{formatShamsiDate(result.processingDate, locale)}
|
||||
</Typography>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Stack>
|
||||
) : null
|
||||
}
|
||||
confirmLabel={t('payout_run')}
|
||||
cancelLabel={t('cancel')}
|
||||
onConfirm={onRunConfirm}
|
||||
onClose={() => setRunConfirmOpen(false)}
|
||||
loading={run.isPending}
|
||||
requireTypedConfirmation={result ? ['تایید', result.totalNetIrr] : []}
|
||||
typedConfirmationLabel={t('payout_run_type_to_confirm')}
|
||||
/>
|
||||
</Dialog>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user