'use client'; import type { UserRole } from '@impuestos/contracts'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { useState } from 'react'; import { Button } from '@/components/ui/button'; import { Card } from '@/components/ui/card'; import { Select } from '@/components/ui/select'; import { Skeleton } from '@/components/ui/skeleton'; import { Link } from '@/i18n/navigation'; import { useT } from '@/i18n/t'; import { api } from '@/lib/api'; const ROLES: UserRole[] = ['user', 'accountant', 'staff', 'superadmin']; /** Flow H: one account at a glance, plus the one destructive thing a superadmin can do. */ export function UserOverview({ id, viewerRole }: { id: string; viewerRole: string }) { const t = useT(); const overview = useQuery({ queryKey: ['admin', 'user', id], queryFn: ({ signal }) => api.getUserOverview(id, signal), }); if (overview.isPending) { return ( ); } if (overview.isError || !overview.data) { return (

{t('common.error.generic')}

); } const { user, profile, counts, recentErrors, lastActivityAt } = overview.data; return (

{user.fullName}

{user.email}

{t('common.back')}
{( [ ['documents', counts.documents], ['needsReview', counts.needsReview], ['declarations', counts.declarations], ] as const ).map(([key, value]) => (

{t(`admin.users.counts.${key}`)}

{value}

))}
{profile ? (
obligation.active) .map((obligation) => t(`obligation.${obligation.code}` as const)) .join(', ')} />
) : (

{t('admin.users.noProfile')}

)}

{lastActivityAt ? // A full date, not "15 ene": in a console the year is part of the fact. t('admin.users.lastActivity', { date: lastActivityAt.slice(0, 10) }) : t('admin.users.noActivity')}

{t('admin.users.openErrors')}

{recentErrors.length === 0 ? (

{t('admin.users.noErrors')}

) : (
    {recentErrors.map((error) => (
  • {t(`admin.errors.stage.${error.stage}` as const)} {error.message} {error.createdAt.slice(0, 10)}
  • ))}
)} {/* The queue itself, for the rows this summary does not show. */} {t('admin.errors.goToQueue')}
{viewerRole === 'superadmin' ? : null}
); } function RoleManager({ user }: { user: { id: string; email: string; role: UserRole } }) { const t = useT(); const queryClient = useQueryClient(); const [role, setRole] = useState(user.role); const [confirming, setConfirming] = useState(false); const change = useMutation({ mutationFn: () => api.setUserRole(user.id, { role }), onSuccess: async () => { setConfirming(false); await queryClient.invalidateQueries({ queryKey: ['admin', 'user', user.id] }); }, }); return (

{t('admin.role.title')}

{confirming ? (

{t('admin.role.confirm', { email: user.email, role: t(`admin.role.${role}` as const) })}

) : null} {change.isError ? (

{t('common.error.generic')}

) : null} {change.isSuccess ?

{t('admin.role.saved')}

: null}
); } /** * Label above value. Side by side across a wide card, a right aligned value ends up next * to the following pair's label and the two read as one field. */ function Row({ label, value }: { label: string; value: string }) { return (
{label}
{value}
); } function docOf(ruc: string | null, rucDv: string | null, ci: string | null): string { if (ruc) return rucDv ? `${ruc}-${rucDv}` : ruc; return ci ?? ''; }