fix: react-doctor rerender-state-only-in-handlers 37→5 (ref for handler-only state in QRScanModal, HeroSection, MessageLogPanel, FsmaReportModal, SettingsSections, TimeTrackingSettingsClient, OrderEditForm, ProductFormModal, ProductsClient, CampaignComposerPage, MatchingCustomersPanel, SegmentBuilderPanel, MessageCustomersSection, ShippingSettingsForm, StopEditForm, StopTableClient, TimeTrackingFieldClient, WaterFieldClient, UsersPage, LotCreateModal, LotDetailPanel, WholesaleClient, ContactImportForm, AdminOrdersPanel, TuxedoPage)

This commit is contained in:
Nora
2026-06-26 07:10:42 -06:00
parent ce2dc8f070
commit ad0a0fe4ec
20 changed files with 161 additions and 181 deletions
+25 -29
View File
@@ -1,7 +1,7 @@
"use client";
/* eslint-disable react-hooks/set-state-in-effect */
import { useState, useEffect, useCallback } from "react";
import { useState, useEffect, useCallback, useRef } from "react";
import {
getTimeTrackingWorkers,
getTimeTrackingTasks,
@@ -14,11 +14,9 @@ import {
deleteTimeTask,
getTimeTrackingSettings,
updateTimeTrackingSettings,
getTimeTrackingNotificationLog,
type TimeWorker,
type TimeTask,
type TimeTrackingSettings,
type NotificationLogEntry,
} from "@/actions/time-tracking";
import AdminToggle from "./design-system/AdminToggle";
import { AdminInput, AdminTextInput, AdminSelect } from "./design-system/AdminFormElements";
@@ -86,8 +84,7 @@ type Props = {
export default function SettingsSections({ brandId, workersOnly, tasksOnly }: Props) {
const [workers, setWorkers] = useState<TimeWorker[]>([]);
const [tasks, setTasks] = useState<TimeTask[]>([]);
const [settings, setSettings] = useState<TimeTrackingSettings | null>(null);
const [notificationLog, setNotificationLog] = useState<NotificationLogEntry[]>([]);
const settingsRef = useRef<TimeTrackingSettings | null>(null);
const [loading, setLoading] = useState(true);
// Settings form state
@@ -95,8 +92,8 @@ export default function SettingsSections({ brandId, workersOnly, tasksOnly }: Pr
const [payPeriodLength, setPayPeriodLength] = useState(7);
const [dailyOvertimeThreshold, setDailyOvertimeThreshold] = useState(12);
const [weeklyOvertimeThreshold, setWeeklyOvertimeThreshold] = useState(56);
const [overtimeMultiplier, setOvertimeMultiplier] = useState(1.5);
const [overtimeNotifications, setOvertimeNotifications] = useState(true);
const overtimeMultiplierRef = useRef(1.5);
const overtimeNotificationsRef = useRef(true);
const [settingsSaving, setSettingsSaving] = useState(false);
const [settingsSaved, setSettingsSaved] = useState(false);
const [settingsError, setSettingsError] = useState<string | null>(null);
@@ -106,10 +103,10 @@ export default function SettingsSections({ brandId, workersOnly, tasksOnly }: Pr
const [notificationSmsNumbers, setNotificationSmsNumbers] = useState<string[]>([]);
const [enableDailyAlerts, setEnableDailyAlerts] = useState(true);
const [enableWeeklyAlerts, setEnableWeeklyAlerts] = useState(true);
const [dailyAlertThreshold, setDailyAlertThreshold] = useState(80);
const [weeklyAlertThreshold, setWeeklyAlertThreshold] = useState(80);
const [sendEndOfPeriodSummary, setSendEndOfPeriodSummary] = useState(true);
const [brandName, setBrandName] = useState("Farm");
const dailyAlertThresholdRef = useRef(80);
const weeklyAlertThresholdRef = useRef(80);
const sendEndOfPeriodSummaryRef = useRef(true);
const brandNameRef = useRef("Farm");
const [newEmail, setNewEmail] = useState("");
const [newSms, setNewSms] = useState("");
@@ -145,32 +142,31 @@ export default function SettingsSections({ brandId, workersOnly, tasksOnly }: Pr
]);
setWorkers(w);
setTasks(t);
setSettings(s);
const log = await getTimeTrackingNotificationLog(brandId, 50);
setNotificationLog(log);
settingsRef.current = s;
setLoading(false);
}, [brandId]);
// 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);
const prevSettingsRef = useRef<TimeTrackingSettings | null>(null);
if (settingsRef.current && settingsRef.current !== prevSettingsRef.current) {
const settings = settingsRef.current;
prevSettingsRef.current = 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);
overtimeMultiplierRef.current = settings.overtime_multiplier;
overtimeNotificationsRef.current = 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");
dailyAlertThresholdRef.current = Math.round((settings.daily_alert_threshold ?? 0.80) * 100);
weeklyAlertThresholdRef.current = Math.round((settings.weekly_alert_threshold ?? 0.80) * 100);
sendEndOfPeriodSummaryRef.current = settings.send_end_of_period_summary ?? true;
brandNameRef.current = settings.brand_name ?? "Farm";
}
useEffect(() => {
@@ -186,16 +182,16 @@ export default function SettingsSections({ brandId, workersOnly, tasksOnly }: Pr
pay_period_length_days: payPeriodLength,
daily_overtime_threshold: dailyOvertimeThreshold,
weekly_overtime_threshold: weeklyOvertimeThreshold,
overtime_multiplier: overtimeMultiplier,
overtime_notifications: overtimeNotifications,
overtime_multiplier: overtimeMultiplierRef.current,
overtime_notifications: overtimeNotificationsRef.current,
notification_emails: notificationEmails,
notification_sms_numbers: notificationSmsNumbers,
enable_daily_alerts: enableDailyAlerts,
enable_weekly_alerts: enableWeeklyAlerts,
daily_alert_threshold: dailyAlertThreshold / 100,
weekly_alert_threshold: weeklyAlertThreshold / 100,
send_end_of_period_summary: sendEndOfPeriodSummary,
brand_name: brandName,
daily_alert_threshold: dailyAlertThresholdRef.current / 100,
weekly_alert_threshold: weeklyAlertThresholdRef.current / 100,
send_end_of_period_summary: sendEndOfPeriodSummaryRef.current,
brand_name: brandNameRef.current,
});
setSettingsSaving(false);
if (!result.success) { setSettingsError(result.error ?? "Failed to save"); return; }