import { useMemo, useState } from 'react' import { Link, Navigate } from 'react-router-dom' import { useTranslation } from 'react-i18next' import { toast } from 'sonner' import { Bell, Building2, Check, KeyRound, Languages, LogOut, Monitor, Pencil, ShieldCheck, SlidersHorizontal, Sparkles, UserRound, X, } from 'lucide-react' import { DetailSection } from '@/components/detail/ActivityTimeline' import { KeyValueGrid } from '@/components/detail/KeyValueGrid' import { Badge } from '@/components/ui/badge' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { SelectNative } from '@/components/ui/select-native' import { Separator } from '@/components/ui/separator' import { PersonAvatar } from '@/components/ui/avatar' import { StatusBadge } from '@/components/ui/status-badge' import { EmptyState } from '@/components/ui/empty-state' import { IconTile } from '@/components/ui/icon-tile' import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table' import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, } from '@/components/ui/dialog' import { branchProfiles, branches, staffActivity, staffProfiles, staffSessions } from '@/mocks/data' import type { StaffProfile, StaffSession } from '@/mocks/data' import { canAccess, demoAccounts, roleModules } from '@/config/permissions' import { sidebarEntries } from '@/config/navigation' import { useAuthStore } from '@/zustand/authStore' import { formatDate, formatDateTime, formatRelative, humanize } from '@/lib/utils' import i18n from '@/i18n/config' const LOCALE_LABEL: Record<'en' | 'am', string> = { en: 'English', am: 'አማርኛ' } const NOTIFICATION_FIELDS: { key: keyof StaffProfile['notifications']; label: string; description: string }[] = [ { key: 'orderAlerts', label: 'Order alerts', description: 'New and at-risk orders on your branch' }, { key: 'lowStockAlerts', label: 'Low stock alerts', description: 'Reorder points and near-expiry batches' }, { key: 'weeklyDigest', label: 'Weekly digest', description: 'A Monday summary of your modules' }, { key: 'securityAlerts', label: 'Security alerts', description: 'New sign-ins and permission changes' }, ] function fallbackProfile(id: string): StaffProfile { return { id, jobTitle: 'Staff member', phone: '', status: 'active', joinedAt: new Date().toISOString(), lastSignInAt: new Date().toISOString(), lastSignInFrom: 'Addis Ababa, Ethiopia', passwordUpdatedAt: new Date().toISOString(), twoFactorEnabled: false, preferredLocale: 'en', notifications: { orderAlerts: false, lowStockAlerts: false, weeklyDigest: false, securityAlerts: true }, } } function ToggleSwitch({ checked, onChange, label, }: { checked: boolean onChange: (next: boolean) => void label: string }) { return ( ) } export function ProfilePage() { const { t } = useTranslation() const user = useAuthStore((s) => s.user) const updateUser = useAuthStore((s) => s.updateUser) // Resolved once: editing your own email must not detach the page from its mocks. const [accountId] = useState( () => demoAccounts.find((a) => a.email === user?.email)?.id ?? user?.id ?? 'unknown', ) const [profile, setProfile] = useState( () => staffProfiles[accountId] ?? fallbackProfile(accountId), ) const [sessions, setSessions] = useState(() => staffSessions[accountId] ?? []) const [editing, setEditing] = useState(false) const [saving, setSaving] = useState(false) const [form, setForm] = useState({ name: user?.name ?? '', email: user?.email ?? '', phone: profile.phone, jobTitle: profile.jobTitle, locale: (user?.preferredLocale ?? profile.preferredLocale) as 'en' | 'am', }) const [passwordOpen, setPasswordOpen] = useState(false) const [passwordForm, setPasswordForm] = useState({ current: '', next: '', confirm: '' }) const role = user?.role const modules = useMemo(() => { if (!role) return [] return roleModules[role].map((module) => { const entry = sidebarEntries.find((e) => e.module === module) return { module, label: entry?.labelKey ? t(entry.labelKey, { defaultValue: entry.title }) : (entry?.title ?? humanize(module)), path: entry?.path, icon: entry?.icon, } }) }, [role, t]) const activity = useMemo( () => (staffActivity[accountId] ?? []) .slice() .sort((a, b) => new Date(b.at).getTime() - new Date(a.at).getTime()), [accountId], ) const branch = useMemo(() => branches.find((b) => b.id === user?.branchId), [user?.branchId]) const branchProfile = user?.branchId ? branchProfiles[user.branchId] : undefined if (!user || !role) return const totalModules = new Set(Object.values(roleModules).flat()).size const persistProfile = (next: StaffProfile) => { staffProfiles[next.id] = next setProfile(next) } const startEditing = () => { setForm({ name: user.name, email: user.email, phone: profile.phone, jobTitle: profile.jobTitle, locale: (user.preferredLocale ?? profile.preferredLocale) as 'en' | 'am', }) setEditing(true) } const saveAccountDetails = () => { if (!form.name.trim()) { toast.error('Enter your full name') return } if (!form.email.includes('@')) { toast.error('Enter a valid email address') return } setSaving(true) window.setTimeout(() => { persistProfile({ ...profile, phone: form.phone.trim(), jobTitle: form.jobTitle.trim(), preferredLocale: form.locale, }) updateUser({ name: form.name.trim(), email: form.email.trim(), preferredLocale: form.locale }) if (i18n.language !== form.locale) void i18n.changeLanguage(form.locale) setSaving(false) setEditing(false) toast.success('Profile updated') }, 400) } const changeLocale = (locale: 'en' | 'am') => { if (locale === (user.preferredLocale ?? profile.preferredLocale)) return persistProfile({ ...profile, preferredLocale: locale }) updateUser({ preferredLocale: locale }) setForm((prev) => ({ ...prev, locale })) void i18n.changeLanguage(locale) toast.success(`Language set to ${LOCALE_LABEL[locale]}`) } const toggleNotification = (key: keyof StaffProfile['notifications'], next: boolean) => { persistProfile({ ...profile, notifications: { ...profile.notifications, [key]: next } }) toast.success(`${NOTIFICATION_FIELDS.find((f) => f.key === key)?.label} ${next ? 'on' : 'off'}`) } const toggleTwoFactor = () => { const next = !profile.twoFactorEnabled persistProfile({ ...profile, twoFactorEnabled: next }) toast.success(next ? 'Two-factor authentication enabled' : 'Two-factor authentication disabled') } const revokeSession = (session: StaffSession) => { setSessions((prev) => prev.filter((s) => s.id !== session.id)) staffSessions[accountId] = (staffSessions[accountId] ?? []).filter((s) => s.id !== session.id) toast.success(`Signed out ${session.device}`) } const revokeOtherSessions = () => { const remaining = sessions.filter((s) => s.current) setSessions(remaining) staffSessions[accountId] = remaining toast.success('All other sessions signed out') } const submitPasswordChange = () => { if (!passwordForm.current) { toast.error('Enter your current password') return } if (passwordForm.next.length < 8) { toast.error('New password must be at least 8 characters') return } if (passwordForm.next !== passwordForm.confirm) { toast.error('New passwords do not match') return } persistProfile({ ...profile, passwordUpdatedAt: new Date().toISOString() }) setPasswordForm({ current: '', next: '', confirm: '' }) setPasswordOpen(false) toast.success('Password updated') } const activeLocale = (user.preferredLocale ?? profile.preferredLocale) as 'en' | 'am' return (

