import { useState } from "react"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { BrowserFrame } from "@/components/Browser/BrowserFrame"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; import { Badge } from "@/components/ui/badge"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, DialogDescription } from "@/components/ui/dialog"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Textarea } from "@/components/ui/textarea"; import { ScrollArea } from "@/components/ui/scroll-area"; import { useToast } from "@/hooks/use-toast"; import { Beaker, FileText, AlertTriangle, ClipboardList, Users, MapPin, Plus, Search, Edit, Trash2, RefreshCw, CheckCircle, XCircle, Clock, Building2, FlaskConical, FileCheck, GraduationCap, Loader2, Eye, Upload, Download, FileUp, BookOpen, Calendar, UserCheck, Award } from "lucide-react"; type TabType = "dashboard" | "samples" | "lab-reports" | "rnc" | "documents" | "field-forms" | "training" | "suppliers"; interface Sample { id: number; sampleCode: string; sampleType: string; collectionDate: string; collectionLocation: string; collectionResponsible: string; laboratoryId?: number; status: string; projectId?: number; } interface LabReport { id: number; sampleId: number; reportNumber: string; laboratoryId?: number; issueDate: string; status: string; conclusion?: string; } interface NonConformity { id: number; rncNumber: string; title: string; description: string; type: string; severity: string; status: string; detectedAt: string; detectedBy: string; dueDate?: string; } interface QualityDocument { id: number; documentCode: string; title: string; type: string; category: string; version: string; status: string; effectiveDate?: string; } interface FieldForm { id: number; formCode: string; formType: string; title: string; collectionDate: string; location: string; status: string; } interface Training { id: number; employeeId: string; trainingName: string; trainingType: string; completedDate: string; expiryDate: string; status: string; } interface HomologatedSupplier { id: number; code: string; name: string; certifications: string[]; qualityScore: number; homologationStatus: string; homologationExpiry: string; } interface DashboardData { samples: { total: number }; nonConformities: { open: number }; expenses: { pending: number; totalPending: number }; documents: { active: number }; trainings: { expiringSoon: number }; } const api = { get: async (url: string) => { const res = await fetch(url, { credentials: "include" }); if (!res.ok) throw new Error("Request failed"); return res.json(); }, post: async (url: string, data: any) => { const res = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data), credentials: "include", }); if (!res.ok) throw new Error("Request failed"); return res.json(); }, put: async (url: string, data: any) => { const res = await fetch(url, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data), credentials: "include", }); if (!res.ok) throw new Error("Request failed"); return res.json(); }, }; const statusColors: Record = { coletada: "bg-blue-500", enviada: "bg-yellow-500", em_analise: "bg-orange-500", resultado_recebido: "bg-purple-500", aprovada: "bg-green-500", reprovada: "bg-red-500", aberta: "bg-red-500", em_tratamento: "bg-yellow-500", pendente_verificacao: "bg-orange-500", fechada: "bg-green-500", vigente: "bg-green-500", em_revisao: "bg-yellow-500", obsoleto: "bg-gray-500", rascunho: "bg-gray-400", preenchido: "bg-blue-500", revisado: "bg-purple-500", aprovado: "bg-green-500", pendente: "bg-yellow-500", em_andamento: "bg-blue-500", concluido: "bg-green-500", vencido: "bg-red-500", approved: "bg-green-500", pending: "bg-yellow-500", expired: "bg-red-500", blocked: "bg-red-700", }; export default function QualityModule() { const { toast } = useToast(); const queryClient = useQueryClient(); const [activeTab, setActiveTab] = useState("dashboard"); const [searchTerm, setSearchTerm] = useState(""); const [showSampleDialog, setShowSampleDialog] = useState(false); const [showRncDialog, setShowRncDialog] = useState(false); const [showDocDialog, setShowDocDialog] = useState(false); const [showDocViewDialog, setShowDocViewDialog] = useState(false); const [selectedDoc, setSelectedDoc] = useState(null); const [editingItem, setEditingItem] = useState(null); const [selectedFile, setSelectedFile] = useState(null); const [viewingFileUrl, setViewingFileUrl] = useState(null); const [showTrainingDialog, setShowTrainingDialog] = useState(false); const [editingTraining, setEditingTraining] = useState(null); const [showTrainingViewDialog, setShowTrainingViewDialog] = useState(false); const [selectedTraining, setSelectedTraining] = useState(null); const sampleTrainings = [ { id: 1, employeeName: "João Silva", employeeId: "emp-001", trainingName: "Amostragem de Água Subterrânea", trainingType: "Técnico", completedDate: "2025-10-15", expiryDate: "2026-10-15", status: "valid", instructor: "Dr. Roberto Lima", workload: 16 }, { id: 2, employeeName: "Maria Santos", employeeId: "emp-002", trainingName: "NR-35 Trabalho em Altura", trainingType: "Segurança", completedDate: "2025-08-20", expiryDate: "2026-08-20", status: "valid", instructor: "SENAI", workload: 8 }, { id: 3, employeeName: "Carlos Oliveira", employeeId: "emp-003", trainingName: "Operação de Sondas", trainingType: "Técnico", completedDate: "2024-12-10", expiryDate: "2025-12-10", status: "expiring", instructor: "Técnico Sr. Paulo", workload: 40 }, { id: 4, employeeName: "Ana Costa", employeeId: "emp-004", trainingName: "ISO 17025 - Requisitos", trainingType: "Qualidade", completedDate: "2024-06-05", expiryDate: "2025-06-05", status: "expired", instructor: "Bureau Veritas", workload: 24 }, ]; const { data: dashboard, isLoading: loadingDashboard } = useQuery({ queryKey: ["/api/quality/dashboard"], queryFn: () => api.get("/api/quality/dashboard"), }); const { data: samples = [], refetch: refetchSamples } = useQuery({ queryKey: ["/api/quality/samples"], queryFn: async () => { const res = await api.get("/api/quality/samples"); return res.data || []; }, }); const { data: labReports = [] } = useQuery({ queryKey: ["/api/quality/lab-reports"], queryFn: async () => { const res = await api.get("/api/quality/lab-reports"); return res.data || []; }, }); const { data: nonConformities = [], refetch: refetchRnc } = useQuery({ queryKey: ["/api/quality/non-conformities"], queryFn: async () => { const res = await api.get("/api/quality/non-conformities"); return res.data || []; }, }); const { data: documents = [], refetch: refetchDocs } = useQuery({ queryKey: ["/api/quality/documents"], queryFn: async () => { const res = await api.get("/api/quality/documents"); return res.data || []; }, }); const { data: fieldForms = [] } = useQuery({ queryKey: ["/api/quality/field-forms"], queryFn: async () => { const res = await api.get("/api/quality/field-forms"); return res.data || []; }, }); const { data: trainings = [] } = useQuery({ queryKey: ["/api/quality/training-matrix"], queryFn: async () => { const res = await api.get("/api/quality/training-matrix"); return res.data || []; }, }); const { data: homologatedSuppliers = [] } = useQuery({ queryKey: ["/api/quality/homologated-suppliers"], queryFn: async () => { const res = await api.get("/api/quality/homologated-suppliers"); return res.data || []; }, }); const createSampleMutation = useMutation({ mutationFn: (data: Partial) => api.post("/api/quality/samples", data), onSuccess: () => { toast({ title: "Amostra criada com sucesso" }); refetchSamples(); setShowSampleDialog(false); }, onError: () => toast({ title: "Erro ao criar amostra", variant: "destructive" }), }); const createRncMutation = useMutation({ mutationFn: (data: Partial) => api.post("/api/quality/non-conformities", data), onSuccess: () => { toast({ title: "RNC criada com sucesso" }); refetchRnc(); setShowRncDialog(false); }, onError: () => toast({ title: "Erro ao criar RNC", variant: "destructive" }), }); const createDocMutation = useMutation({ mutationFn: (data: Partial) => api.post("/api/quality/documents", data), onSuccess: () => { toast({ title: "Documento criado com sucesso" }); refetchDocs(); setShowDocDialog(false); }, onError: () => toast({ title: "Erro ao criar documento", variant: "destructive" }), }); const getStatusBadge = (status: string) => { const color = statusColors[status] || "bg-gray-500"; const label = status.replace(/_/g, " ").replace(/^\w/, c => c.toUpperCase()); return {label}; }; const filteredSamples = samples.filter(s => s.sampleCode?.toLowerCase().includes(searchTerm.toLowerCase()) || s.collectionLocation?.toLowerCase().includes(searchTerm.toLowerCase()) ); const filteredRnc = nonConformities.filter(r => r.rncNumber?.toLowerCase().includes(searchTerm.toLowerCase()) || r.title?.toLowerCase().includes(searchTerm.toLowerCase()) ); const filteredDocs = documents.filter(d => d.documentCode?.toLowerCase().includes(searchTerm.toLowerCase()) || d.title?.toLowerCase().includes(searchTerm.toLowerCase()) ); return (

Gestão da Qualidade - ISO 17025

Controle de amostras, laudos, documentos e conformidade

setActiveTab(v as TabType)}> Dashboard Amostras Laudos RNC Documentos QMS Formulários Campo Treinamentos Homologação {loadingDashboard ? (
) : (
Amostras
{dashboard?.samples?.total || 0}

Total cadastradas

RNC Abertas
{dashboard?.nonConformities?.open || 0}

Pendentes tratamento

Documentos Vigentes
{dashboard?.documents?.active || 0}

QMS ativos

Treinamentos Vencendo
{dashboard?.trainings?.expiringSoon || 0}

Próximos 30 dias

Despesas Pendentes
{dashboard?.expenses?.pending || 0}

Aguardando aprovação

)}
Últimas Amostras Código Tipo Local Status {samples.slice(0, 5).map((sample) => ( {sample.sampleCode} {sample.sampleType} {sample.collectionLocation} {getStatusBadge(sample.status)} ))} {samples.length === 0 && ( Nenhuma amostra cadastrada )}
RNC Abertas Número Título Severidade Status {nonConformities.filter(r => r.status === "aberta").slice(0, 5).map((rnc) => ( {rnc.rncNumber} {rnc.title} {rnc.severity} {getStatusBadge(rnc.status)} ))} {nonConformities.filter(r => r.status === "aberta").length === 0 && ( Nenhuma RNC aberta )}
setSearchTerm(e.target.value)} className="w-64" data-testid="search-samples" />
Código Tipo Data Coleta Local Responsável Status Ações {filteredSamples.map((sample) => ( {sample.sampleCode} {sample.sampleType} {sample.collectionDate ? new Date(sample.collectionDate).toLocaleDateString("pt-BR") : "-"} {sample.collectionLocation} {sample.collectionResponsible} {getStatusBadge(sample.status)} ))} {filteredSamples.length === 0 && ( Nenhuma amostra encontrada )}

