2026-03-18 13:02:58 -07:00

198 lines
7.4 KiB
TypeScript

"use client";
import { useEffect, useState } from "react";
import { AppShell } from "../../components/app-shell";
type ApiResponse<T> = {
data: T;
meta: { timestamp: string; version: "v1" };
error: null | { message: string; code?: string };
};
type ProfileData = {
user: {
id: string;
email: string;
fullName: string;
phone?: string | null;
companyName?: string | null;
addressLine1i: string | null;
addressLine2i: string | null;
city?: string | null;
state?: string | null;
postalCode?: string | null;
country?: string | null;
};
};
export default function ProfilePage() {
const [token, setToken] = useState("");
const [status, setStatus] = useState("");
const [fullName, setFullName] = useState("");
const [phone, setPhone] = useState("");
const [companyName, setCompanyName] = useState("");
const [addressLine1, setAddressLine1] = useState("");
const [addressLine2, setAddressLine2] = useState("");
const [city, setCity] = useState("");
const [state, setState] = useState("");
const [postalCode, setPostalCode] = useState("");
const [country, setCountry] = useState("");
useEffect(() => {
const stored = localStorage.getItem("ledgerone_token") ?? "";
setToken(stored);
}, []);
const onSubmit = async (event: React.FormEvent) => {
event.preventDefault();
if (!token) {
setStatus("Please sign in to update your profile.");
return;
}
setStatus("Saving profile...");
try {
const res = await fetch("/api/auth/profile", {
method: "PATCH",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`
},
body: JSON.stringify({
fullName,
phone: phone || undefined,
companyName: companyName || undefined,
addressLine1: addressLine1 || undefined,
addressLine2: addressLine2 || undefined,
city: city || undefined,
state: state || undefined,
postalCode: postalCode || undefined,
country: country || undefined
})
});
const payload = (await res.json()) as ApiResponse<ProfileData>;
if (!res.ok || payload.error) {
setStatus(payload.error?.message ?? "Profile update failed.");
return;
}
setStatus("Profile updated.");
} catch {
setStatus("Profile update failed.");
}
};
return (
<AppShell title="Profile" subtitle="Keep your ledger profile current for exports.">
<div className="app-card p-10">
<p className="text-xs uppercase tracking-[0.3em] text-muted">LedgerOne</p>
<h2 className="mt-4 text-2xl font-semibold">Complete your profile</h2>
<p className="mt-2 text-sm text-muted">
Add the details we need to personalize your ledger workspace.
</p>
<form className="mt-8 grid gap-5 md:grid-cols-2" onSubmit={onSubmit}>
<div className="space-y-2 md:col-span-2">
<label className="text-xs uppercase tracking-[0.2em] text-muted">
Full name
</label>
<input
className="w-full rounded-2xl border border-ink/10 bg-white px-4 py-3 text-sm"
type="text"
value={fullName}
onChange={(event) => setFullName(event.target.value)}
required
/>
</div>
<div className="space-y-2">
<label className="text-xs uppercase tracking-[0.2em] text-muted">Phone</label>
<input
className="w-full rounded-2xl border border-ink/10 bg-white px-4 py-3 text-sm"
type="tel"
value={phone}
onChange={(event) => setPhone(event.target.value)}
/>
</div>
<div className="space-y-2">
<label className="text-xs uppercase tracking-[0.2em] text-muted">
Company
</label>
<input
className="w-full rounded-2xl border border-ink/10 bg-white px-4 py-3 text-sm"
type="text"
value={companyName}
onChange={(event) => setCompanyName(event.target.value)}
/>
</div>
<div className="space-y-2 md:col-span-2">
<label className="text-xs uppercase tracking-[0.2em] text-muted">
Address line 1
</label>
<input
className="w-full rounded-2xl border border-ink/10 bg-white px-4 py-3 text-sm"
type="text"
value={addressLine1}
onChange={(event) => setAddressLine1(event.target.value)}
/>
</div>
<div className="space-y-2 md:col-span-2">
<label className="text-xs uppercase tracking-[0.2em] text-muted">
Address line 2
</label>
<input
className="w-full rounded-2xl border border-ink/10 bg-white px-4 py-3 text-sm"
type="text"
value={addressLine2}
onChange={(event) => setAddressLine2(event.target.value)}
/>
</div>
<div className="space-y-2">
<label className="text-xs uppercase tracking-[0.2em] text-muted">City</label>
<input
className="w-full rounded-2xl border border-ink/10 bg-white px-4 py-3 text-sm"
type="text"
value={city}
onChange={(event) => setCity(event.target.value)}
/>
</div>
<div className="space-y-2">
<label className="text-xs uppercase tracking-[0.2em] text-muted">State</label>
<input
className="w-full rounded-2xl border border-ink/10 bg-white px-4 py-3 text-sm"
type="text"
value={state}
onChange={(event) => setState(event.target.value)}
/>
</div>
<div className="space-y-2">
<label className="text-xs uppercase tracking-[0.2em] text-muted">
Postal code
</label>
<input
className="w-full rounded-2xl border border-ink/10 bg-white px-4 py-3 text-sm"
type="text"
value={postalCode}
onChange={(event) => setPostalCode(event.target.value)}
/>
</div>
<div className="space-y-2">
<label className="text-xs uppercase tracking-[0.2em] text-muted">Country</label>
<input
className="w-full rounded-2xl border border-ink/10 bg-white px-4 py-3 text-sm"
type="text"
value={country}
onChange={(event) => setCountry(event.target.value)}
/>
</div>
<div className="md:col-span-2">
<button
type="submit"
className="w-full rounded-2xl bg-ink px-4 py-3 text-sm font-semibold text-haze"
>
Save profile
</button>
</div>
</form>
{status ? <p className="mt-4 text-xs text-muted">{status}</p> : null}
</div>
</AppShell>
);
}