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
+35 -35
View File
@@ -46,7 +46,7 @@ function Accordion({ title, id, description, defaultOpen = false, accentColor =
return (
<div id={id} className={`rounded-xl border ${borderColor} bg-white shadow-sm overflow-hidden transition-all duration-200`}>
<button
<button type="button"
onClick={() => setOpen(!open)}
className="w-full flex items-center justify-between px-5 py-4 hover:bg-stone-50 transition-colors"
aria-expanded={open}
@@ -314,15 +314,15 @@ export default function SettingsSections({ brandId, workersOnly, tasksOnly }: Pr
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div>
<label className="block text-xs font-semibold uppercase tracking-widest text-stone-500 mb-2">Work week starts on</label>
<select value={payPeriodStartDay} onChange={e => setPayPeriodStartDay(Number(e.target.value))}
<select aria-label="Select" value={payPeriodStartDay} onChange={e => setPayPeriodStartDay(Number(e.target.value))}
className="w-full px-4 py-3 rounded-xl border border-stone-200 bg-white text-stone-900 focus:outline-none focus:border-emerald-500 transition-colors">
{DAYS.map((d, i) => <option key={i} value={i}>{d}</option>)}
{DAYS.map((d, i) => <option key={d} value={i}>{d}</option>)}
</select>
</div>
<div>
<label className="block text-xs font-semibold uppercase tracking-widest text-stone-500 mb-2">Pay period length</label>
<div className="flex items-center gap-3">
<input type="number" min={1} max={31} value={payPeriodLength}
<input aria-label="Number" type="number" min={1} max={31} value={payPeriodLength}
onChange={e => setPayPeriodLength(Number(e.target.value))}
className="flex-1 px-4 py-3 rounded-xl border border-stone-200 bg-white text-stone-900 focus:outline-none focus:border-emerald-500 transition-colors" />
<span className="text-sm text-stone-500">days</span>
@@ -331,7 +331,7 @@ export default function SettingsSections({ brandId, workersOnly, tasksOnly }: Pr
<div>
<label className="block text-xs font-semibold uppercase tracking-widest text-stone-500 mb-2">Daily overtime threshold</label>
<div className="flex items-center gap-3">
<input type="number" min={1} max={24} value={dailyOvertimeThreshold}
<input aria-label="Number" type="number" min={1} max={24} value={dailyOvertimeThreshold}
onChange={e => setDailyOvertimeThreshold(Number(e.target.value))}
className="flex-1 px-4 py-3 rounded-xl border border-stone-200 bg-white text-stone-900 focus:outline-none focus:border-emerald-500 transition-colors" />
<span className="text-sm text-stone-500">hrs</span>
@@ -340,7 +340,7 @@ export default function SettingsSections({ brandId, workersOnly, tasksOnly }: Pr
<div>
<label className="block text-xs font-semibold uppercase tracking-widest text-stone-500 mb-2">Weekly overtime threshold</label>
<div className="flex items-center gap-3">
<input type="number" min={1} max={80} value={weeklyOvertimeThreshold}
<input aria-label="Number" type="number" min={1} max={80} value={weeklyOvertimeThreshold}
onChange={e => setWeeklyOvertimeThreshold(Number(e.target.value))}
className="flex-1 px-4 py-3 rounded-xl border border-stone-200 bg-white text-stone-900 focus:outline-none focus:border-emerald-500 transition-colors" />
<span className="text-sm text-stone-500">hrs</span>
@@ -396,18 +396,18 @@ export default function SettingsSections({ brandId, workersOnly, tasksOnly }: Pr
<div>
<label className="block text-xs font-semibold uppercase tracking-widest text-stone-500 mb-2">Email addresses</label>
<div className="flex gap-2">
<input value={newEmail}
<input aria-label="Manager@farm.com" value={newEmail}
onChange={e => setNewEmail(e.target.value)}
onKeyDown={e => e.key === "Enter" && (addEmail(), e.preventDefault())}
className="flex-1 px-4 py-3 rounded-xl border border-stone-200 bg-white text-stone-900 placeholder:text-stone-400 focus:outline-none focus:border-emerald-500"
placeholder="manager@farm.com" />
<button onClick={addEmail} className="px-5 py-3 rounded-xl bg-emerald-600 hover:bg-emerald-500 text-white font-semibold text-sm">Add</button>
<button type="button" onClick={addEmail} className="px-5 py-3 rounded-xl bg-emerald-600 hover:bg-emerald-500 text-white font-semibold text-sm">Add</button>
</div>
<div className="flex flex-wrap gap-2 mt-2">
{notificationEmails.map(e => (
<span key={e} className="inline-flex items-center gap-1.5 bg-stone-100 text-stone-700 text-xs px-2.5 py-1 rounded-lg">
{e}
<button onClick={() => removeEmail(e)} className="text-stone-400 hover:text-red-500 ml-1">
<button type="button" onClick={() => removeEmail(e)} className="text-stone-400 hover:text-red-500 ml-1">
<svg className="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
<path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
</svg>
@@ -419,18 +419,18 @@ export default function SettingsSections({ brandId, workersOnly, tasksOnly }: Pr
<div>
<label className="block text-xs font-semibold uppercase tracking-widest text-stone-500 mb-2">SMS numbers</label>
<div className="flex gap-2">
<input value={newSms}
<input aria-label="+1234567890" value={newSms}
onChange={e => setNewSms(e.target.value)}
onKeyDown={e => e.key === "Enter" && (addSms(), e.preventDefault())}
className="flex-1 px-4 py-3 rounded-xl border border-stone-200 bg-white text-stone-900 placeholder:text-stone-400 focus:outline-none focus:border-emerald-500"
placeholder="+1234567890" />
<button onClick={addSms} className="px-5 py-3 rounded-xl bg-emerald-600 hover:bg-emerald-500 text-white font-semibold text-sm">Add</button>
<button type="button" onClick={addSms} className="px-5 py-3 rounded-xl bg-emerald-600 hover:bg-emerald-500 text-white font-semibold text-sm">Add</button>
</div>
<div className="flex flex-wrap gap-2 mt-2">
{notificationSmsNumbers.map(n => (
<span key={n} className="inline-flex items-center gap-1.5 bg-stone-100 text-stone-700 text-xs px-2.5 py-1 rounded-lg">
{n}
<button onClick={() => removeSms(n)} className="text-stone-400 hover:text-red-500 ml-1">
<button type="button" onClick={() => removeSms(n)} className="text-stone-400 hover:text-red-500 ml-1">
<svg className="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
<path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
</svg>
@@ -497,7 +497,7 @@ export default function SettingsSections({ brandId, workersOnly, tasksOnly }: Pr
<div>
<div className="flex items-center justify-between mb-4">
<p className="text-xs text-stone-500">Manage time tracking workers and PIN codes.</p>
<button onClick={openAddWorker}
<button type="button" onClick={openAddWorker}
className="text-xs bg-emerald-600 hover:bg-emerald-500 text-white px-3 py-1.5 rounded-lg font-semibold transition-all">+ Add Worker</button>
</div>
<div className="bg-white border border-stone-200 rounded-xl overflow-hidden">
@@ -528,9 +528,9 @@ export default function SettingsSections({ brandId, workersOnly, tasksOnly }: Pr
<td className="px-4 py-3.5 text-stone-400 text-xs hidden md:table-cell">{w.last_used_at ? new Date(w.last_used_at).toLocaleDateString() : "—"}</td>
<td className="px-4 py-3.5 text-right">
<div className="flex items-center justify-end gap-1">
<button onClick={() => openEditWorker(w)} className="text-xs text-stone-500 hover:text-stone-900 px-2 py-1 rounded-lg hover:bg-stone-100 transition-all">Edit</button>
<button onClick={() => handleResetPin(w.id)} className="text-xs text-amber-600 hover:text-amber-800 px-2 py-1 rounded-lg hover:bg-amber-50 transition-all">Reset PIN</button>
<button onClick={() => handleDeleteWorker(w.id)} className="text-xs text-red-500 hover:text-red-700 px-2 py-1 rounded-lg hover:bg-red-50 transition-all">Delete</button>
<button type="button" onClick={() => openEditWorker(w)} className="text-xs text-stone-500 hover:text-stone-900 px-2 py-1 rounded-lg hover:bg-stone-100 transition-all">Edit</button>
<button type="button" onClick={() => handleResetPin(w.id)} className="text-xs text-amber-600 hover:text-amber-800 px-2 py-1 rounded-lg hover:bg-amber-50 transition-all">Reset PIN</button>
<button type="button" onClick={() => handleDeleteWorker(w.id)} className="text-xs text-red-500 hover:text-red-700 px-2 py-1 rounded-lg hover:bg-red-50 transition-all">Delete</button>
</div>
</td>
</tr>
@@ -554,7 +554,7 @@ export default function SettingsSections({ brandId, workersOnly, tasksOnly }: Pr
<div>
<div className="flex items-center justify-between mb-4">
<p className="text-xs text-stone-500">Define tasks workers can clock into.</p>
<button onClick={openAddTask}
<button type="button" onClick={openAddTask}
className="text-xs bg-amber-500 hover:bg-amber-600 text-white px-3 py-1.5 rounded-lg font-semibold transition-all">+ Add Task</button>
</div>
<div className="bg-white border border-stone-200 rounded-xl overflow-hidden">
@@ -585,8 +585,8 @@ export default function SettingsSections({ brandId, workersOnly, tasksOnly }: Pr
</td>
<td className="px-4 py-3.5 text-right">
<div className="flex items-center justify-end gap-1">
<button onClick={() => openEditTask(t)} className="text-xs text-stone-500 hover:text-stone-900 px-2 py-1 rounded-lg hover:bg-stone-100 transition-all">Edit</button>
<button onClick={() => handleDeleteTask(t.id)} className="text-xs text-red-500 hover:text-red-700 px-2 py-1 rounded-lg hover:bg-red-50 transition-all">Delete</button>
<button type="button" onClick={() => openEditTask(t)} className="text-xs text-stone-500 hover:text-stone-900 px-2 py-1 rounded-lg hover:bg-stone-100 transition-all">Edit</button>
<button type="button" onClick={() => handleDeleteTask(t.id)} className="text-xs text-red-500 hover:text-red-700 px-2 py-1 rounded-lg hover:bg-red-50 transition-all">Delete</button>
</div>
</td>
</tr>
@@ -604,7 +604,7 @@ export default function SettingsSections({ brandId, workersOnly, tasksOnly }: Pr
<div className="bg-white border border-stone-200 rounded-2xl w-full max-w-md shadow-xl">
<div className="px-6 py-4 border-b border-stone-100 flex items-center justify-between">
<h3 className="text-lg font-bold text-stone-900">{editingWorker ? "Edit Worker" : "Add Worker"}</h3>
<button onClick={() => setShowWorkerModal(false)} className="text-stone-400 hover:text-stone-600">
<button type="button" onClick={() => setShowWorkerModal(false)} className="text-stone-400 hover:text-stone-600">
<svg className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
<path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
</svg>
@@ -621,14 +621,14 @@ export default function SettingsSections({ brandId, workersOnly, tasksOnly }: Pr
)}
<div>
<label className="block text-xs font-semibold uppercase tracking-widest text-stone-500 mb-1.5">Name *</label>
<input value={workerName} onChange={e => setWorkerName(e.target.value)}
<input aria-label="Worker Name" value={workerName} onChange={e => setWorkerName(e.target.value)}
className="w-full px-4 py-3 rounded-xl border border-stone-200 bg-white text-stone-900 placeholder:text-stone-400 focus:outline-none focus:border-emerald-500 transition-colors"
placeholder="Worker name" />
</div>
<div className="grid grid-cols-2 gap-3">
<div>
<label className="block text-xs font-semibold uppercase tracking-widest text-stone-500 mb-1.5">Role</label>
<select value={workerRole} onChange={e => setWorkerRole(e.target.value)}
<select aria-label="Select" value={workerRole} onChange={e => setWorkerRole(e.target.value)}
className="w-full px-4 py-3 rounded-xl border border-stone-200 bg-white text-stone-900 focus:outline-none focus:border-emerald-500 transition-colors">
<option value="worker">Worker</option>
<option value="time_admin">Time Admin</option>
@@ -636,7 +636,7 @@ export default function SettingsSections({ brandId, workersOnly, tasksOnly }: Pr
</div>
<div>
<label className="block text-xs font-semibold uppercase tracking-widest text-stone-500 mb-1.5">Lang</label>
<select value={workerLang} onChange={e => setWorkerLang(e.target.value)}
<select aria-label="Select" value={workerLang} onChange={e => setWorkerLang(e.target.value)}
className="w-full px-4 py-3 rounded-xl border border-stone-200 bg-white text-stone-900 focus:outline-none focus:border-emerald-500 transition-colors">
<option value="en">English</option>
<option value="es">Español</option>
@@ -644,23 +644,23 @@ export default function SettingsSections({ brandId, workersOnly, tasksOnly }: Pr
</div>
</div>
<div className="flex items-center gap-3">
<input type="checkbox" id="workerActive" checked={workerActive} onChange={e => setWorkerActive(e.target.checked)}
<input aria-label="WorkerActive" type="checkbox" id="workerActive" checked={workerActive} onChange={e => setWorkerActive(e.target.checked)}
className="w-4 h-4 rounded border-stone-300 bg-white text-emerald-600" />
<label htmlFor="workerActive" className="text-sm text-stone-700">Active</label>
</div>
{editingWorker && (
<div className="pt-2 border-t border-stone-100">
<button onClick={() => handleResetPin(editingWorker.id)} className="text-xs text-amber-600 hover:text-amber-800 font-semibold underline underline-offset-2">
<button type="button" onClick={() => handleResetPin(editingWorker.id)} className="text-xs text-amber-600 hover:text-amber-800 font-semibold underline underline-offset-2">
Reset PIN for this worker
</button>
</div>
)}
<div className="flex gap-3 pt-2">
<button onClick={() => setShowWorkerModal(false)}
<button type="button" onClick={() => setShowWorkerModal(false)}
className="flex-1 py-3 rounded-xl font-semibold text-sm text-stone-500 hover:text-stone-700 border border-stone-200 hover:border-stone-300 transition-all">
Cancel
</button>
<button onClick={handleSaveWorker} disabled={submitting}
<button type="button" onClick={handleSaveWorker} disabled={submitting}
className="flex-1 py-3 rounded-xl font-bold text-sm bg-emerald-600 hover:bg-emerald-500 disabled:opacity-40 text-white transition-all">
{submitting ? "..." : editingWorker ? "Save" : "Add Worker"}
</button>
@@ -676,7 +676,7 @@ export default function SettingsSections({ brandId, workersOnly, tasksOnly }: Pr
<div className="bg-white border border-stone-200 rounded-2xl w-full max-w-md shadow-xl">
<div className="px-6 py-4 border-b border-stone-100 flex items-center justify-between">
<h3 className="text-lg font-bold text-stone-900">{editingTask ? "Edit Task" : "Add Task"}</h3>
<button onClick={() => setShowTaskModal(false)} className="text-stone-400 hover:text-stone-600">
<button type="button" onClick={() => setShowTaskModal(false)} className="text-stone-400 hover:text-stone-600">
<svg className="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}>
<path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
</svg>
@@ -686,20 +686,20 @@ export default function SettingsSections({ brandId, workersOnly, tasksOnly }: Pr
{taskError && <div className="bg-red-50 border border-red-200 rounded-xl py-3 px-4 text-red-700 text-sm">{taskError}</div>}
<div>
<label className="block text-xs font-semibold uppercase tracking-widest text-stone-500 mb-1.5">Name (EN) *</label>
<input value={taskName} onChange={e => setTaskName(e.target.value)}
<input aria-label=". Harvesting" value={taskName} onChange={e => setTaskName(e.target.value)}
className="w-full px-4 py-3 rounded-xl border border-stone-200 bg-white text-stone-900 placeholder:text-stone-400 focus:outline-none focus:border-emerald-500 transition-colors"
placeholder="e.g. Harvesting" />
</div>
<div>
<label className="block text-xs font-semibold uppercase tracking-widest text-stone-500 mb-1.5">Name (ES)</label>
<input value={taskNameEs} onChange={e => setTaskNameEs(e.target.value)}
<input aria-label=". Cosecha" value={taskNameEs} onChange={e => setTaskNameEs(e.target.value)}
className="w-full px-4 py-3 rounded-xl border border-stone-200 bg-white text-stone-900 placeholder:text-stone-400 focus:outline-none focus:border-emerald-500 transition-colors"
placeholder="e.g. Cosecha" />
</div>
<div className="grid grid-cols-2 gap-3">
<div>
<label className="block text-xs font-semibold uppercase tracking-widest text-stone-500 mb-1.5">Unit</label>
<select value={taskUnit} onChange={e => setTaskUnit(e.target.value)}
<select aria-label="Select" value={taskUnit} onChange={e => setTaskUnit(e.target.value)}
className="w-full px-4 py-3 rounded-xl border border-stone-200 bg-white text-stone-900 focus:outline-none focus:border-emerald-500 transition-colors">
<option value="hours">Hours</option>
<option value="pieces">Pieces</option>
@@ -708,21 +708,21 @@ export default function SettingsSections({ brandId, workersOnly, tasksOnly }: Pr
</div>
<div>
<label className="block text-xs font-semibold uppercase tracking-widest text-stone-500 mb-1.5">Sort Order</label>
<input type="number" value={taskSortOrder} onChange={e => setTaskSortOrder(parseInt(e.target.value) || 0)}
<input aria-label="Number" type="number" value={taskSortOrder} onChange={e => setTaskSortOrder(parseInt(e.target.value) || 0)}
className="w-full px-4 py-3 rounded-xl border border-stone-200 bg-white text-stone-900 focus:outline-none focus:border-emerald-500 transition-colors" />
</div>
</div>
<div className="flex items-center gap-3">
<input type="checkbox" id="taskActive" checked={taskActive} onChange={e => setTaskActive(e.target.checked)}
<input aria-label="TaskActive" type="checkbox" id="taskActive" checked={taskActive} onChange={e => setTaskActive(e.target.checked)}
className="w-4 h-4 rounded border-stone-300 bg-white text-emerald-600" />
<label htmlFor="taskActive" className="text-sm text-stone-700">Active</label>
</div>
<div className="flex gap-3 pt-2">
<button onClick={() => setShowTaskModal(false)}
<button type="button" onClick={() => setShowTaskModal(false)}
className="flex-1 py-3 rounded-xl font-semibold text-sm text-stone-500 hover:text-stone-700 border border-stone-200 hover:border-stone-300 transition-all">
Cancel
</button>
<button onClick={handleSaveTask} disabled={submitting}
<button type="button" onClick={handleSaveTask} disabled={submitting}
className="flex-1 py-3 rounded-xl font-bold text-sm bg-emerald-600 hover:bg-emerald-500 disabled:opacity-40 text-white transition-all">
{submitting ? "..." : editingTask ? "Save" : "Add Task"}
</button>