- SessionServiceCard menu gains «ویرایش» and «تاریخچه تغییرات» items.
- SessionAuditModal shows the session's change history (field, op badge,
old->new, actor, time) from /session/{uuid}/audit-log.
- PaymentStep payment rows get edit (modal) + delete (confirm) controls for
non-wallet payments, calling the new PATCH/DELETE payment endpoints.
- CreateStep accepts an editSession prop (prefill + PATCH); EditSessionPage
reuses it at /patients/:recordUuid/session/:sessionUuid/edit.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
78 lines
3.9 KiB
TypeScript
78 lines
3.9 KiB
TypeScript
import { useQuery } from '@tanstack/react-query';
|
|
import { api } from '../../lib/api';
|
|
import type { ApiResponse } from '../../lib/api';
|
|
import type { SessionAuditEntry } from '../../types';
|
|
import { formatDateTime, formatRial } from '../../lib/utils';
|
|
import Modal from '../ui/Modal';
|
|
|
|
const FIELD_LABELS: Record<string, string> = {
|
|
visit_price_rials: 'قیمت ویزیت',
|
|
services: 'سرویسها',
|
|
consumables: 'کالاهای مصرفی',
|
|
services_total_rials: 'مجموع سرویسها',
|
|
final_price_rials: 'مبلغ نهایی',
|
|
payment: 'پرداخت',
|
|
discount: 'تخفیف',
|
|
};
|
|
|
|
const OP_LABELS: Record<string, { label: string; color: string }> = {
|
|
create: { label: 'ایجاد', color: '#3c9a4f' },
|
|
update: { label: 'ویرایش', color: '#5559ce' },
|
|
delete: { label: 'حذف', color: '#EF4444' },
|
|
};
|
|
|
|
// مقادیر پولی (ریال) را با فرمت تومان نشان بده؛ بقیه را خام.
|
|
const RIAL_FIELDS = new Set(['visit_price_rials', 'services_total_rials', 'final_price_rials', 'payment', 'discount']);
|
|
const fmtValue = (field: string, v: string | null): string => {
|
|
if (v === null || v === '') return '—';
|
|
if (RIAL_FIELDS.has(field) && /^\d+$/.test(v)) return formatRial(Number(v));
|
|
return v;
|
|
};
|
|
|
|
export default function SessionAuditModal({ sessionUuid, onClose }: { sessionUuid: string | null; onClose: () => void }) {
|
|
const { data, isLoading } = useQuery({
|
|
queryKey: ['session-audit-log', sessionUuid],
|
|
queryFn: () => api.get<ApiResponse<SessionAuditEntry[]>>(`/api/v1/session/${sessionUuid}/audit-log`),
|
|
enabled: !!sessionUuid,
|
|
});
|
|
const rows: SessionAuditEntry[] = (data?.data as any) ?? [];
|
|
|
|
if (!sessionUuid) return null;
|
|
|
|
return (
|
|
<Modal open title="تاریخچه تغییرات" size="lg" onClose={onClose}>
|
|
{isLoading ? (
|
|
<div style={{ padding: 16, color: 'var(--text-3)', fontSize: 13 }}>در حال بارگذاری...</div>
|
|
) : rows.length === 0 ? (
|
|
<div style={{ padding: 24, textAlign: 'center', color: 'var(--text-3)', fontSize: 14 }}>تغییری ثبت نشده است.</div>
|
|
) : (
|
|
<div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
|
|
{rows.map((r, i) => {
|
|
const op = OP_LABELS[r.operation] ?? { label: r.operation, color: 'var(--text-3)' };
|
|
return (
|
|
<div key={i} className="card" style={{ padding: 12, display: 'flex', flexDirection: 'column', gap: 6 }}>
|
|
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8 }}>
|
|
<span style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
|
<span className="badge" style={{ fontSize: 11, color: '#fff', background: op.color, borderRadius: 4, padding: '2px 8px' }}>{op.label}</span>
|
|
<span style={{ fontSize: 13, fontWeight: 600, color: 'var(--text)' }}>{FIELD_LABELS[r.field] ?? r.field}</span>
|
|
</span>
|
|
<span style={{ fontSize: 12, color: 'var(--text-3)' }}>{formatDateTime(r.created_at)}</span>
|
|
</div>
|
|
<div style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 13, flexWrap: 'wrap' }}>
|
|
<span style={{ color: 'var(--text-3)' }}>{fmtValue(r.field, r.old_value)}</span>
|
|
<span style={{ color: 'var(--text-3)' }}>←</span>
|
|
<span style={{ color: 'var(--text)', fontWeight: 600 }}>{fmtValue(r.field, r.new_value)}</span>
|
|
</div>
|
|
<div style={{ display: 'flex', gap: 12, fontSize: 12, color: 'var(--text-3)' }}>
|
|
{r.actor_name && <span>توسط: {r.actor_name}</span>}
|
|
{r.note && <span>{r.note}</span>}
|
|
</div>
|
|
</div>
|
|
);
|
|
})}
|
|
</div>
|
|
)}
|
|
</Modal>
|
|
);
|
|
}
|