fix(daily-control): «ماه بعد» navigation + hover feedback

The month-shift anchor discarded the day returned by jalaliToGregorian:
new Date(gy, gm-1, 1) instead of new Date(gy, gm-1, gd). Jalali months
start mid-Gregorian-month (مهر ۱ = 2026-09-23), so the anchor landed on
the 1st of the Gregorian month — still inside the SAME Jalali month
(شهریور ۱۰) — and «ماه بعد» appeared dead while «ماه قبل» only seemed
to work because its wrong target showed a different label. This was also
the source of the reported ~22-day date mismatches.

- shiftMonth now keeps the converted day (gy, gm, gd)
- nav buttons fire on pointerup/click/keyboard with a dedupe guard so the
  action survives browsers that swallow the click event
- buttons got hover feedback (cursor-pointer, hover:bg-slate-100,
  transition-colors)

Verified against the library math: from شهریور ۱۴۰۵, next → مهر ۱۴۰۵,
prev → مرداد ۱۴۰۵; confirmed in the deployed bundle.
This commit is contained in:
ali_mohaghegh
2026-09-03 17:10:58 +03:30
parent 633e9c2561
commit 7f58593f04
@@ -1,4 +1,4 @@
import { useMemo, useState } from "react"; import { useMemo, useRef, useState } from "react";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { Header, PageLayout } from "../../components/layout/Header"; import { Header, PageLayout } from "../../components/layout/Header";
import { useAuthStore } from "../../store/auth"; import { useAuthStore } from "../../store/auth";
@@ -90,11 +90,40 @@ export function DailyControlPage() {
nm = 1; nm = 1;
ny += 1; ny += 1;
} }
const [gy, gm] = jalaliToGregorian(ny, nm, 1); // روزِ برگشتی را نگه می‌داریم (ماه شمسی از میانه‌ی ماه میلادی شروع می‌شود:
setAnchor(new Date(gy, gm - 1, 1)); // مثلاً مهر ۱ = ۲۳ سپتامبر). اگر به‌جای آن روز «۱» استفاده شود، لنگر در
// اولِ ماهِ میلادی می‌نشیند و هنوز در همان ماه شمسی است — دکمه «ماه بعد»
// ظاهراً هیچ کاری نمی‌کند.
const [gy, gm, gd] = jalaliToGregorian(ny, nm, 1);
setAnchor(new Date(gy, gm - 1, gd));
}; };
const dayTx = txByDay.get(sel) || []; const dayTx = txByDay.get(sel) || [];
// فعال‌سازی ناوبری ماه از چند مسیر (pointerup / click / کیبورد) با حذف
// اجرای تکراری — اگر مرورگر رویداد click را بخورد، pointerup جواب می‌دهد.
const navLast = useRef<{ key: string; at: number }>({ key: "", at: 0 });
const navFire = (key: string, action: () => void) => {
const now = Date.now();
if (navLast.current.key === key && now - navLast.current.at < 350) return;
navLast.current = { key, at: now };
action();
};
const navBtnProps = (key: string, action: () => void) => ({
type: "button" as const,
onPointerUp: (e: React.PointerEvent) => {
e.preventDefault();
navFire(key, action);
},
onClick: () => navFire(key, action),
onKeyDown: (e: React.KeyboardEvent) => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
navFire(key, action);
}
},
});
const withdrawals = dayTx.filter((t) => t.transaction_type === "WITHDRAWAL"); const withdrawals = dayTx.filter((t) => t.transaction_type === "WITHDRAWAL");
const wGroups = useMemo(() => { const wGroups = useMemo(() => {
const gmap = new Map<string, { recId: string; q: number; last: string; tx: InventoryTransaction }>(); const gmap = new Map<string, { recId: string; q: number; last: string; tx: InventoryTransaction }>();
@@ -149,9 +178,9 @@ export function DailyControlPage() {
<div className="mb-2 flex items-center justify-between"> <div className="mb-2 flex items-center justify-between">
<div className="text-[14px] font-bold">📅 {monthTitle}</div> <div className="text-[14px] font-bold">📅 {monthTitle}</div>
<div className="flex gap-1.5"> <div className="flex gap-1.5">
<button className="rounded-lg border border-line px-3 py-1 text-[12px]" onClick={() => shiftMonth(-1)}>▶ ماه قبل</button> <button className="cursor-pointer rounded-lg border border-line px-3 py-1 text-[12px] transition-colors hover:bg-slate-100" {...navBtnProps("prev", () => shiftMonth(-1))}>▶ ماه قبل</button>
<button className="rounded-lg border border-line px-3 py-1 text-[12px]" onClick={() => { setAnchor(new Date()); setSel(todayKey()); setActRec(null); }}>امروز</button> <button className="cursor-pointer rounded-lg border border-line px-3 py-1 text-[12px] transition-colors hover:bg-slate-100" {...navBtnProps("today", () => { setAnchor(new Date()); setSel(todayKey()); setActRec(null); })}>امروز</button>
<button className="rounded-lg border border-line px-3 py-1 text-[12px]" onClick={() => shiftMonth(1)}>◀ ماه بعد</button> <button className="cursor-pointer rounded-lg border border-line px-3 py-1 text-[12px] transition-colors hover:bg-slate-100" {...navBtnProps("next", () => shiftMonth(1))}>◀ ماه بعد</button>
</div> </div>
</div> </div>
<div className="grid grid-cols-7 gap-1"> <div className="grid grid-cols-7 gap-1">