'use client'; import { useState, useEffect, useRef } from 'react'; import { CheckCircle2, Clock, Loader2, Wrench, BarChart2 } from 'lucide-react'; import Link from 'next/link'; import { trpc } from '@/lib/trpc/client'; import type { RouterOutputs } from '@/lib/trpc/server'; type Status = 'OPEN' | 'CLAIMED' | 'RESOLVED'; type QueueItem = RouterOutputs['maintenanceRequest']['queue']['items'][number]; // ── Helpers ──────────────────────────────────────────────────────────────── function timeAgo(date: Date | string): string { const diffMs = Date.now() - new Date(date).getTime(); const mins = Math.floor(diffMs / 60_000); if (mins < 1) return 'agora'; if (mins < 60) return `há ${mins}m`; const hours = Math.floor(mins / 60); if (hours < 24) return `há ${hours}h`; return `há ${Math.floor(hours / 24)}d`; } function playBeep() { try { const ctx = new AudioContext(); const osc = ctx.createOscillator(); const gain = ctx.createGain(); osc.connect(gain); gain.connect(ctx.destination); osc.type = 'sine'; osc.frequency.value = 880; gain.gain.setValueAtTime(0.2, ctx.currentTime); gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.4); osc.start(ctx.currentTime); osc.stop(ctx.currentTime + 0.4); } catch { // AudioContext may be blocked before user interaction — ignore. } } const STATUS_LABEL: Record = { OPEN: 'Aberto', CLAIMED: 'Em curso', RESOLVED: 'Resolvido', }; const STATUS_CLASS: Record = { OPEN: 'bg-orange-100 text-orange-700', CLAIMED: 'bg-blue-100 text-blue-700', RESOLVED: 'bg-green-100 text-green-700', }; // ── Thumbnail ─────────────────────────────────────────────────────────────── function Thumbnail({ photoKey }: { photoKey: string | null }) { const { data } = trpc.storage.signPhotoDownload.useQuery( { photoKey: photoKey! }, { enabled: !!photoKey, staleTime: 50_000 }, ); if (!photoKey) { return
; } if (!data?.url) { return
; } return ( // eslint-disable-next-line @next/next/no-img-element Foto ); } // ── Request card ──────────────────────────────────────────────────────────── function RequestCard({ item, onClaim, onResolve, claiming, }: { item: QueueItem; onClaim: () => void; onResolve: () => void; claiming: boolean; }) { return (
{/* Top row: thumbnail + main info */}

{item.workstation.code} — {item.workstation.name}{' '} · {item.workstation.area}

{item.description}

Reportado por {item.reportedBy.email} · {timeAgo(item.createdAt)}

{/* Footer: badge + actions */}
{STATUS_LABEL[item.status as Status]} {item.status === 'OPEN' && ( )} {item.status === 'CLAIMED' && (

Aceite por {item.claimedBy?.email ?? '?'} · {timeAgo(item.claimedAt!)}

)} {item.status === 'RESOLVED' && (

Resolvido por {item.resolvedBy?.email ?? '?'} · {timeAgo(item.resolvedAt!)}

)}
); } // ── Resolve dialog ────────────────────────────────────────────────────────── function ResolveDialog({ onConfirm, onCancel, note, onNoteChange, resolving, }: { onConfirm: () => void; onCancel: () => void; note: string; onNoteChange: (v: string) => void; resolving: boolean; }) { return (

Marcar como resolvido