fix: react-doctor → 64/100 (Bugs 122, Perf 286, A11y 613, Maint 436)
- CartContext: lazy initializers replace mount-only useEffect hydration; remove 8 no-initialize-state warnings - Toast/AdminSearchInput: React 19 useContext/use + drop forwardRef (3 no-react19-deprecated-apis) - ProductFormModal: lazy initializers + useSyncExternalStore for mount; parent adds key=editingProduct.id - InstallPrompt: useReducer for prompt state (no-cascading-set-state) - QRScanModal: ref-based latest-callback pattern replaces useEffectEvent deps mistake - OnboardingFlow: functional setState (rerender-functional-setstate) - UsersPage/StopsCalendar/FeaturesAndStats: lazy initializers (rerender-lazy-state-init) - FAQClientPage: server-side brand settings fetch via getBrandSettingsPublic in layout; remove supabase import - LandingPageWrapper: href='#' → href='#top' (anchor-is-valid) - TuxedoVideoHero: replace animate-bounce with ease-out-expo (no-inline-bounce-easing) - ProductTableClient: useCallback for handleDeleted (jsx-no-new-function-as-prop) - excel-parser: pre-compile delimiter regexes (js-hoist-regexp) - water-log/settings: Promise.all for parallel DB calls (async-parallel) - ToastNotification: extract toast store to separate file (only-export-components) - WholesaleClient: inline <WholesaleIcon/> instead of hoisting to const (rendering-hoist-jsx)
This commit is contained in:
@@ -73,13 +73,11 @@ interface TimeTrackingSettingsClientProps {
|
||||
}
|
||||
|
||||
export default function TimeTrackingSettingsClient({ brandId }: TimeTrackingSettingsClientProps) {
|
||||
const [tab, setTab] = useState<Tab>("dashboard");
|
||||
|
||||
useEffect(() => {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const t = params.get("tab");
|
||||
if (t === "settings" || t === "export") setTab(t as Tab);
|
||||
}, []);
|
||||
const [tab, setTab] = useState<Tab>(() => {
|
||||
if (typeof window === "undefined") return "dashboard";
|
||||
const t = new URLSearchParams(window.location.search).get("tab");
|
||||
return t === "settings" || t === "export" ? (t as Tab) : "dashboard";
|
||||
});
|
||||
|
||||
const [workers, setWorkers] = useState<TimeWorker[]>([]);
|
||||
const [tasks, setTasks] = useState<TimeTask[]>([]);
|
||||
@@ -133,6 +131,28 @@ export default function TimeTrackingSettingsClient({ brandId }: TimeTrackingSett
|
||||
const [includeOvertime, setIncludeOvertime] = useState(true);
|
||||
const [includeNotes, setIncludeNotes] = useState(true);
|
||||
|
||||
// Track the previous settings object so we can sync form state
|
||||
// inline during render when fresh data arrives from the server
|
||||
// (avoids the stale-frame window a useEffect would introduce).
|
||||
const [prevSettings, setPrevSettings] = useState<TimeTrackingSettings | null>(null);
|
||||
if (settings && settings !== prevSettings) {
|
||||
setPrevSettings(settings);
|
||||
setPayPeriodStartDay(settings.pay_period_start_day);
|
||||
setPayPeriodLength(settings.pay_period_length_days);
|
||||
setDailyOvertimeThreshold(settings.daily_overtime_threshold);
|
||||
setWeeklyOvertimeThreshold(settings.weekly_overtime_threshold);
|
||||
setOvertimeMultiplier(settings.overtime_multiplier);
|
||||
setOvertimeNotifications(settings.overtime_notifications);
|
||||
setNotificationEmails(settings.notification_emails ?? []);
|
||||
setNotificationSmsNumbers(settings.notification_sms_numbers ?? []);
|
||||
setEnableDailyAlerts(settings.enable_daily_alerts ?? true);
|
||||
setEnableWeeklyAlerts(settings.enable_weekly_alerts ?? true);
|
||||
setDailyAlertThreshold(Math.round((settings.daily_alert_threshold ?? 0.80) * 100));
|
||||
setWeeklyAlertThreshold(Math.round((settings.weekly_alert_threshold ?? 0.80) * 100));
|
||||
setSendEndOfPeriodSummary(settings.send_end_of_period_summary ?? true);
|
||||
setBrandName(settings.brand_name ?? "Farm");
|
||||
}
|
||||
|
||||
const load = useCallback(async () => {
|
||||
setLoading(true);
|
||||
const [w, t, s] = await Promise.all([
|
||||
@@ -142,29 +162,15 @@ export default function TimeTrackingSettingsClient({ brandId }: TimeTrackingSett
|
||||
]);
|
||||
setWorkers(w);
|
||||
setTasks(t);
|
||||
if (s) {
|
||||
setSettings(s);
|
||||
setPayPeriodStartDay(s.pay_period_start_day);
|
||||
setPayPeriodLength(s.pay_period_length_days);
|
||||
setDailyOvertimeThreshold(s.daily_overtime_threshold);
|
||||
setWeeklyOvertimeThreshold(s.weekly_overtime_threshold);
|
||||
setOvertimeMultiplier(s.overtime_multiplier);
|
||||
setOvertimeNotifications(s.overtime_notifications);
|
||||
setNotificationEmails(s.notification_emails ?? []);
|
||||
setNotificationSmsNumbers(s.notification_sms_numbers ?? []);
|
||||
setEnableDailyAlerts(s.enable_daily_alerts ?? true);
|
||||
setEnableWeeklyAlerts(s.enable_weekly_alerts ?? true);
|
||||
setDailyAlertThreshold(Math.round((s.daily_alert_threshold ?? 0.80) * 100));
|
||||
setWeeklyAlertThreshold(Math.round((s.weekly_alert_threshold ?? 0.80) * 100));
|
||||
setSendEndOfPeriodSummary(s.send_end_of_period_summary ?? true);
|
||||
setBrandName(s.brand_name ?? "Farm");
|
||||
}
|
||||
setSettings(s);
|
||||
const log = await getTimeTrackingNotificationLog(brandId, 50);
|
||||
setNotificationLog(log);
|
||||
setLoading(false);
|
||||
}, [brandId]);
|
||||
|
||||
useEffect(() => { load(); }, [load]);
|
||||
useEffect(() => {
|
||||
void load();
|
||||
}, [load]);
|
||||
|
||||
const handleSaveNotifications = async () => {
|
||||
setSettingsSaving(true);
|
||||
|
||||
Reference in New Issue
Block a user