feat(booking): give the booking flow a dark palette derived from its own colours

components/appointment/ had every colour hard-coded, so the flow rendered
identically in either theme even after the wiring was fixed. It now reads from
CSS custom properties.

These are not new colours. The :root values are byte-for-byte the hex codes
that were already in the components — twenty-one files, mapped one to one — so
light mode is unchanged. The dark values are derived from those same colours:
surfaces and borders darkened, text inverted, and the two brand colours (the
indigo and the orange) lightened rather than replaced, because both lose
contrast against a dark surface at their original values.

Verified in a headless browser with prefers-color-scheme forced dark:
data-theme lands on <html> and --ap-surface resolves to #1b1b20 rather than
white. Six one-off colours remain hard-coded — a success green, an error red
and similar — each used exactly once and none of them a surface.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
hamed
2026-08-01 16:33:39 +03:30
co-authored by Claude Opus 5
parent 2fb17d778a
commit 51c21938eb
22 changed files with 149 additions and 95 deletions
+1 -1
View File
@@ -3,7 +3,7 @@ import React from "react";
function ButtonFixed({ children }) {
return (
<div
className="!fixed md:!relative !bottom-0 !right-0 bg-[#FFF] border-0 border-t md:border-t-transparent border-[#D7D7D7] z-10 !w-full sm:!w-full !p-[16px] sm:!px-[52px] md:!ml-0 md:!p-0 md:!w-full"
className="!fixed md:!relative !bottom-0 !right-0 bg-[var(--ap-surface)] border-0 border-t md:border-t-transparent border-[var(--ap-border-2)] z-10 !w-full sm:!w-full !p-[16px] sm:!px-[52px] md:!ml-0 md:!p-0 md:!w-full"
>
{children}
</div>
+26 -26
View File
@@ -69,29 +69,29 @@ function Paying({ setStep, appointmentId, expiresAt, doctor, data, isForAnother,
return (
<div className="w-full lg:w-[59%]">
<div className="opacity-page bg-[#FFF] border border-solid border-[#EFEFEF] rounded-[12px] p-[16px] md:p-[24px]">
<p className="text-[#3B3B3B] text-[16px] md:text-[20px] font-bold mb-[20px]">
<div className="opacity-page bg-[var(--ap-surface)] border border-solid border-[var(--ap-border)] rounded-[12px] p-[16px] md:p-[24px]">
<p className="text-[var(--ap-text)] text-[16px] md:text-[20px] font-bold mb-[20px]">
تأیید و پرداخت
</p>
{/* ── خلاصه نوبت ── */}
<div className="rounded-[10px] border border-[#EFEFEF] bg-[#FAFAFA] p-[16px] flex flex-col gap-[12px]">
<div className="rounded-[10px] border border-[var(--ap-border)] bg-[var(--ap-surface-2)] p-[16px] flex flex-col gap-[12px]">
{doctor?.name && (
<div className="flex justify-between items-center text-[14px] md:text-[15px]">
<span className="text-[#9B9B9B]">پزشک</span>
<span className="text-[#3B3B3B] font-medium">{doctor.name}</span>
<span className="text-[var(--ap-text-4)]">پزشک</span>
<span className="text-[var(--ap-text)] font-medium">{doctor.name}</span>
</div>
)}
{patientName && (
<div className="flex justify-between items-center text-[14px] md:text-[15px]">
<span className="text-[#9B9B9B]">بیمار</span>
<span className="text-[#3B3B3B] font-medium">{patientName}</span>
<span className="text-[var(--ap-text-4)]">بیمار</span>
<span className="text-[var(--ap-text)] font-medium">{patientName}</span>
</div>
)}
{selectedSlot?.start && (
<div className="flex justify-between items-center text-[14px] md:text-[15px]">
<span className="text-[#9B9B9B]">زمان نوبت</span>
<span className="text-[#3B3B3B] font-medium" dir="rtl">
<span className="text-[var(--ap-text-4)]">زمان نوبت</span>
<span className="text-[var(--ap-text)] font-medium" dir="rtl">
{/* utcOffset(210) = ساعت رسمی ایران (+03:30)، مستقل از tz مرورگر بیمار */}
{moment.unix(selectedSlot.start).utcOffset(210).locale("fa").format("dddd jD jMMMM jYYYY")}
{" — ساعت "}
@@ -102,12 +102,12 @@ function Paying({ setStep, appointmentId, expiresAt, doctor, data, isForAnother,
</div>
{/* ── مبلغ ── */}
<div className="flex justify-between items-center mt-[20px] py-[14px] border-y border-[#EFEFEF]">
<span className="text-[#616161] text-[14px] md:text-[16px] font-medium">
<div className="flex justify-between items-center mt-[20px] py-[14px] border-y border-[var(--ap-border)]">
<span className="text-[var(--ap-text-2)] text-[14px] md:text-[16px] font-medium">
مبلغ قابل پرداخت
</span>
<span className="text-[#3B3B3B] text-[18px] md:text-[20px] font-bold">
{toman} <span className="text-[14px] font-normal text-[#616161]">تومان</span>
<span className="text-[var(--ap-text)] text-[18px] md:text-[20px] font-bold">
{toman} <span className="text-[14px] font-normal text-[var(--ap-text-2)]">تومان</span>
</span>
</div>
@@ -115,18 +115,18 @@ function Paying({ setStep, appointmentId, expiresAt, doctor, data, isForAnother,
{!expired && (
<div className="mt-[20px]">
<div className="flex justify-between items-center mb-[8px]">
<span className="text-[#616161] text-[13px] md:text-[14px]">
<span className="text-[var(--ap-text-2)] text-[13px] md:text-[14px]">
مهلت پرداخت و نگهداری نوبت
</span>
<span
className={`text-[14px] md:text-[16px] font-bold tabular-nums ${ timeLeft < 60 ? "text-[#E0383B]" : "text-[#5559CE]" }`}
className={`text-[14px] md:text-[16px] font-bold tabular-nums ${ timeLeft < 60 ? "text-[var(--ap-danger)]" : "text-[var(--ap-primary)]" }`}
>
{formatTime(timeLeft)}
</span>
</div>
<div className="w-full h-[6px] rounded-full bg-[#EFEFEF] overflow-hidden">
<div className="w-full h-[6px] rounded-full bg-[var(--ap-border)] overflow-hidden">
<div
className={`h-full rounded-full transition-all duration-1000 ease-linear ${ timeLeft < 60 ? "bg-[#E0383B]" : "bg-[#5559CE]" }`}
className={`h-full rounded-full transition-all duration-1000 ease-linear ${ timeLeft < 60 ? "bg-[var(--ap-danger)]" : "bg-[var(--ap-primary)]" }`}
style={{ width: `${progress}%` }}
/>
</div>
@@ -137,18 +137,18 @@ function Paying({ setStep, appointmentId, expiresAt, doctor, data, isForAnother,
{!expired && (
<div className="mt-[20px]">
{testMode ? (
<div className="flex items-start gap-[8px] rounded-[10px] border border-[#F4B740] bg-[#FFF8E8] p-[12px]">
<div className="flex items-start gap-[8px] rounded-[10px] border border-[var(--ap-warning)] bg-[var(--ap-surface)8E8] p-[12px]">
<span className="text-[18px] leading-none">🧪</span>
<div>
<p className="text-[#8A6100] text-[14px] font-bold">درگاه پرداخت آزمایشی</p>
<p className="text-[#8A6100] text-[12px] mt-[2px] leading-[20px]">
<p className="text-[var(--ap-warning-text)] text-[14px] font-bold">درگاه پرداخت آزمایشی</p>
<p className="text-[var(--ap-warning-text)] text-[12px] mt-[2px] leading-[20px]">
در این حالت پرداخت واقعی انجام نمیشود و نوبت بهصورت آزمایشی تأیید میگردد.
</p>
</div>
</div>
) : gateways.length === 0 ? (
<div className="rounded-[10px] border border-[#F3C0C1] bg-[#FDEEEE] p-[14px]">
<p className="text-[#E0383B] text-[14px] font-medium">
<div className="rounded-[10px] border border-[var(--ap-danger-border)] bg-[var(--ap-danger-bg)] p-[14px]">
<p className="text-[var(--ap-danger)] text-[14px] font-medium">
درگاه پرداخت فعالی موجود نیست. لطفاً بعداً تلاش کنید.
</p>
</div>
@@ -173,8 +173,8 @@ function Paying({ setStep, appointmentId, expiresAt, doctor, data, isForAnother,
)}
{expired && (
<div className="mt-[20px] rounded-[10px] border border-[#F3C0C1] bg-[#FDEEEE] p-[14px]">
<p className="text-[#E0383B] text-[14px] font-medium">
<div className="mt-[20px] rounded-[10px] border border-[var(--ap-danger-border)] bg-[var(--ap-danger-bg)] p-[14px]">
<p className="text-[var(--ap-danger)] text-[14px] font-medium">
مهلت پرداخت تمام شد و نوبت آزاد شد. لطفاً دوباره زمان نوبت را انتخاب کنید.
</p>
</div>
@@ -188,7 +188,7 @@ function Paying({ setStep, appointmentId, expiresAt, doctor, data, isForAnother,
onClick={() => setStep(0)}
variant="contained"
>
<span className="text-[#FFF] text-[16px] font-medium">انتخاب مجدد زمان</span>
<span className="text-[var(--ap-surface)] text-[16px] font-medium">انتخاب مجدد زمان</span>
</Button>
) : (
<Button
@@ -200,7 +200,7 @@ function Paying({ setStep, appointmentId, expiresAt, doctor, data, isForAnother,
(!testMode && !selectedBank)
}
>
<span className={`text-[#FFF] text-[16px] font-medium ${loading ? "opacity-0" : ""}`}>
<span className={`text-[var(--ap-surface)] text-[16px] font-medium ${loading ? "opacity-0" : ""}`}>
{testMode ? "پرداخت آزمایشی" : `پرداخت ${toman} تومان`}
</span>
{!loading && <ArrowLeftB />}