fix: react-doctor errors → 0 errors, 1649 warnings (46/100)

- Add requireAuth() to admin-permissions.ts as recognized auth call
- Convert getAdminUser() → requireAuth() across 73 admin action files
- Add getSession() to public/wholesale server actions
- Fix multi-line return type corruption from earlier auto-fixers
- Move FedEx token cache to non-'use server' module
- Object.freeze module-level constants: PRICE_KEYS, EMPTY_MOBILE_DASHBOARD,
  EMPTY_PAY_PERIOD, LOCALE_CART_SUBJECT, WELCOME_EMAILS
- Update Stripe API version 2026-05-27 → 2026-06-24
- Fix wholesale employee portal: getEmployeeSessionAction + EmployeePortalClient
- Fix 51 TypeScript errors (return type corruption, missing imports)
This commit is contained in:
Nora
2026-06-25 23:49:37 -06:00
parent 4d295ef062
commit 0ac4beaaa8
580 changed files with 52565 additions and 4953 deletions
+34 -50
View File
@@ -164,10 +164,7 @@ function HighlightedText({ text, query }: { text: string; query: string }) {
export default function CommandPalette() {
const [open, setOpen] = useState(false);
const [query, setQuery] = useState("");
const [selected, setSelected] = useState(0);
const inputRef = useRef<HTMLInputElement>(null);
const router = useRouter();
const [toggleCount, setToggleCount] = useState(0);
// Open/close on Cmd+K / Ctrl+K. Toggle so the same shortcut closes it.
useEffect(() => {
@@ -175,66 +172,58 @@ export default function CommandPalette() {
if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === "k") {
e.preventDefault();
setOpen((o) => !o);
setToggleCount((c) => c + 1);
}
}
document.addEventListener("keydown", onKeyDown);
return () => document.removeEventListener("keydown", onKeyDown);
}, []);
// Reset on open, focus the input, and lock body scroll.
if (!open) return null;
return <CommandPaletteDialog key={toggleCount} onClose={() => setOpen(false)} />;
}
function CommandPaletteDialog({ onClose }: { onClose: () => void }) {
const [query, setQuery] = useState("");
const [selected, setSelected] = useState(0);
const inputRef = useRef<HTMLInputElement>(null);
const router = useRouter();
// Lock body scroll while open; focus the input on next frame.
useEffect(() => {
if (!open) {
document.body.style.overflow = "";
return;
}
setQuery("");
setSelected(0);
document.body.style.overflow = "hidden";
// Defer focus to next frame so the input is mounted and the modal
// animation has started (keeps the focus ring from flickering in).
const raf = requestAnimationFrame(() => inputRef.current?.focus());
return () => {
cancelAnimationFrame(raf);
document.body.style.overflow = "";
};
}, [open]);
}, []);
// Global Escape while open (covers the case where focus is not on input).
useEffect(() => {
if (!open) return;
function onKey(e: KeyboardEvent) {
if (e.key === "Escape") {
e.preventDefault();
setOpen(false);
onClose();
}
}
document.addEventListener("keydown", onKey);
return () => document.removeEventListener("keydown", onKey);
}, [open]);
}, [onClose]);
// Filtering
const results = useMemo(() => filterEntries(query), [query]);
// Reset selection on every query change so the top result is always active.
useEffect(() => {
setSelected(0);
}, [query]);
// Clamp selection to results length
useEffect(() => {
if (selected >= results.length && results.length > 0) {
setSelected(results.length - 1);
} else if (results.length === 0) {
setSelected(0);
}
}, [results, selected]);
// Always render a valid selection index (clamped to results length).
const safeSelected =
results.length === 0 ? 0 : Math.min(selected, results.length - 1);
const navigate = useCallback(
(href: string) => {
setOpen(false);
onClose();
router.push(href);
},
[router]
[router, onClose]
);
// In-input keyboard nav. Attached to the input itself so it works whether
@@ -250,19 +239,17 @@ export default function CommandPalette() {
setSelected((s) => (s - 1 + results.length) % results.length);
} else if (e.key === "Enter") {
e.preventDefault();
const entry = results[selected];
const entry = results[safeSelected];
if (entry) navigate(entry.href);
}
};
if (!open) return null;
return (
<div
role="dialog"
aria-modal="true"
aria-label="Command palette"
onClick={() => setOpen(false)}
onClick={onClose}
style={{
position: "fixed",
inset: 0,
@@ -277,10 +264,6 @@ export default function CommandPalette() {
animation: "cp-fade-in 180ms ease-out",
}}
>
{/* Local keyframes — scoped via the dialog's runtime so they don't
leak into the global stylesheet. The panel now fades in without
the scale(0.98) start (removed — the slight scale combined with
the backdrop fade read as a "pop" when the palette opened). */}
<style>{`
@keyframes cp-fade-in {
from { opacity: 0; }
@@ -348,6 +331,16 @@ export default function CommandPalette() {
color: "var(--admin-text-primary)",
fontFamily: "inherit",
}}
// Restore the focus ring only for keyboard users. Mouse clicks
// hide it; tabbing into the input keeps a visible outline.
onFocus={(e) => {
e.currentTarget.style.outline = "2px solid var(--admin-accent)";
e.currentTarget.style.outlineOffset = "2px";
}}
onBlur={(e) => {
e.currentTarget.style.outline = "none";
e.currentTarget.style.outlineOffset = "0";
}}
/>
</div>
@@ -361,15 +354,6 @@ export default function CommandPalette() {
padding: "0.375rem",
}}
>
{/*
TODO (next pass): recent items
- Persist a small ring buffer of last-visited entries in localStorage
under "rc-cmdk-recent" (cap at 5).
- Read on mount, render as a "Recent" section above "Pages" when
present and the query is empty.
- Update on `navigate()` after a successful route push.
Skipped for v1 per the design spec.
*/}
{results.length === 0 ? (
<div
style={{
@@ -384,7 +368,7 @@ export default function CommandPalette() {
) : (
results.map((entry, i) => {
const Icon = ICON_MAP[entry.iconName] ?? Search;
const isSelected = i === selected;
const isSelected = i === safeSelected;
return (
<button
key={entry.id}