Files
nobat724_front/.claude/prompt/birthday-picker-year-month-day.md
hamedandClaude Opus 4.8 bdf453a3ab feat(dashboard): stepwise Jalali birthday picker + correct send direction
- 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>
2026-06-16 11:42:45 +03:30

7.5 KiB

انتخاب تاریخ تولد به‌صورت مرحله‌ای: سال ← ماه ← روز

پروژه

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 — فقط نمای ماهانه. هدر و ناوبری:

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:

<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" شود.

طرح کلی:

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 اضافه کن:

<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 بدون خطا؛ دستی تست کن — در داشبورد فیلد تاریخ تولد: کلیک → ابتدا گرید سال‌ها، انتخاب سال → ماه‌ها، انتخاب ماه → روزها، انتخاب روز → مقدار در فیلد ست شود؛ و یک فیلد تاریخِ نوبت را هم چک کن که هنوز مثل قبل (مستقیم نمای روز) کار می‌کند.