feat: split company physical and postal addresses into structured fields

This commit is contained in:
Dave 2026-08-14 21:00:14 +02:00
parent 4c0a256d63
commit c1b98a6eca
3 changed files with 119 additions and 24 deletions

View file

@ -87,8 +87,14 @@ function validateCompany(c: OnboardingCompany): string | null {
return "Company must have a 3-slot proposed_names array";
}
if (!c.proposed_names[0].trim()) return "At least one proposed name required";
if (!c.physical_address?.trim()) return "Physical address required";
if (!c.postal_address?.trim()) return "Postal address required";
if (!c.physical_address_line_1?.trim()) return "Physical address line 1 required";
if (!c.physical_town_city?.trim()) return "Physical town/city required";
if (!c.physical_province?.trim()) return "Physical province required";
if (!c.physical_postal_code?.trim()) return "Physical postal code required";
if (!c.postal_address_line_1?.trim()) return "Postal address line 1 required";
if (!c.postal_town_city?.trim()) return "Postal town/city required";
if (!c.postal_province?.trim()) return "Postal province required";
if (!c.postal_postal_code?.trim()) return "Postal postal code required";
if (!c.company_email?.trim()) return "Company email required";
return null;
}

View file

@ -45,7 +45,17 @@ const EMPTY_COMPANY: OnboardingCompany = {
financial_year_end_month: 2,
authorised_shares: "1000",
physical_address: "",
physical_address_line_1: "",
physical_address_line_2: "",
physical_town_city: "",
physical_province: "",
physical_postal_code: "",
postal_address: "",
postal_address_line_1: "",
postal_address_line_2: "",
postal_town_city: "",
postal_province: "",
postal_postal_code: "",
company_cell_phone: "",
company_email: "",
public_officer_name: "",
@ -191,8 +201,14 @@ function validateCompany(c: OnboardingCompany): string[] {
if (c.financial_year_end_month < 1 || c.financial_year_end_month > 12) {
errs.push("Financial year end must be a month");
}
if (!c.physical_address.trim()) errs.push("Physical address required");
if (!c.postal_address.trim()) errs.push("Postal address required");
if (!c.physical_address_line_1.trim()) errs.push("Physical address line 1 required");
if (!c.physical_town_city.trim()) errs.push("Physical town/city required");
if (!c.physical_province.trim()) errs.push("Physical province required");
if (!c.physical_postal_code.trim()) errs.push("Physical postal code required");
if (!c.postal_address_line_1.trim()) errs.push("Postal address line 1 required");
if (!c.postal_town_city.trim()) errs.push("Postal town/city required");
if (!c.postal_province.trim()) errs.push("Postal province required");
if (!c.postal_postal_code.trim()) errs.push("Postal postal code required");
if (!c.company_cell_phone.trim()) errs.push("Company cell phone required");
const emailErr = validateEmail(c.company_email);
if (emailErr) errs.push(emailErr);
@ -689,24 +705,75 @@ function CompanyStep({
</Field>
</div>
<Field label="Trading / physical address" required>
<textarea
className={INPUT_CLS + " min-h-[88px] resize-y"}
value={company.physical_address}
onChange={(e) => onCompanyChange({ physical_address: e.target.value })}
placeholder="Unit 12, 55 Long Street, Cape Town, 8001"
required
/>
</Field>
<Field label="Postal address" required>
<textarea
className={INPUT_CLS + " min-h-[88px] resize-y"}
value={company.postal_address}
onChange={(e) => onCompanyChange({ postal_address: e.target.value })}
placeholder="PO Box 1234, Cape Town, 8000"
required
/>
</Field>
<div className="space-y-1">
<p className="text-xs font-semibold text-muted uppercase tracking-wider">Trading / physical address</p>
<Field label="Address line 1" required>
<input className={INPUT_CLS} value={company.physical_address_line_1}
onChange={(e) => onCompanyChange({ physical_address_line_1: e.target.value })}
placeholder="Unit 12, 55 Long Street" required />
</Field>
<Field label="Address line 2">
<input className={INPUT_CLS} value={company.physical_address_line_2}
onChange={(e) => onCompanyChange({ physical_address_line_2: e.target.value })}
placeholder="Sea Point" />
</Field>
<div className="grid grid-cols-2 gap-3">
<Field label="Town / City" required>
<input className={INPUT_CLS} value={company.physical_town_city}
onChange={(e) => onCompanyChange({ physical_town_city: e.target.value })}
placeholder="Cape Town" required />
</Field>
<Field label="Province" required>
<select className={INPUT_CLS} value={company.physical_province}
onChange={(e) => onCompanyChange({ physical_province: e.target.value })} required>
<option value="">Select province</option>
{["Eastern Cape","Free State","Gauteng","KwaZulu-Natal","Limpopo","Mpumalanga","Northern Cape","North West","Western Cape"].map(p => (
<option key={p} value={p}>{p}</option>
))}
</select>
</Field>
</div>
<Field label="Postal code" required>
<input className={INPUT_CLS} value={company.physical_postal_code}
onChange={(e) => onCompanyChange({ physical_postal_code: e.target.value })}
placeholder="8001" required />
</Field>
</div>
<div className="space-y-1">
<p className="text-xs font-semibold text-muted uppercase tracking-wider">Postal address</p>
<Field label="Address line 1">
<input className={INPUT_CLS} value={company.postal_address_line_1}
onChange={(e) => onCompanyChange({ postal_address_line_1: e.target.value })}
placeholder="PO Box 1234" />
</Field>
<Field label="Address line 2">
<input className={INPUT_CLS} value={company.postal_address_line_2}
onChange={(e) => onCompanyChange({ postal_address_line_2: e.target.value })}
placeholder="Cape Town" />
</Field>
<div className="grid grid-cols-2 gap-3">
<Field label="Town / City">
<input className={INPUT_CLS} value={company.postal_town_city}
onChange={(e) => onCompanyChange({ postal_town_city: e.target.value })}
placeholder="Cape Town" />
</Field>
<Field label="Province">
<select className={INPUT_CLS} value={company.postal_province}
onChange={(e) => onCompanyChange({ postal_province: e.target.value })}>
<option value="">Select province</option>
{["Eastern Cape","Free State","Gauteng","KwaZulu-Natal","Limpopo","Mpumalanga","Northern Cape","North West","Western Cape"].map(p => (
<option key={p} value={p}>{p}</option>
))}
</select>
</Field>
</div>
<Field label="Postal code">
<input className={INPUT_CLS} value={company.postal_postal_code}
onChange={(e) => onCompanyChange({ postal_postal_code: e.target.value })}
placeholder="8000" />
</Field>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
<Field label="Company cell phone" required>
@ -1267,8 +1334,20 @@ function ReviewStep({
{company.proposed_names[2] ? <SummaryRow label="Proposed name 3" value={company.proposed_names[2]} /> : null}
<SummaryRow label="Financial year end" value={MONTH_NAMES[company.financial_year_end_month - 1]} />
<SummaryRow label="Authorised shares" value={company.authorised_shares} />
<SummaryRow label="Physical address" value={company.physical_address || "—"} />
<SummaryRow label="Postal address" value={company.postal_address || "—"} />
<SummaryRow label="Physical address" value={[
company.physical_address_line_1,
company.physical_address_line_2,
company.physical_town_city,
company.physical_province,
company.physical_postal_code,
].filter(Boolean).join(", ") || "—"} />
<SummaryRow label="Postal address" value={[
company.postal_address_line_1,
company.postal_address_line_2,
company.postal_town_city,
company.postal_province,
company.postal_postal_code,
].filter(Boolean).join(", ") || "—"} />
<SummaryRow label="Cell phone" value={company.company_cell_phone || "—"} />
<SummaryRow label="Email" value={company.company_email || "—"} />
<SummaryRow label="Public officer" value={company.public_officer_name || "—"} />

View file

@ -170,7 +170,17 @@ export interface OnboardingCompany {
financial_year_end_month: number; // 1-12
authorised_shares: "100" | "1000";
physical_address: string;
physical_address_line_1: string;
physical_address_line_2: string;
physical_town_city: string;
physical_province: string;
physical_postal_code: string;
postal_address: string;
postal_address_line_1: string;
postal_address_line_2: string;
postal_town_city: string;
postal_province: string;
postal_postal_code: string;
company_cell_phone: string;
company_email: string;
public_officer_name: string;