Simpl-Resultat/src/pages/ProfileSelectionPage.tsx
escouade-bot e5be6f5a56
All checks were successful
PR Check / rust (push) Successful in 16m33s
PR Check / frontend (push) Successful in 2m14s
PR Check / rust (pull_request) Successful in 16m33s
PR Check / frontend (pull_request) Successful in 2m15s
fix: wrap rehash updateProfile in try/catch for best-effort (#54)
Both handlePinSuccess handlers (ProfileSwitcher and ProfileSelectionPage)
now catch updateProfile errors so that a failed rehash persistence does
not block switchProfile.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 15:46:27 -04:00

94 lines
3.5 KiB
TypeScript

import { useState } from "react";
import { useTranslation } from "react-i18next";
import { Lock, Plus } from "lucide-react";
import { useProfile } from "../contexts/ProfileContext";
import { APP_NAME } from "../shared/constants";
import PinDialog from "../components/profile/PinDialog";
import ProfileFormModal from "../components/profile/ProfileFormModal";
export default function ProfileSelectionPage() {
const { t } = useTranslation();
const { profiles, switchProfile, updateProfile } = useProfile();
const [pinProfileId, setPinProfileId] = useState<string | null>(null);
const [showCreate, setShowCreate] = useState(false);
const handleSelect = (profileId: string) => {
const profile = profiles.find((p) => p.id === profileId);
if (!profile) return;
if (profile.pin_hash) {
setPinProfileId(profileId);
} else {
switchProfile(profileId);
}
};
const handlePinSuccess = async (rehashed?: string | null) => {
if (pinProfileId) {
if (rehashed) {
try {
await updateProfile(pinProfileId, { pin_hash: rehashed });
} catch {
// Best-effort rehash: don't block profile switch if persistence fails
}
}
switchProfile(pinProfileId);
setPinProfileId(null);
}
};
const pinProfile = profiles.find((p) => p.id === pinProfileId);
return (
<div className="flex flex-col items-center justify-center min-h-screen bg-[var(--background)] p-8">
<h1 className="text-3xl font-bold text-[var(--foreground)] mb-2">{APP_NAME}</h1>
<p className="text-[var(--muted-foreground)] mb-10">{t("profile.select")}</p>
<div className="grid grid-cols-2 sm:grid-cols-3 gap-4 max-w-lg w-full">
{profiles.map((profile) => (
<button
key={profile.id}
onClick={() => handleSelect(profile.id)}
className="flex flex-col items-center gap-3 p-6 rounded-xl bg-[var(--card)] border border-[var(--border)] hover:border-[var(--primary)] transition-colors cursor-pointer"
>
<div
className="w-14 h-14 rounded-full flex items-center justify-center text-white text-xl font-bold"
style={{ backgroundColor: profile.color }}
>
{profile.name.charAt(0).toUpperCase()}
</div>
<span className="text-sm font-medium text-[var(--foreground)]">{profile.name}</span>
{profile.pin_hash && (
<Lock size={14} className="text-[var(--muted-foreground)]" />
)}
</button>
))}
<button
onClick={() => setShowCreate(true)}
className="flex flex-col items-center justify-center gap-3 p-6 rounded-xl border-2 border-dashed border-[var(--border)] hover:border-[var(--primary)] transition-colors cursor-pointer"
>
<div className="w-14 h-14 rounded-full flex items-center justify-center bg-[var(--muted)]">
<Plus size={24} className="text-[var(--muted-foreground)]" />
</div>
<span className="text-sm font-medium text-[var(--muted-foreground)]">
{t("profile.create")}
</span>
</button>
</div>
{pinProfileId && pinProfile && (
<PinDialog
profileName={pinProfile.name}
storedHash={pinProfile.pin_hash!}
onSuccess={handlePinSuccess}
onCancel={() => setPinProfileId(null)}
/>
)}
{showCreate && (
<ProfileFormModal onClose={() => setShowCreate(false)} />
)}
</div>
);
}