Files
clinicpro/assets/admin/components/appointments/TurnsStatInfo.tsx
T
hamedandClaude Opus 4.8 a6ae6220cb feat: redesign appointments (نوبت‌ها) admin UI to match tauri turns + expandable sidebar
Rebuild the /admin/appointments page visual layer to match the tauri
clinic-pro-tauri "turns" design while keeping all existing data wiring and
backend endpoints unchanged (add/edit/move/transfer-reserve/replace already
supported via PATCH /api/v1/appointment/{uuid} and POST /api/v1/my/appointment).

Frontend (assets/admin):
- Sidebar: نوبت‌ها becomes an expandable parent with sub-items
  «نوبت های تایید شده» (/admin/appointments) and «افزودن نوبت»
  (/admin/appointments/new); auto-expands on active child. Applied to
  admin/clinic/doctor/secretary roles. Adds nav-subitem styling.
- New presentational components under components/appointments/: tauri status
  palette (turnStatus), TurnsStatInfo, TurnsViewToggle (sliding), DoctorTabs
  (underline), TurnsTimeline (marker rail + status cards, empty slot → افزودن
  نوبت), TurnsTable.
- AppointmentsPage recomposed with the new components (stats bar, doctor tabs,
  view toggle, timeline/table), preserving queries, filters, pagination,
  quick-book modal and the row actions menu.
- AppointmentCreatePage: full-page create form (CreateTurn layout) at
  /admin/appointments/new, reusing POST /api/v1/my|admin/appointment.

Tests: TurnsStatInfo, TurnsTimeline, Sidebar (expandable), AppointmentsPage,
AppointmentCreatePage. Backend move/reserve/replace verified green via existing
tests/Appointment/AppointmentUpdateTest + AppointmentWorkflowFieldsTest.

No API endpoints changed → no docs/api change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 15:33:03 +03:30

62 lines
2.5 KiB
TypeScript

import { UsersIcon, UserPlusIcon, ClockIcon, XCircleIcon } from '@heroicons/react/24/outline';
/**
* نوار آمار نوبت‌ها — بازسازیِ `TurnsStatInfo.jsx` طرح tauri (کارت سفید با
* جداکننده‌های عمودی و چهار آمار). رنگ‌ها از `data/turnsHeaderStats.json` مبدأ.
*/
export interface TurnsStats {
total: number;
completed: number;
waiting: number;
cancelled: number;
}
interface StatDef {
title: string;
value: number;
Icon: React.ElementType;
iconColor: string;
iconBg: string;
}
function BadgeIcon({ Icon, color, bg }: { Icon: React.ElementType; color: string; bg: string }) {
return (
<div style={{
width: 44, height: 44, borderRadius: 10, background: bg,
display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0,
}}>
<Icon style={{ width: 22, height: 22, color }} />
</div>
);
}
export default function TurnsStatInfo({ stats }: { stats: TurnsStats }) {
const items: StatDef[] = [
{ title: 'کل نوبت های امروز', value: stats.total, Icon: UsersIcon, iconColor: '#494cb3', iconBg: '#e9ebfb' },
{ title: 'نوبت های انجام شده', value: stats.completed, Icon: UserPlusIcon, iconColor: '#0d9f43', iconBg: '#e7f6ed' },
{ title: 'بیماران در انتظار', value: stats.waiting, Icon: ClockIcon, iconColor: '#e0a838', iconBg: '#fff2dc' },
{ title: 'نوبت های لغو شده', value: stats.cancelled, Icon: XCircleIcon, iconColor: '#ee5c6d', iconBg: '#fad6da' },
];
return (
<div style={{
display: 'flex', alignItems: 'center', background: 'var(--surface)',
border: '1px solid var(--border)', borderRadius: 'var(--r)',
padding: '14px 20px', marginBottom: 16, gap: 4,
}}>
{items.map((it, i) => (
<div key={it.title} style={{ display: 'flex', alignItems: 'center', flex: 1, gap: 10 }}>
{i > 0 && <div style={{ width: 1, height: 48, background: 'var(--border)', marginInlineEnd: 8 }} />}
<BadgeIcon Icon={it.Icon} color={it.iconColor} bg={it.iconBg} />
<div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
<span style={{ fontSize: 12, color: 'var(--text-2)', whiteSpace: 'nowrap' }}>{it.title}</span>
<span style={{ fontSize: 18, fontWeight: 700, color: 'var(--text)' }}>
{it.value.toLocaleString('fa-IR')}
</span>
</div>
</div>
))}
</div>
);
}