- JalaliDatePicker gains an opt-in `stepwise` mode (year → month → day): the year step is a scrollable list from the current Jalali year down to 1332, used by the birthday field. Default-off so appointment/panel pickers keep the month-grid behavior. Parse incoming Jalali string values safely (fixes NaN keys). - Birthday create path now converts Jalali→Unix on send (changeDateType true), matching the update path and the Unix-based backend contract. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
108 lines
7.5 KiB
Markdown
108 lines
7.5 KiB
Markdown
# انتخاب تاریخ تولد بهصورت مرحلهای: سال ← ماه ← روز
|
|
|
|
## پروژه
|
|
|
|
`nobat724_front` (سایت عمومی، داشبورد کاربر).
|
|
|
|
> کاملاً frontend است. هیچ تغییری در بکاند یا قرارداد API لازم نیست؛ خروجی همان رشتهی جلالی فعلی است.
|
|
|
|
## زمینه
|
|
|
|
در داشبورد (`/dashboard` → اطلاعات کاربری)، فیلد «تاریخ تولد» از `components/common/JalaliDatePicker` استفاده میکند که فقط یک نمای **ماهانه** دارد: هدر فقط «ماه سال» را نشان میدهد و با فلشها فقط ماهبهماه جلو/عقب میرود. برای انتخاب سال تولد (مثلاً ۱۳۷۰) کاربر باید دهها بار فلش بزند — تجربهی بدی برای تاریخ تولد است.
|
|
|
|
خواسته: تقویمِ تاریخ تولد باید مرحلهای باشد — **اول سال، بعد ماه، بعد روز**.
|
|
|
|
## مشکل / هدف
|
|
|
|
افزودن یک حالتِ انتخابِ مرحلهای (year → month → day) به `JalaliDatePicker` بهصورت **opt-in با prop**، و فعالکردن آن فقط برای فیلد تاریخ تولد. سایر استفادهها (انتخاب تاریخ نوبت و پنل) باید **دستنخورده** بمانند.
|
|
|
|
## فایلهای مرتبط
|
|
|
|
| فایل | نقش |
|
|
|------|-----|
|
|
| `components/common/JalaliDatePicker.js` | date picker جلالیِ مشترک — افزودن حالت مرحلهای |
|
|
| `components/dashboard/userAccount/detailUser/information/form/DateBirthday.js` | فیلد تاریخ تولد — فعالکردن حالت جدید با prop |
|
|
|
|
> **هشدار سازگاری:** `JalaliDatePicker` در این فایلها هم استفاده میشود و نباید رفتارشان تغییر کند:
|
|
> `app/component/DatePickerContent.js`, `app/component/date/datePicker/date/FirstDatePicker.js`, `.../SecondDatePicker.js`, `components/layoutPanel/userDetail/Date.js`. پس حالت جدید باید پیشفرض **خاموش** باشد.
|
|
|
|
## وضعیت فعلی (کد واقعی)
|
|
|
|
`JalaliDatePicker.js` — فقط نمای ماهانه. هدر و ناوبری:
|
|
```jsx
|
|
const handlePrevMonth = () => setDisplayDate(moment(displayDate).subtract(1, "jMonth"));
|
|
const handleNextMonth = () => setDisplayDate(moment(displayDate).add(1, "jMonth"));
|
|
// ...
|
|
// هدر: فقط ماه و سال + دو فلش ماه
|
|
<Box sx={{ fontWeight: 600, fontSize: "14px" }}>
|
|
{MONTHS[displayDate.jMonth()]} {displayDate.jYear()}
|
|
</Box>
|
|
// ... سپس گرید روزها
|
|
```
|
|
`MONTHS` (۱۲ ماه جلالی) از قبل در همین فایل تعریف شده. `moment` = `moment-jalaali`؛ متدها: `jYear()`, `jMonth()`, `jDate()`, `moment.jDaysInMonth(y, m)`.
|
|
|
|
`DateBirthday.js`:
|
|
```jsx
|
|
<JalaliDatePicker
|
|
value={data}
|
|
placeholder="تاریخ تولد"
|
|
error={error}
|
|
onChange={(e) => changeData(convertToJalali(e._d), "birthday")}
|
|
textFieldProps={{ /* CalendarEdit icon */ }}
|
|
/>
|
|
```
|
|
|
|
## وظایف
|
|
|
|
### ۱. افزودن حالت مرحلهای به `JalaliDatePicker`
|
|
|
|
- یک prop جدید بگیر: `stepwise = false` (پیشفرض خاموش تا سازگاری حفظ شود).
|
|
- یک state داخلی `viewMode` با مقادیر `"year" | "month" | "day"`. وقتی Popover با `stepwise=true` باز میشود، `viewMode` روی `"year"` شروع شود؛ در غیر این صورت رفتار فعلی (مستقیم نمای روز) حفظ شود.
|
|
- **نمای سال:** یک گرید از سالها (مثلاً ۱۲ سال در هر صفحه، با دو فلش برای صفحهی قبل/بعدِ بازهی سالها). بازهی منطقی برای تولد: از `currentJalaliYear - 100` تا `currentJalaliYear`. انتخاب سال → `displayDate` با آن سال بهروز شود و `viewMode = "month"`.
|
|
- **نمای ماه:** گرید ۱۲ ماه از `MONTHS`. انتخاب ماه → `displayDate` با آن ماه، و `viewMode = "day"`.
|
|
- **نمای روز:** همان گرید روزهای فعلی (بدون تغییر). انتخاب روز → `onChange` صدا زده شود و Popover بسته شود (مثل حالا).
|
|
- در هدرِ حالت مرحلهای، عنوان قابلکلیک باشد تا کاربر بتواند به مرحلهی بالاتر برگردد: در نمای ماه، کلیک روی سال → برگشت به نمای سال؛ در نمای روز، کلیک روی «ماه سال» → برگشت به نمای ماه. (ناوبری رو به عقب)
|
|
- وقتی Popover بسته شد، `viewMode` برای بار بعد دوباره `"year"` شود.
|
|
|
|
طرح کلی:
|
|
```jsx
|
|
function JalaliDatePicker({ /* ...props */, stepwise = false }) {
|
|
const [viewMode, setViewMode] = useState(stepwise ? "year" : "day");
|
|
// باز شدن popover (stepwise): setViewMode("year")
|
|
// بسته شدن: setViewMode(stepwise ? "year" : "day")
|
|
|
|
// نمای سال
|
|
const renderYears = () => { /* گرید سالها currentYear-100..currentYear */ };
|
|
// نمای ماه
|
|
const renderMonths = () => { /* گرید MONTHS، onClick → setDisplayDate(jMonth) + setViewMode("day") */ };
|
|
// نمای روز = همان رندر فعلی
|
|
|
|
// در بدنهی Popover:
|
|
// stepwise ? (viewMode==="year" ? renderYears() : viewMode==="month" ? renderMonths() : renderDays())
|
|
// : renderDays()
|
|
}
|
|
```
|
|
- منطق `value`/`selectedDate`/`onChange` و خروجی (همان `moment` object که `DateBirthday` با `e._d` و `convertToJalali` مصرف میکند) **بدون تغییر** بماند.
|
|
|
|
### ۲. فعالکردن در `DateBirthday.js`
|
|
فقط prop اضافه کن:
|
|
```jsx
|
|
<JalaliDatePicker
|
|
value={data}
|
|
placeholder="تاریخ تولد"
|
|
error={error}
|
|
stepwise
|
|
onChange={(e) => changeData(convertToJalali(e._d), "birthday")}
|
|
textFieldProps={{ /* بدون تغییر */ }}
|
|
/>
|
|
```
|
|
|
|
## نکات مهم
|
|
|
|
- **سازگاری عقبرو:** `stepwise` پیشفرض `false`؛ هیچیک از ۴ مصرفکنندهی دیگر (تاریخ نوبت/پنل) نباید رفتارشان عوض شود. این را با grep تأیید کن و دست به آن فایلها نزن.
|
|
- خروجی `onChange` دقیقاً همان شیء `moment` فعلی باشد تا `convertToJalali(e._d)` در `DateBirthday` و مصرف بکاند (`birthday`/`date_of_birth`) نشکند.
|
|
- بازهی سالها برای تولد: `[امسالِ جلالی - 100, امسالِ جلالی]`، نزولی (سالهای جدیدتر بالا) یا صعودی — هرکدام UX بهتری دارد؛ پیشفرض نزولی منطقیتر است.
|
|
- RTL، فونت Vazir، MUI v5 (`Box`/`Popover`/`IconButton` که از قبل import شدهاند)؛ کتابخانهی جدید اضافه نکن. از `MONTHS` و `moment-jalaali` موجود در همان فایل استفاده کن.
|
|
- اعداد فارسی/لاتین: مطابق رفتار فعلی فایل (`usePersianDigits: false`) نگهدار تا یکدست بماند.
|
|
- بعد از تغییر: `npm run build` بدون خطا؛ دستی تست کن — در داشبورد فیلد تاریخ تولد: کلیک → ابتدا گرید سالها، انتخاب سال → ماهها، انتخاب ماه → روزها، انتخاب روز → مقدار در فیلد ست شود؛ و یک فیلد تاریخِ نوبت را هم چک کن که هنوز مثل قبل (مستقیم نمای روز) کار میکند.
|