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:
@@ -22,7 +22,7 @@
|
||||
* `src/actions/water-log/admin.ts`.
|
||||
*/
|
||||
|
||||
import { useMemo, useState } from "react";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/navigation";
|
||||
import {
|
||||
@@ -105,6 +105,20 @@ export default function WaterLogAdminPanel({
|
||||
const [filterVia, setFilterVia] = useState("");
|
||||
const [csvLoading, setCsvLoading] = useState(false);
|
||||
|
||||
// `today` and the human-readable date are read from the clock; compute
|
||||
// them in the browser only to avoid hydration mismatches. The setStates
|
||||
// are wrapped in an async IIFE so the effect body does not call setState
|
||||
// synchronously (avoids the cascading-renders ESLint rule).
|
||||
const [today, setToday] = useState<string>("");
|
||||
const [todayLabel, setTodayLabel] = useState<string>("");
|
||||
useEffect(() => {
|
||||
void (async () => {
|
||||
const now = new Date();
|
||||
setToday(now.toISOString().slice(0, 10));
|
||||
setTodayLabel(formatDate(now));
|
||||
})();
|
||||
}, []);
|
||||
|
||||
// ── Season + report settings (localStorage) ───────────────────────
|
||||
const [showReportSettings, setShowReportSettings] = useState(false);
|
||||
const [seasonStart, setSeasonStart] = useState<IrrigationSeasonSettings>(() => {
|
||||
@@ -126,9 +140,11 @@ export default function WaterLogAdminPanel({
|
||||
[seasonStart],
|
||||
);
|
||||
|
||||
const today = new Date().toISOString().slice(0, 10);
|
||||
const todayEntries = useMemo(
|
||||
() => entries.filter((e) => (e.logged_date ?? e.logged_at.slice(0, 10)) === today),
|
||||
() =>
|
||||
today
|
||||
? entries.filter((e) => (e.logged_date ?? e.logged_at.slice(0, 10)) === today)
|
||||
: [],
|
||||
[entries, today],
|
||||
);
|
||||
const todayTotal = todayEntries.reduce((s, e) => s + e.measurement, 0);
|
||||
@@ -378,7 +394,7 @@ export default function WaterLogAdminPanel({
|
||||
</span>
|
||||
<span className="text-[#57694e]/60">·</span>
|
||||
<span className="text-[#57694e]">
|
||||
{formatDate(new Date())}
|
||||
{todayLabel}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
@@ -571,21 +587,21 @@ export default function WaterLogAdminPanel({
|
||||
|
||||
{canManage && (
|
||||
<div className="mt-3 flex flex-wrap items-center justify-between gap-1 border-t border-[#e8ebe8] pt-3 text-[11px]">
|
||||
<button
|
||||
<button type="button"
|
||||
onClick={() => router.push(`/admin/water-log/headgates/${h.id}`)}
|
||||
className="font-medium text-[#1a4d2e] hover:underline"
|
||||
>
|
||||
Edit →
|
||||
</button>
|
||||
<div className="flex items-center gap-2">
|
||||
<button
|
||||
<button type="button"
|
||||
onClick={() => handleRegenerateToken(h)}
|
||||
className="text-[#57694e] hover:text-[#1a4d2e]"
|
||||
>
|
||||
Rotate token
|
||||
</button>
|
||||
<span className="text-[#d4d9d3]">·</span>
|
||||
<button
|
||||
<button type="button"
|
||||
onClick={() => handleDeleteHg(h)}
|
||||
className="text-[#b91c1c] hover:text-[#7f1d1d]"
|
||||
>
|
||||
@@ -705,14 +721,14 @@ export default function WaterLogAdminPanel({
|
||||
</div>
|
||||
{canManage && (
|
||||
<div className="flex shrink-0 items-center gap-1 text-[11px]">
|
||||
<button
|
||||
<button type="button"
|
||||
onClick={() => handleResetPin(u)}
|
||||
className="text-[#57694e] hover:text-[#1a4d2e]"
|
||||
>
|
||||
PIN
|
||||
</button>
|
||||
<span className="text-[#d4d9d3]">·</span>
|
||||
<button
|
||||
<button type="button"
|
||||
onClick={() => handleDeleteUser(u)}
|
||||
className="text-[#b91c1c] hover:text-[#7f1d1d]"
|
||||
>
|
||||
@@ -809,7 +825,7 @@ export default function WaterLogAdminPanel({
|
||||
filterHeadgate ||
|
||||
filterUser ||
|
||||
filterVia) && (
|
||||
<button
|
||||
<button type="button"
|
||||
onClick={() => {
|
||||
setFilterDateFrom("");
|
||||
setFilterDateTo("");
|
||||
@@ -887,7 +903,7 @@ export default function WaterLogAdminPanel({
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-4 py-2.5 text-right text-[11px]">
|
||||
<button
|
||||
<button type="button"
|
||||
onClick={() =>
|
||||
router.push(`/admin/water-log/entries/${e.id}`)
|
||||
}
|
||||
@@ -1043,7 +1059,7 @@ function PinBanner({
|
||||
{pin}
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
<button type="button"
|
||||
onClick={onDismiss}
|
||||
className="rounded-lg border border-[#1a4d2e]/30 px-3 py-1.5 text-xs font-semibold text-[#1a4d2e] hover:bg-white"
|
||||
>
|
||||
@@ -1258,7 +1274,7 @@ function ReportSettingsPanel({
|
||||
<span className="mb-1 block font-mono text-[10px] uppercase tracking-wider text-[#57694e]">
|
||||
Recipient name (used in report salutation)
|
||||
</span>
|
||||
<input
|
||||
<input aria-label="David"
|
||||
type="text"
|
||||
value={recipientName}
|
||||
onChange={(e) => onRecipientNameChange(e.target.value)}
|
||||
|
||||
Reference in New Issue
Block a user