Files
clinicpro/assets/admin/components/SessionPaymentAccordion.tsx
hamed 55ab2f5dfc Implement comprehensive dark/light mode overhaul for admin panel
- Refactor color palette in `ui-design-spec.md` to utilize CSS variables exclusively, eliminating fixed hex values and Tailwind utility classes.
- Complete dark mode implementation in `uiStore.ts`, ensuring proper theme application via `applyTheme()` and `applyBrand()`.
- Create `admin-theme-dark-light-audit.md` to document the transition process, outlining issues with inline styles and fixed colors.
- Introduce `theme-tokens.test.ts` to enforce rules against fixed hex colors and ensure compliance with the design system.
- Update various components and styles to replace inline styles and fixed colors with CSS variables, ensuring consistent theming across light and dark modes.
- Ensure all changes maintain visual integrity in both light and dark modes, with a focus on accessibility and contrast standards.
2026-07-27 16:41:52 +03:30

106 lines
4.8 KiB
TypeScript

import { ChevronDownIcon } from '@heroicons/react/24/outline';
import { formatDate, formatRial } from '../lib/utils';
import { PAYMENT_METHOD_LABELS } from '../lib/paymentMethods';
import { FilesServicePaymentsCheck } from './icons/FilesServiceIcons';
export interface SessionPaymentData {
uuid: string;
services?: Array<{ service_name?: string; name?: string }>;
visit_price_rials?: number;
doctor_name?: string | null;
final_price_rials?: number;
payment_method?: string;
is_paid?: boolean;
created_at?: number;
updated_at?: number;
}
const PAYMENT_LABELS = PAYMENT_METHOD_LABELS;
/** vertical hairline divider between meta columns (tauri MUI vertical Divider). */
function VDivider({ h = 24 }: { h?: number }) {
return <span style={{ width: 1, height: h, background: 'var(--surface-2)', flexShrink: 0, margin: '0 16px', alignSelf: 'center' }} />;
}
function Meta({ label, value }: { label: string; value: string }) {
return (
<span style={{ display: 'inline-flex', alignItems: 'center', gap: 6, fontSize: 13 }}>
<span style={{ color: 'var(--text-2)' }}>{label}:</span>
<span style={{ color: 'var(--text)', fontWeight: 500 }}>{value}</span>
</span>
);
}
/**
* A patient «پرداخت‌ها» accordion — one settled/unsettled مراجعه (session) —
* ported from tauri files/tabs/PaymentsSection. Header shows the service, date,
* final price and settlement badge; the body lists the session's settlement
* (real model = one payment_method per session), or the empty message.
*/
export default function SessionPaymentAccordion({ session, expanded, onToggle }: {
session: SessionPaymentData;
expanded: boolean;
onToggle: () => void;
}) {
const names = (session.services ?? []).map((s) => s.service_name || s.name).filter(Boolean) as string[];
if ((session.visit_price_rials ?? 0) > 0) names.unshift('ویزیت');
const name = names.length ? names.join(' - ') : 'ویزیت';
const paid = !!session.is_paid;
const finalPrice = formatRial(session.final_price_rials ?? 0);
return (
<div style={{ border: '1px solid var(--border)', borderRadius: 8, marginBottom: 16, overflow: 'hidden' }}>
{/* summary */}
<button
type="button"
onClick={onToggle}
aria-expanded={expanded}
className="bg-[var(--surface)]"
style={{ display: 'flex', alignItems: 'center', gap: 4, width: '100%', padding: '12px 16px', border: 'none', cursor: 'pointer', textAlign: 'start', flexWrap: 'wrap' }}
>
<span style={{ minWidth: 150, color: 'var(--text)', fontWeight: 500, fontSize: 14 }}>{name}</span>
<VDivider />
<Meta label="تاریخ" value={session.created_at ? formatDate(session.created_at) : '—'} />
<VDivider />
<Meta label="مبلغ نهایی" value={finalPrice} />
<span style={{ flex: 1 }} />
<VDivider />
<span style={{
color: 'var(--on-primary)', padding: '3px 16px', borderRadius: 4, fontSize: 12, fontWeight: 600,
textAlign: 'center', minWidth: 90, background: paid ? 'var(--success)' : 'var(--warning)',
}}>
{paid ? 'پرداخت شده' : 'تسویه نشده'}
</span>
<ChevronDownIcon style={{ width: 20, height: 20, color: 'var(--text)', marginInlineStart: 8, transition: 'transform .2s', transform: expanded ? 'rotate(180deg)' : 'none' }} />
</button>
{/* details */}
{expanded && (
<div className="dark:bg-[var(--surface)]" style={{ borderTop: '1px solid var(--border)' }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '12px 16px 8px' }}>
<FilesServicePaymentsCheck color="var(--text-2)" size={18} style={{ transform: 'rotate(180deg)' }} />
<span style={{ color: 'var(--text-2)', fontWeight: 600, fontSize: 13 }}>پرداختی‌ها</span>
</div>
<div style={{ padding: '0 16px 16px' }}>
{paid ? (
<div style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '12px 0', flexWrap: 'wrap' }}>
<FilesServicePaymentsCheck color="var(--text-2)" size={20} />
<Meta label="تاریخ" value={formatDate(session.updated_at ?? session.created_at ?? 0)} />
<VDivider h={32} />
<Meta label="مبلغ" value={finalPrice} />
<VDivider h={32} />
<Meta label="نحوه پرداخت" value={PAYMENT_LABELS[session.payment_method ?? ''] ?? session.payment_method ?? '—'} />
<VDivider h={32} />
<Meta label="پرسنل" value={session.doctor_name || '—'} />
</div>
) : (
<div style={{ padding: 16, textAlign: 'center', color: 'var(--text-2)', fontSize: 13 }}>هیچ پرداختی ثبت نشده است.</div>
)}
</div>
</div>
)}
</div>
);
}