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:
@@ -74,6 +74,60 @@ body {
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
|
||||
/* ── توکنهای جریان رزرو ──────────────────────────────────────────────────
|
||||
*
|
||||
* جریان رزرو رنگهایش را hard-code داشت، پس در دارکمود دقیقاً همان سفید میماند.
|
||||
* اینها **رنگ تازه نیستند**: مقادیر روشن عیناً همان چیزی است که تا امروز بود، و
|
||||
* نسخهٔ تیره از همانها مشتق شده. طراحی تازهای اینجا اختراع نشده.
|
||||
*/
|
||||
:root {
|
||||
--ap-surface: #ffffff;
|
||||
--ap-surface-2: #fafafa;
|
||||
--ap-surface-3: #f5f5f5;
|
||||
--ap-border: #efefef;
|
||||
--ap-border-2: #d7d7d7;
|
||||
--ap-heading: #2f2f2f;
|
||||
--ap-text: #3b3b3b;
|
||||
--ap-text-2: #616161;
|
||||
--ap-text-3: #7a7a7a;
|
||||
--ap-text-4: #9b9b9b;
|
||||
--ap-muted: #525252;
|
||||
--ap-primary: #5559ce;
|
||||
--ap-accent: #f17732;
|
||||
--ap-accent-strong: #b4541f;
|
||||
--ap-danger: #e0383b;
|
||||
--ap-danger-bg: #fdeeee;
|
||||
--ap-danger-border: #f3c0c1;
|
||||
--ap-warning: #f4b740;
|
||||
--ap-warning-bg: #fff8e8;
|
||||
--ap-warning-text: #8a6100;
|
||||
}
|
||||
|
||||
[data-theme="dark"] {
|
||||
--ap-surface: #1b1b20;
|
||||
--ap-surface-2: #232329;
|
||||
--ap-surface-3: #2a2a31;
|
||||
--ap-border: #2e2e36;
|
||||
--ap-border-2: #3a3a44;
|
||||
--ap-heading: #f0f0f2;
|
||||
--ap-text: #e8e8ea;
|
||||
--ap-text-2: #b8b8be;
|
||||
--ap-text-3: #98989f;
|
||||
--ap-text-4: #82828a;
|
||||
--ap-muted: #c9c9cf;
|
||||
/* بنفش و نارنجی روی زمینهٔ تیره کمکنتراستاند؛ کمی روشنتر شدهاند نه عوض. */
|
||||
--ap-primary: #8a8de6;
|
||||
--ap-accent: #ff9152;
|
||||
--ap-accent-strong: #ffb184;
|
||||
--ap-danger: #ff6b6e;
|
||||
--ap-danger-bg: #3a1f20;
|
||||
--ap-danger-border: #5a2e30;
|
||||
--ap-warning: #ffca5c;
|
||||
--ap-warning-bg: #33290f;
|
||||
--ap-warning-text: #ffd98a;
|
||||
}
|
||||
|
||||
/* transition-colors only on theme-sensitive elements, not every DOM node */
|
||||
.dark,
|
||||
[data-theme] {
|
||||
|
||||
@@ -61,7 +61,7 @@ function Container({
|
||||
let dateStep;
|
||||
if (bookingLocations.length === 0) {
|
||||
dateStep = (
|
||||
<p className="w-full max-w-[520px] mx-auto py-[40px] text-center text-[14px] text-[#7A7A7A]">
|
||||
<p className="w-full max-w-[520px] mx-auto py-[40px] text-center text-[14px] text-[var(--ap-text-3)]">
|
||||
نوبتدهی آنلاین برای این پزشک فعال نیست.
|
||||
</p>
|
||||
);
|
||||
|
||||
@@ -3,7 +3,7 @@ import SelectDatePicker from "./SelectDatePicker";
|
||||
function Time({ isStep, setDate, disabledDates, clinicUuid }) {
|
||||
return (
|
||||
<div className="flex relative mt-[24px] flex-col items-start gap-[19px] justify-start">
|
||||
<p className="text-[#3B3B3B] text-[14px] md:text-[16px] font-bold">
|
||||
<p className="text-[var(--ap-text)] text-[14px] md:text-[16px] font-bold">
|
||||
1. انتخاب روز
|
||||
</p>
|
||||
<SelectDatePicker setDate={setDate} disabledDates={disabledDates} clinicUuid={clinicUuid} />
|
||||
|
||||
@@ -3,7 +3,7 @@ import DateTime from "@/app/component/date/dateTime";
|
||||
function Hour({ doctor, setStep, date, isStep, setSelectedSlot, setSelectedDate, serviceMode = false, selectedServiceUuids = [], clinicUuid = null, selectedLocation = null }) {
|
||||
return (
|
||||
<div className="flex relative mt-[24px] flex-col items-start gap-[12px] justify-start">
|
||||
<p className="text-[#3B3B3B] text-[14px] md:text-[16px] font-bold">
|
||||
<p className="text-[var(--ap-text)] text-[14px] md:text-[16px] font-bold">
|
||||
2. انتخاب ساعت
|
||||
</p>
|
||||
<DateTime
|
||||
|
||||
@@ -27,7 +27,7 @@ function Date({
|
||||
return (
|
||||
<div className="w-full lg:w-[61%]">
|
||||
<div
|
||||
className="p-0 lg:p-[24px] rounded-[8px] border border-solid border-transparent lg:border-[#EFEFEF] bg-transparent lg:bg-[#FFF]"
|
||||
className="p-0 lg:p-[24px] rounded-[8px] border border-solid border-transparent lg:border-[var(--ap-border)] bg-transparent lg:bg-[var(--ap-surface)]"
|
||||
>
|
||||
{(onChangeLocation || (serviceMode && onChangeService)) && (
|
||||
<div className="mb-3 flex flex-wrap items-center gap-x-4 gap-y-1">
|
||||
@@ -35,7 +35,7 @@ function Date({
|
||||
<button
|
||||
type="button"
|
||||
onClick={onChangeLocation}
|
||||
className="text-[13px] text-[#5559CE] hover:underline"
|
||||
className="text-[13px] text-[var(--ap-primary)] hover:underline"
|
||||
>
|
||||
← تغییر محل{selectedLocation?.title ? ` (${selectedLocation.title})` : ""}
|
||||
</button>
|
||||
@@ -44,7 +44,7 @@ function Date({
|
||||
<button
|
||||
type="button"
|
||||
onClick={onChangeService}
|
||||
className="text-[13px] text-[#5559CE] hover:underline"
|
||||
className="text-[13px] text-[var(--ap-primary)] hover:underline"
|
||||
>
|
||||
← تغییر سرویس
|
||||
</button>
|
||||
@@ -52,14 +52,14 @@ function Date({
|
||||
</div>
|
||||
)}
|
||||
{closedOnDate && (
|
||||
<div className="mb-3 rounded-[8px] border border-solid border-[#F17732] bg-[rgba(241,119,50,0.08)] p-[12px]">
|
||||
<p className="text-[13px] text-[#B4541F] leading-relaxed">
|
||||
<div className="mb-3 rounded-[8px] border border-solid border-[var(--ap-accent)] bg-[rgba(241,119,50,0.08)] p-[12px]">
|
||||
<p className="text-[13px] text-[var(--ap-accent-strong)] leading-relaxed">
|
||||
«{selectedLocation?.title}» در روز انتخابشده نوبتدهی ندارد.
|
||||
{onChangeLocation && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={onChangeLocation}
|
||||
className="mr-1 font-medium text-[#5559CE] hover:underline"
|
||||
className="mr-1 font-medium text-[var(--ap-primary)] hover:underline"
|
||||
>
|
||||
انتخاب محل دیگر
|
||||
</button>
|
||||
|
||||
@@ -2,9 +2,9 @@ function Content({ isConfirmed, children, title }) {
|
||||
return (
|
||||
<div className="flex w-full flex-col items-start justify-start gap-[8px] sm:gap-[10px] md:gap-[13px] lg:gap-[16px]">
|
||||
<div className="flex min-h-[32px] items-center justify-start gap-[8px] md:gap-[6px] lg:gap-[4px]">
|
||||
<p className="text-[#525252] text-[14px] font-medium">{title}</p>
|
||||
<p className="text-[var(--ap-muted)] text-[14px] font-medium">{title}</p>
|
||||
{isConfirmed && (
|
||||
<p className="py-[4px] md:py-[2px] leading-[8px] sm:leading-[14px] md:leading-[21px] lg:leading-[28px] px-[5px] bg-[#05BA58] rounded-[40px] text-[#FFF] text-[10px] md:text-[12px] font-normal">
|
||||
<p className="py-[4px] md:py-[2px] leading-[8px] sm:leading-[14px] md:leading-[21px] lg:leading-[28px] px-[5px] bg-[#05BA58] rounded-[40px] text-[var(--ap-surface)] text-[10px] md:text-[12px] font-normal">
|
||||
تایید شده
|
||||
</p>
|
||||
)}
|
||||
|
||||
@@ -27,8 +27,8 @@ function Form({ changeData, insurance, supplementaryInsurance, data, isForAnothe
|
||||
{isForAnother ? (
|
||||
<EditField changeData={changeData} data={data?.phone} name="phone" error={errors?.phone} />
|
||||
) : (
|
||||
<div className="flex items-center justify-between px-[14px] py-[12.5px] border border-[#D7D7D7] rounded-lg bg-[#F5F5F5]">
|
||||
<span className="text-[#3B3B3B]">{data?.phone?.value}</span>
|
||||
<div className="flex items-center justify-between px-[14px] py-[12.5px] border border-[var(--ap-border-2)] rounded-lg bg-[var(--ap-surface-3)]">
|
||||
<span className="text-[var(--ap-text)]">{data?.phone?.value}</span>
|
||||
<span className="text-[#4CAF50] text-[14px]">تایید شده</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -195,7 +195,7 @@ function SubmitData({ setStep, data, prevData, setErrors, doctor, isForAnother,
|
||||
onClick={handleSubmit}
|
||||
>
|
||||
<p
|
||||
className={`${loading && "opacity-0"} text-[#EFEFEF] text-[16px] font-medium`}
|
||||
className={`${loading && "opacity-0"} text-[var(--ap-border)] text-[16px] font-medium`}
|
||||
>
|
||||
ثبت اطلاعات
|
||||
</p>
|
||||
|
||||
@@ -78,10 +78,10 @@ function Detail({
|
||||
}, []);
|
||||
|
||||
return (
|
||||
// ${typePay === 'success' || typePay === 'failed' ? 'bg-transparent border-transparent md:bg-[#FFF] md:border-[#EFEFEF]' : ''}
|
||||
// ${typePay === 'success' || typePay === 'failed' ? 'bg-transparent border-transparent md:bg-[var(--ap-surface)] md:border-[var(--ap-border)]' : ''}
|
||||
<div className="w-full lg:w-[59%]">
|
||||
<div
|
||||
className={`opacity-page bg-[#FFF] border border-solid border-[#EFEFEF] rounded-[8px] p-[12px] sm:p-[16px] md:p-[20px] lg:p-[24px] `}
|
||||
className={`opacity-page bg-[var(--ap-surface)] border border-solid border-[var(--ap-border)] rounded-[8px] p-[12px] sm:p-[16px] md:p-[20px] lg:p-[24px] `}
|
||||
>
|
||||
{/* دکمه برگشت */}
|
||||
<div
|
||||
@@ -91,7 +91,7 @@ function Detail({
|
||||
<ArrowRightS />
|
||||
</div>
|
||||
|
||||
<p className="text-[#3B3B3B] text-[20px] font-bold">
|
||||
<p className="text-[var(--ap-text)] text-[20px] font-bold">
|
||||
{isForAnother ? "اطلاعات کاربر جدید" : "اطلاعات حساب کاربری"}
|
||||
</p>
|
||||
<Form
|
||||
@@ -107,7 +107,7 @@ function Detail({
|
||||
onClick={() => (isForAnother ? switchToSelf() : switchToAnother())}
|
||||
>
|
||||
<AddCircleBlueA />
|
||||
<p className="text-[#5559CE] text-[16px] font-medium">
|
||||
<p className="text-[var(--ap-primary)] text-[16px] font-medium">
|
||||
{isForAnother ? "نوبت برای خودم است" : "نوبت برای شخص دیگری است"}
|
||||
</p>
|
||||
</Button>
|
||||
|
||||
@@ -8,7 +8,7 @@ function FailedPay({ setStep, fadeElement }) {
|
||||
return (
|
||||
<div className="w-full lg:w-[59%]">
|
||||
<div
|
||||
className={`opacity-page bg-transparent lg:bg-[#FFF] border border-solid border-transparent lg:border-[#EFEFEF] rounded-[8px] p-[12px] sm:p-[16px] md:p-[20px] lg:p-[24px]`}
|
||||
className={`opacity-page bg-transparent lg:bg-[var(--ap-surface)] border border-solid border-transparent lg:border-[var(--ap-border)] rounded-[8px] p-[12px] sm:p-[16px] md:p-[20px] lg:p-[24px]`}
|
||||
>
|
||||
<div className="mt-[7px]">
|
||||
<div className="flex flex-col justify-center items-center gap-[14px]">
|
||||
@@ -24,7 +24,7 @@ function FailedPay({ setStep, fadeElement }) {
|
||||
className="!flex !rounded-[8px] !items-center !p-2 !justify-center !gap-[4px]"
|
||||
>
|
||||
<RedoA />
|
||||
<p className="text-[#5559CE] text-[16px] font-medium">
|
||||
<p className="text-[var(--ap-primary)] text-[16px] font-medium">
|
||||
صفحه اصلی
|
||||
</p>
|
||||
</Button>
|
||||
@@ -35,7 +35,7 @@ function FailedPay({ setStep, fadeElement }) {
|
||||
className="!flex !rounded-[8px] !items-center !p-2 !justify-center !gap-[4px]"
|
||||
>
|
||||
<RefreshA />
|
||||
<p className="text-[#FAFAFA] text-[16px] font-medium">
|
||||
<p className="text-[var(--ap-surface-2)] text-[16px] font-medium">
|
||||
تلاش دوباره
|
||||
</p>
|
||||
</Button>
|
||||
|
||||
@@ -215,8 +215,8 @@ function AppointmentPage({ doctor, disabledDates, matchedCity, initialClinicUuid
|
||||
return (
|
||||
<div className="flex items-center justify-center min-h-screen">
|
||||
<div className="flex flex-col items-center gap-4">
|
||||
<div className="w-12 h-12 border-4 border-[#5559CE] border-t-transparent rounded-full animate-spin"></div>
|
||||
<p className="text-[#3B3B3B] text-[16px]">در حال بارگذاری...</p>
|
||||
<div className="w-12 h-12 border-4 border-[var(--ap-primary)] border-t-transparent rounded-full animate-spin"></div>
|
||||
<p className="text-[var(--ap-text)] text-[16px]">در حال بارگذاری...</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -29,7 +29,7 @@ function Detail({ doctor, step, setStep, selectedSlot, selectedDate }) {
|
||||
<div className="w-[18px] h-[18px] md:w-[20px] md:h-[20px]">
|
||||
<LocationA />
|
||||
</div>
|
||||
<p className="text-[#616161] text-[14px] md:text-[16px] font-normal">
|
||||
<p className="text-[var(--ap-text-2)] text-[14px] md:text-[16px] font-normal">
|
||||
آدرس: {address}
|
||||
</p>
|
||||
</li>
|
||||
@@ -39,10 +39,10 @@ function Detail({ doctor, step, setStep, selectedSlot, selectedDate }) {
|
||||
<CalendarA />
|
||||
</div>
|
||||
<div className="flex items-center justify-start">
|
||||
<p className="text-[#616161] text-[14px] md:text-[16px] font-medium">
|
||||
<p className="text-[var(--ap-text-2)] text-[14px] md:text-[16px] font-medium">
|
||||
تاریخ نوبت:{" "}
|
||||
</p>
|
||||
<p className="text-[#2F2F2F] text-[14px] md:text-[16px] font-medium mr-1">
|
||||
<p className="text-[var(--ap-heading)] text-[14px] md:text-[16px] font-medium mr-1">
|
||||
{formattedDate}
|
||||
</p>
|
||||
</div>
|
||||
@@ -53,7 +53,7 @@ function Detail({ doctor, step, setStep, selectedSlot, selectedDate }) {
|
||||
onClick={() => setStep(0)}
|
||||
variant="text"
|
||||
>
|
||||
<p className="!text-[#F17732] !text-[12px] md:!text-[14px] !font-normal">
|
||||
<p className="!text-[var(--ap-accent)] !text-[12px] md:!text-[14px] !font-normal">
|
||||
تغییر{" "}
|
||||
</p>
|
||||
<div className="w-[16px] h-[16px] sm:w-[18px] sm:h-[18px] md:w-[21px] md:h-[21px] lg:w-[24px] lg:h-[24px]">
|
||||
@@ -68,10 +68,10 @@ function Detail({ doctor, step, setStep, selectedSlot, selectedDate }) {
|
||||
<ClockA />
|
||||
</div>
|
||||
<div className="flex items-center justify-start">
|
||||
<p className="text-[#616161] text-[14px] md:text-[16px] font-medium">
|
||||
<p className="text-[var(--ap-text-2)] text-[14px] md:text-[16px] font-medium">
|
||||
زمان نوبت:{" "}
|
||||
</p>
|
||||
<p className="text-[#2F2F2F] text-[14px] md:text-[16px] font-medium mr-1">
|
||||
<p className="text-[var(--ap-heading)] text-[14px] md:text-[16px] font-medium mr-1">
|
||||
ساعت {formattedTime}
|
||||
</p>
|
||||
</div>
|
||||
@@ -82,7 +82,7 @@ function Detail({ doctor, step, setStep, selectedSlot, selectedDate }) {
|
||||
onClick={() => setStep(0)}
|
||||
variant="text"
|
||||
>
|
||||
<p className="!text-[#F17732] !text-[12px] md:!text-[14px] !font-normal">
|
||||
<p className="!text-[var(--ap-accent)] !text-[12px] md:!text-[14px] !font-normal">
|
||||
تغییر{" "}
|
||||
</p>
|
||||
<div className="w-[16px] h-[16px] sm:w-[18px] sm:h-[18px] md:w-[21px] md:h-[21px] lg:w-[24px] lg:h-[24px]">
|
||||
@@ -93,7 +93,7 @@ function Detail({ doctor, step, setStep, selectedSlot, selectedDate }) {
|
||||
</li>
|
||||
</ul>
|
||||
<span
|
||||
className="mt-[10px] sm:mt-[16px] md:mt-[22px] lg:mt-[28px] mb-[8px] sm:mb-[13px] md:mb-[19px] lg:mb-[24px] w-full h-px bg-[#D7D7D7] block"
|
||||
className="mt-[10px] sm:mt-[16px] md:mt-[22px] lg:mt-[28px] mb-[8px] sm:mb-[13px] md:mb-[19px] lg:mb-[24px] w-full h-px bg-[var(--ap-border-2)] block"
|
||||
></span>
|
||||
<ul className="flex flex-col items-start justify-start gap-[8px] sm:gap-[13px] md:gap-[19px] lg:gap-[24px]">
|
||||
{/* <li className="flex items-center justify-start gap-[4px] md:gap-[6px] lg:gap-[8px]">
|
||||
@@ -102,7 +102,7 @@ function Detail({ doctor, step, setStep, selectedSlot, selectedDate }) {
|
||||
>
|
||||
<TickCircleOrange />
|
||||
</div>
|
||||
<p className="text-[#616161] text-[12px] md:text-[14px] lg:text-[16px] font-normal">
|
||||
<p className="text-[var(--ap-text-2)] text-[12px] md:text-[14px] lg:text-[16px] font-normal">
|
||||
فقط در صورت لغو نوبت تا 5 ساعت قبل از زمان ویزیت، امکان استرداد وجه
|
||||
ممکن می باشد.
|
||||
</p>
|
||||
@@ -113,7 +113,7 @@ function Detail({ doctor, step, setStep, selectedSlot, selectedDate }) {
|
||||
>
|
||||
<TickCircleOrange />
|
||||
</div>
|
||||
<p className="text-[#616161] text-[12px] md:text-[14px] lg:text-[16px] font-normal">
|
||||
<p className="text-[var(--ap-text-2)] text-[12px] md:text-[14px] lg:text-[16px] font-normal">
|
||||
لطفا به موقع در مطب حضور داشته باشید.
|
||||
</p>
|
||||
</li>
|
||||
|
||||
@@ -16,10 +16,10 @@ function Head({ doctor }) {
|
||||
className=" w-[40px] sm:w-[58px] md:w-[77px] lg:w-[95px] h-[40px] sm:h-[58px] md:h-[77px] lg:h-[95px] "
|
||||
/>
|
||||
<div className="flex flex-col items-start justify-center gap-[8px] sm:gap-[12px] md:gap-[16px] lg:gap-[20px]">
|
||||
<p className="text-[#3B3B3B] text-[14px] sm:text-[16px] md:text-[18px] lg:text-[20px] font-bold">
|
||||
<p className="text-[var(--ap-text)] text-[14px] sm:text-[16px] md:text-[18px] lg:text-[20px] font-bold">
|
||||
{doctor?.name}
|
||||
</p>
|
||||
<p className="text-[#616161] text-[14px] md:text-[16px] font-medium">
|
||||
<p className="text-[var(--ap-text-2)] text-[14px] md:text-[16px] font-medium">
|
||||
تخصص: {expertiseText}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -9,9 +9,9 @@ function Information({ doctor, step, setStep, typePay, isPay, disableSide, selec
|
||||
className={`w-full lg:w-[41%] relative ${disableSide ? "hidden lg:flex" : "" }`}
|
||||
>
|
||||
<div
|
||||
className={`p-[12px] sm:p-[16px] md:p-[20px] lg:p-[24px] sticky right-0 w-full opacity-page rounded-[8px] border border-solid border-[#EFEFEF] bg-[#FFF] top-[calc(12px_+_75px_+_32px)] sm:top-[calc(21px_+_75px_+_45px)] md:top-[calc(30px_+_75px_+_58px)] lg:top-[calc(40px_+_75px_+_78px)] ${typePay === "success" || typePay === "failed" ? "hidden md:block" : "block" } `}
|
||||
className={`p-[12px] sm:p-[16px] md:p-[20px] lg:p-[24px] sticky right-0 w-full opacity-page rounded-[8px] border border-solid border-[var(--ap-border)] bg-[var(--ap-surface)] top-[calc(12px_+_75px_+_32px)] sm:top-[calc(21px_+_75px_+_45px)] md:top-[calc(30px_+_75px_+_58px)] lg:top-[calc(40px_+_75px_+_78px)] ${typePay === "success" || typePay === "failed" ? "hidden md:block" : "block" } `}
|
||||
>
|
||||
<p className="text-[#3B3B3B] text-[14px] sm:text-[16px] md:text-[18px] lg:text-[20px] font-bold">
|
||||
<p className="text-[var(--ap-text)] text-[14px] sm:text-[16px] md:text-[18px] lg:text-[20px] font-bold">
|
||||
جزئیات نوبت
|
||||
</p>
|
||||
<Head doctor={doctor} />
|
||||
|
||||
@@ -7,7 +7,7 @@ function Address({ doctor, selectedLocation }) {
|
||||
return (
|
||||
<ul className="hidden mt-[4px] lg:flex flex-col">
|
||||
<li>
|
||||
<div className="flex py-[12px] text-[#616161] text-[14px] items-start justify-start">
|
||||
<div className="flex py-[12px] text-[var(--ap-text-2)] text-[14px] items-start justify-start">
|
||||
<p className="font-bold min-w-[115px]">{`${selectedLocation.title}: `}</p>
|
||||
<p className="font-normal">{selectedLocation.address || "—"}</p>
|
||||
</div>
|
||||
@@ -20,12 +20,12 @@ function Address({ doctor, selectedLocation }) {
|
||||
<ul className="hidden mt-[4px] lg:flex flex-col">
|
||||
{doctor?.address?.map((item, idx) => (
|
||||
<li key={idx}>
|
||||
<div className="flex py-[12px] text-[#616161] text-[14px] items-start justify-start">
|
||||
<div className="flex py-[12px] text-[var(--ap-text-2)] text-[14px] items-start justify-start">
|
||||
<p className="font-bold min-w-[115px]">{`${item.name}: `}</p>
|
||||
<p className="font-normal">{item.address || item.location}</p>
|
||||
</div>
|
||||
{doctor.address.length > idx + 1 && (
|
||||
<span className="bg-[#EFEFEF] block h-px w-full"></span>
|
||||
<span className="bg-[var(--ap-border)] block h-px w-full"></span>
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
|
||||
@@ -14,10 +14,10 @@ function Head({ doctor }) {
|
||||
className=" w-[48px] sm:w-[51px] md:w-[54px] lg:w-[56px] h-[48px] sm:h-[51px] md:h-[54px] lg:h-[56px] "
|
||||
/>
|
||||
<div className="flex flex-col items-start justify-start gap-[8px]">
|
||||
<h2 className="text-[#3B3B3B] text-[12px] md:text-[14px] font-bold">
|
||||
<h2 className="text-[var(--ap-text)] text-[12px] md:text-[14px] font-bold">
|
||||
{doctor?.name}
|
||||
</h2>
|
||||
<h3 className="text-[#525252] text-[12px] md:text-[14px] font-medium">
|
||||
<h3 className="text-[var(--ap-muted)] text-[12px] md:text-[14px] font-medium">
|
||||
تخصص: {expertiseText}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
@@ -18,10 +18,10 @@ function LocationSelect({ locations = [], selected, onSelect }) {
|
||||
|
||||
return (
|
||||
<div className="w-full max-w-[520px] mx-auto">
|
||||
<h2 className="text-[16px] font-bold text-[#3B3B3B] mb-4">۱. انتخاب محل نوبتدهی</h2>
|
||||
<h2 className="text-[16px] font-bold text-[var(--ap-text)] mb-4">۱. انتخاب محل نوبتدهی</h2>
|
||||
|
||||
{!anyOpen && locations.length > 0 && (
|
||||
<p className="mb-3 text-[13px] text-[#B4541F]">
|
||||
<p className="mb-3 text-[13px] text-[var(--ap-accent-strong)]">
|
||||
در روز انتخابشده هیچکدام از محلها نوبتدهی ندارند. روز دیگری را انتخاب کنید.
|
||||
</p>
|
||||
)}
|
||||
@@ -43,32 +43,32 @@ function LocationSelect({ locations = [], selected, onSelect }) {
|
||||
onClick={() => !closed && onSelect(location)}
|
||||
className={`w-full text-right p-[16px] rounded-[8px] border border-solid transition-colors ${
|
||||
closed
|
||||
? "border-[#EFEFEF] bg-[#FAFAFA] opacity-60 cursor-not-allowed"
|
||||
? "border-[var(--ap-border)] bg-[var(--ap-surface-2)] opacity-60 cursor-not-allowed"
|
||||
: active
|
||||
? "border-[#5559CE] bg-[rgba(85,89,206,0.06)]"
|
||||
: "border-[#EFEFEF] bg-[#FFF] hover:border-[#C7C9EC]"
|
||||
? "border-[var(--ap-primary)] bg-[rgba(85,89,206,0.06)]"
|
||||
: "border-[var(--ap-border)] bg-[var(--ap-surface)] hover:border-[#C7C9EC]"
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center justify-between gap-[8px]">
|
||||
<p className="text-[14px] md:text-[16px] font-bold text-[#3B3B3B]">
|
||||
<p className="text-[14px] md:text-[16px] font-bold text-[var(--ap-text)]">
|
||||
{location.title}
|
||||
</p>
|
||||
{location.type === "clinic" && (
|
||||
<span className="text-[11px] text-[#5559CE] bg-[rgba(85,89,206,0.10)] rounded-[4px] px-[6px] py-[2px]">
|
||||
<span className="text-[11px] text-[var(--ap-primary)] bg-[rgba(85,89,206,0.10)] rounded-[4px] px-[6px] py-[2px]">
|
||||
کلینیک
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{location.address && (
|
||||
<p className="mt-[6px] text-[13px] text-[#616161] font-normal">
|
||||
<p className="mt-[6px] text-[13px] text-[var(--ap-text-2)] font-normal">
|
||||
{location.address}
|
||||
</p>
|
||||
)}
|
||||
|
||||
<p
|
||||
className={`mt-[8px] text-[12px] ${
|
||||
closed ? "text-[#B4541F]" : nextLabel ? "text-[#009D79]" : "text-[#A1A1A1]"
|
||||
closed ? "text-[var(--ap-accent-strong)]" : nextLabel ? "text-[#009D79]" : "text-[#A1A1A1]"
|
||||
}`}
|
||||
>
|
||||
{closed
|
||||
|
||||
@@ -5,10 +5,10 @@ function Location({ doctor, selectedLocation }) {
|
||||
return (
|
||||
<div className="h-fit lg:h-screen w-full lg:w-[39%]">
|
||||
<div
|
||||
className="p-0 lg:p-[16px] sticky rounded-[8px] bg-transparent lg:bg-[#FFF] border border-solid border-transparent lg:border-[#EFEFEF] top-[calc(12px_+_75px_+_32px)] sm:top-[calc(21px_+_75px_+_45px)] md:top-[calc(30px_+_75px_+_58px)] lg:top-[calc(40px_+_75px_+_78px)]"
|
||||
className="p-0 lg:p-[16px] sticky rounded-[8px] bg-transparent lg:bg-[var(--ap-surface)] border border-solid border-transparent lg:border-[var(--ap-border)] top-[calc(12px_+_75px_+_32px)] sm:top-[calc(21px_+_75px_+_45px)] md:top-[calc(30px_+_75px_+_58px)] lg:top-[calc(40px_+_75px_+_78px)]"
|
||||
>
|
||||
<Head doctor={doctor} />
|
||||
<span className="block bg-[#EFEFEF] lg:bg-transparent w-full h-px lg:my-[6px] mt-[16px]"></span>
|
||||
<span className="block bg-[var(--ap-border)] lg:bg-transparent w-full h-px lg:my-[6px] mt-[16px]"></span>
|
||||
<Address doctor={doctor} selectedLocation={selectedLocation} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 />}
|
||||
|
||||
@@ -68,10 +68,10 @@ function ServiceSelect({ services = [], onContinue, doctorUuid, clinicUuid = nul
|
||||
|
||||
return (
|
||||
<div className="w-full max-w-[520px] mx-auto">
|
||||
<h2 className="text-[16px] font-bold text-[#3B3B3B] mb-4">۱. انتخاب سرویس</h2>
|
||||
<h2 className="text-[16px] font-bold text-[var(--ap-text)] mb-4">۱. انتخاب سرویس</h2>
|
||||
|
||||
{services.length === 0 ? (
|
||||
<p className="text-[14px] text-[#7A7A7A]">
|
||||
<p className="text-[14px] text-[var(--ap-text-3)]">
|
||||
در حال حاضر سرویسی برای نوبتدهی آنلاین تعریف نشده است.
|
||||
</p>
|
||||
) : (
|
||||
@@ -86,27 +86,27 @@ function ServiceSelect({ services = [], onContinue, doctorUuid, clinicUuid = nul
|
||||
onClick={() => toggle(s.uuid)}
|
||||
className={`flex items-center justify-between gap-3 p-3 rounded-xl border text-right transition-colors ${
|
||||
active
|
||||
? "border-[#5559CE] bg-[#5559CE]/5"
|
||||
: "border-gray-200 bg-white hover:border-[#5559CE]"
|
||||
? "border-[var(--ap-primary)] bg-[var(--ap-primary)]/5"
|
||||
: "border-gray-200 bg-white hover:border-[var(--ap-primary)]"
|
||||
}`}
|
||||
>
|
||||
<span className="flex items-center gap-2 min-w-0">
|
||||
<span
|
||||
className={`w-4 h-4 rounded border shrink-0 grid place-items-center ${
|
||||
active ? "border-[#5559CE] bg-[#5559CE]" : "border-gray-300"
|
||||
active ? "border-[var(--ap-primary)] bg-[var(--ap-primary)]" : "border-gray-300"
|
||||
}`}
|
||||
>
|
||||
{active && (
|
||||
<span className="w-2 h-2 bg-white rounded-[2px]" />
|
||||
)}
|
||||
</span>
|
||||
<span className="text-[14px] text-[#3B3B3B] truncate">
|
||||
<span className="text-[14px] text-[var(--ap-text)] truncate">
|
||||
{s.name}
|
||||
</span>
|
||||
</span>
|
||||
<span className="flex items-center gap-2 shrink-0 text-[12px] text-[#7A7A7A]">
|
||||
<span className="flex items-center gap-2 shrink-0 text-[12px] text-[var(--ap-text-3)]">
|
||||
{s.duration_minutes ? <span>{s.duration_minutes} دقیقه</span> : null}
|
||||
{toman ? <span className="text-[#5559CE]">{toman} تومان</span> : null}
|
||||
{toman ? <span className="text-[var(--ap-primary)]">{toman} تومان</span> : null}
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
@@ -115,11 +115,11 @@ function ServiceSelect({ services = [], onContinue, doctorUuid, clinicUuid = nul
|
||||
)}
|
||||
|
||||
{draft.length > 0 && (
|
||||
<div className="mt-4 text-[13px] text-[#7A7A7A]">
|
||||
<div className="mt-4 text-[13px] text-[var(--ap-text-3)]">
|
||||
{/* تا وقتی عدد سرور نرسیده، «تقریبی» است — عددِ جمعِ کلاینت ممکن است با
|
||||
مدت واقعی نوبت یکی نباشد. */}
|
||||
{serverMinutes === null ? "مدت تقریبی" : "مدت کل"}:{" "}
|
||||
<b className="text-[#3B3B3B]">{totalMinutes} دقیقه</b>
|
||||
<b className="text-[var(--ap-text)]">{totalMinutes} دقیقه</b>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -127,7 +127,7 @@ function ServiceSelect({ services = [], onContinue, doctorUuid, clinicUuid = nul
|
||||
type="button"
|
||||
disabled={draft.length === 0}
|
||||
onClick={() => onContinue(draft)}
|
||||
className="mt-5 w-full h-[48px] rounded-xl bg-[#5559CE] text-white text-[15px] font-medium disabled:opacity-50"
|
||||
className="mt-5 w-full h-[48px] rounded-xl bg-[var(--ap-primary)] text-white text-[15px] font-medium disabled:opacity-50"
|
||||
>
|
||||
انتخاب زمان
|
||||
</button>
|
||||
|
||||
@@ -7,17 +7,17 @@ function SuccessPay({ setStep }) {
|
||||
return (
|
||||
<div className="w-full lg:w-[59%]">
|
||||
<div
|
||||
className={`opacity-page bg-transparent lg:bg-[#FFF] border border-solid border-transparent lg:border-[#EFEFEF] rounded-[8px] p-[12px] sm:p-[16px] md:p-[20px] lg:p-[24px]`}
|
||||
className={`opacity-page bg-transparent lg:bg-[var(--ap-surface)] border border-solid border-transparent lg:border-[var(--ap-border)] rounded-[8px] p-[12px] sm:p-[16px] md:p-[20px] lg:p-[24px]`}
|
||||
>
|
||||
<div className="pt-[8px] pb-[4px] grid place-items-center">
|
||||
<div className="flex flex-col justify-center items-center gap-[14px]">
|
||||
<TickCircleGreenA />
|
||||
<p className="text-[#3B3B3B] text-[20px] font-bold">
|
||||
<p className="text-[var(--ap-text)] text-[20px] font-bold">
|
||||
پرداخت شما با موفقیت انجام شد.
|
||||
</p>
|
||||
</div>
|
||||
<p
|
||||
className="text-[#3B3B3B] text-[16px] font-normal text-center mt-[24px] sm:mt-[26px] md:mt-[29px] lg:mt-[32px] mb-[24px] md:mb-[16px]"
|
||||
className="text-[var(--ap-text)] text-[16px] font-normal text-center mt-[24px] sm:mt-[26px] md:mt-[29px] lg:mt-[32px] mb-[24px] md:mb-[16px]"
|
||||
>
|
||||
نوبت شما تأیید شد و پیامک تأیید برای بیمار ارسال میشود. برای نمایش نوبتها به قسمت نوبتهای من بروید.{" "}
|
||||
</p>
|
||||
@@ -26,7 +26,7 @@ function SuccessPay({ setStep }) {
|
||||
variant="text"
|
||||
className="!flex !items-center !justify-center !gap-[4px]"
|
||||
>
|
||||
<p className="text-[16px] font-medium text-[#5559CE]">
|
||||
<p className="text-[16px] font-medium text-[var(--ap-primary)]">
|
||||
برو به نوبت های من
|
||||
</p>
|
||||
<ArrowLeftBlueA />
|
||||
|
||||
Reference in New Issue
Block a user