"use client"; import { useState } from "react"; import { useRouter } from "next/navigation"; import { useTranslations } from "next-intl"; import type { OrgBilling } from "@/types"; import { Card } from "@/components/ui/card"; interface Props { /** Existing billing record, or null on first edit. */ initial: OrgBilling | null; /** * True if the caller is on a personal org. Personal customers * (B2C — private individuals) don't have a company name or VAT * number; the form re-labels the company-name field as "Full name" * and hides VAT. */ isPersonal: boolean; /** Default company name for company orgs on first edit. */ orgName: string; /** Default full-name for personal orgs on first edit. */ userName: string; /** * Default billing email — the address the user registered with. * Used on first edit (when `initial` is null). Customers can still * type a different address (e.g. accounting@…) but the registration * email is a sensible starting point. */ userEmail: string; } /** * Editable billing form. Used by /settings/billing; the wizard's * inline billing step (Bug 35 phase 2) reuses the same shape but is * implemented separately because of its different submit semantics * (one combined wizard submit, vs. this page's standalone PUT). * * The form does NOT do client-side VAT format validation — too many * country variations to get right, and the API will reject empty * VAT for company orgs anyway. The asterisk on the field plus the * server error suffices. */ export function BillingSettingsForm({ initial, isPersonal, orgName, userName, userEmail, }: Props) { const t = useTranslations("settingsBilling"); const tCommon = useTranslations("common"); const router = useRouter(); const [companyName, setCompanyName] = useState( initial?.companyName ?? (isPersonal ? userName : orgName) ); const [streetAddress, setStreetAddress] = useState( initial?.streetAddress ?? "" ); const [postalCode, setPostalCode] = useState(initial?.postalCode ?? ""); const [city, setCity] = useState(initial?.city ?? ""); const [country, setCountry] = useState(initial?.country ?? "CH"); const [vatNumber, setVatNumber] = useState(initial?.vatNumber ?? ""); // Default billing email to the user's registration email when no // record exists yet. They can change it (a separate accounting // address is common); we just want sensible pre-fill on first edit. const [billingEmail, setBillingEmail] = useState( initial?.billingEmail ?? userEmail ?? "" ); const [notes, setNotes] = useState(initial?.notes ?? ""); const [submitting, setSubmitting] = useState(false); const [error, setError] = useState(""); const [success, setSuccess] = useState(false); const onSubmit = async (e: React.FormEvent) => { e.preventDefault(); setSubmitting(true); setError(""); setSuccess(false); try { const res = await fetch("/api/billing", { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ companyName, streetAddress, postalCode, city, country, vatNumber: vatNumber.trim() || null, billingEmail, notes: notes.trim() || null, }), }); if (!res.ok) { const data = await res.json().catch(() => ({})); throw new Error(data.error || t("saveFailed")); } setSuccess(true); // Refresh server props so the form re-renders with the saved // record's timestamps. Subtle but useful: the "last updated" // line below ticks forward. router.refresh(); } catch (e: any) { setError(e.message); } finally { setSubmitting(false); } }; return (
{/* Bug 35: this field stores `company_name` in the DB but the label changes by customer type: - Company (B2B): "Company name" — the legal entity. - Personal (B2C): "Full name" — the individual's invoice name (may differ from their session display name; e.g. legal name vs friendly name). Required for both. The DB column is NOT NULL either way. */}
setCompanyName(e.target.value)} className="w-full px-3 py-2 rounded-lg border border-border bg-surface-2 text-text-primary text-sm focus:outline-none focus:border-text-secondary" />
setStreetAddress(e.target.value)} className="w-full px-3 py-2 rounded-lg border border-border bg-surface-2 text-text-primary text-sm focus:outline-none focus:border-text-secondary" />
setPostalCode(e.target.value)} className="w-full px-3 py-2 rounded-lg border border-border bg-surface-2 text-text-primary text-sm focus:outline-none focus:border-text-secondary" />
setCity(e.target.value)} className="w-full px-3 py-2 rounded-lg border border-border bg-surface-2 text-text-primary text-sm focus:outline-none focus:border-text-secondary" />
{/* Bug 35: VAT visible only for company customers (B2B). Personal customers (B2C — private individuals) don't have a VAT number; the API likewise doesn't require one for them. */} {!isPersonal && (
setVatNumber(e.target.value)} placeholder="CHE-123.456.789 MWST" className="w-full px-3 py-2 rounded-lg border border-border bg-surface-2 text-text-primary text-sm focus:outline-none focus:border-text-secondary" />

{t("vatHelp")}

)}
setBillingEmail(e.target.value)} placeholder="invoices@example.com" className="w-full px-3 py-2 rounded-lg border border-border bg-surface-2 text-text-primary text-sm focus:outline-none focus:border-text-secondary" />

{t("billingEmailHelp")}