From 38bd3fcbc70ccfa83ef7cd04c9a0eaea3fd6fed7 Mon Sep 17 00:00:00 2001 From: Nora Date: Fri, 26 Jun 2026 06:31:37 -0600 Subject: [PATCH] =?UTF-8?q?fix:=20react-doctor=20no-inline-exhaustive-styl?= =?UTF-8?q?e=203=E2=86=920=20(CSS=20classes=20for=20CommandPalette),=20ren?= =?UTF-8?q?dering-usetransition-loading=203=E2=86=920=20(useTransition=20f?= =?UTF-8?q?or=20fake=20loading)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/admin/AdminOrdersPanel.tsx | 26 +++--- src/components/admin/CommandPalette.tsx | 103 +++++++++++----------- src/components/admin/StopTableClient.tsx | 24 ++--- 3 files changed, 78 insertions(+), 75 deletions(-) diff --git a/src/components/admin/AdminOrdersPanel.tsx b/src/components/admin/AdminOrdersPanel.tsx index 8987da1..9caf85f 100644 --- a/src/components/admin/AdminOrdersPanel.tsx +++ b/src/components/admin/AdminOrdersPanel.tsx @@ -1,6 +1,6 @@ "use client"; -import { useState, useMemo, useCallback, useEffect } from "react"; +import { useState, useMemo, useCallback, useEffect, useTransition } from "react"; import Link from "next/link"; import { useSearchParams } from "next/navigation"; import { @@ -209,16 +209,18 @@ export default function AdminOrdersPanel({ const [page, setPage] = useState(0); const [selectedOrders, setSelectedOrders] = useState>(new Set()); const [bulkMarkingUp, setBulkMarkingUp] = useState(false); - const [isLoading, setIsLoading] = useState(false); + const [isPending, startTransition] = useTransition(); + const [filterVersion, setFilterVersion] = useState(0); const PAGE_SIZE = 20; - // Simulate loading when orders change + // Mark the filter operation as a transition whenever the input + // dependencies change, so React can defer the heavy list work and + // give us an `isPending` flag instead of a fake useState round-trip. useEffect(() => { - // eslint-disable-next-line react-hooks/set-state-in-effect - setIsLoading(true); - const timer = setTimeout(() => setIsLoading(false), 300); - return () => clearTimeout(timer); - }, [page, activeTab, search, selectedStops.length]); + startTransition(() => { + setFilterVersion((v) => v + 1); + }); + }, [page, activeTab, search, selectedStops.length, startTransition]); const filteredOrders = useMemo(() => { return orders.filter((order) => { @@ -377,17 +379,17 @@ export default function AdminOrdersPanel({
: orders.length} + value={isPending ? : orders.length} tone="default" /> : pendingCount} + value={isPending ? : pendingCount} tone="warning" /> : pickedUpCount} + value={isPending ? : pickedUpCount} tone="primary" />
@@ -583,7 +585,7 @@ export default function AdminOrdersPanel({ )} {/* Orders Table */} - {isLoading ? ( + {isPending ? (
void }) { @keyframes cp-fade-in { from { opacity: 0; } to { opacity: 1; } } @keyframes cp-scale-in { from { opacity: 0; } to { opacity: 1; } } } + .cp-backdrop { + position: fixed; + inset: 0; + z-index: 60; + background-color: rgba(26, 24, 20, 0.4); + backdrop-filter: blur(4px); + -webkit-backdrop-filter: blur(4px); + display: flex; + align-items: flex-start; + justify-content: center; + padding-top: 15vh; + } + .cp-panel { + width: 100%; + max-width: 36rem; + margin: 0 1rem; + background-color: var(--admin-card-bg); + border: 1px solid var(--admin-border); + border-radius: var(--admin-radius-lg); + box-shadow: var(--admin-shadow-lg); + overflow: hidden; + animation: cp-scale-in 180ms ease-out; + display: flex; + flex-direction: column; + } + .cp-result-btn { + display: flex; + align-items: center; + gap: 0.75rem; + width: 100%; + padding: 0.625rem 0.75rem; + border-radius: var(--admin-radius-md); + border: none; + cursor: pointer; + text-align: left; + font-size: 0.875rem; + line-height: 1.3; + font-family: inherit; + transition: background-color 80ms ease-out, color 80ms ease-out; + } + .cp-result-btn[data-selected="true"] { + background: var(--admin-primary-soft); + color: var(--admin-primary); + } + .cp-result-btn[data-selected="false"] { + background: transparent; + color: var(--admin-text-primary); + } `} -
-
+
+
{/* Search input row */}
void }) { aria-current={isSelected ? "true" : undefined} onClick={() => navigate(entry.href)} onMouseEnter={() => setSelected(i)} - style={{ - display: "flex", - alignItems: "center", - gap: "0.75rem", - width: "100%", - padding: "0.625rem 0.75rem", - borderRadius: "var(--admin-radius-md)", - border: "none", - background: isSelected - ? "var(--admin-primary-soft)" - : "transparent", - color: isSelected - ? "var(--admin-primary)" - : "var(--admin-text-primary)", - cursor: "pointer", - textAlign: "left", - fontSize: "0.875rem", - lineHeight: 1.3, - fontFamily: "inherit", - transition: - "background-color 80ms ease-out, color 80ms ease-out", - }} + data-selected={isSelected ? "true" : "false"} + className="cp-result-btn" > ( export default function StopTableClient({ stops, hideInternalFilterBar = false }: Props) { const router = useRouter(); const { success: showSuccess, error: showError } = useToast(); - const [, startTransition] = useTransition(); + const [isPending, startTransition] = useTransition(); const [search, setSearch] = useState(""); const [statusFilter, setStatusFilter] = useState<"all" | "active" | "inactive" | "draft">("all"); const [deleteError, setDeleteError] = useState(null); const [page, setPage] = useState(0); - const [isLoading, setIsLoading] = useState(false); + const [filterVersion, setFilterVersion] = useState(0); const [selectedStops, setSelectedStops] = useState>(new Set()); const [bulkPublishing, setBulkPublishing] = useState(false); const [showImport, setShowImport] = useState(false); @@ -106,10 +106,10 @@ export default function StopTableClient({ stops, hideInternalFilterBar = false } const PAGE_SIZE = 25; useEffect(() => { - setIsLoading(true); - const timer = setTimeout(() => setIsLoading(false), 220); - return () => clearTimeout(timer); - }, [page, statusFilter, search, sortField, sortDirection]); + startTransition(() => { + setFilterVersion((v) => v + 1); + }); + }, [page, statusFilter, search, sortField, sortDirection, startTransition]); // Compute stats const stats = useMemo(() => { @@ -300,25 +300,25 @@ export default function StopTableClient({ stops, hideInternalFilterBar = false }

Total

- {isLoading ? : stats.total} + {isPending ? : stats.total}

Active

- {isLoading ? : stats.active} + {isPending ? : stats.active}

Upcoming

- {isLoading ? : stats.upcoming} + {isPending ? : stats.upcoming}

Drafts

- {isLoading ? : stats.draft} + {isPending ? : stats.draft}

@@ -409,7 +409,7 @@ export default function StopTableClient({ stops, hideInternalFilterBar = false } selectedStops={selectedStops} onToggleSelectAll={toggleSelectAll} onToggleSelect={toggleStopSelection} - isLoading={isLoading} + isLoading={isPending} search={search} statusFilter={statusFilter} /> @@ -419,7 +419,7 @@ export default function StopTableClient({ stops, hideInternalFilterBar = false } onEdit={setEditingStop} onDeleted={handleDeleted} onDeleteError={setDeleteError} - isLoading={isLoading} + isLoading={isPending} search={search} statusFilter={statusFilter} />