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:
@@ -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; }
|
||||
|
||||
Reference in New Issue
Block a user