Files
clinicpro/assets/admin/components/AppointmentSegmentsCard.tsx
hamedandClaude Opus 5 635bf3d2a8 fix(admin): correct two design-system mismatches found by looking at the pages
Screenshotting the pages under dark mode and compact density (rather than
trusting that design tokens were enough) turned up two mistakes repeated across
every page this feature set added:

- `.card` carries only the surface, border and radius — padding comes from the
  separate `.card-pad`. Fifteen cards were rendering with their content flush
  against the edges.
- `.field` *is* the input box, a 40px-tall flex row. Wrapping a label plus a
  control in it produced a joined addon rather than a label above its field.
  `.field-block` is the label-above layout, and thirty-seven wrappers now use it.

Both were invisible to type-checking and to the tests, which is exactly why the
visual pass was worth running. Numbers in the new UI now go through
formatNumber so they render as Persian digits, and the utilization page's
header no longer repeats the sentence that appears under its filters verbatim.

The QA driver gained a `--ui` flag: theme and density live in
localStorage['clinicpro-ui'], so without seeding them dark mode and compact
density cannot be screenshotted at all.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 21:42:11 +03:30

65 lines
2.7 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React from 'react';
import { useAppointmentSegments } from '../hooks/useResourceBooking';
import { formatNumber } from '../lib/utils';
const timeOf = (ts: number) =>
new Date(ts * 1000).toLocaleTimeString('fa-IR', { hour: '2-digit', minute: '2-digit' });
/**
* بخش‌های ثبت‌شدهٔ نوبت — همان چیزی که لحظهٔ رزرو تثبیت شد، نه الگوی امروزِ خدمت.
*
* بخشی که بیمار در آن حاضر نیست کم‌رنگ می‌آید: اپراتوری که «۹۰ دقیقه» را روی نوبت
* می‌بیند باید بفهمد بیمار همهٔ آن مدت روی صندلی نیست.
*
* نوبت اسلاتی بخشی ندارد و کارت اصلاً رندر نمی‌شود — جدول خالی یعنی چیزی خراب است.
*/
export default function AppointmentSegmentsCard({ appointmentUuid }: { appointmentUuid: string }) {
const { segments, loading } = useAppointmentSegments(appointmentUuid);
if (loading || segments.length === 0) return null;
const total = segments.reduce((sum, s) => sum + s.duration_minutes, 0);
return (
<div className="bg-[var(--surface)] rounded-2xl border border-[var(--border)] shadow-sm p-6">
<div className="flex items-center justify-between mb-4">
<h3 className="font-semibold text-[var(--text)]">بخش‌های نوبت</h3>
<span className="text-sm text-[var(--text-2)]">{formatNumber(total)} دقیقه</span>
</div>
<div style={{ display: 'flex', gap: 2, marginBottom: 14 }}>
{segments.map((s) => (
<div
key={s.sequence}
title={`${s.name}${formatNumber(s.duration_minutes)} دقیقه`}
style={{
flex: s.duration_minutes,
height: 10,
borderRadius: 'var(--r-pill)',
background: 'var(--primary)',
opacity: s.patient_present ? 1 : 0.35,
}}
/>
))}
</div>
<ol style={{ display: 'flex', flexDirection: 'column', gap: 8, fontSize: 13 }}>
{segments.map((s) => (
<li key={s.sequence} style={{ display: 'flex', gap: 10, alignItems: 'center' }}>
<span style={{ color: 'var(--text-3)', minWidth: 18 }}>{formatNumber(s.sequence)}</span>
<span style={{ flex: 1 }}>{s.name}</span>
<span dir="ltr" style={{ color: 'var(--text-2)' }}>
{timeOf(s.starts_at)} {timeOf(s.ends_at)}
</span>
{!s.patient_present && (
<span className="badge" style={{ fontSize: 11 }}>
بدون حضور بیمار
</span>
)}
</li>
))}
</ol>
</div>
);
}