refactor(store): single account name on users; rename profile/laps tables

users.display_name becomes users.name and is now the only human-facing
name -- profiles.name is dropped (it duplicated the account name; the
Profile page's Name field now edits users.name through PUT /api/profile,
which carries Name alongside the profiles columns). The profile table
becomes profiles and laps becomes activity_laps, homogeneous with
activity_samples. Onboarding pre-fills the display name from the OIDC
claim's name.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-04 16:11:23 +02:00
parent e2b2bf9611
commit 042579a396
19 changed files with 109 additions and 79 deletions

View File

@@ -84,6 +84,7 @@ export function LoginGate() {
if (!session!.has_profile) {
return (
<OnboardingWizard
defaultName={session!.name}
onCreated={(displayName) => setSession((s) => (s ? { ...s, has_profile: true, display_name: displayName } : s))}
/>
);

View File

@@ -15,9 +15,17 @@ type Step = "name" | "garmin";
// database -- there's no half-created account to clean up or re-prompt
// for later, so there's deliberately no "Log out" escape hatch anywhere in
// this flow: closing the tab *is* the escape hatch.
export function OnboardingWizard({ onCreated }: { onCreated: (displayName: string) => void }) {
// defaultName pre-fills the display-name field with the OIDC claim's name --
// most people keep it, and a single ✓ click through is nicer than retyping.
export function OnboardingWizard({
onCreated,
defaultName = "",
}: {
onCreated: (displayName: string) => void;
defaultName?: string;
}) {
const [step, setStep] = useState<Step>("name");
const [displayName, setDisplayName] = useState("");
const [displayName, setDisplayName] = useState(defaultName);
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [code, setCode] = useState("");