Laudos Laboratoriais

Número Amostra Laboratório Data Emissão Status Ações {labReports.map((report) => ( {report.reportNumber} #{report.sampleId} Lab {report.laboratoryId} {report.issueDate ? new Date(report.issueDate).toLocaleDateString("pt-BR") : "-"} {getStatusBadge(report.status)} ))} {labReports.length === 0 && ( Nenhum laudo cadastrado )}
setSearchTerm(e.target.value)} className="w-64" />
Número Título Tipo Severidade Detectado em Status Ações {filteredRnc.map((rnc) => ( {rnc.rncNumber} {rnc.title} {rnc.type?.replace(/_/g, " ")} {rnc.severity} {rnc.detectedAt ? new Date(rnc.detectedAt).toLocaleDateString("pt-BR") : "-"} {getStatusBadge(rnc.status)} ))} {filteredRnc.length === 0 && ( Nenhuma não conformidade encontrada )}
setSearchTerm(e.target.value)} className="w-64" />
Documentos do Sistema de Gestão Código Título Tipo Versão Status Ações {filteredDocs.map((doc) => ( {doc.documentCode} {doc.title} {doc.type} {doc.version} {getStatusBadge(doc.status)} ))} {filteredDocs.length === 0 && ( Nenhum documento encontrado )}
Caixa de Leitura Documentos para leitura obrigatória
POP-001 Novo

Procedimento de Amostragem de Água

IT-015 Atualizado

Instrução Técnica - Monitoramento

FT-057 Pendente

Formulário de Treinamento

Categorias de Documentos
Procedimentos (POP) 12
Instruções Técnicas (IT) 8
Formulários (FT) 25
Manuais (MQ) 3
Políticas (PL) 5

Formulários de Campo

Plano de Amostragem PA - Planejamento de coleta de amostras Plano de Trabalho (PT) Orientações para equipe de campo Monitoramento de Poços Ficha de medição e coleta Ficha de Campo Registro geral de atividades Checklist Equipamentos Verificação de equipamentos Relatório Fotográfico Documentação visual de campo
Código Tipo Título Data Local Status {fieldForms.map((form) => ( {form.formCode} {form.formType} {form.title} {form.collectionDate ? new Date(form.collectionDate).toLocaleDateString("pt-BR") : "-"} {form.location} {getStatusBadge(form.status)} ))} {fieldForms.length === 0 && ( Nenhum formulário de campo cadastrado )}

Matriz de Treinamentos (FT-57)

Controle de competência técnica conforme ISO 17025

12

Colaboradores

45

Treinamentos Válidos

8

Vencendo em 30 dias

3

Vencidos

Registro de Treinamentos
Colaborador Treinamento Tipo Data Conclusão Validade Status Ações {(trainings.length === 0 ? sampleTrainings : trainings).map((t: any) => ( {t.employeeName || t.employeeId} {t.trainingName} {t.trainingType} {t.completedDate ? new Date(t.completedDate).toLocaleDateString("pt-BR") : "-"} {t.expiryDate ? new Date(t.expiryDate).toLocaleDateString("pt-BR") : "-"} {t.status === "valid" && Válido} {t.status === "expiring" && Vencendo} {t.status === "expired" && Vencido} {t.status === "scheduled" && Agendado} ))}

Fornecedores Homologados - ISO 17025

Código Nome Certificações Score Validade Status {homologatedSuppliers.map((s) => ( {s.code} {s.name}
{s.certifications?.map((c, i) => ( {c} ))}
= 80 ? 'bg-green-500' : s.qualityScore >= 60 ? 'bg-yellow-500' : 'bg-red-500'}`} style={{ width: `${s.qualityScore}%` }} />
{s.qualityScore}%
{s.homologationExpiry ? new Date(s.homologationExpiry).toLocaleDateString("pt-BR") : "-"} {getStatusBadge(s.homologationStatus)} ))} {homologatedSuppliers.length === 0 && ( Nenhum fornecedor homologado )}
{editingItem ? "Editar Amostra" : "Nova Amostra"}
{ e.preventDefault(); const formData = new FormData(e.currentTarget); createSampleMutation.mutate({ sampleCode: formData.get("sampleCode") as string, sampleType: formData.get("sampleType") as string, collectionLocation: formData.get("collectionLocation") as string, collectionResponsible: formData.get("collectionResponsible") as string, status: "coletada", }); }}>
Nova Não Conformidade
{ e.preventDefault(); const formData = new FormData(e.currentTarget); createRncMutation.mutate({ rncNumber: `RNC-${Date.now()}`, title: formData.get("title") as string, description: formData.get("description") as string, type: formData.get("type") as string, severity: formData.get("severity") as string, detectedBy: formData.get("detectedBy") as string, status: "aberta", }); }}>