Files
route-commerce/src/components/admin/HeadgateEditForm.tsx
T
Nora 0ac4beaaa8 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)
2026-06-25 23:49:37 -06:00

124 lines
3.7 KiB
TypeScript

"use client";
import { useState } from "react";
import { useRouter } from "next/navigation";
import { updateWaterHeadgate, deleteWaterHeadgate } from "@/actions/water-log/admin";
import { AdminInput, AdminTextInput, AdminSelect } from "./design-system";
import { AdminButton } from "./design-system";
type Headgate = {
id: string;
name: string;
active: boolean;
unit: string;
created_at: string;
};
const UNIT_OPTIONS = ["CFS", "GPM", "gal", "ac-in", "ac-ft"];
type Props = {
headgate: Headgate;
backHref?: string;
};
export default function HeadgateEditForm({ headgate, backHref = "/admin/water-log" }: Props) {
const router = useRouter();
// Holds user edits only; missing keys fall back to the current prop so we
// never copy the prop into useState. When the prop changes, the fallback
// value tracks the new prop automatically.
const [draft, setDraft] = useState<{ name?: string; active?: boolean; unit?: string }>({});
const [saving, setSaving] = useState(false);
const [deleting, setDeleting] = useState(false);
const [error, setError] = useState<string | null>(null);
const name = draft.name ?? headgate.name;
const active = draft.active ?? headgate.active;
const unit = draft.unit ?? headgate.unit;
const setName = (v: string) => setDraft((d) => ({ ...d, name: v }));
const setActive = (v: boolean) => setDraft((d) => ({ ...d, active: v }));
const setUnit = (v: string) => setDraft((d) => ({ ...d, unit: v }));
async function handleSave(e: React.FormEvent) {
e.preventDefault();
setSaving(true);
setError(null);
const result = await updateWaterHeadgate(headgate.id, name.trim(), active, unit);
if (result.success) {
router.push(backHref);
} else {
setError(result.error ?? "Failed to save");
setSaving(false);
}
}
async function handleDelete() {
if (!window.confirm(`Delete headgate "${headgate.name}"? Existing log entries will be preserved.`)) return;
setDeleting(true);
const result = await deleteWaterHeadgate(headgate.id);
if (result.success) {
router.push(backHref);
} else {
setError(result.error ?? "Failed to delete");
setDeleting(false);
}
}
return (
<form onSubmit={handleSave} className="space-y-4">
{error && (
<div className="rounded-lg bg-red-100 border border-red-200 p-3 text-sm text-red-700">
{error}
</div>
)}
<div className="grid grid-cols-3 gap-4">
<div className="col-span-2">
<AdminInput label="Name" required>
<AdminTextInput
value={name}
onChange={(e) => setName(e.target.value)}
placeholder="Headgate name"
/>
</AdminInput>
</div>
<AdminInput label="Default Unit">
<AdminSelect
value={unit}
onChange={(e) => setUnit(e.target.value)}
options={UNIT_OPTIONS.map((u) => ({ value: u, label: u }))}
/>
</AdminInput>
</div>
<AdminInput label="Status">
<AdminSelect
value={active ? "1" : "0"}
onChange={(e) => setActive(e.target.value === "1")}
options={[
{ value: "1", label: "Active" },
{ value: "0", label: "Inactive" },
]}
/>
</AdminInput>
<div className="flex items-center justify-between pt-2">
<AdminButton
type="button"
variant="danger"
onClick={handleDelete}
disabled={deleting}
isLoading={deleting}
>
Delete Headgate
</AdminButton>
<AdminButton
type="submit"
disabled={saving}
isLoading={saving}
>
Save Changes
</AdminButton>
</div>
</form>
);
}