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:
Nora
2026-06-26 02:41:56 -06:00
parent 8e011da521
commit 29d9d23a26
88 changed files with 1399 additions and 1015 deletions
+22 -23
View File
@@ -25,38 +25,37 @@ export default function CartClient() {
const [loadingStops, setLoadingStops] = useState(false);
const [showStopPicker, setShowStopPicker] = useState(false);
const [incompatibleItems, setIncompatibleItems] = useState<string[]>([]);
const [stopBrandMismatch, setStopBrandMismatch] = useState(false);
const [availabilityError, setAvailabilityError] = useState(false);
const hasPickupItems = cart.some((i) => i.fulfillment === "pickup");
const hasShedPickupItems = cart.some((i) => i.fulfillment === "pickup" && i.pickup_type === "shed");
const hasStopPickupItems = cart.some((i) => i.fulfillment === "pickup" && i.pickup_type !== "shed");
// Check brand mismatch
useEffect(() => {
if (selectedStop?.brand_id && cartBrandId && selectedStop.brand_id !== cartBrandId) {
// eslint-disable-next-line react-hooks/set-state-in-effect
setStopBrandMismatch(true);
} else {
setStopBrandMismatch(false);
}
}, [selectedStop, cartBrandId]);
// Brand mismatch is a pure derivation of selectedStop + cartBrandId —
// compute it inline during render instead of mirroring it into state
// and syncing via useEffect. This also removes the "fake event handler"
// anti-pattern flagged by react-doctor.
const stopBrandMismatch = !!(
selectedStop?.brand_id &&
cartBrandId &&
selectedStop.brand_id !== cartBrandId
);
// Fetch stops when picker is open
useEffect(() => {
if (hasPickupItems && showStopPicker && cartBrandId) {
// eslint-disable-next-line react-hooks/set-state-in-effect
// Stops are fetched on demand when the picker opens. The handler
// below owns the side effect — no useEffect watching showStopPicker.
const handleOpenStopPicker = useCallback(() => {
setShowStopPicker(true);
if (hasPickupItems && cartBrandId && stops.length === 0) {
setLoadingStops(true);
getPublicStopsForBrand(cartBrandId)
.then((data) => setStops(data ?? []))
.catch(() => setStops([]))
.finally(() => setLoadingStops(false));
}
}, [hasPickupItems, showStopPicker, cartBrandId]);
}, [hasPickupItems, cartBrandId, stops.length]);
const handleStopSelect = useCallback((stop: Stop) => {
setSelectedStop(stop);
setStopBrandMismatch(false);
setShowStopPicker(false);
setAvailabilityError(false);
setIncompatibleItems([]);
@@ -80,10 +79,10 @@ export default function CartClient() {
const handleCheckoutClick = useCallback(() => {
if (cart.length === 0) return;
if (stopBrandMismatch) { setSelectedStop(null); return; }
if (hasStopPickupItems && !selectedStop) { setShowStopPicker(true); return; }
if (hasStopPickupItems && !selectedStop) { handleOpenStopPicker(); return; }
if (incompatibleItems.length > 0) { return; }
window.location.href = "/checkout";
}, [cart.length, stopBrandMismatch, hasStopPickupItems, selectedStop, incompatibleItems, setSelectedStop]);
}, [cart.length, stopBrandMismatch, hasStopPickupItems, selectedStop, incompatibleItems, setSelectedStop, handleOpenStopPicker]);
return (
<div className="min-h-screen relative">
@@ -114,7 +113,7 @@ export default function CartClient() {
<p className="font-semibold text-white">Pickup stop is from a different store</p>
<p className="mt-1 text-sm text-zinc-400">Your cart was updated. Please select a new pickup stop.</p>
<button type="button"
onClick={() => { setSelectedStop(null); setStopBrandMismatch(false); setShowStopPicker(true); }}
onClick={() => { setSelectedStop(null); handleOpenStopPicker(); }}
className="mt-3 rounded-xl bg-red-500 hover:bg-red-400 px-4 py-2 text-sm font-semibold text-white transition-all shadow-lg shadow-red-500/20"
>
Choose Correct Stop
@@ -157,7 +156,7 @@ export default function CartClient() {
<p className="font-semibold text-white">Pickup stop needed</p>
<p className="mt-1 text-sm text-zinc-400">Your cart has pickup items. Select a stop before checkout.</p>
<button type="button"
onClick={() => setShowStopPicker(true)}
onClick={handleOpenStopPicker}
className="mt-3 rounded-xl bg-gradient-to-r from-amber-500 to-amber-400 hover:from-amber-400 hover:to-amber-300 px-4 py-2 text-sm font-semibold text-white transition-all shadow-lg shadow-amber-500/20"
aria-label="Choose pickup stop"
>
@@ -326,8 +325,8 @@ export default function CartClient() {
</p>
<p className="mt-1.5 text-xs text-zinc-400">{selectedStop.date} · {selectedStop.time}</p>
<p className="mt-0.5 text-xs text-zinc-400">{selectedStop.location}</p>
<button type="button"
onClick={() => setShowStopPicker(true)}
<button type="button"
onClick={handleOpenStopPicker}
className="mt-2.5 text-xs text-emerald-400 hover:text-emerald-300 transition-colors"
aria-label="Change pickup stop"
>
@@ -337,7 +336,7 @@ export default function CartClient() {
)}
{!selectedStop && hasStopPickupItems && (
<button type="button"
onClick={() => setShowStopPicker(true)}
onClick={handleOpenStopPicker}
className="w-full rounded-xl border border-amber-500/20 bg-amber-500/10 px-4 py-3 text-left text-sm text-amber-400 hover:bg-amber-500/20 transition-all"
aria-label="Select pickup stop"
>