fix(dashboard): appointment detail fields + sidebar hydration
- Appointment detail (DetailLg/DetailSm) read the real response shape: date/time from slot_start, specialty from doctor.specialties[0].name, phone from address.telephone. - Sidebar Head reads userInfo (cookie) after mount to avoid an SSR/client hydration mismatch on the user's name. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
# اصلاح فیلدهای «جزئیات نوبت» در داشبورد
|
||||
|
||||
## پروژه
|
||||
|
||||
`nobat724_front` (سایت عمومی، داشبورد).
|
||||
|
||||
> **Cross-repo:** وابسته به پرامپت بکاند `clinicpro/.claude/prompt/appointment-detail-enrich.md` که `doctor.specialties` و `address` را به پاسخ نوبت اضافه میکند. آن **اول** اجرا شود.
|
||||
|
||||
## زمینه
|
||||
|
||||
در داشبورد → «نوبتهای من» → «مشاهده جزئیات»، کارت «جزئیات نوبت شما» (`DetailLg`/`DetailSm`) فیلدهایی میخواند که با شکل واقعی پاسخ نوبت نمیخوانند، پس تاریخ/ساعت/تخصص/آدرس/تلفن خالی یا غلط نمایش داده میشوند.
|
||||
|
||||
پاسخ واقعی نوبت (پس از پرامپت بکاند): `{ uuid, doctor:{name, specialties:[{uuid,name}]}, address:{address, telephone, map:{latitude,longitude}, ...}, slot_start, slot_end, status, ... }`. تاریخها Unixاند.
|
||||
|
||||
## مشکل / هدف
|
||||
|
||||
نگاشت فیلدها در `DetailLg.js` و `DetailSm.js` به مقادیر واقعی:
|
||||
|
||||
| نمایش | فعلی (غلط) | درست |
|
||||
|------|-----------|------|
|
||||
| تاریخ نوبت | `appointmentData?.start_time` | `appointmentData?.slot_start` |
|
||||
| ساعت نوبت | `appointmentData?.slot?.time` | `convertTimestampToTime(appointmentData?.slot_start)` |
|
||||
| تخصص | `appointmentData?.doctor?.specialty?.name` | `appointmentData?.doctor?.specialties?.[0]?.name` |
|
||||
| آدرس | `appointmentData?.address?.address` | همان (درست) |
|
||||
| تلفن | `appointmentData?.address?.phone` | `appointmentData?.address?.telephone` |
|
||||
| نقشه | `appointmentData?.address?.map` | همان (درست) |
|
||||
|
||||
## فایلهای مرتبط
|
||||
|
||||
| فایل | نقش |
|
||||
|------|-----|
|
||||
| `components/dashboard/userAccount/sidebars/turns/isTurnsDetails/DetailLg.js` | نمای دسکتاپ جزئیات |
|
||||
| `components/dashboard/userAccount/sidebars/turns/isTurnsDetails/DetailSm.js` | نمای موبایل جزئیات |
|
||||
|
||||
## وضعیت فعلی (کد واقعی — هر دو فایل مشابه)
|
||||
|
||||
```jsx
|
||||
{convertTimestampToJalali(appointmentData?.start_time)} // ❌ start_time
|
||||
ساعت {appointmentData?.slot?.time} // ❌ slot.time
|
||||
تخصص: {appointmentData?.doctor?.specialty?.name} // ❌ specialty.name
|
||||
{appointmentData?.address?.phone} // ❌ phone
|
||||
```
|
||||
`convertTimestampToTime` از قبل در `helper` هست و در `DetailLg` import شده.
|
||||
|
||||
## وظایف
|
||||
|
||||
### ۱. اصلاح `DetailLg.js`
|
||||
- تاریخ: `convertTimestampToJalali(appointmentData?.slot_start)`
|
||||
- ساعت: `convertTimestampToTime(appointmentData?.slot_start)` (بهجای `appointmentData?.slot?.time`)
|
||||
- تخصص: `appointmentData?.doctor?.specialties?.[0]?.name`
|
||||
- تلفن: `appointmentData?.address?.telephone`
|
||||
- آدرس و `map` (مسیریابی) بدون تغییر (درستاند).
|
||||
|
||||
### ۲. اصلاح `DetailSm.js`
|
||||
همان نگاشتها را اعمال کن (این فایل `convertTimestampToTime` را import نکرده باشد، اضافه کن). ساعت از `slot_start`.
|
||||
|
||||
## نکات مهم
|
||||
|
||||
- اگر تخصص خالی بود (`specialties` خالی) یا آدرس `null` بود (پزشک بدون آدرس)، بخش مربوط نباید crash کند — با optional chaining (`?.`) و عدم نمایش خطی که داده ندارد، تحمل کن.
|
||||
- `slot_start` Unix ثانیه است؛ `convertTimestampToJalali`/`convertTimestampToTime` همان را میگیرند (مثل `List.js`).
|
||||
- `address.map.latitude/longitude` رشتهاند (از `DoctorAddress::toArray`) — برای لینک مسیریابی کافیاند.
|
||||
- App Router، RTL، فونت Vazir؛ ساختار/استایل کارت حفظ شود، فقط منبع داده اصلاح شود.
|
||||
- بعد از تغییر: `npm run build` بدون خطا؛ دستی — یک نوبت را باز کن، تاریخ/ساعت/پزشک/تخصص/آدرس/تلفن درست نمایش داده شوند.
|
||||
@@ -60,7 +60,7 @@ function DetailLg({ appointmentData, loading }) {
|
||||
</TextLoading>
|
||||
<TextLoading loading={loading} width={80} height={18}>
|
||||
<p className="text-[#616161] text-[14px] md:text-[15px] lg:text-[16px] font-medium">
|
||||
تخصص: {appointmentData?.doctor?.specialty?.name}
|
||||
تخصص: {appointmentData?.doctor?.specialties?.[0]?.name}
|
||||
</p>
|
||||
</TextLoading>
|
||||
</div>
|
||||
@@ -73,7 +73,7 @@ function DetailLg({ appointmentData, loading }) {
|
||||
<p className="text-[#616161] text-[14px] lg:text-[20px] font-medium">
|
||||
تاریخ نوبت:{" "}
|
||||
<span className="text-[#2F2F2F] text-[14px] lg:text-[20px] font-bold">
|
||||
{convertTimestampToJalali(appointmentData?.start_time)}
|
||||
{convertTimestampToJalali(appointmentData?.slot_start)}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
@@ -84,7 +84,7 @@ function DetailLg({ appointmentData, loading }) {
|
||||
<p className="text-[#616161] text-[14px] lg:text-[20px] font-medium">
|
||||
زمان نوبت:{" "}
|
||||
<span className="text-[#2F2F2F] text-[14px] lg:text-[20px] font-bold">
|
||||
ساعت {appointmentData?.slot?.time}
|
||||
ساعت {convertTimestampToTime(appointmentData?.slot_start)}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
@@ -126,7 +126,7 @@ function DetailLg({ appointmentData, loading }) {
|
||||
<p className="text-[#616161] text-[14px] lg:text-[16px] font-normal">
|
||||
تلفن:{" "}
|
||||
<span className="text-[#2F2F2F] text-[14px] lg:text-[16px] font-normal">
|
||||
{appointmentData?.address?.phone}
|
||||
{appointmentData?.address?.telephone}
|
||||
</span>
|
||||
</p>
|
||||
</li>
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Button } from "@mui/material";
|
||||
import React, { useRef } from "react";
|
||||
import ButtonData from "./ButtonData";
|
||||
import TextLoading from "@/app/component/loading/Text";
|
||||
import { convertTimestampToJalali } from "@/helper";
|
||||
import { convertTimestampToJalali, convertTimestampToTime } from "@/helper";
|
||||
import html2canvas from "html2canvas";
|
||||
import jsPDF from "jspdf";
|
||||
|
||||
@@ -56,7 +56,7 @@ function DetailSm({ appointmentData, loading }) {
|
||||
</TextLoading>
|
||||
<TextLoading loading={loading} width={70} height={14}>
|
||||
<p className="text-[#7E7E7E] text-[14px] font-normal">
|
||||
{appointmentData?.doctor?.specialty?.name}
|
||||
{appointmentData?.doctor?.specialties?.[0]?.name}
|
||||
</p>
|
||||
</TextLoading>
|
||||
</div>
|
||||
@@ -70,7 +70,7 @@ function DetailSm({ appointmentData, loading }) {
|
||||
</TextLoading>
|
||||
<TextLoading loading={loading} width={55} height={16}>
|
||||
<p className="text-[#525252] text-[16px] font-medium">
|
||||
{convertTimestampToJalali(appointmentData?.start_time)}
|
||||
{convertTimestampToJalali(appointmentData?.slot_start)}
|
||||
</p>
|
||||
</TextLoading>
|
||||
</li>
|
||||
@@ -81,7 +81,7 @@ function DetailSm({ appointmentData, loading }) {
|
||||
</TextLoading>
|
||||
<TextLoading loading={loading} width={60} height={16}>
|
||||
<p className="text-[#525252] text-[16px] font-medium">
|
||||
{appointmentData?.slot?.time}
|
||||
{convertTimestampToTime(appointmentData?.slot_start)}
|
||||
</p>
|
||||
</TextLoading>
|
||||
</li>
|
||||
@@ -112,7 +112,7 @@ function DetailSm({ appointmentData, loading }) {
|
||||
</li>
|
||||
<TextLoading loading={loading} width={100} height={18}>
|
||||
<p className="text-[#7E7E7E] text-[14px] font-normal mt-[12px]">
|
||||
تلفن: <span className="text-[#616161]">{appointmentData?.address?.phone}</span>
|
||||
تلفن: <span className="text-[#616161]">{appointmentData?.address?.telephone}</span>
|
||||
</p>
|
||||
</TextLoading>
|
||||
</ul>
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
import { getParsedUserInfo } from "@/helper";
|
||||
import Image from "next/image";
|
||||
import { imageUrl } from "@/helper";
|
||||
"use client";
|
||||
|
||||
function Head({ user }) {
|
||||
const userInfo = getParsedUserInfo();
|
||||
import { useEffect, useState } from "react";
|
||||
import { getParsedUserInfo, imageUrl } from "@/helper";
|
||||
import Image from "next/image";
|
||||
|
||||
function Head() {
|
||||
const [userInfo, setUserInfo] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
setUserInfo(getParsedUserInfo());
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="w-full flex flex-row md:flex-col items-center justify-center md:justify-start gap-[12px] md:gap-[4px]">
|
||||
|
||||
Reference in New Issue
Block a user