{user.name}

{user.email}

{profile.jobTitle}
{editing ? ( <> ) : ( <> )}
Edit ) } > {editing ? (
setForm((prev) => ({ ...prev, name: e.target.value }))} />
setForm((prev) => ({ ...prev, email: e.target.value }))} />
setForm((prev) => ({ ...prev, phone: e.target.value }))} />
setForm((prev) => ({ ...prev, jobTitle: e.target.value }))} />
setForm((prev) => ({ ...prev, locale: e.target.value as 'en' | 'am' }))} >
) : ( )}
} >
{modules.map(({ module, label, path, icon }) => { const content = ( <> {icon ? ( ) : ( )} {label} {module} ) return path ? ( {content} ) : (
{content}
) })}

Module access is granted by role. Ask a super admin if you need a module that is not listed here.

{branch ? (
{canAccess(role, 'settings') ? ( ) : null}
) : (

All branches

This account is platform-wide and is not scoped to a single branch.

)}
{activity.length === 0 ? ( ) : ( When Activity Module Outcome {activity.map((event) => { const linkable = event.targetPath && canAccess(role, event.module) return ( {formatRelative(event.at)} {formatDateTime(event.at)} {event.summary} {event.action} {linkable ? ( {event.target} ) : event.target ? ( {event.target} ) : null} {humanize(event.module)} {humanize(event.outcome)} ) })}
)}
{formatDateTime(profile.lastSignInAt)} {profile.lastSignInFrom} ), }, { label: 'Password updated', value: `${formatDate(profile.passwordUpdatedAt)} · ${formatRelative(profile.passwordUpdatedAt)}`, }, ]} />

Two-factor authentication

{profile.twoFactorEnabled ? 'Required at every sign-in' : 'Not enabled on this account'}

Active sessions

{sessions.some((s) => !s.current) ? ( ) : null}
{sessions.length === 0 ? ( ) : (
    {sessions.map((session) => (
  • {session.device} {session.current ? This device : null}

    {session.browser} · {session.location}

    {session.ip} · {formatRelative(session.lastActiveAt)}

    {session.current ? null : ( )}
  • ))}
)}
{(['en', 'am'] as const).map((locale) => ( ))}
    {NOTIFICATION_FIELDS.map((field) => (
  • {field.label}

    {field.description}

    toggleNotification(field.key, next)} label={field.label} />
  • ))}
Change password Choose a password of at least 8 characters. You stay signed in on this device.
setPasswordForm((prev) => ({ ...prev, current: e.target.value }))} />
setPasswordForm((prev) => ({ ...prev, next: e.target.value }))} />
setPasswordForm((prev) => ({ ...prev, confirm: e.target.value }))} />
) }