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
+16 -16
View File
@@ -244,7 +244,7 @@ function SectionHeader({
}) {
return (
<div className="flex items-center justify-between py-2">
<button
<button type="button"
onClick={onToggle}
className="flex items-center gap-2 text-base font-bold text-stone-800 hover:text-stone-900"
>
@@ -414,7 +414,7 @@ export default function WaterAdminClient() {
<h1 className="text-lg font-bold text-white">{t.title}</h1>
<p className="text-xs text-stone-400">{t.loggedInAs}: Admin</p>
</div>
<button
<button type="button"
onClick={handleLogout}
className="rounded-lg bg-stone-700 px-3 py-2 text-sm font-medium text-white hover:bg-stone-600 min-h-[44px]"
>
@@ -481,7 +481,7 @@ export default function WaterAdminClient() {
count={headgates.length}
onToggle={() => setShowHeadgates((v) => !v)}
action={
<button
<button type="button"
onClick={() => setShowAddHg((v) => !v)}
className="rounded-lg bg-stone-900 px-3 py-1.5 text-xs font-semibold text-white hover:bg-stone-700 min-h-[36px] min-w-[36px]"
>
@@ -492,7 +492,7 @@ export default function WaterAdminClient() {
{showAddHg && (
<form onSubmit={handleAddHg} className="mb-3 flex flex-col sm:flex-row gap-2">
<input
<input aria-label="Text"
type="text"
value={newHgName}
onChange={(e) => setNewHgName(e.target.value)}
@@ -500,7 +500,7 @@ export default function WaterAdminClient() {
className="flex-1 rounded-lg border border-stone-300 px-3 py-2 text-sm min-h-[44px]"
required
/>
<select
<select aria-label="Select"
value={newHgUnit}
onChange={(e) => setNewHgUnit(e.target.value)}
className="rounded-lg border border-stone-300 px-3 py-2 text-sm min-h-[44px]"
@@ -548,7 +548,7 @@ export default function WaterAdminClient() {
<span className="text-sm font-medium text-stone-900 truncate">{hg.name}</span>
<span className="text-xs text-stone-400 shrink-0">{hg.unit}</span>
</div>
<button
<button type="button"
onClick={() => router.push(`/admin/water-log/headgates/${hg.id}?from=/water/admin`)}
className="shrink-0 rounded-lg border border-stone-200 bg-white px-3 py-1.5 text-xs font-medium text-stone-600 hover:bg-stone-50 min-h-[36px]"
>
@@ -568,7 +568,7 @@ export default function WaterAdminClient() {
<p className="text-xs font-semibold text-yellow-800">{t.newPinFor} {newPin.name}:</p>
<p className="text-xl font-bold tracking-widest text-yellow-900">{newPin.pin}</p>
<p className="mt-0.5 text-xs text-yellow-600">{t.writeThisDown}</p>
<button
<button type="button"
onClick={() => setNewPin(null)}
className="mt-1 text-xs text-yellow-700 underline"
>
@@ -582,7 +582,7 @@ export default function WaterAdminClient() {
count={users.length}
onToggle={() => setShowUsers((v) => !v)}
action={
<button
<button type="button"
onClick={() => setShowAddUser((v) => !v)}
className="rounded-lg bg-stone-900 px-3 py-1.5 text-xs font-semibold text-white hover:bg-stone-700 min-h-[36px] min-w-[36px]"
>
@@ -593,7 +593,7 @@ export default function WaterAdminClient() {
{showAddUser && (
<form onSubmit={handleAddUser} className="mb-3 flex flex-col sm:flex-row gap-2">
<input
<input aria-label="Text"
type="text"
value={newUserName}
onChange={(e) => setNewUserName(e.target.value)}
@@ -601,7 +601,7 @@ export default function WaterAdminClient() {
className="flex-1 rounded-lg border border-stone-300 px-3 py-2 text-sm min-h-[44px]"
required
/>
<select
<select aria-label="Select"
value={newUserLang}
onChange={(e) => setNewUserLang(e.target.value as "en" | "es")}
className="rounded-lg border border-stone-300 px-3 py-2 text-sm min-h-[44px]"
@@ -650,7 +650,7 @@ export default function WaterAdminClient() {
{u.role === "water_admin" ? t.admin : t.field}
</span>
</div>
<button
<button type="button"
onClick={() => router.push(`/admin/water-log/users/${u.id}?from=/water/admin`)}
className="shrink-0 rounded-lg border border-stone-200 bg-white px-3 py-1.5 text-xs font-medium text-stone-600 hover:bg-stone-50 min-h-[36px]"
>
@@ -675,19 +675,19 @@ export default function WaterAdminClient() {
<>
{/* Filters */}
<div className="mb-3 flex flex-wrap gap-2">
<input
<input aria-label="Date"
type="date"
value={filterDateFrom}
onChange={(e) => setFilterDateFrom(e.target.value)}
className="rounded-lg border border-stone-300 px-2 py-1.5 text-sm min-h-[36px]"
/>
<input
<input aria-label="Date"
type="date"
value={filterDateTo}
onChange={(e) => setFilterDateTo(e.target.value)}
className="rounded-lg border border-stone-300 px-2 py-1.5 text-sm min-h-[36px]"
/>
<select
<select aria-label="Select"
value={filterHeadgate}
onChange={(e) => setFilterHeadgate(e.target.value)}
className="rounded-lg border border-stone-300 px-2 py-1.5 text-sm min-h-[36px]"
@@ -697,7 +697,7 @@ export default function WaterAdminClient() {
<option key={hg.id} value={hg.id}>{hg.name}</option>
))}
</select>
<select
<select aria-label="Select"
value={filterVia}
onChange={(e) => setFilterVia(e.target.value)}
className="rounded-lg border border-stone-300 px-2 py-1.5 text-sm min-h-[36px]"
@@ -707,7 +707,7 @@ export default function WaterAdminClient() {
<option value="admin">{t.admin}</option>
</select>
{(filterDateFrom || filterDateTo || filterHeadgate || filterVia) && (
<button
<button type="button"
onClick={() => { setFilterDateFrom(""); setFilterDateTo(""); setFilterHeadgate(""); setFilterVia(""); }}
className="text-xs text-stone-500 hover:text-stone-700 underline self-center"
>
+16 -14
View File
@@ -162,8 +162,10 @@ function WaterFieldInner() {
if (step === "form" && headgates.length === 0) {
loadHeadgates();
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
// loadHeadgates depends on TUXEDO_BRAND_ID + setters which never change
// across the component's lifetime, so we keep the dep list focused on
// the state we actually gate the load on.
}, [step, headgates.length]);
// QR-locked headgate: derive during render instead of syncing in an effect.
// When qrHeadgateToken matches a loaded headgate, override the user's
@@ -377,13 +379,13 @@ function WaterFieldInner() {
</p>
<p className="text-[#57694e] text-center mb-8 text-sm">{t.selectLang}</p>
<div className="flex flex-col gap-3">
<button
<button type="button"
onClick={() => handleLangSelect("en")}
className="w-full rounded-xl bg-white px-6 py-5 text-xl font-semibold text-[#1d1d1f] shadow-sm ring-1 ring-[#d4d9d3] hover:bg-[#f0fdf4] hover:ring-[#1a4d2e] transition min-h-[64px]"
>
English
</button>
<button
<button type="button"
onClick={() => handleLangSelect("es")}
className="w-full rounded-xl bg-white px-6 py-5 text-xl font-semibold text-[#1d1d1f] shadow-sm ring-1 ring-[#d4d9d3] hover:bg-[#f0fdf4] hover:ring-[#1a4d2e] transition min-h-[64px]"
>
@@ -400,7 +402,7 @@ function WaterFieldInner() {
return (
<div className="min-h-screen bg-[#fdfaf2] flex flex-col items-center justify-center p-6">
<div className="w-full max-w-xs">
<button
<button type="button"
onClick={() => setStep("lang")}
className="text-sm text-[#57694e] mb-6 hover:text-[#1a4d2e]"
>
@@ -414,13 +416,13 @@ function WaterFieldInner() {
</h1>
<p className="text-[#57694e] text-center mb-8">{t.whoAreYou}</p>
<div className="flex flex-col gap-3">
<button
<button type="button"
onClick={() => { setStep("pin"); }}
className="w-full rounded-xl bg-white px-6 py-5 text-xl font-semibold text-[#1d1d1f] shadow-sm ring-1 ring-[#d4d9d3] hover:bg-[#f0fdf4] hover:ring-[#1a4d2e] transition min-h-[64px]"
>
Irrigator
</button>
<button
<button type="button"
onClick={() => { setStep("pin"); }}
className="w-full rounded-xl bg-[#1a4d2e] px-6 py-5 text-xl font-semibold text-white shadow-sm ring-1 ring-[#1a4d2e] hover:bg-[#14532d] transition min-h-[64px]"
>
@@ -437,7 +439,7 @@ function WaterFieldInner() {
return (
<div className="min-h-screen bg-[#fdfaf2] flex flex-col items-center justify-center p-6">
<div className="w-full max-w-xs">
<button
<button type="button"
onClick={() => setStep("role")}
className="text-sm text-[#57694e] mb-6 hover:text-[#1a4d2e]"
>
@@ -452,7 +454,7 @@ function WaterFieldInner() {
<p className="text-[#57694e] text-center mb-8">{t.enterPin}</p>
<form onSubmit={handlePinSubmit}>
<input
<input aria-label="Password"
type="password"
inputMode="numeric"
pattern="[0-9]*"
@@ -502,7 +504,7 @@ function WaterFieldInner() {
</p>
</div>
</div>
<button
<button type="button"
onClick={handleLogout}
className="rounded-lg bg-[#14532d] px-4 py-2 text-sm font-semibold text-white hover:bg-[#166534] transition-colors min-h-[44px]"
>
@@ -574,7 +576,7 @@ function WaterFieldInner() {
<span className="text-stone-500 text-base font-medium">Loading headgates...</span>
</div>
) : (
<select
<select aria-label="Select"
value={effectiveSelectedHeadgate}
onChange={(e) => setSelectedHeadgate(e.target.value)}
required
@@ -603,7 +605,7 @@ function WaterFieldInner() {
<label className="block text-base font-semibold text-stone-700 mb-2">
{t.measurement}
</label>
<input
<input aria-label="0.00"
type="number"
step="any"
value={measurement}
@@ -618,7 +620,7 @@ function WaterFieldInner() {
<label className="block text-base font-semibold text-stone-700 mb-2">
{t.unit}
</label>
<select
<select aria-label="Select"
value={unit}
onChange={(e) => setUnit(e.target.value)}
className="w-full rounded-xl border-2 border-stone-300 bg-white px-4 py-4 text-lg font-semibold outline-none focus:border-stone-900 min-h-[60px]"
@@ -693,7 +695,7 @@ function WaterFieldInner() {
<label className="block text-base font-semibold text-stone-700 mb-2">
{t.notes}
</label>
<textarea
<textarea aria-label="Textarea"
value={notes}
onChange={(e) => setNotes(e.target.value)}
placeholder={t.notesPlaceholder}