69 lines
2.6 KiB
TypeScript
69 lines
2.6 KiB
TypeScript
import { redirect } from "next/navigation";
|
|
import { getTranslations } from "next-intl/server";
|
|
import { getSessionUser } from "@/lib/session";
|
|
import { getHumanUserDetail } from "@/lib/zitadel";
|
|
import { ProfileSettingsForm } from "@/components/settings/profile-form";
|
|
|
|
/**
|
|
* /settings/profile — every authenticated user can edit their own
|
|
* first + last name. Email is shown read-only; changing it requires
|
|
* verification and is left to ZITADEL's own self-service flow.
|
|
*
|
|
* Personal vs company accounts:
|
|
* - Both can edit their first/last name in ZITADEL.
|
|
* - Personal accounts get an extra hint: editing the ZITADEL name
|
|
* does NOT change how the customer's name appears on invoices.
|
|
* Invoice identity is in org_billing.company_name (the "Full
|
|
* name" field on /settings/billing) and is intentionally
|
|
* editable separately, because legal/billing identity may not
|
|
* match preferred display identity.
|
|
* - Company accounts see an org-membership hint instead.
|
|
*
|
|
* Server-fetches the current profile from ZITADEL via the
|
|
* service-account PAT so the form starts with the canonical values
|
|
* rather than whatever happens to be in the JWT (the JWT name might
|
|
* be stale if the user updated their name in ZITADEL Console).
|
|
*/
|
|
export default async function ProfileSettingsPage() {
|
|
const user = await getSessionUser();
|
|
if (!user) redirect("/login");
|
|
|
|
const t = await getTranslations("settingsProfile");
|
|
|
|
let initial = { firstName: "", lastName: "", email: user.email };
|
|
try {
|
|
const profile = await getHumanUserDetail(user.id);
|
|
initial = {
|
|
firstName: profile.givenName,
|
|
lastName: profile.familyName,
|
|
email: profile.email || user.email,
|
|
};
|
|
} catch (e) {
|
|
// Identity provider unreachable: render the form with whatever
|
|
// we know from the session. The session has a combined `name`,
|
|
// not split parts, so we leave first/last empty and let the user
|
|
// re-enter. Server logs catch the underlying failure.
|
|
console.error("ProfileSettingsPage: getHumanUserDetail failed:", e);
|
|
}
|
|
|
|
return (
|
|
<main className="max-w-3xl mx-auto px-6 py-8">
|
|
<div className="mb-8 animate-in">
|
|
<h1 className="font-display text-2xl font-semibold accent-rule">
|
|
{t("title")}
|
|
</h1>
|
|
<p className="text-sm text-text-secondary mt-3">
|
|
{user.isPersonal ? t("subtitlePersonal") : t("subtitle")}
|
|
</p>
|
|
</div>
|
|
<div className="animate-in animate-in-delay-1">
|
|
<ProfileSettingsForm
|
|
initial={initial}
|
|
isPersonal={user.isPersonal}
|
|
orgName={user.orgName}
|
|
/>
|
|
</div>
|
|
</main>
|
|
);
|
|
}
|