475 lines
26 KiB
TypeScript
475 lines
26 KiB
TypeScript
"use client";
|
||
|
||
// components/EditEntityModal.tsx
|
||
// Pre-filled edit form for existing director or company records.
|
||
// Opens from the Edit button on a table row or from the detail page.
|
||
// PUT/PATCH via /api/pb/records/:id, invalidates list + detail cache.
|
||
|
||
import { useState, useEffect } from "react";
|
||
import { useQueryClient } from "@tanstack/react-query";
|
||
import { pbPatch, pbList } from "@/lib/api";
|
||
|
||
interface Director {
|
||
id: string;
|
||
surname?: string | null;
|
||
first_names?: string | null;
|
||
id_no?: string | null;
|
||
telephone?: string | null;
|
||
email?: string | null;
|
||
postal_address?: string | null;
|
||
director_type?: string | null;
|
||
notes?: string | null;
|
||
}
|
||
|
||
interface Company {
|
||
id: string;
|
||
enterprise_no?: string | null;
|
||
enterprise_name?: string | null;
|
||
enterprise_type?: string | null;
|
||
registration_date?: string | null;
|
||
physical_address?: string | null;
|
||
postal_address?: string | null;
|
||
director?: string | null;
|
||
notes?: string | null;
|
||
sars_login_username?: string | null;
|
||
sars_login_password?: string | null;
|
||
sars_paye_no?: string | null;
|
||
sars_vat_no?: string | null;
|
||
sars_tax_number?: string | null;
|
||
sars_vat_returns_period?: number | null;
|
||
sars_paye_return_period?: number | null;
|
||
groups?: string[] | null;
|
||
}
|
||
|
||
interface GroupOption {
|
||
id: string;
|
||
name: string;
|
||
description?: string | null;
|
||
}
|
||
|
||
interface Props {
|
||
entity: "director" | "company";
|
||
/** Original record data for pre-filling the form */
|
||
record: Director | Company;
|
||
open: boolean;
|
||
onClose: () => void;
|
||
/** Called after a successful save */
|
||
onAfterSave?: () => void;
|
||
}
|
||
|
||
export default function EditEntityModal({ entity, record, open, onClose, onAfterSave }: Props) {
|
||
const qc = useQueryClient();
|
||
const [loading, setLoading] = useState(false);
|
||
const [error, setError] = useState<string | null>(null);
|
||
|
||
// Director fields
|
||
const [surname, setSurname] = useState("");
|
||
const [firstNames, setFirstNames] = useState("");
|
||
const [idNo, setIdNo] = useState("");
|
||
const [telephone, setTelephone] = useState("");
|
||
const [email, setEmail] = useState("");
|
||
const [postalAddress, setPostalAddress] = useState("");
|
||
const [directorType, setDirectorType] = useState("");
|
||
const [notes, setNotes] = useState("");
|
||
|
||
// Company fields
|
||
const [enterpriseNo, setEnterpriseNo] = useState("");
|
||
const [enterpriseName, setEnterpriseName] = useState("");
|
||
const [enterpriseType, setEnterpriseType] = useState("");
|
||
const [registrationDate, setRegistrationDate] = useState("");
|
||
const [physicalAddress, setPhysicalAddress] = useState("");
|
||
const [postalAddressCo, setPostalAddressCo] = useState("");
|
||
const [notesCo, setNotesCo] = useState("");
|
||
// SARS fields
|
||
const [sarsLoginUsername, setSarsLoginUsername] = useState("");
|
||
const [sarsLoginPassword, setSarsLoginPassword] = useState("");
|
||
const [sarsPayeNo, setSarsPayeNo] = useState("");
|
||
const [sarsVatNo, setSarsVatNo] = useState("");
|
||
const [sarsTaxNo, setSarsTaxNo] = useState("");
|
||
const [sarsVatReturnsPeriod, setSarsVatReturnsPeriod] = useState("");
|
||
const [sarsPayeReturnPeriod, setSarsPayeReturnPeriod] = useState("");
|
||
|
||
// Group fields
|
||
const [selectedGroups, setSelectedGroups] = useState<string[]>([]);
|
||
const [groupOptions, setGroupOptions] = useState<GroupOption[]>([]);
|
||
|
||
// Populate form when modal opens or record changes
|
||
useEffect(() => {
|
||
if (!open) return;
|
||
setError(null);
|
||
|
||
if (entity === "director") {
|
||
const d = record as Director;
|
||
setSurname(d.surname ?? "");
|
||
setFirstNames(d.first_names ?? "");
|
||
setIdNo(d.id_no ?? "");
|
||
setTelephone(d.telephone ?? "");
|
||
setEmail(d.email ?? "");
|
||
setPostalAddress(d.postal_address ?? "");
|
||
setDirectorType(d.director_type ?? "");
|
||
setNotes(d.notes ?? "");
|
||
} else {
|
||
const c = record as Company;
|
||
setEnterpriseNo(c.enterprise_no ?? "");
|
||
setEnterpriseName(c.enterprise_name ?? "");
|
||
setEnterpriseType(c.enterprise_type ?? "");
|
||
setRegistrationDate(c.registration_date ?? "");
|
||
setPhysicalAddress(c.physical_address ?? "");
|
||
setPostalAddressCo(c.postal_address ?? "");
|
||
setNotesCo(c.notes ?? "");
|
||
setSarsLoginUsername(c.sars_login_username ?? "");
|
||
setSarsLoginPassword(c.sars_login_password ?? "");
|
||
setSarsPayeNo(c.sars_paye_no ?? "");
|
||
setSarsVatNo(c.sars_vat_no ?? "");
|
||
setSarsTaxNo(c.sars_tax_number ?? "");
|
||
setSarsVatReturnsPeriod(c.sars_vat_returns_period != null ? String(c.sars_vat_returns_period) : "");
|
||
setSarsPayeReturnPeriod(c.sars_paye_return_period != null ? String(c.sars_paye_return_period) : "");
|
||
setSelectedGroups(c.groups ?? []);
|
||
}
|
||
}, [open, entity, record]);
|
||
|
||
// Fetch group options when modal opens (company entity only)
|
||
useEffect(() => {
|
||
if (!open || entity !== "company") return;
|
||
pbList<GroupOption>({ collection: "groups", perPage: 200, sort: "name" })
|
||
.then((res) => setGroupOptions(res.items))
|
||
.catch(() => {});
|
||
}, [open, entity]);
|
||
|
||
if (!open) return null;
|
||
|
||
const isDirector = entity === "director";
|
||
const title = isDirector ? `Edit director — ${(record as Director).surname}, ${(record as Director).first_names}` : `Edit company — ${(record as Company).enterprise_name}`;
|
||
const collection = isDirector ? "ysterbaard_directors" : "ysterbaard_companies";
|
||
|
||
const handleSubmit = async (e: React.FormEvent) => {
|
||
e.preventDefault();
|
||
setError(null);
|
||
setLoading(true);
|
||
|
||
try {
|
||
const fields: Record<string, string | number | string[]> = {};
|
||
|
||
if (isDirector) {
|
||
if (!surname.trim() || !firstNames.trim()) {
|
||
setError("Surname and first names are required.");
|
||
setLoading(false);
|
||
return;
|
||
}
|
||
fields.surname = surname.trim();
|
||
fields.first_names = firstNames.trim();
|
||
if (idNo.trim()) fields.id_no = idNo.trim();
|
||
if (telephone.trim()) fields.telephone = telephone.trim();
|
||
if (email.trim()) fields.email = email.trim();
|
||
if (postalAddress.trim()) fields.postal_address = postalAddress.trim();
|
||
if (directorType.trim()) fields.director_type = directorType.trim();
|
||
if (notes.trim()) fields.notes = notes.trim();
|
||
} else {
|
||
if (!enterpriseNo.trim() || !enterpriseName.trim()) {
|
||
setError("Enterprise number and name are required.");
|
||
setLoading(false);
|
||
return;
|
||
}
|
||
fields.enterprise_no = enterpriseNo.trim().toUpperCase();
|
||
fields.enterprise_name = enterpriseName.trim().toUpperCase();
|
||
if (enterpriseType.trim()) fields.enterprise_type = enterpriseType.trim();
|
||
if (registrationDate.trim()) fields.registration_date = registrationDate.trim();
|
||
if (physicalAddress.trim()) fields.physical_address = physicalAddress.trim();
|
||
if (postalAddressCo.trim()) fields.postal_address = postalAddressCo.trim();
|
||
if (notesCo.trim()) fields.notes = notesCo.trim();
|
||
if (sarsLoginUsername.trim()) fields.sars_login_username = sarsLoginUsername.trim();
|
||
if (sarsLoginPassword.trim()) fields.sars_login_password = sarsLoginPassword.trim();
|
||
if (sarsPayeNo.trim()) fields.sars_paye_no = sarsPayeNo.trim();
|
||
if (sarsVatNo.trim()) fields.sars_vat_no = sarsVatNo.trim();
|
||
if (sarsTaxNo.trim()) fields.sars_tax_number = sarsTaxNo.trim();
|
||
// Period fields: store the selected numeric value as string (PocketBase number field accepts string->number conversion)
|
||
const vatPeriod = sarsVatReturnsPeriod;
|
||
const payePeriod = sarsPayeReturnPeriod;
|
||
if (vatPeriod !== "" || (record as Company).sars_vat_returns_period != null) {
|
||
fields.sars_vat_returns_period = vatPeriod !== "" ? Number(vatPeriod) : 0;
|
||
}
|
||
if (payePeriod !== "" || (record as Company).sars_paye_return_period != null) {
|
||
fields.sars_paye_return_period = payePeriod !== "" ? Number(payePeriod) : 0;
|
||
}
|
||
// Groups — always send current selection (empty array is valid to clear)
|
||
fields.groups = selectedGroups;
|
||
}
|
||
|
||
await pbPatch({ collection, id: record.id, fields });
|
||
|
||
// Invalidate both list and detail caches
|
||
await qc.invalidateQueries({ queryKey: ["pb-list", collection] });
|
||
await qc.invalidateQueries({ queryKey: ["director", record.id] });
|
||
await qc.invalidateQueries({ queryKey: ["company", record.id] });
|
||
|
||
onAfterSave?.();
|
||
onClose();
|
||
} catch (err) {
|
||
setError(err instanceof Error ? err.message : "Something went wrong. Please try again.");
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
};
|
||
|
||
return (
|
||
<div
|
||
className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 backdrop-blur-sm p-4"
|
||
role="dialog"
|
||
aria-modal="true"
|
||
aria-label={title}
|
||
>
|
||
<div className="bg-surface border border-border rounded-xl shadow-2xl w-full max-w-lg flex flex-col max-h-[90vh]">
|
||
{/* Header */}
|
||
<div className="px-6 pt-6 pb-4 border-b border-border flex-shrink-0">
|
||
<div className="flex items-start justify-between">
|
||
<div>
|
||
<h2 className="text-lg font-bold text-fg">Edit {isDirector ? "director" : "company"}</h2>
|
||
<p className="text-sm text-muted mt-0.5">{title}</p>
|
||
</div>
|
||
<button
|
||
onClick={onClose}
|
||
className="ml-4 text-muted hover:text-fg text-xl leading-none p-1 rounded transition-colors"
|
||
aria-label="Close"
|
||
>
|
||
×
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Form */}
|
||
<form onSubmit={handleSubmit} className="flex-1 overflow-y-auto px-6 py-5 space-y-4">
|
||
{error && (
|
||
<div className="bg-red-500/10 border border-red-500/30 rounded-lg px-4 py-3 text-sm text-red-400">
|
||
{error}
|
||
</div>
|
||
)}
|
||
|
||
{isDirector ? (
|
||
<>
|
||
<div className="grid grid-cols-2 gap-4">
|
||
<div>
|
||
<label className="block text-sm font-semibold text-fg mb-1.5" htmlFor="edit-surname">Surname <span className="text-red-400">*</span></label>
|
||
<input id="edit-surname" type="text" value={surname}
|
||
onChange={(e) => setSurname(e.target.value.toUpperCase())}
|
||
className="w-full rounded-lg border border-border bg-surface-warm px-3 py-2.5 text-sm text-fg focus:outline-none focus:border-accent focus:ring-1 focus:ring-accent"
|
||
required />
|
||
</div>
|
||
<div>
|
||
<label className="block text-sm font-semibold text-fg mb-1.5" htmlFor="edit-firstnames">First names <span className="text-red-400">*</span></label>
|
||
<input id="edit-firstnames" type="text" value={firstNames}
|
||
onChange={(e) => setFirstNames(e.target.value.toUpperCase())}
|
||
className="w-full rounded-lg border border-border bg-surface-warm px-3 py-2.5 text-sm text-fg focus:outline-none focus:border-accent focus:ring-1 focus:ring-accent"
|
||
required />
|
||
</div>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-semibold text-fg mb-1.5" htmlFor="edit-idno">SA ID number <span className="text-xs text-muted font-normal">(optional)</span></label>
|
||
<input id="edit-idno" type="tel" value={idNo}
|
||
onChange={(e) => setIdNo(e.target.value.replace(/\D/g, "").slice(0, 13))}
|
||
className="w-full rounded-lg border border-border bg-surface-warm px-3 py-2.5 text-sm text-fg font-mono focus:outline-none focus:border-accent focus:ring-1 focus:ring-accent"
|
||
maxLength={13} />
|
||
</div>
|
||
|
||
<div className="grid grid-cols-2 gap-4">
|
||
<div>
|
||
<label className="block text-sm font-semibold text-fg mb-1.5" htmlFor="edit-tel">Telephone <span className="text-xs text-muted font-normal">(optional)</span></label>
|
||
<input id="edit-tel" type="tel" value={telephone}
|
||
onChange={(e) => setTelephone(e.target.value)}
|
||
className="w-full rounded-lg border border-border bg-surface-warm px-3 py-2.5 text-sm text-fg focus:outline-none focus:border-accent focus:ring-1 focus:ring-accent" />
|
||
</div>
|
||
<div>
|
||
<label className="block text-sm font-semibold text-fg mb-1.5" htmlFor="edit-email">Email <span className="text-xs text-muted font-normal">(optional)</span></label>
|
||
<input id="edit-email" type="email" value={email}
|
||
onChange={(e) => setEmail(e.target.value)}
|
||
className="w-full rounded-lg border border-border bg-surface-warm px-3 py-2.5 text-sm text-fg focus:outline-none focus:border-accent focus:ring-1 focus:ring-accent" />
|
||
</div>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-semibold text-fg mb-1.5" htmlFor="edit-postal">Postal address <span className="text-xs text-muted font-normal">(optional)</span></label>
|
||
<textarea id="edit-postal" value={postalAddress} rows={2}
|
||
onChange={(e) => setPostalAddress(e.target.value)}
|
||
className="w-full rounded-lg border border-border bg-surface-warm px-3 py-2.5 text-sm text-fg resize-none focus:outline-none focus:border-accent focus:ring-1 focus:ring-accent" />
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-semibold text-fg mb-1.5" htmlFor="edit-type">Director type <span className="text-xs text-muted font-normal">(optional)</span></label>
|
||
<input id="edit-type" type="text" value={directorType}
|
||
onChange={(e) => setDirectorType(e.target.value)}
|
||
className="w-full rounded-lg border border-border bg-surface-warm px-3 py-2.5 text-sm text-fg focus:outline-none focus:border-accent focus:ring-1 focus:ring-accent"
|
||
placeholder="e.g. Individual, Corporate" />
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-semibold text-fg mb-1.5" htmlFor="edit-notes">Notes <span className="text-xs text-muted font-normal">(optional)</span></label>
|
||
<textarea id="edit-notes" value={notes} rows={2}
|
||
onChange={(e) => setNotes(e.target.value)}
|
||
className="w-full rounded-lg border border-border bg-surface-warm px-3 py-2.5 text-sm text-fg resize-none focus:outline-none focus:border-accent focus:ring-1 focus:ring-accent" />
|
||
</div>
|
||
</>
|
||
) : (
|
||
<>
|
||
<div className="grid grid-cols-2 gap-4">
|
||
<div>
|
||
<label className="block text-sm font-semibold text-fg mb-1.5" htmlFor="edit-eno">Enterprise No. <span className="text-red-400">*</span></label>
|
||
<input id="edit-eno" type="text" value={enterpriseNo}
|
||
onChange={(e) => setEnterpriseNo(e.target.value.toUpperCase())}
|
||
className="w-full rounded-lg border border-border bg-surface-warm px-3 py-2.5 text-sm text-fg font-mono focus:outline-none focus:border-accent focus:ring-1 focus:ring-accent"
|
||
required />
|
||
</div>
|
||
<div>
|
||
<label className="block text-sm font-semibold text-fg mb-1.5" htmlFor="edit-etype">Enterprise type <span className="text-xs text-muted font-normal">(optional)</span></label>
|
||
<input id="edit-etype" type="text" value={enterpriseType}
|
||
onChange={(e) => setEnterpriseType(e.target.value)}
|
||
className="w-full rounded-lg border border-border bg-surface-warm px-3 py-2.5 text-sm text-fg focus:outline-none focus:border-accent focus:ring-1 focus:ring-accent"
|
||
placeholder="Private Company" />
|
||
</div>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-semibold text-fg mb-1.5" htmlFor="edit-ename">Enterprise name <span className="text-red-400">*</span></label>
|
||
<input id="edit-ename" type="text" value={enterpriseName}
|
||
onChange={(e) => setEnterpriseName(e.target.value.toUpperCase())}
|
||
className="w-full rounded-lg border border-border bg-surface-warm px-3 py-2.5 text-sm text-fg focus:outline-none focus:border-accent focus:ring-1 focus:ring-accent"
|
||
required />
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-semibold text-fg mb-1.5" htmlFor="edit-rdate">Registration date <span className="text-xs text-muted font-normal">(optional)</span></label>
|
||
<input id="edit-rdate" type="date" value={registrationDate}
|
||
onChange={(e) => setRegistrationDate(e.target.value)}
|
||
className="w-full rounded-lg border border-border bg-surface-warm px-3 py-2.5 text-sm text-fg focus:outline-none focus:border-accent focus:ring-1 focus:ring-accent" />
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-semibold text-fg mb-1.5" htmlFor="edit-phys">Physical address <span className="text-xs text-muted font-normal">(optional)</span></label>
|
||
<textarea id="edit-phys" value={physicalAddress} rows={2}
|
||
onChange={(e) => setPhysicalAddress(e.target.value)}
|
||
className="w-full rounded-lg border border-border bg-surface-warm px-3 py-2.5 text-sm text-fg resize-none focus:outline-none focus:border-accent focus:ring-1 focus:ring-accent" />
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-semibold text-fg mb-1.5" htmlFor="edit-postalco">Postal address <span className="text-xs text-muted font-normal">(optional)</span></label>
|
||
<textarea id="edit-postalco" value={postalAddressCo} rows={2}
|
||
onChange={(e) => setPostalAddressCo(e.target.value)}
|
||
className="w-full rounded-lg border border-border bg-surface-warm px-3 py-2.5 text-sm text-fg resize-none focus:outline-none focus:border-accent focus:ring-1 focus:ring-accent" />
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-semibold text-fg mb-1.5" htmlFor="edit-notesco">Notes <span className="text-xs text-muted font-normal">(optional)</span></label>
|
||
<textarea id="edit-notesco" value={notesCo} rows={2}
|
||
onChange={(e) => setNotesCo(e.target.value)}
|
||
className="w-full rounded-lg border border-border bg-surface-warm px-3 py-2.5 text-sm text-fg resize-none focus:outline-none focus:border-accent focus:ring-1 focus:ring-accent" />
|
||
</div>
|
||
|
||
{/* Group membership */}
|
||
<div>
|
||
<label className="block text-sm font-semibold text-fg mb-1.5" htmlFor="edit-groups">Group Membership <span className="text-xs text-muted font-normal">(optional)</span></label>
|
||
<p className="text-xs text-muted mb-2">Hold Ctrl (or Cmd on Mac) to select multiple groups.</p>
|
||
<select
|
||
id="edit-groups"
|
||
multiple
|
||
value={selectedGroups}
|
||
onChange={(e) => {
|
||
const opts = Array.from(e.target.selectedOptions).map((o) => o.value);
|
||
setSelectedGroups(opts);
|
||
}}
|
||
className="w-full rounded-lg border border-border bg-surface-warm px-3 py-2.5 text-sm text-fg focus:outline-none focus:border-accent focus:ring-1 focus:ring-accent min-h-[100px]">
|
||
{groupOptions.length === 0 ? (
|
||
<option value="" disabled>No groups created yet — save and add groups via PocketBase</option>
|
||
) : (
|
||
groupOptions.map((g) => (
|
||
<option key={g.id} value={g.id}>{g.name}</option>
|
||
))
|
||
)}
|
||
</select>
|
||
</div>
|
||
|
||
{/* SARS credentials */}
|
||
<div className="border-t border-border pt-4 mt-2">
|
||
<p className="text-sm font-semibold text-fg mb-3">SARS eFiling Details</p>
|
||
<div className="grid grid-cols-2 gap-4">
|
||
<div>
|
||
<label className="block text-sm font-semibold text-fg mb-1.5" htmlFor="edit-sars-user">SARS Login Username <span className="text-xs text-muted font-normal">(optional)</span></label>
|
||
<input id="edit-sars-user" type="text" value={sarsLoginUsername}
|
||
onChange={(e) => setSarsLoginUsername(e.target.value)}
|
||
className="w-full rounded-lg border border-border bg-surface-warm px-3 py-2.5 text-sm text-fg focus:outline-none focus:border-accent focus:ring-1 focus:ring-accent"
|
||
placeholder="eFiling username" />
|
||
</div>
|
||
<div>
|
||
<label className="block text-sm font-semibold text-fg mb-1.5" htmlFor="edit-sars-pass">SARS Login Password <span className="text-xs text-muted font-normal">(optional)</span></label>
|
||
<input id="edit-sars-pass" type="text" value={sarsLoginPassword}
|
||
onChange={(e) => setSarsLoginPassword(e.target.value)}
|
||
className="w-full rounded-lg border border-border bg-surface-warm px-3 py-2.5 text-sm text-fg focus:outline-none focus:border-accent focus:ring-1 focus:ring-accent"
|
||
placeholder="eFiling password" />
|
||
</div>
|
||
</div>
|
||
<div className="grid grid-cols-2 gap-4 mt-3">
|
||
<div>
|
||
<label className="block text-sm font-semibold text-fg mb-1.5" htmlFor="edit-sars-paye">SARS PAYE No. <span className="text-xs text-muted font-normal">(optional)</span></label>
|
||
<input id="edit-sars-paye" type="text" value={sarsPayeNo}
|
||
onChange={(e) => setSarsPayeNo(e.target.value)}
|
||
className="w-full rounded-lg border border-border bg-surface-warm px-3 py-2.5 text-sm text-fg font-mono focus:outline-none focus:border-accent focus:ring-1 focus:ring-accent"
|
||
placeholder="e.g. 7020829828" />
|
||
</div>
|
||
<div>
|
||
<label className="block text-sm font-semibold text-fg mb-1.5" htmlFor="edit-sars-vat">SARS VAT No. <span className="text-xs text-muted font-normal">(optional)</span></label>
|
||
<input id="edit-sars-vat" type="text" value={sarsVatNo}
|
||
onChange={(e) => setSarsVatNo(e.target.value)}
|
||
className="w-full rounded-lg border border-border bg-surface-warm px-3 py-2.5 text-sm text-fg font-mono focus:outline-none focus:border-accent focus:ring-1 focus:ring-accent"
|
||
placeholder="e.g. 4123456789" />
|
||
</div>
|
||
</div>
|
||
<div className="mt-3">
|
||
<label className="block text-sm font-semibold text-fg mb-1.5" htmlFor="edit-sars-tax">SARS Tax No. <span className="text-xs text-muted font-normal">(optional)</span></label>
|
||
<input id="edit-sars-tax" type="text" value={sarsTaxNo}
|
||
onChange={(e) => setSarsTaxNo(e.target.value)}
|
||
className="w-full rounded-lg border border-border bg-surface-warm px-3 py-2.5 text-sm text-fg font-mono focus:outline-none focus:border-accent focus:ring-1 focus:ring-accent"
|
||
placeholder="e.g. 9004615283" />
|
||
</div>
|
||
<div className="grid grid-cols-2 gap-4 mt-3">
|
||
<div>
|
||
<label className="block text-sm font-semibold text-fg mb-1.5" htmlFor="edit-sars-vat-period">SARS VAT Returns Period</label>
|
||
<select id="edit-sars-vat-period" value={sarsVatReturnsPeriod}
|
||
onChange={(e) => setSarsVatReturnsPeriod(e.target.value)}
|
||
className="w-full rounded-lg border border-border bg-surface-warm px-3 py-2.5 text-sm text-fg focus:outline-none focus:border-accent focus:ring-1 focus:ring-accent">
|
||
<option value="">— select —</option>
|
||
<option value="0">None</option>
|
||
<option value="1">Every Month</option>
|
||
<option value="2">Even Month</option>
|
||
<option value="3">Uneven Months</option>
|
||
<option value="6">Bi Annually</option>
|
||
<option value="12">Annually</option>
|
||
</select>
|
||
</div>
|
||
<div>
|
||
<label className="block text-sm font-semibold text-fg mb-1.5" htmlFor="edit-sars-paye-period">SARS PAYE Returns Period</label>
|
||
<select id="edit-sars-paye-period" value={sarsPayeReturnPeriod}
|
||
onChange={(e) => setSarsPayeReturnPeriod(e.target.value)}
|
||
className="w-full rounded-lg border border-border bg-surface-warm px-3 py-2.5 text-sm text-fg focus:outline-none focus:border-accent focus:ring-1 focus:ring-accent">
|
||
<option value="">— select —</option>
|
||
<option value="0">None</option>
|
||
<option value="1">Every month</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</>
|
||
)}
|
||
|
||
<div className="pt-2 flex gap-3">
|
||
<button type="button" onClick={onClose}
|
||
className="flex-1 px-4 py-2.5 rounded-lg border border-border bg-surface-warm text-sm font-semibold text-fg hover:bg-surface transition-colors">
|
||
Cancel
|
||
</button>
|
||
<button type="submit" disabled={loading}
|
||
className="flex-1 px-4 py-2.5 rounded-lg bg-accent text-accent-on text-sm font-bold hover:bg-accent-hover transition-colors disabled:opacity-60 disabled:cursor-not-allowed">
|
||
{loading ? "Saving..." : "Save changes"}
|
||
</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|