Phase6c: Optional Company contact name
All checks were successful
Build and Push / build (push) Successful in 1m40s

This commit is contained in:
2026-05-25 13:50:16 +02:00
parent 002867850d
commit a1769eeb00
10 changed files with 530 additions and 7 deletions

View File

@@ -0,0 +1,68 @@
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>
);
}