import { useState } from 'react' import { createFileRoute, Link, redirect, useRouter } from '@tanstack/react-router' import { ConfirmDialog } from '#/components/confirm-dialog' import { StaffMemberDetailDialog } from '#/components/staff-member-detail-dialog' import { StaffTabs } from '#/components/staff-tabs' import { getCurrentStaffUser } from '#/lib/auth' import { authClient } from '#/lib/auth-client' import { phoneSchema } from '#/lib/phone' import { listStaffMembers } from '#/lib/staff' export const Route = createFileRoute('/staff/team')({ component: StaffTeam, beforeLoad: async () => { const staffUser = await getCurrentStaffUser() if (!staffUser) { throw redirect({ to: '/staff/login' }) } if (staffUser.role !== 'admin') { throw redirect({ to: '/staff' }) } return { staffUser } }, loader: async ({ context }) => { const members = await listStaffMembers() return { staffUser: context.staffUser, members } }, }) type StaffMember = Awaited>[number] type FormState = { name: string email: string phone: string password: string role: 'admin' | 'user' } function emptyForm(): FormState { return { name: '', email: '', phone: '', password: '', role: 'user' } } function StaffTeam() { const router = useRouter() const { staffUser, members } = Route.useLoaderData() const [form, setForm] = useState(emptyForm) const [error, setError] = useState(null) const [success, setSuccess] = useState(null) const [isSubmitting, setIsSubmitting] = useState(false) const [busyId, setBusyId] = useState(null) const [viewingMember, setViewingMember] = useState(null) const [pendingRemove, setPendingRemove] = useState(null) async function handleCreate(event: React.FormEvent) { event.preventDefault() setError(null) setSuccess(null) const phoneResult = phoneSchema.safeParse(form.phone) if (!phoneResult.success) { setError('Bitte geben Sie eine gültige Telefonnummer an') return } setIsSubmitting(true) const { error: createError } = await authClient.admin.createUser({ name: form.name, email: form.email, password: form.password, role: form.role, data: { phone: phoneResult.data }, }) if (createError) { setError(createError.message ?? 'Etwas ist schiefgelaufen') setIsSubmitting(false) return } setSuccess('Mitarbeiter wurde angelegt.') setForm(emptyForm()) setIsSubmitting(false) await router.invalidate() } async function confirmRemove() { const member = pendingRemove if (!member) return setPendingRemove(null) setBusyId(member.id) setError(null) setSuccess(null) const { error: removeError } = await authClient.admin.removeUser({ userId: member.id }) if (removeError) { setError(removeError.message ?? 'Etwas ist schiefgelaufen') } else { setSuccess('Zugang wurde entfernt.') await router.invalidate() } setBusyId(null) } async function handleToggleRole(member: StaffMember) { const nextRole = member.role === 'admin' ? 'user' : 'admin' setBusyId(member.id) setError(null) setSuccess(null) const { error: roleError } = await authClient.admin.setRole({ userId: member.id, role: nextRole }) if (roleError) { setError(roleError.message ?? 'Etwas ist schiefgelaufen') } else { setSuccess('Rolle wurde aktualisiert.') await router.invalidate() } setBusyId(null) } async function handleToggleBan(member: StaffMember) { setBusyId(member.id) setError(null) setSuccess(null) const { error: banError } = member.banned ? await authClient.admin.unbanUser({ userId: member.id }) : await authClient.admin.banUser({ userId: member.id }) if (banError) { setError(banError.message ?? 'Etwas ist schiefgelaufen') } else { setSuccess(member.banned ? 'Zugang wurde reaktiviert.' : 'Zugang wurde gesperrt.') await router.invalidate() } setBusyId(null) } async function handleLogout() { await authClient.signOut() await router.navigate({ to: '/staff/login' }) await router.invalidate() } async function handleSetPhone(member: StaffMember) { const newPhone = window.prompt(`Telefonnummer für ${member.name}:`, member.phone ?? '') if (newPhone === null) { return } const phoneResult = phoneSchema.safeParse(newPhone) if (!phoneResult.success) { setError('Bitte geben Sie eine gültige Telefonnummer an') return } setBusyId(member.id) setError(null) setSuccess(null) const { error: phoneError } = await authClient.admin.updateUser({ userId: member.id, data: { phone: phoneResult.data }, }) if (phoneError) { setError(phoneError.message ?? 'Etwas ist schiefgelaufen') } else { setSuccess(`Telefonnummer für ${member.name} wurde aktualisiert.`) await router.invalidate() } setBusyId(null) } async function handleSetPassword(member: StaffMember) { const newPassword = window.prompt(`Neues Passwort für ${member.name} (mind. 8 Zeichen):`) if (!newPassword) { return } setBusyId(member.id) setError(null) setSuccess(null) const { error: passwordError } = await authClient.admin.setUserPassword({ userId: member.id, newPassword, }) if (passwordError) { setError(passwordError.message ?? 'Etwas ist schiefgelaufen') } else { setSuccess(`Passwort für ${member.name} wurde geändert.`) } setBusyId(null) } return (
Steakhaus bei Mirko
Angemeldet als {staffUser.name}

Mitarbeiter

Neuer Mitarbeiter

{error &&

{error}

} {success &&

{success}

}
{members.map((member) => ( setViewingMember(member)}> ))}
Name E-Mail Telefon Rolle Status
{member.name} {member.email} {member.phone || —} {member.role === 'admin' ? 'Admin' : 'Mitarbeiter'} {member.banned ? 'Gesperrt' : 'Aktiv'} event.stopPropagation()}>
setViewingMember(null)} /> Zugang für {pendingRemove.name} wirklich entfernen?} warning="Diese Aktion kann nicht rückgängig gemacht werden." confirmLabel="Entfernen" danger onConfirm={confirmRemove} onCancel={() => setPendingRemove(null)} />
) }