fix: react-doctor no-inline-exhaustive-style 3→0 (CSS classes for CommandPalette), rendering-usetransition-loading 3→0 (useTransition for fake loading)

This commit is contained in:
Nora
2026-06-26 06:31:37 -06:00
parent 9633680e23
commit 38bd3fcbc7
3 changed files with 78 additions and 75 deletions
+14 -12
View File
@@ -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<Set<string>>(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({
<div className="grid grid-cols-2 sm:grid-cols-3 gap-3 mb-6">
<KPIStat
label="Total"
value={isLoading ? <Skeleton variant="text" className="w-16 h-8" /> : orders.length}
value={isPending ? <Skeleton variant="text" className="w-16 h-8" /> : orders.length}
tone="default"
/>
<KPIStat
label="Pending"
value={isLoading ? <Skeleton variant="text" className="w-12 h-8" /> : pendingCount}
value={isPending ? <Skeleton variant="text" className="w-12 h-8" /> : pendingCount}
tone="warning"
/>
<KPIStat
label="Picked Up"
value={isLoading ? <Skeleton variant="text" className="w-12 h-8" /> : pickedUpCount}
value={isPending ? <Skeleton variant="text" className="w-12 h-8" /> : pickedUpCount}
tone="primary"
/>
</div>
@@ -583,7 +585,7 @@ export default function AdminOrdersPanel({
)}
{/* Orders Table */}
{isLoading ? (
{isPending ? (
<div
className="rounded-xl border p-6"
style={{
+52 -51
View File
@@ -266,37 +266,58 @@ function CommandPaletteDialog({ onClose }: { onClose: () => 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);
}
`}</style>
<div
style={{
position: "fixed",
inset: 0,
zIndex: 60,
backgroundColor: "rgba(26, 24, 20, 0.4)",
backdropFilter: "blur(4px)",
WebkitBackdropFilter: "blur(4px)",
display: "flex",
alignItems: "flex-start",
justifyContent: "center",
paddingTop: "15vh",
}}
>
<div
style={{
width: "100%",
maxWidth: "36rem",
margin: "0 1rem",
backgroundColor: "var(--admin-card-bg)",
border: "1px solid var(--admin-border)",
borderRadius: "var(--admin-radius-lg)",
boxShadow: "var(--admin-shadow-lg)",
overflow: "hidden",
animation: "cp-scale-in 180ms ease-out",
display: "flex",
flexDirection: "column",
}}
>
<div className="cp-backdrop">
<div className="cp-panel">
{/* Search input row */}
<div
style={{
@@ -373,28 +394,8 @@ function CommandPaletteDialog({ onClose }: { onClose: () => 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"
>
<Icon
size={16}
+12 -12
View File
@@ -89,12 +89,12 @@ const StopIconHeader = () => (
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<string | null>(null);
const [page, setPage] = useState(0);
const [isLoading, setIsLoading] = useState(false);
const [filterVersion, setFilterVersion] = useState(0);
const [selectedStops, setSelectedStops] = useState<Set<string>>(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 }
<div className="bg-white rounded-xl border border-[var(--admin-border)] p-4">
<p className="text-[10px] sm:text-xs text-[var(--admin-text-muted)] font-medium">Total</p>
<p className="text-xl sm:text-2xl font-bold text-[var(--admin-text-primary)] mt-1 tabular-nums">
{isLoading ? <Skeleton variant="text" className="w-12 h-6" /> : stats.total}
{isPending ? <Skeleton variant="text" className="w-12 h-6" /> : stats.total}
</p>
</div>
<div className="bg-white rounded-xl border border-[var(--admin-border)] p-4">
<p className="text-[10px] sm:text-xs text-[var(--admin-text-muted)] font-medium">Active</p>
<p className="text-xl sm:text-2xl font-bold text-[var(--admin-accent)] mt-1 tabular-nums">
{isLoading ? <Skeleton variant="text" className="w-10 h-6" /> : stats.active}
{isPending ? <Skeleton variant="text" className="w-10 h-6" /> : stats.active}
</p>
</div>
<div className="bg-white rounded-xl border border-[var(--admin-border)] p-4">
<p className="text-[10px] sm:text-xs text-[var(--admin-text-muted)] font-medium">Upcoming</p>
<p className="text-xl sm:text-2xl font-bold text-sky-600 mt-1 tabular-nums">
{isLoading ? <Skeleton variant="text" className="w-10 h-6" /> : stats.upcoming}
{isPending ? <Skeleton variant="text" className="w-10 h-6" /> : stats.upcoming}
</p>
</div>
<div className="bg-white rounded-xl border border-[var(--admin-border)] p-4">
<p className="text-[10px] sm:text-xs text-[var(--admin-text-muted)] font-medium">Drafts</p>
<p className="text-xl sm:text-2xl font-bold text-stone-400 mt-1 tabular-nums">
{isLoading ? <Skeleton variant="text" className="w-8 h-6" /> : stats.draft}
{isPending ? <Skeleton variant="text" className="w-8 h-6" /> : stats.draft}
</p>
</div>
</div>
@@ -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}
/>