Phase 2: multi-user links for machines (Convex schema + mutations + admin API); UI to add/remove links; user editor lists machines via linkedUsers
This commit is contained in:
parent
6653ef250e
commit
22f0768492
5 changed files with 208 additions and 10 deletions
|
|
@ -657,6 +657,7 @@ export type MachinesQueryItem = {
|
|||
inventory: MachineInventory | null
|
||||
postureAlerts?: Array<Record<string, unknown>> | null
|
||||
lastPostureAt?: number | null
|
||||
linkedUsers?: Array<{ id: string; email: string; name: string }>
|
||||
}
|
||||
|
||||
function useMachinesQuery(tenantId: string): MachinesQueryItem[] {
|
||||
|
|
@ -1859,16 +1860,83 @@ export function MachineDetails({ machine }: MachineDetailsProps) {
|
|||
|
||||
<section className="space-y-2">
|
||||
<h4 className="text-sm font-semibold">Usuários vinculados</h4>
|
||||
{collaborator?.email ? (
|
||||
<div className="flex flex-wrap items-center gap-2 rounded-md border border-slate-200 bg-slate-50/80 px-3 py-2 text-sm">
|
||||
<ShieldCheck className="size-4 text-slate-500" />
|
||||
<span className="font-medium text-neutral-800">{collaborator.name || collaborator.email}</span>
|
||||
<span className="text-neutral-500">{collaborator.name ? `· ${collaborator.email}` : ''}</span>
|
||||
<Link href="/admin" className="ml-auto text-xs underline underline-offset-4">gerenciar usuários</Link>
|
||||
<div className="space-y-2">
|
||||
{collaborator?.email ? (
|
||||
<div className="flex flex-wrap items-center gap-2 rounded-md border border-slate-200 bg-slate-50/80 px-3 py-2 text-sm">
|
||||
<ShieldCheck className="size-4 text-slate-500" />
|
||||
<span className="font-medium text-neutral-800">{collaborator.name || collaborator.email}</span>
|
||||
<span className="text-neutral-500">{collaborator.name ? `· ${collaborator.email}` : ''}</span>
|
||||
<span className="ml-2 rounded-full border border-slate-200 bg-white px-2 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-slate-600">Principal</span>
|
||||
</div>
|
||||
) : null}
|
||||
{Array.isArray(machine.linkedUsers) && machine.linkedUsers.length > 0 ? (
|
||||
<ul className="divide-y divide-slate-200 overflow-hidden rounded-md border border-slate-200 bg-slate-50/60">
|
||||
{machine.linkedUsers.map((u) => (
|
||||
<li key={`lu-${u.id}`} className="flex items-center gap-2 px-3 py-2 text-sm">
|
||||
<span className="font-medium text-neutral-800">{u.name || u.email}</span>
|
||||
<span className="text-neutral-500">{u.name ? `· ${u.email}` : ''}</span>
|
||||
<div className="ml-auto flex items-center gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="border-rose-200 text-rose-600 hover:bg-rose-50 hover:text-rose-700"
|
||||
onClick={async () => {
|
||||
try {
|
||||
const res = await fetch(`/api/admin/machines/links?machineId=${machine.id}&userId=${u.id}`, { method: 'DELETE', credentials: 'include' })
|
||||
if (!res.ok) throw new Error('HTTP ' + res.status)
|
||||
toast.success('Vínculo removido')
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
toast.error('Falha ao remover vínculo')
|
||||
}
|
||||
}}
|
||||
>
|
||||
Remover
|
||||
</Button>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
) : null}
|
||||
{!collaborator?.email && (!machine.linkedUsers || machine.linkedUsers.length === 0) ? (
|
||||
<p className="text-xs text-neutral-500">Nenhum usuário vinculado.</p>
|
||||
) : null}
|
||||
<div className="mt-2 flex flex-wrap items-center gap-2">
|
||||
<Input
|
||||
value={accessEmail}
|
||||
onChange={(e) => setAccessEmail(e.target.value)}
|
||||
placeholder="e-mail do usuário para vincular"
|
||||
className="max-w-xs"
|
||||
type="email"
|
||||
/>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={async () => {
|
||||
if (!accessEmail.trim()) {
|
||||
toast.error('Informe um e-mail para vincular')
|
||||
return
|
||||
}
|
||||
try {
|
||||
const res = await fetch('/api/admin/machines/links', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
credentials: 'include',
|
||||
body: JSON.stringify({ machineId: machine.id, email: accessEmail.trim() }),
|
||||
})
|
||||
if (!res.ok) throw new Error('HTTP ' + res.status)
|
||||
toast.success('Usuário vinculado')
|
||||
setAccessEmail('')
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
toast.error('Falha ao vincular usuário')
|
||||
}
|
||||
}}
|
||||
>
|
||||
Adicionar vínculo
|
||||
</Button>
|
||||
<Link href="/admin" className="text-xs underline underline-offset-4">gerenciar usuários</Link>
|
||||
</div>
|
||||
) : (
|
||||
<p className="text-xs text-neutral-500">Nenhum usuário vinculado. Use “Ajustar acesso” para associar um colaborador/gestor.</p>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Renomear máquina */}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue