diff --git a/.claude/skills/qa-clinicpro/driver.mjs b/.claude/skills/qa-clinicpro/driver.mjs
index cfacc455..5c870e92 100644
--- a/.claude/skills/qa-clinicpro/driver.mjs
+++ b/.claude/skills/qa-clinicpro/driver.mjs
@@ -227,9 +227,16 @@ async function withPage(url, opts, fn) {
state: { token: access_token, refreshToken: refresh_token, isAuthenticated: true },
version: 0,
};
+ // `--ui '{"darkMode":true,"density":"compact"}'` تم را پیش از اولین رندر میکارد.
+ const ui = opts.ui
+ ? `localStorage.setItem('clinicpro-ui', ${JSON.stringify(
+ JSON.stringify({ state: JSON.parse(opts.ui), version: 0 }),
+ )});`
+ : '';
+
await S('Runtime.evaluate', {
expression: `localStorage.setItem('clinicpro-auth', ${JSON.stringify(JSON.stringify(auth))});
- localStorage.setItem('pwa-dismissed','1');`,
+ localStorage.setItem('pwa-dismissed','1');${ui}`,
});
// Errors before this point belong to the login page, not the page under test.
@@ -458,6 +465,9 @@ const opts = {
wait: Number(flag('wait', 4000)),
full: argv.includes('--full'),
body: flag('body', null),
+ // تم و چگالی در `localStorage['clinicpro-ui']` مینشینند و بدون seed کردنشان
+ // دارکمود و حالت فشرده اصلاً قابل اسکرینشات نیستند.
+ ui: flag('ui', null),
};
try {
diff --git a/assets/admin/components/AppointmentSegmentsCard.test.tsx b/assets/admin/components/AppointmentSegmentsCard.test.tsx
index 15d0cf5a..38a90dc6 100644
--- a/assets/admin/components/AppointmentSegmentsCard.test.tsx
+++ b/assets/admin/components/AppointmentSegmentsCard.test.tsx
@@ -37,7 +37,8 @@ describe('AppointmentSegmentsCard', () => {
renderWithProviders();
- await waitFor(() => expect(screen.getByText('60 دقیقه')).toBeInTheDocument());
+ // ارقام فارسیاند: ۲۰ + ۴۰ = ۶۰
+ await waitFor(() => expect(screen.getByText('۶۰ دقیقه')).toBeInTheDocument());
});
/** ⭐ مدتی که بیمار روی صندلی نیست باید دیده شود، وگرنه «۹۰ دقیقه» گمراهکننده است. */
diff --git a/assets/admin/components/AppointmentSegmentsCard.tsx b/assets/admin/components/AppointmentSegmentsCard.tsx
index e2dacf02..d9b793aa 100644
--- a/assets/admin/components/AppointmentSegmentsCard.tsx
+++ b/assets/admin/components/AppointmentSegmentsCard.tsx
@@ -1,5 +1,6 @@
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' });
@@ -23,14 +24,14 @@ export default function AppointmentSegmentsCard({ appointmentUuid }: { appointme
بخشهای نوبت
- {total} دقیقه
+ {formatNumber(total)} دقیقه
{segments.map((s) => (
{segments.map((s) => (
- {s.sequence}
+ {formatNumber(s.sequence)}
{s.name}
{timeOf(s.starts_at)} – {timeOf(s.ends_at)}
diff --git a/assets/admin/components/ResourceBlocksModal.tsx b/assets/admin/components/ResourceBlocksModal.tsx
index 0b930e8f..cf48301e 100644
--- a/assets/admin/components/ResourceBlocksModal.tsx
+++ b/assets/admin/components/ResourceBlocksModal.tsx
@@ -89,7 +89,7 @@ export default function ResourceBlocksModal({ resourceUuid, resourceName, onClos
-
+
@@ -121,7 +121,7 @@ export default function ResourceBlocksModal({ resourceUuid, resourceName, onClos
-
+
{/* ── گروهها ───────────────────────────────────────────────────────── */}
-
+
گروههای انتخاب
@@ -71,7 +71,7 @@ export default function ServiceGroupsTab({ serviceUuid, canEdit, initialRelation
{canEdit && (
-
+
-
+
{/* ── روابط ─────────────────────────────────────────────────────────── */}
-
+
ناسازگاری و پیشنیاز
@@ -299,7 +299,7 @@ export default function ServiceGroupsTab({ serviceUuid, canEdit, initialRelation
{/* ── پیشنمایش زنده ────────────────────────────────────────────────── */}
-
+
پیشنمایش انتخاب
diff --git a/assets/admin/components/ServiceSegmentsTab.tsx b/assets/admin/components/ServiceSegmentsTab.tsx
index a9189fd4..4c6ecc67 100644
--- a/assets/admin/components/ServiceSegmentsTab.tsx
+++ b/assets/admin/components/ServiceSegmentsTab.tsx
@@ -141,7 +141,7 @@ export default function ServiceSegmentsTab({ serviceUuid, canEdit }: Props) {
return (
-
+
بخشهای نوبت
@@ -378,9 +378,9 @@ export default function ServiceSegmentsTab({ serviceUuid, canEdit }: Props) {
{/* ── نوار پیشنمایش زمانی ──────────────────────────────────────────── */}
-
+
-
+
-
+
{loading &&
در حال بارگذاری…}
-
+
-
+
{mode === 'percent' && (
-
+
+
@@ -123,7 +123,7 @@ export default function CancellationPolicyPage() {
اعتبار پکیج پس از لغو برمیگردد
-
+
{!draft.uuid && (
-
+
)}
-
+
(
-
+
{draft && (
-
+
-
+
-
+
-
+
-
+
-
-
+
+
setDays(String(v ?? '30'))} options={RANGES} />
diff --git a/assets/admin/pages/PolicyFormPage.tsx b/assets/admin/pages/PolicyFormPage.tsx
index fda095ca..072271d8 100644
--- a/assets/admin/pages/PolicyFormPage.tsx
+++ b/assets/admin/pages/PolicyFormPage.tsx
@@ -72,7 +72,7 @@ export default function PolicyFormPage() {
backTo="/admin/policies"
/>
-
+
-
+
-
+
-
+
{template?.inputs.map((input) => (
-
+
) : (
<>
-
+
{draft && (
-
+
-
+
-
+
setDraft({ ...draft, valid_from: isoToUnix(iso) ?? draft.valid_from })}
/>
-
+
-
-
+
+
-
+
-
+
{error && (
-
+
{error instanceof ApiError ? error.message : 'جستجوی وقت ناموفق بود'}
)}
{result && (
-
+
- مدت نوبت: {result.plan.total_minutes} دقیقه
+ مدت نوبت: {formatNumber(result.plan.total_minutes)} دقیقه
- {result.plan.segments.length} بخش · {result.slots.length} وقت پیدا شد
+ {formatNumber(result.plan.segments.length)} بخش · {formatNumber(result.slots.length)} وقت پیدا شد
@@ -215,7 +215,7 @@ export default function ResourceBookingPage() {
)}
{result && result.slots.length > 0 && (
-
+
@@ -262,7 +262,7 @@ export default function ResourceBookingPage() {
)}
{pickedSlot && (
-
+
{formatDate(pickedSlot.start)} · {timeOf(pickedSlot.start)}
@@ -301,7 +301,7 @@ export default function ResourceBookingPage() {
{/* در جابهجایی پزشک عوض نمیشود؛ پرسیدنش یعنی دعوت به تغییری که خواسته نشده. */}
{!rebookUuid && (
-
+
- setName(e.target.value)} placeholder="دستگاه لیزر" />
+ setName(e.target.value)} placeholder="دستگاه لیزر" />