[{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\app\\(main-routes)\\calendar\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\app\\(main-routes)\\consultas\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\app\\(main-routes)\\dashboard\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\app\\(main-routes)\\dashboard\\relatorios\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\app\\(main-routes)\\doutores\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\app\\(main-routes)\\layout.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\app\\(main-routes)\\pacientes\\loading.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\app\\(main-routes)\\pacientes\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\app\\agenda\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\app\\financeiro\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\app\\layout.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\app\\login-admin\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\app\\login-paciente\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\app\\login\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\app\\paciente\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\app\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\app\\procedimento\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\app\\profissional\\page.tsx","messages":[{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useEffect has a missing dependency: 'user'. Either include it or remove the dependency array.","line":167,"column":6,"nodeType":"ArrayExpression","endLine":167,"endColumn":26,"suggestions":[{"desc":"Update the dependencies array to be: [user.id, doctorId, user]","fix":{"range":[6724,6744],"text":"[user.id, doctorId, user]"}}]},{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useEffect has a missing dependency: 'user'. Either include it or remove the dependency array.","line":219,"column":6,"nodeType":"ArrayExpression","endLine":219,"endColumn":29,"suggestions":[{"desc":"Update the dependencies array to be: [user.id, user.email, user]","fix":{"range":[9697,9720],"text":"[user.id, user.email, user]"}}]},{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useEffect has a missing dependency: 'reports'. Either include it or remove the dependency array. Outer scope values like 'user.id' aren't valid dependencies because mutating them doesn't re-render the component.","line":988,"column":8,"nodeType":"ArrayExpression","endLine":988,"endColumn":18,"suggestions":[{"desc":"Update the dependencies array to be: [reports]","fix":{"range":[40874,40884],"text":"[reports]"}}]},{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useEffect has a missing dependency: 'laudos'. Either include it or remove the dependency array.","line":993,"column":8,"nodeType":"ArrayExpression","endLine":993,"endColumn":17,"suggestions":[{"desc":"Update the dependencies array to be: [laudos, reports]","fix":{"range":[41050,41059],"text":"[laudos, reports]"}}]},{"ruleId":"unicorn/prefer-module","severity":2,"message":"Do not use \"require\".","line":1457,"column":21,"nodeType":"Identifier","messageId":"error/identifier","endLine":1457,"endColumn":28},{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useEffect has an unnecessary dependency: 'user'. Either exclude it or remove the dependency array. Outer scope values like 'user' aren't valid dependencies because mutating them doesn't re-render the component.","line":1606,"column":8,"nodeType":"ArrayExpression","endLine":1606,"endColumn":70,"suggestions":[{"desc":"Update the dependencies array to be: [laudo, isNewLaudo, pacienteSelecionado, listaPacientes]","fix":{"range":[71698,71760],"text":"[laudo, isNewLaudo, pacienteSelecionado, listaPacientes]"}}]},{"ruleId":"@next/next/no-img-element","severity":1,"message":"Using `` could result in slower LCP and higher bandwidth. Consider using `` from `next/image` or a custom image loader to automatically optimize images. This may incur additional usage or cost from your provider. See: https://nextjs.org/docs/messages/no-img-element","line":2038,"column":27,"nodeType":"JSXOpeningElement","endLine":2042,"endColumn":29},{"ruleId":"@next/next/no-img-element","severity":1,"message":"Using `` could result in slower LCP and higher bandwidth. Consider using `` from `next/image` or a custom image loader to automatically optimize images. This may incur additional usage or cost from your provider. See: https://nextjs.org/docs/messages/no-img-element","line":2205,"column":29,"nodeType":"JSXOpeningElement","endLine":2210,"endColumn":31},{"ruleId":"@next/next/no-img-element","severity":1,"message":"Using `` could result in slower LCP and higher bandwidth. Consider using `` from `next/image` or a custom image loader to automatically optimize images. This may incur additional usage or cost from your provider. See: https://nextjs.org/docs/messages/no-img-element","line":2220,"column":29,"nodeType":"JSXOpeningElement","endLine":2220,"endColumn":126},{"ruleId":"unicorn/no-lonely-if","severity":2,"message":"Unexpected `if` as the only statement in a `if` block without `else`.","line":2316,"column":31,"nodeType":"IfStatement","messageId":"no-lonely-if","endLine":2316,"endColumn":98,"fix":{"range":[109490,109657],"text":"(val !== undefined && val !== null && JSON.stringify(origVal) !== JSON.stringify(val)) diff[k] = val;"}},{"ruleId":"react/no-unescaped-entities","severity":2,"message":"`\"` can be escaped with `"`, `“`, `"`, `”`.","line":2414,"column":36,"nodeType":"JSXText","messageId":"unescapedEntityAlts","suggestions":[{"messageId":"replaceWithAlt","data":{"alt":"""},"fix":{"range":[114977,115006],"text":""Ok, obrigado pelo lembrete!\""},"desc":"Replace with `"`."},{"messageId":"replaceWithAlt","data":{"alt":"“"},"fix":{"range":[114977,115006],"text":"“Ok, obrigado pelo lembrete!\""},"desc":"Replace with `“`."},{"messageId":"replaceWithAlt","data":{"alt":"""},"fix":{"range":[114977,115006],"text":""Ok, obrigado pelo lembrete!\""},"desc":"Replace with `"`."},{"messageId":"replaceWithAlt","data":{"alt":"”"},"fix":{"range":[114977,115006],"text":"”Ok, obrigado pelo lembrete!\""},"desc":"Replace with `”`."}]},{"ruleId":"react/no-unescaped-entities","severity":2,"message":"`\"` can be escaped with `"`, `“`, `"`, `”`.","line":2414,"column":64,"nodeType":"JSXText","messageId":"unescapedEntityAlts","suggestions":[{"messageId":"replaceWithAlt","data":{"alt":"""},"fix":{"range":[114977,115006],"text":"\"Ok, obrigado pelo lembrete!""},"desc":"Replace with `"`."},{"messageId":"replaceWithAlt","data":{"alt":"“"},"fix":{"range":[114977,115006],"text":"\"Ok, obrigado pelo lembrete!“"},"desc":"Replace with `“`."},{"messageId":"replaceWithAlt","data":{"alt":"""},"fix":{"range":[114977,115006],"text":"\"Ok, obrigado pelo lembrete!""},"desc":"Replace with `"`."},{"messageId":"replaceWithAlt","data":{"alt":"”"},"fix":{"range":[114977,115006],"text":"\"Ok, obrigado pelo lembrete!”"},"desc":"Replace with `”`."}]}],"suppressedMessages":[{"ruleId":"react-hooks/exhaustive-deps","severity":1,"message":"React Hook useEffect has missing dependencies: 'history' and 'historyIndex'. Either include them or remove the dependency array.","line":1620,"column":8,"nodeType":"ArrayExpression","endLine":1620,"endColumn":17,"suggestions":[{"desc":"Update the dependencies array to be: [content, history, historyIndex]","fix":{"range":[72242,72251],"text":"[content, history, historyIndex]"}}],"suppressions":[{"kind":"directive","justification":""}]}],"errorCount":4,"fatalErrorCount":0,"warningCount":8,"fixableErrorCount":1,"fixableWarningCount":0,"source":"\"use client\";\r\nimport React, { useState, useRef, useEffect } from \"react\";\r\nimport SignatureCanvas from \"react-signature-canvas\";\r\nimport Link from \"next/link\";\r\nimport ProtectedRoute from \"@/components/ProtectedRoute\";\r\nimport { useAuth } from \"@/hooks/useAuth\";\r\nimport { buscarPacientes, listarPacientes, buscarPacientePorId, buscarPacientesPorIds, buscarMedicoPorId, buscarMedicosPorIds, buscarMedicos, type Paciente, buscarRelatorioPorId, atualizarMedico } from \"@/lib/api\";\r\nimport { useReports } from \"@/hooks/useReports\";\r\nimport { CreateReportData } from \"@/types/report-types\";\r\nimport { Button } from \"@/components/ui/button\";\r\nimport { Input } from \"@/components/ui/input\";\r\nimport { Label } from \"@/components/ui/label\";\r\nimport { Textarea } from \"@/components/ui/textarea\";\r\nimport { SimpleThemeToggle } from \"@/components/simple-theme-toggle\";\r\nimport {\r\n Table,\r\n TableBody,\r\n TableCell,\r\n TableHead,\r\n TableHeader,\r\n TableRow,\r\n} from \"@/components/ui/table\";\r\nimport {\r\n Select,\r\n SelectContent,\r\n SelectItem,\r\n SelectTrigger,\r\n SelectValue,\r\n} from \"@/components/ui/select\";\r\nimport { Avatar, AvatarImage, AvatarFallback } from \"@/components/ui/avatar\"\r\nimport { User, FolderOpen, X, Users, MessageSquare, ClipboardList, Plus, Edit, Trash2, ChevronLeft, ChevronRight, Clock, FileCheck, Upload, Download, Eye, History, Stethoscope, Pill, Activity, Search } from \"lucide-react\"\r\nimport { Calendar as CalendarIcon, FileText, Settings } from \"lucide-react\";\r\nimport {\r\n Tooltip,\r\n TooltipContent,\r\n TooltipProvider,\r\n TooltipTrigger,\r\n} from \"@/components/ui/tooltip\";\r\n\r\n\r\nimport dynamic from \"next/dynamic\";\r\nimport dayGridPlugin from \"@fullcalendar/daygrid\";\r\nimport timeGridPlugin from \"@fullcalendar/timegrid\";\r\nimport interactionPlugin from \"@fullcalendar/interaction\";\r\nimport ptBrLocale from \"@fullcalendar/core/locales/pt-br\";\r\n\r\nconst FullCalendar = dynamic(() => import(\"@fullcalendar/react\"), {\r\n ssr: false,\r\n});\r\n\r\n// pacientes will be loaded inside the component (hooks must run in component body)\r\n\r\n// removed static medico placeholder; will load real profile for logged-in user\r\n\r\n\r\nconst colorsByType = {\r\n Rotina: \"#4dabf7\",\r\n Cardiologia: \"#f76c6c\",\r\n Otorrino: \"#f7b84d\",\r\n Pediatria: \"#6cf78b\",\r\n Dermatologia: \"#9b59b6\",\r\n Oftalmologia: \"#2ecc71\"\r\n};\r\n\r\n // Helpers para normalizar dados de paciente (suporta schema antigo e novo)\r\n const getPatientName = (p: any) => p?.full_name ?? p?.nome ?? '';\r\n const getPatientCpf = (p: any) => p?.cpf ?? '';\r\n const getPatientSex = (p: any) => p?.sex ?? p?.sexo ?? '';\r\n const getPatientId = (p: any) => p?.id ?? '';\r\n const getPatientAge = (p: any) => {\r\n if (!p) return '';\r\n // Prefer birth_date (ISO) to calcular idade\r\n const bd = p?.birth_date ?? p?.data_nascimento ?? p?.birthDate;\r\n if (bd) {\r\n const d = new Date(bd);\r\n if (!isNaN(d.getTime())) {\r\n const age = Math.floor((Date.now() - d.getTime()) / (1000 * 60 * 60 * 24 * 365.25));\r\n return `${age}`;\r\n }\r\n }\r\n // Fallback para campo idade/idade_anterior\r\n return p?.idade ?? p?.age ?? '';\r\n };\r\n\r\n // Helpers para normalizar campos do laudo/relatório\r\n const getReportPatientName = (r: any) => r?.paciente?.full_name ?? r?.paciente?.nome ?? r?.patient?.full_name ?? r?.patient?.nome ?? r?.patient_name ?? r?.patient_full_name ?? '';\r\n const getReportPatientId = (r: any) => r?.paciente?.id ?? r?.patient?.id ?? r?.patient_id ?? r?.patientId ?? r?.patient_id_raw ?? r?.patient_id ?? r?.id ?? '';\r\n const getReportPatientCpf = (r: any) => r?.paciente?.cpf ?? r?.patient?.cpf ?? r?.patient_cpf ?? '';\r\n const getReportExecutor = (r: any) => r?.executante ?? r?.requested_by ?? r?.requestedBy ?? r?.created_by ?? r?.createdBy ?? r?.requested_by_name ?? r?.executor ?? '';\r\n const getReportExam = (r: any) => r?.exame ?? r?.exam ?? r?.especialidade ?? r?.cid_code ?? r?.report_type ?? '-';\r\n const getReportDate = (r: any) => r?.data ?? r?.created_at ?? r?.due_at ?? r?.report_date ?? '';\r\n const formatReportDate = (raw?: string) => {\r\n if (!raw) return '-';\r\n try {\r\n const d = new Date(raw);\r\n if (isNaN(d.getTime())) return raw;\r\n return d.toLocaleDateString('pt-BR');\r\n } catch (e) {\r\n return raw;\r\n }\r\n };\r\n\r\nconst ProfissionalPage = () => {\r\n const { logout, user } = useAuth();\r\n const [activeSection, setActiveSection] = useState('calendario');\r\n const [pacienteSelecionado, setPacienteSelecionado] = useState(null);\r\n \r\n // Estados para edição de laudo\r\n const [isEditingLaudoForPatient, setIsEditingLaudoForPatient] = useState(false);\r\n const [patientForLaudo, setPatientForLaudo] = useState(null);\r\n \r\n // Estados para o perfil do médico\r\n const [isEditingProfile, setIsEditingProfile] = useState(false);\r\n const [doctorId, setDoctorId] = useState(null);\r\n // Removemos o placeholder extenso — inicializamos com valores minimalistas e vazios.\r\n const [profileData, setProfileData] = useState({\r\n nome: '',\r\n email: user?.email || '',\r\n telefone: '',\r\n endereco: '',\r\n cidade: '',\r\n cep: '',\r\n crm: '',\r\n especialidade: '',\r\n // biografia field removed — not present in Medico records\r\n fotoUrl: ''\r\n });\r\n\r\n // pacientes carregados dinamicamente (hooks devem ficar dentro do componente)\r\n const [pacientes, setPacientes] = useState([]);\r\n useEffect(() => {\r\n let mounted = true;\r\n (async () => {\r\n try {\r\n if (!user || !user.id) {\r\n if (mounted) setPacientes([]);\r\n return;\r\n }\r\n\r\n const assignmentsMod = await import('@/lib/assignment');\r\n if (!assignmentsMod || typeof assignmentsMod.listAssignmentsForUser !== 'function') {\r\n if (mounted) setPacientes([]);\r\n return;\r\n }\r\n\r\n const assignments = await assignmentsMod.listAssignmentsForUser(user.id || '');\r\n const patientIds = Array.isArray(assignments) ? assignments.map((a:any) => String(a.patient_id)).filter(Boolean) : [];\r\n if (!patientIds.length) {\r\n if (mounted) setPacientes([]);\r\n return;\r\n }\r\n\r\n const patients = await buscarPacientesPorIds(patientIds);\r\n const normalized = (patients || []).map((p: any) => ({\r\n ...p,\r\n nome: p.full_name ?? (p as any).nome ?? '',\r\n cpf: p.cpf ?? '',\r\n idade: getPatientAge(p) // preencher idade para a tabela de pacientes\r\n }));\r\n if (mounted) setPacientes(normalized);\r\n } catch (err) {\r\n console.warn('[ProfissionalPage] falha ao carregar pacientes atribuídos:', err);\r\n if (mounted) setPacientes([]);\r\n }\r\n })();\r\n return () => { mounted = false; };\r\n }, [user?.id, doctorId]);\r\n\r\n // Carregar perfil do médico correspondente ao usuário logado\r\n useEffect(() => {\r\n let mounted = true;\r\n (async () => {\r\n try {\r\n if (!user || !user.email) return;\r\n // Tenta buscar médicos pelo email do usuário (buscarMedicos lida com queries por email)\r\n const docs = await buscarMedicos(user.email);\r\n if (!mounted) return;\r\n if (Array.isArray(docs) && docs.length > 0) {\r\n // preferir registro cujo user_id bate com user.id\r\n let chosen = docs.find(d => String((d as any).user_id) === String(user.id)) || docs[0];\r\n if (chosen) {\r\n // store the doctor's id so we can update it later\r\n try { setDoctorId((chosen as any).id ?? null); } catch {};\r\n // Especialidade pode vir como 'specialty' (inglês), 'especialidade' (pt),\r\n // ou até uma lista/array. Normalizamos para string.\r\n const rawSpecialty = (chosen as any).specialty ?? (chosen as any).especialidade ?? (chosen as any).especialidades ?? (chosen as any).especiality;\r\n let specialtyStr = '';\r\n if (Array.isArray(rawSpecialty)) {\r\n specialtyStr = rawSpecialty.join(', ');\r\n } else if (rawSpecialty) {\r\n specialtyStr = String(rawSpecialty);\r\n }\r\n\r\n // Foto pode vir como 'foto_url' ou 'fotoUrl' ou 'avatar_url'\r\n const foto = (chosen as any).foto_url || (chosen as any).fotoUrl || (chosen as any).avatar_url || '';\r\n\r\n setProfileData((prev) => ({\r\n ...prev,\r\n nome: (chosen as any).full_name || (chosen as any).nome_social || prev.nome || user?.email?.split('@')[0] || '',\r\n email: (chosen as any).email || user?.email || prev.email,\r\n telefone: (chosen as any).phone_mobile || (chosen as any).celular || (chosen as any).telefone || (chosen as any).phone || (chosen as any).mobile || (user as any)?.user_metadata?.phone || prev.telefone,\r\n endereco: (chosen as any).street || (chosen as any).endereco || prev.endereco,\r\n cidade: (chosen as any).city || (chosen as any).cidade || prev.cidade,\r\n cep: (chosen as any).cep || prev.cep,\r\n // store raw CRM (only the number) to avoid double-prefixing when rendering\r\n crm: (chosen as any).crm ? String((chosen as any).crm).replace(/^(?:CRM\\s*)+/i, '').trim() : (prev.crm || ''),\r\n especialidade: specialtyStr || prev.especialidade || '',\r\n // biografia removed: prefer to ignore observacoes/curriculo_url here\r\n // (if needed elsewhere, render directly from chosen.observacoes)\r\n fotoUrl: foto || prev.fotoUrl || ''\r\n }));\r\n }\r\n }\r\n } catch (e) {\r\n console.warn('[ProfissionalPage] falha ao carregar perfil do médico pelo email:', e);\r\n }\r\n })();\r\n return () => { mounted = false; };\r\n }, [user?.id, user?.email]);\r\n\r\n\r\n\r\n // Estados para campos principais da consulta\r\n const [consultaAtual, setConsultaAtual] = useState({\r\n patient_id: \"\",\r\n order_number: \"\",\r\n exam: \"\",\r\n diagnosis: \"\",\r\n conclusion: \"\",\r\n cid_code: \"\",\r\n content_html: \"\",\r\n content_json: {},\r\n status: \"draft\",\r\n requested_by: \"\",\r\n due_at: new Date().toISOString(),\r\n hide_date: true,\r\n hide_signature: true\r\n });\r\n\r\n \r\n \r\n const [events, setEvents] = useState([\r\n \r\n {\r\n id: 1,\r\n title: \"Ana Souza\",\r\n type: \"Cardiologia\",\r\n time: \"09:00\",\r\n date: new Date().toISOString().split('T')[0], \r\n pacienteId: \"123.456.789-00\",\r\n color: colorsByType.Cardiologia\r\n },\r\n {\r\n id: 2,\r\n title: \"Bruno Lima\",\r\n type: \"Cardiologia\",\r\n time: \"10:30\",\r\n date: new Date().toISOString().split('T')[0], \r\n pacienteId: \"987.654.321-00\",\r\n color: colorsByType.Cardiologia\r\n },\r\n {\r\n id: 3,\r\n title: \"Carla Menezes\",\r\n type: \"Dermatologia\",\r\n time: \"14:00\",\r\n date: new Date().toISOString().split('T')[0], \r\n pacienteId: \"111.222.333-44\",\r\n color: colorsByType.Dermatologia\r\n }\r\n ]);\r\n const [editingEvent, setEditingEvent] = useState(null);\r\n const [showPopup, setShowPopup] = useState(false);\r\n const [showActionModal, setShowActionModal] = useState(false);\r\n const [step, setStep] = useState(1);\r\n const [newEvent, setNewEvent] = useState({ \r\n title: \"\", \r\n type: \"\", \r\n time: \"\",\r\n pacienteId: \"\" \r\n });\r\n const [selectedDate, setSelectedDate] = useState(null);\r\n const [selectedEvent, setSelectedEvent] = useState(null);\r\n const [currentCalendarDate, setCurrentCalendarDate] = useState(new Date());\r\n\r\n const handleSave = (event: React.MouseEvent) => {\r\n event.preventDefault();\r\n console.log(\"Laudo salvo!\");\r\n window.scrollTo({ top: 0, behavior: \"smooth\" });\r\n };\r\n\r\n \r\n\r\n const handleEditarLaudo = (paciente: any) => {\r\n setPatientForLaudo(paciente);\r\n setIsEditingLaudoForPatient(true);\r\n setActiveSection('laudos');\r\n };\r\n\r\n \r\n const navigateDate = (direction: 'prev' | 'next') => {\r\n const newDate = new Date(currentCalendarDate);\r\n newDate.setDate(newDate.getDate() + (direction === 'next' ? 1 : -1));\r\n setCurrentCalendarDate(newDate);\r\n };\r\n\r\n const goToToday = () => {\r\n setCurrentCalendarDate(new Date());\r\n };\r\n\r\n const formatDate = (date: Date) => {\r\n return date.toLocaleDateString('pt-BR', { \r\n weekday: 'long', \r\n day: 'numeric', \r\n month: 'long', \r\n year: 'numeric' \r\n });\r\n };\r\n\r\n // Filtrar eventos do dia atual\r\n const getTodayEvents = () => {\r\n const today = currentCalendarDate.toISOString().split('T')[0];\r\n return events\r\n .filter(event => event.date === today)\r\n .sort((a, b) => a.time.localeCompare(b.time));\r\n };\r\n\r\n const getStatusColor = (type: string) => {\r\n return colorsByType[type as keyof typeof colorsByType] || \"#4dabf7\";\r\n };\r\n\r\n // Funções para o perfil\r\n const handleProfileChange = (field: string, value: string) => {\r\n setProfileData(prev => ({\r\n ...prev,\r\n [field]: value\r\n }));\r\n };\r\n\r\n const handleSaveProfile = () => {\r\n (async () => {\r\n if (!doctorId) {\r\n alert('Não foi possível localizar o registro do médico para atualizar.');\r\n setIsEditingProfile(false);\r\n return;\r\n }\r\n\r\n // Build payload mapping UI fields to DB columns\r\n const payload: any = {};\r\n if (profileData.email) payload.email = profileData.email;\r\n if (profileData.telefone) payload.phone_mobile = profileData.telefone;\r\n if (profileData.endereco) payload.street = profileData.endereco;\r\n if (profileData.cidade) payload.city = profileData.cidade;\r\n if (profileData.cep) payload.cep = profileData.cep;\r\n if (profileData.especialidade) payload.specialty = profileData.especialidade || profileData.especialidade;\r\n if (profileData.fotoUrl) payload.foto_url = profileData.fotoUrl;\r\n\r\n // Don't allow updating full_name or crm from this UI\r\n\r\n try {\r\n const updated = await atualizarMedico(doctorId, payload as any);\r\n console.debug('[ProfissionalPage] médico atualizado:', updated);\r\n alert('Perfil atualizado com sucesso!');\r\n } catch (err: any) {\r\n console.error('[ProfissionalPage] falha ao atualizar médico:', err);\r\n // Mostrar mensagem amigável (o erro já é tratado em lib/api)\r\n alert(err?.message || 'Falha ao atualizar perfil. Verifique logs.');\r\n } finally {\r\n setIsEditingProfile(false);\r\n }\r\n })();\r\n };\r\n\r\n const handleCancelEdit = () => {\r\n setIsEditingProfile(false);\r\n };\r\n\r\n\r\n\r\n \r\n const handleDateClick = (arg: any) => {\r\n setSelectedDate(arg.dateStr);\r\n setNewEvent({ title: \"\", type: \"\", time: \"\", pacienteId: \"\" });\r\n setStep(1);\r\n setEditingEvent(null);\r\n setShowPopup(true);\r\n };\r\n\r\n \r\n const handleAddEvent = () => {\r\n const paciente = pacientes.find(p => p.nome === newEvent.title);\r\n const eventToAdd = {\r\n id: Date.now(),\r\n title: newEvent.title,\r\n type: newEvent.type,\r\n time: newEvent.time,\r\n date: selectedDate || currentCalendarDate.toISOString().split('T')[0],\r\n pacienteId: paciente ? paciente.cpf : \"\",\r\n color: colorsByType[newEvent.type as keyof typeof colorsByType] || \"#4dabf7\"\r\n };\r\n setEvents((prev) => [...prev, eventToAdd]);\r\n setShowPopup(false);\r\n };\r\n\r\n\r\n const handleEditEvent = () => {\r\n setEvents((prevEvents) =>\r\n prevEvents.map((ev) =>\r\n ev.id.toString() === editingEvent.id.toString()\r\n ? {\r\n ...ev,\r\n title: newEvent.title,\r\n type: newEvent.type,\r\n time: newEvent.time,\r\n color: colorsByType[newEvent.type as keyof typeof colorsByType] || \"#4dabf7\"\r\n }\r\n : ev\r\n )\r\n );\r\n setEditingEvent(null);\r\n setShowPopup(false);\r\n setShowActionModal(false);\r\n };\r\n\r\n \r\n const handleNextStep = () => {\r\n if (step < 3) setStep(step + 1);\r\n else editingEvent ? handleEditEvent() : handleAddEvent();\r\n };\r\n\r\n \r\n const handleEventClick = (clickInfo: any) => {\r\n setSelectedEvent(clickInfo.event);\r\n setShowActionModal(true);\r\n };\r\n\r\n\r\n const handleDeleteEvent = () => {\r\n if (!selectedEvent) return;\r\n setEvents((prevEvents) =>\r\n prevEvents.filter((ev: any) => ev.id.toString() !== selectedEvent.id.toString())\r\n );\r\n setShowActionModal(false);\r\n };\r\n\r\n \r\n const handleStartEdit = () => {\r\n if (!selectedEvent) return;\r\n setEditingEvent(selectedEvent);\r\n setNewEvent({\r\n title: selectedEvent.title,\r\n type: selectedEvent.extendedProps.type,\r\n time: selectedEvent.extendedProps.time,\r\n pacienteId: selectedEvent.extendedProps.pacienteId || \"\"\r\n });\r\n setStep(1);\r\n setShowActionModal(false);\r\n setShowPopup(true);\r\n };\r\n\r\n \r\n const renderEventContent = (eventInfo: any) => {\r\n const bg = eventInfo.event.backgroundColor || eventInfo.event.extendedProps?.color || \"#4dabf7\";\r\n\r\n return (\r\n \r\n {eventInfo.event.title}\r\n \r\n {eventInfo.event.extendedProps.type}\r\n \r\n {eventInfo.event.extendedProps.time}\r\n \r\n );\r\n };\r\n\r\n \r\n const renderCalendarioSection = () => {\r\n const todayEvents = getTodayEvents();\r\n \r\n return (\r\n
\r\n
\r\n

Agenda do Dia

\r\n
\r\n \r\n {/* Navegação de Data */}\r\n
\r\n
\r\n \r\n

\r\n {formatDate(currentCalendarDate)}\r\n

\r\n \r\n \r\n
\r\n
\r\n {todayEvents.length} consulta{todayEvents.length !== 1 ? 's' : ''} agendada{todayEvents.length !== 1 ? 's' : ''}\r\n
\r\n
\r\n\r\n {/* Lista de Pacientes do Dia */}\r\n
\r\n {todayEvents.length === 0 ? (\r\n
\r\n \r\n

Nenhuma consulta agendada para este dia

\r\n

Agenda livre para este dia

\r\n
\r\n ) : (\r\n todayEvents.map((appointment) => {\r\n const paciente = pacientes.find(p => p.nome === appointment.title);\r\n return (\r\n \r\n
\r\n
\r\n
\r\n
\r\n
\r\n \r\n {appointment.title}\r\n
\r\n {paciente && (\r\n
\r\n CPF: {getPatientCpf(paciente)} • {getPatientAge(paciente)} anos\r\n
\r\n )}\r\n
\r\n
\r\n
\r\n \r\n {appointment.time}\r\n
\r\n
\r\n
\r\n {appointment.type}\r\n
\r\n
\r\n
\r\n
\r\n
\r\n Ver informações do paciente\r\n
\r\n
\r\n
\r\n\r\n
\r\n
\r\n
\r\n );\r\n })\r\n )}\r\n \r\n
\r\n );\r\n };\r\n\r\n \r\n \r\n \r\n const renderLaudosSection = () => (\r\n
\r\n {\r\n setIsEditingLaudoForPatient(false);\r\n setPatientForLaudo(null);\r\n }}\r\n />\r\n
\r\n );\r\n\r\n // --- NOVO SISTEMA DE LAUDOS COMPLETO ---\r\n function LaudoManager({ isEditingForPatient, selectedPatientForLaudo, onClosePatientEditor }: { isEditingForPatient?: boolean; selectedPatientForLaudo?: any; onClosePatientEditor?: () => void }) {\r\n const [pacientesDisponiveis] = useState([\r\n { id: \"95170038\", nome: \"Ana Souza\", cpf: \"123.456.789-00\", idade: 42, sexo: \"Feminino\" },\r\n { id: \"93203056\", nome: \"Bruno Lima\", cpf: \"987.654.321-00\", idade: 33, sexo: \"Masculino\" },\r\n { id: \"92953542\", nome: \"Carla Menezes\", cpf: \"111.222.333-44\", idade: 67, sexo: \"Feminino\" },\r\n ]);\r\n\r\n const { reports, loadReports, loadReportById, loading: reportsLoading, createNewReport, updateExistingReport } = useReports();\r\n const [laudos, setLaudos] = useState([]);\r\n const [selectedRange, setSelectedRange] = useState<'todos'|'semana'|'mes'|'custom'>('mes');\r\n const [startDate, setStartDate] = useState(null);\r\n const [endDate, setEndDate] = useState(null);\r\n\r\n // helper to check if a date string is in range\r\n const isInRange = (dateStr: string | undefined, range: 'todos'|'semana'|'mes'|'custom') => {\r\n if (range === 'todos') return true;\r\n if (!dateStr) return false;\r\n const d = new Date(dateStr);\r\n if (isNaN(d.getTime())) return false;\r\n const now = new Date();\r\n \r\n if (range === 'semana') {\r\n const start = new Date(now);\r\n start.setDate(now.getDate() - now.getDay()); // sunday start\r\n const end = new Date(start);\r\n end.setDate(start.getDate() + 6);\r\n return d >= start && d <= end;\r\n }\r\n // mes\r\n return d.getFullYear() === now.getFullYear() && d.getMonth() === now.getMonth();\r\n };\r\n\r\n // helper: ensure report has paciente object populated (fetch by id if necessary)\r\n const ensurePaciente = async (report: any) => {\r\n if (!report) return report;\r\n try {\r\n if (!report.paciente) {\r\n const pid = report.patient_id ?? report.patient ?? report.paciente ?? null;\r\n if (pid) {\r\n try {\r\n const p = await buscarPacientePorId(String(pid));\r\n if (p) report.paciente = p;\r\n } catch (e) {\r\n // ignore\r\n }\r\n }\r\n }\r\n } catch (e) {\r\n // ignore\r\n }\r\n return report;\r\n };\r\n\r\n // When selectedRange changes (and isn't custom), compute start/end dates\r\n useEffect(() => {\r\n const now = new Date();\r\n if (selectedRange === 'todos') {\r\n setStartDate(null);\r\n setEndDate(null);\r\n return;\r\n }\r\n \r\n if (selectedRange === 'semana') {\r\n const start = new Date(now);\r\n start.setDate(now.getDate() - now.getDay()); // sunday\r\n const end = new Date(start);\r\n end.setDate(start.getDate() + 6);\r\n setStartDate(start.toISOString().slice(0,10));\r\n setEndDate(end.toISOString().slice(0,10));\r\n return;\r\n }\r\n if (selectedRange === 'mes') {\r\n const start = new Date(now.getFullYear(), now.getMonth(), 1);\r\n const end = new Date(now.getFullYear(), now.getMonth() + 1, 0);\r\n setStartDate(start.toISOString().slice(0,10));\r\n setEndDate(end.toISOString().slice(0,10));\r\n return;\r\n }\r\n // custom: leave startDate/endDate as-is\r\n }, [selectedRange]);\r\n\r\n const filteredLaudos = (laudos || []).filter(l => {\r\n // If a specific start/end date is set, use that range\r\n if (startDate && endDate) {\r\n const ds = getReportDate(l);\r\n if (!ds) return false;\r\n const d = new Date(ds);\r\n if (isNaN(d.getTime())) return false;\r\n const start = new Date(startDate + 'T00:00:00');\r\n const end = new Date(endDate + 'T23:59:59');\r\n return d >= start && d <= end;\r\n }\r\n // Fallback to selectedRange heuristics\r\n if (!selectedRange) return true;\r\n const ds = getReportDate(l);\r\n return isInRange(ds, selectedRange);\r\n });\r\n\r\n function DateRangeButtons() {\r\n return (\r\n <>\r\n setSelectedRange('todos')}\r\n className=\"hover:bg-blue-50\"\r\n >\r\n Todos\r\n \r\n setSelectedRange('semana')}\r\n className=\"hover:bg-blue-50\"\r\n >\r\n Semana\r\n \r\n setSelectedRange('mes')}\r\n className=\"hover:bg-blue-50\"\r\n >\r\n Mês\r\n \r\n \r\n );\r\n }\r\n\r\n // SearchBox inserido aqui para acessar reports, setLaudos e loadReports\r\n function SearchBox() {\r\n const [searchTerm, setSearchTerm] = useState('');\r\n const [searching, setSearching] = useState(false);\r\n const { token } = useAuth();\r\n\r\n const isMaybeId = (s: string) => {\r\n const t = s.trim();\r\n if (!t) return false;\r\n if (t.includes('-') && t.length > 10) return true;\r\n if (t.toUpperCase().startsWith('REL-')) return true;\r\n const digits = t.replace(/\\D/g, '');\r\n if (digits.length >= 8) return true;\r\n return false;\r\n };\r\n\r\n const doSearch = async () => {\r\n const term = searchTerm.trim();\r\n if (!term) return;\r\n setSearching(true);\r\n try {\r\n if (isMaybeId(term)) {\r\n try {\r\n const r = await buscarRelatorioPorId(term);\r\n if (r) {\r\n // If token exists, attempt batch enrichment like useReports\r\n const enriched: any = { ...r };\r\n\r\n // Collect possible patient/doctor ids from payload\r\n const pidCandidates: string[] = [];\r\n const didCandidates: string[] = [];\r\n const pid = (r as any).patient_id ?? (r as any).patient ?? (r as any).paciente ?? null;\r\n if (pid) pidCandidates.push(String(pid));\r\n const possiblePatientName = (r as any).patient_name ?? (r as any).patient_full_name ?? (r as any).paciente?.full_name ?? (r as any).paciente?.nome ?? null;\r\n if (possiblePatientName) {\r\n enriched.paciente = enriched.paciente ?? {};\r\n enriched.paciente.full_name = possiblePatientName;\r\n }\r\n\r\n const did = (r as any).requested_by ?? (r as any).created_by ?? (r as any).executante ?? null;\r\n if (did) didCandidates.push(String(did));\r\n\r\n // If token available, perform batch fetch to get full patient/doctor objects\r\n if (token) {\r\n try {\r\n if (pidCandidates.length) {\r\n const patients = await buscarPacientesPorIds(pidCandidates);\r\n if (patients && patients.length) {\r\n const p = patients[0];\r\n enriched.paciente = enriched.paciente ?? {};\r\n enriched.paciente.full_name = enriched.paciente.full_name || p.full_name || (p as any).nome;\r\n enriched.paciente.id = enriched.paciente.id || p.id;\r\n enriched.paciente.cpf = enriched.paciente.cpf || p.cpf;\r\n }\r\n }\r\n if (didCandidates.length) {\r\n const doctors = await buscarMedicosPorIds(didCandidates);\r\n if (doctors && doctors.length) {\r\n const d = doctors[0];\r\n enriched.executante = enriched.executante || d.full_name || (d as any).nome;\r\n }\r\n }\r\n } catch (e) {\r\n // fallback: continue with payload-only enrichment\r\n console.warn('[SearchBox] batch enrichment failed, falling back to payload-only enrichment', e);\r\n }\r\n }\r\n\r\n // Final payload-only fallbacks (ensure id/cpf/order_number are populated)\r\n const possiblePatientId = (r as any).paciente?.id ?? (r as any).patient?.id ?? (r as any).patient_id ?? (r as any).patientId ?? (r as any).id ?? undefined;\r\n if (possiblePatientId && !enriched.paciente?.id) {\r\n enriched.paciente = enriched.paciente ?? {};\r\n enriched.paciente.id = possiblePatientId;\r\n }\r\n const possibleCpf = (r as any).patient_cpf ?? (r as any).paciente?.cpf ?? (r as any).patient?.cpf ?? null;\r\n if (possibleCpf) {\r\n enriched.paciente = enriched.paciente ?? {};\r\n enriched.paciente.cpf = possibleCpf;\r\n }\r\n const execName = (r as any).requested_by_name ?? (r as any).requester_name ?? (r as any).requestedByName ?? (r as any).executante_name ?? (r as any).created_by_name ?? (r as any).createdByName ?? (r as any).executante ?? (r as any).requested_by ?? (r as any).created_by ?? '';\r\n if (execName) enriched.executante = enriched.executante || execName;\r\n if ((r as any).order_number) enriched.order_number = (r as any).order_number;\r\n\r\n setLaudos([enriched]);\r\n return;\r\n }\r\n } catch (err: any) {\r\n console.warn('Relatório não encontrado por ID:', err);\r\n }\r\n }\r\n\r\n const lower = term.toLowerCase();\r\n const filtered = (reports || []).filter((x: any) => {\r\n const name = (x.paciente?.full_name || x.patient_name || x.patient_full_name || x.order_number || x.exame || x.exam || '').toString().toLowerCase();\r\n return name.includes(lower);\r\n });\r\n if (filtered.length) setLaudos(filtered);\r\n else setLaudos([]);\r\n } finally {\r\n setSearching(false);\r\n }\r\n };\r\n\r\n const handleKey = (e: React.KeyboardEvent) => {\r\n if (e.key === 'Enter') doSearch();\r\n };\r\n\r\n const handleClear = async () => {\r\n setSearchTerm('');\r\n await loadReports();\r\n setLaudos(reports || []);\r\n };\r\n\r\n return (\r\n
\r\n
\r\n setSearchTerm(e.target.value)}\r\n onKeyDown={handleKey}\r\n />\r\n \r\n \r\n \r\n
\r\n
\r\n \r\n \r\n
\r\n
\r\n );\r\n }\r\n\r\n // carregar laudos ao montar - somente dos pacientes atribuídos ao médico logado\r\n useEffect(() => {\r\n let mounted = true;\r\n (async () => {\r\n try {\r\n // obter assignments para o usuário logado\r\n const assignments = await import('@/lib/assignment').then(m => m.listAssignmentsForUser(user?.id || ''));\r\n const patientIds = Array.isArray(assignments) ? assignments.map(a => String(a.patient_id)).filter(Boolean) : [];\r\n\r\n if (patientIds.length === 0) {\r\n if (mounted) setLaudos([]);\r\n return;\r\n }\r\n\r\n // Tentar carregar todos os relatórios em uma única chamada usando in.(...)\r\n try {\r\n const reportsMod = await import('@/lib/reports');\r\n if (typeof reportsMod.listarRelatoriosPorPacientes === 'function') {\r\n const batch = await reportsMod.listarRelatoriosPorPacientes(patientIds);\r\n // Filtrar apenas relatórios criados/solicitados por este usuário (evita mostrar laudos de outros médicos)\r\n const mineOnly = (batch || []).filter((r: any) => {\r\n const requester = ((r.requested_by ?? r.created_by ?? r.executante ?? r.requestedBy ?? r.createdBy) || '').toString();\r\n return user?.id && requester && requester === user.id;\r\n });\r\n // Enrich reports with paciente objects so UI shows name/cpf immediately\r\n const enriched = await (async (reportsArr: any[]) => {\r\n if (!reportsArr || !reportsArr.length) return reportsArr;\r\n const pids = reportsArr.map(r => String(getReportPatientId(r))).filter(Boolean);\r\n if (!pids.length) return reportsArr;\r\n try {\r\n const patients = await buscarPacientesPorIds(pids);\r\n const map = new Map((patients || []).map((p: any) => [String(p.id), p]));\r\n return reportsArr.map(r => {\r\n const pid = String(getReportPatientId(r));\r\n return { ...r, paciente: r.paciente ?? map.get(pid) ?? r.paciente };\r\n });\r\n } catch (e) {\r\n return reportsArr;\r\n }\r\n })(mineOnly);\r\n if (mounted) setLaudos(enriched || []);\r\n } else {\r\n // fallback: 请求 por paciente individual\r\n const allReports: any[] = [];\r\n for (const pid of patientIds) {\r\n try {\r\n const rels = await import('@/lib/reports').then(m => m.listarRelatoriosPorPaciente(pid));\r\n if (Array.isArray(rels) && rels.length) {\r\n // filtrar por autor (requested_by / created_by / executante)\r\n const mine = rels.filter((r: any) => {\r\n const requester = ((r.requested_by ?? r.created_by ?? r.executante ?? r.requestedBy ?? r.createdBy) || '').toString();\r\n return user?.id && requester && requester === user.id;\r\n });\r\n if (mine.length) allReports.push(...mine);\r\n }\r\n } catch (err) {\r\n console.warn('[LaudoManager] falha ao carregar relatórios para paciente', pid, err);\r\n }\r\n }\r\n // enrich fallback results too\r\n const enrichedAll = await (async (reportsArr: any[]) => {\r\n if (!reportsArr || !reportsArr.length) return reportsArr;\r\n const pids = reportsArr.map(r => String(getReportPatientId(r))).filter(Boolean);\r\n if (!pids.length) return reportsArr;\r\n try {\r\n const patients = await buscarPacientesPorIds(pids);\r\n const map = new Map((patients || []).map((p: any) => [String(p.id), p]));\r\n return reportsArr.map(r => ({ ...r, paciente: r.paciente ?? map.get(String(getReportPatientId(r))) ?? r.paciente }));\r\n } catch (e) {\r\n return reportsArr;\r\n }\r\n })(allReports);\r\n if (mounted) setLaudos(enrichedAll);\r\n }\r\n } catch (err) {\r\n console.warn('[LaudoManager] erro ao carregar relatórios em batch, tentando por paciente individual', err);\r\n const allReports: any[] = [];\r\n for (const pid of patientIds) {\r\n try {\r\n const rels = await import('@/lib/reports').then(m => m.listarRelatoriosPorPaciente(pid));\r\n if (Array.isArray(rels) && rels.length) {\r\n const mine = rels.filter((r: any) => {\r\n const requester = ((r.requested_by ?? r.created_by ?? r.executante ?? r.requestedBy ?? r.createdBy) || '').toString();\r\n return user?.id && requester && requester === user.id;\r\n });\r\n if (mine.length) allReports.push(...mine);\r\n }\r\n } catch (e) {\r\n console.warn('[LaudoManager] falha ao carregar relatórios para paciente', pid, e);\r\n }\r\n }\r\n const enrichedAll = await (async (reportsArr: any[]) => {\r\n if (!reportsArr || !reportsArr.length) return reportsArr;\r\n const pids = reportsArr.map(r => String(getReportPatientId(r))).filter(Boolean);\r\n if (!pids.length) return reportsArr;\r\n try {\r\n const patients = await buscarPacientesPorIds(pids);\r\n const map = new Map((patients || []).map((p: any) => [String(p.id), p]));\r\n return reportsArr.map(r => ({ ...r, paciente: r.paciente ?? map.get(String(getReportPatientId(r))) ?? r.paciente }));\r\n } catch (e) {\r\n return reportsArr;\r\n }\r\n })(allReports);\r\n if (mounted) setLaudos(enrichedAll);\r\n }\r\n } catch (e) {\r\n console.warn('[LaudoManager] erro ao carregar laudos para pacientes atribuídos:', e);\r\n if (mounted) setLaudos(reports || []);\r\n }\r\n })();\r\n return () => { mounted = false; };\r\n }, [user?.id]);\r\n\r\n // sincroniza quando reports mudarem no hook (fallback)\r\n useEffect(() => {\r\n if (!laudos || laudos.length === 0) setLaudos(reports || []);\r\n }, [reports]);\r\n\r\n // Sort reports newest-first (more recent dates at the top)\r\n const sortedLaudos = React.useMemo(() => {\r\n const arr = (filteredLaudos || []).slice();\r\n arr.sort((a: any, b: any) => {\r\n try {\r\n const da = new Date(getReportDate(a) || 0).getTime() || 0;\r\n const db = new Date(getReportDate(b) || 0).getTime() || 0;\r\n return db - da;\r\n } catch (e) {\r\n return 0;\r\n }\r\n });\r\n return arr;\r\n }, [filteredLaudos]);\r\n\r\n const [activeTab, setActiveTab] = useState(\"descobrir\");\r\n const [laudoSelecionado, setLaudoSelecionado] = useState(null);\r\n const [isViewing, setIsViewing] = useState(false);\r\n const [isCreatingNew, setIsCreatingNew] = useState(false);\r\n\r\n\r\n\r\n\r\n return (\r\n
\r\n {/* Header */}\r\n
\r\n
\r\n
\r\n

Gerenciamento de Laudo

\r\n

Nesta seção você pode gerenciar todos os laudos gerados.

\r\n
\r\n \r\n
\r\n
\r\n\r\n {/* Tabs */}\r\n
\r\n
\r\n setActiveTab(\"descobrir\")}\r\n className={`px-4 py-3 text-sm font-medium border-b-2 transition-colors ${\r\n activeTab === \"descobrir\"\r\n ? \"border-blue-500 text-blue-600\"\r\n : \"border-transparent text-muted-foreground hover:text-foreground\"\r\n }`}\r\n >\r\n A descobrir\r\n \r\n
\r\n\r\n {/* Filtros */}\r\n
\r\n
\r\n
\r\n {/* Search input integrado com busca por ID */}\r\n \r\n
\r\n \r\n
\r\n
\r\n \r\n { setStartDate(e.target.value); setSelectedRange('custom'); }} className=\"p-1 text-sm h-10\" />\r\n -\r\n { setEndDate(e.target.value); setSelectedRange('custom'); }} className=\"p-1 text-sm h-10\" />\r\n
\r\n
\r\n\r\n
\r\n {/* date range buttons: Semana / Mês */}\r\n \r\n
\r\n\r\n {/* Filtros e pesquisa removidos por solicitação */}\r\n
\r\n
\r\n\r\n {/* Tabela para desktop e cards empilháveis para mobile */}\r\n
\r\n {/* Desktop / tablet (md+) - tabela com scroll horizontal */}\r\n
\r\n \r\n \r\n \r\n Pedido\r\n Data\r\n Prazo\r\n Paciente\r\n Executante/Solicitante\r\n Exame/Classificação\r\n Ação\r\n \r\n \r\n \r\n {sortedLaudos.map((laudo, idx) => (\r\n \r\n \r\n
\r\n {laudo.urgente && (\r\n
\r\n )}\r\n \r\n {getReportPatientName(laudo) || laudo.order_number || getShortId(laudo.id)}\r\n \r\n
\r\n
\r\n \r\n
\r\n
{formatReportDate(getReportDate(laudo))}
\r\n
{laudo?.hora || new Date(laudo?.data || laudo?.created_at || laudo?.due_at || Date.now()).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
\r\n
\r\n
\r\n \r\n
\r\n
{(laudo?.prazo ?? laudo?.due_at) ? formatReportDate(laudo?.due_at ?? laudo?.prazo) : '-'}
\r\n
{\r\n (() => {\r\n // prefer explicit fields\r\n const explicit = laudo?.prazo_hora ?? laudo?.due_time ?? laudo?.hora ?? null;\r\n if (explicit) return explicit;\r\n // fallback: try to parse due_at / prazo datetime and extract time\r\n const due = laudo?.due_at ?? laudo?.prazo ?? laudo?.dueDate ?? laudo?.data ?? null;\r\n if (!due) return '-';\r\n try {\r\n const d = new Date(due);\r\n if (isNaN(d.getTime())) return '-';\r\n return d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });\r\n } catch (e) {\r\n return '-';\r\n }\r\n })()\r\n }
\r\n
\r\n
\r\n \r\n
\r\n
\r\n \r\n
\r\n
{getReportPatientName(laudo) || '—'}
\r\n
{getReportPatientCpf(laudo) ? `CPF: ${getReportPatientCpf(laudo)}` : ''}
\r\n
\r\n
\r\n {\r\n (() => {\r\n const possibleName = laudo.requested_by_name ?? laudo.requester_name ?? laudo.requestedByName ?? laudo.executante_name ?? laudo.executante ?? laudo.executante_name ?? laudo.executante;\r\n if (possibleName && typeof possibleName === 'string' && possibleName.trim().length) return possibleName;\r\n const possibleId = (laudo.requested_by ?? laudo.created_by ?? laudo.executante ?? laudo.requestedBy ?? laudo.createdBy) || '';\r\n if (possibleId && user?.id && possibleId === user.id) return (profileData as any)?.nome || user?.name || possibleId;\r\n return possibleName || possibleId || '-';\r\n })()\r\n }\r\n {getReportExam(laudo) || \"-\"}\r\n \r\n
\r\n {\r\n try {\r\n const full = (laudo?.id || laudo?.order_number) ? await loadReportById(String(laudo?.id ?? laudo?.order_number)) : laudo;\r\n await ensurePaciente(full);\r\n setLaudoSelecionado(full);\r\n setIsViewing(true);\r\n } catch (e) {\r\n // fallback\r\n setLaudoSelecionado(laudo);\r\n setIsViewing(true);\r\n }\r\n }}\r\n className=\"flex items-center gap-1 hover:bg-blue-50 dark:hover:bg-accent dark:hover:text-accent-foreground\"\r\n >\r\n \r\n Ver Laudo\r\n \r\n {\r\n setPatientForLaudo(laudo);\r\n setIsEditingLaudoForPatient(true);\r\n }}\r\n className=\"flex items-center gap-1 bg-green-600 hover:bg-green-700 text-white\"\r\n title=\"Editar laudo para este paciente\"\r\n >\r\n \r\n Editar Laudo\r\n \r\n
\r\n
\r\n
\r\n ))}\r\n
\r\n
\r\n
\r\n\r\n {/* Mobile - cards empilháveis */}\r\n
\r\n {sortedLaudos.map((laudo, idx) => (\r\n
\r\n
\r\n
\r\n
\r\n
\r\n
{getReportExam(laudo) || '-'}
\r\n
{formatReportDate(getReportDate(laudo))} {laudo?.hora ? `• ${laudo.hora}` : ''}
\r\n
\r\n
{getReportPatientName(laudo) ? getShortId(laudo.id) : ''}
\r\n
\r\n
\r\n
{getReportPatientName(laudo) || '—'}
\r\n
{getReportPatientCpf(laudo) ? `CPF: ${getReportPatientCpf(laudo)}` : ''}
\r\n
\r\n
\r\n
\r\n
{\r\n (() => {\r\n const possibleName = laudo.requested_by_name ?? laudo.requester_name ?? laudo.requestedByName ?? laudo.executante_name ?? laudo.executante ?? laudo.executante_name ?? laudo.executante;\r\n if (possibleName && typeof possibleName === 'string' && possibleName.trim().length) return possibleName;\r\n const possibleId = (laudo.requested_by ?? laudo.created_by ?? laudo.executante ?? laudo.requestedBy ?? laudo.createdBy) || '';\r\n if (possibleId && user?.id && possibleId === user.id) return (profileData as any)?.nome || user?.name || possibleId;\r\n return possibleName || possibleId || '-';\r\n })()\r\n }
\r\n
\r\n {\r\n setLaudoSelecionado(laudo);\r\n setIsViewing(true);\r\n }}\r\n className=\"flex items-center gap-1\"\r\n >\r\n \r\n \r\n {\r\n setPatientForLaudo(laudo);\r\n setIsEditingLaudoForPatient(true);\r\n }}\r\n className=\"flex items-center gap-1 bg-green-600 hover:bg-green-700 text-white\"\r\n title=\"Editar laudo\"\r\n >\r\n \r\n \r\n
\r\n
\r\n
\r\n
\r\n ))}\r\n
\r\n
\r\n
\r\n\r\n {/* Visualizador de Laudo */}\r\n {isViewing && laudoSelecionado && (\r\n setIsViewing(false)} />\r\n )}\r\n\r\n {/* Editor para Novo Laudo */}\r\n {isCreatingNew && (\r\n setIsCreatingNew(false)}\r\n isNewLaudo={true}\r\n createNewReport={createNewReport}\r\n updateExistingReport={updateExistingReport}\r\n reloadReports={loadReports}\r\n onSaved={async (r:any) => {\r\n try {\r\n // If report has an id, fetch full report and open viewer\r\n if (r && (r.id || r.order_number)) {\r\n const id = r.id ?? r.order_number;\r\n const full = await loadReportById(String(id));\r\n await ensurePaciente(full);\r\n // prepend to laudos list so it appears immediately\r\n setLaudos(prev => [full, ...(prev || [])]);\r\n setLaudoSelecionado(full);\r\n setIsViewing(true);\r\n } else {\r\n setLaudoSelecionado(r);\r\n setIsViewing(true);\r\n }\r\n // refresh global reports list too\r\n try { await loadReports(); } catch {}\r\n } catch (e) {\r\n // fallback: open what we have\r\n setLaudoSelecionado(r);\r\n setIsViewing(true);\r\n }\r\n }}\r\n />\r\n )}\r\n\r\n {/* Editor para Paciente Específico */}\r\n {isEditingForPatient && selectedPatientForLaudo && (\r\n {})}\r\n isNewLaudo={!selectedPatientForLaudo?.id}\r\n preSelectedPatient={selectedPatientForLaudo.paciente || selectedPatientForLaudo}\r\n createNewReport={createNewReport}\r\n updateExistingReport={updateExistingReport}\r\n reloadReports={loadReports}\r\n onSaved={async (r:any) => {\r\n try {\r\n if (r && (r.id || r.order_number)) {\r\n const id = r.id ?? r.order_number;\r\n const full = await loadReportById(String(id));\r\n await ensurePaciente(full);\r\n setLaudos(prev => [full, ...(prev || [])]);\r\n setLaudoSelecionado(full);\r\n setIsViewing(true);\r\n } else {\r\n setLaudoSelecionado(r);\r\n setIsViewing(true);\r\n }\r\n try { await loadReports(); } catch {}\r\n } catch (e) {\r\n setLaudoSelecionado(r);\r\n setIsViewing(true);\r\n }\r\n }}\r\n />\r\n )}\r\n
\r\n );\r\n }\r\n\r\n // Visualizador de Laudo (somente leitura)\r\n function LaudoViewer({ laudo, onClose }: { laudo: any; onClose: () => void }) {\r\n return (\r\n
\r\n
\r\n {/* Header */}\r\n
\r\n
\r\n

Visualizar Laudo

\r\n

\r\n Paciente: {getPatientName(laudo?.paciente) || getPatientName(laudo) || '—'} | CPF: {getReportPatientCpf(laudo) ?? laudo?.patient_cpf ?? '-'} | {laudo?.especialidade ?? laudo?.exame ?? '-'}\r\n

\r\n
\r\n \r\n
\r\n\r\n {/* Content */}\r\n
\r\n
\r\n {/* Header do Laudo */}\r\n
\r\n

LAUDO MÉDICO - {(laudo.especialidade ?? laudo.exame ?? '').toString().toUpperCase()}

\r\n

\r\n Data: {formatReportDate(getReportDate(laudo))}\r\n

\r\n
\r\n\r\n {/* Dados do Paciente */}\r\n
\r\n

Dados do Paciente:

\r\n
\r\n

Nome: {getPatientName(laudo?.paciente) || getPatientName(laudo) || '-'}

\r\n

CPF: {getPatientCpf(laudo?.paciente) ?? laudo?.patient_cpf ?? '-'}

\r\n

Idade: {getPatientAge(laudo?.paciente) ? `${getPatientAge(laudo?.paciente)} anos` : (getPatientAge(laudo) ? `${getPatientAge(laudo)} anos` : '-')}

\r\n

Sexo: {getPatientSex(laudo?.paciente) ?? getPatientSex(laudo) ?? '-'}

\r\n

CID: {laudo?.cid ?? laudo?.cid_code ?? '-'}

\r\n
\r\n
\r\n\r\n {/* Conteúdo do Laudo */}\r\n
\r\n
') \r\n }}\r\n />\r\n
\r\n\r\n {/* Exame */}\r\n {((laudo.exame ?? laudo.exam ?? laudo.especialidade ?? laudo.report_type) || '').toString().length > 0 && (\r\n
\r\n

Exame / Especialidade:

\r\n

{laudo.exame ?? laudo.exam ?? laudo.especialidade ?? laudo.report_type}

\r\n
\r\n )}\r\n\r\n {/* Diagnóstico */}\r\n {((laudo.diagnostico ?? laudo.diagnosis) || '').toString().length > 0 && (\r\n
\r\n

Diagnóstico:

\r\n

{laudo.diagnostico ?? laudo.diagnosis}

\r\n
\r\n )}\r\n\r\n {/* Conclusão */}\r\n {((laudo.conclusao ?? laudo.conclusion) || '').toString().length > 0 && (\r\n
\r\n

Conclusão:

\r\n

{laudo.conclusao ?? laudo.conclusion}

\r\n
\r\n )}\r\n\r\n {/* Diagnóstico e Conclusão */}\r\n {laudo.diagnostico && (\r\n
\r\n

Diagnóstico:

\r\n

{laudo.diagnostico}

\r\n
\r\n )}\r\n\r\n {laudo.conclusao && (\r\n
\r\n

Conclusão:

\r\n

{laudo.conclusao}

\r\n
\r\n )}\r\n\r\n {/* Assinatura */}\r\n
\r\n
\r\n {(() => {\r\n const signatureName = laudo?.created_by_name ?? laudo?.createdByName ?? ((laudo?.created_by && user?.id && laudo.created_by === user.id) ? profileData.nome : (laudo?.created_by_name || profileData.nome));\r\n return (\r\n <>\r\n

{signatureName}

\r\n

{profileData.crm ? `CRM: ${String(profileData.crm).replace(/^(?:CRM\\s*)+/i, '').trim()}` : 'CRM não informado'}{laudo.especialidade ? ` - ${laudo.especialidade}` : ''}

\r\n

Data: {formatReportDate(getReportDate(laudo))}

\r\n \r\n );\r\n })()}\r\n
\r\n
\r\n
\r\n\r\n {/* Footer */}\r\n
\r\n
\r\n \r\n
\r\n
\r\n
\r\n
\r\n );\r\n }\r\n\r\n // Editor de Laudo Avançado (para novos laudos)\r\n function LaudoEditor({ pacientes, laudo, onClose, isNewLaudo, preSelectedPatient, createNewReport, updateExistingReport, reloadReports, onSaved }: { pacientes?: any[]; laudo?: any; onClose: () => void; isNewLaudo?: boolean; preSelectedPatient?: any; createNewReport?: (data: any) => Promise; updateExistingReport?: (id: string, data: any) => Promise; reloadReports?: () => Promise; onSaved?: (r:any) => void }) {\r\n // Import useToast at the top level of the component\r\n const { toast } = require('@/hooks/use-toast').useToast();\r\n const [activeTab, setActiveTab] = useState(\"editor\");\r\n const [content, setContent] = useState(laudo?.conteudo || \"\");\r\n const [showPreview, setShowPreview] = useState(false);\r\n const [pacienteSelecionado, setPacienteSelecionado] = useState(preSelectedPatient || null);\r\n const [listaPacientes, setListaPacientes] = useState([]);\r\n // Novo: campos para solicitante e prazo\r\n // solicitanteId será enviado ao backend (sempre o id do usuário logado)\r\n const [solicitanteId, setSolicitanteId] = useState(user?.id || \"\");\r\n // displaySolicitante é apenas para exibição (nome do usuário) e NÃO é enviado ao backend\r\n // Prefer profileData.nome (nome do médico carregado) — cai back para user.name ou email\r\n const displaySolicitante = ((profileData as any) && ((profileData as any).nome || (profileData as any).nome_social)) || user?.name || (user?.profile as any)?.full_name || user?.email || '';\r\n const [prazoDate, setPrazoDate] = useState(\"\");\r\n const [prazoTime, setPrazoTime] = useState(\"\");\r\n\r\n // Pega token do usuário logado (passado explicitamente para listarPacientes)\r\n const { token } = useAuth();\r\n\r\n // Carregar pacientes reais do Supabase ao abrir o modal ou quando o token mudar\r\n useEffect(() => {\r\n async function fetchPacientes() {\r\n try {\r\n if (!token) {\r\n setListaPacientes([]);\r\n return;\r\n }\r\n const pacientes = await listarPacientes();\r\n setListaPacientes(pacientes || []);\r\n } catch (err) {\r\n console.warn('Erro ao carregar pacientes:', err);\r\n setListaPacientes([]);\r\n }\r\n }\r\n fetchPacientes();\r\n }, [token]);\r\n const [campos, setCampos] = useState({\r\n cid: laudo?.cid || \"\",\r\n diagnostico: laudo?.diagnostico || \"\",\r\n conclusao: laudo?.conclusao || \"\",\r\n exame: laudo?.exame || \"\",\r\n especialidade: laudo?.especialidade || \"\",\r\n mostrarData: true,\r\n mostrarAssinatura: true\r\n });\r\n const [imagens, setImagens] = useState([]);\r\n const [templates] = useState([\r\n \"Exame normal, sem alterações significativas\",\r\n \"Paciente em acompanhamento ambulatorial\",\r\n \"Recomenda-se retorno em 30 dias\",\r\n \"Alterações compatíveis com processo inflamatório\",\r\n \"Resultado dentro dos parâmetros de normalidade\",\r\n \"Recomendo seguimento com especialista\"\r\n ]);\r\n\r\n\r\n const sigCanvasRef = useRef(null);\r\n\r\n // Estado para imagem da assinatura\r\n const [assinaturaImg, setAssinaturaImg] = useState(null);\r\n\r\n useEffect(() => {\r\n if (!sigCanvasRef.current) return;\r\n const handleEnd = () => {\r\n const url = sigCanvasRef.current.getTrimmedCanvas().toDataURL('image/png');\r\n setAssinaturaImg(url);\r\n };\r\n const canvas = sigCanvasRef.current;\r\n if (canvas && canvas.canvas) {\r\n canvas.canvas.addEventListener('mouseup', handleEnd);\r\n canvas.canvas.addEventListener('touchend', handleEnd);\r\n }\r\n return () => {\r\n if (canvas && canvas.canvas) {\r\n canvas.canvas.removeEventListener('mouseup', handleEnd);\r\n canvas.canvas.removeEventListener('touchend', handleEnd);\r\n }\r\n };\r\n }, [sigCanvasRef]);\r\n\r\n const handleClearSignature = () => {\r\n if (sigCanvasRef.current) {\r\n sigCanvasRef.current.clear();\r\n }\r\n setAssinaturaImg(null);\r\n };\r\n\r\n // Carregar dados do laudo existente quando disponível (mais robusto: suporta vários nomes de campo)\r\n useEffect(() => {\r\n if (laudo && !isNewLaudo) {\r\n // Conteúdo: aceita 'conteudo', 'content_html', 'contentHtml', 'content'\r\n const contentValue = laudo.conteudo ?? laudo.content_html ?? laudo.contentHtml ?? laudo.content ?? \"\";\r\n setContent(contentValue);\r\n\r\n // Campos: use vários fallbacks\r\n const cidValue = laudo.cid ?? laudo.cid_code ?? '';\r\n const diagnosticoValue = laudo.diagnostico ?? laudo.diagnosis ?? '';\r\n const conclusaoValue = laudo.conclusao ?? laudo.conclusion ?? '';\r\n const exameValue = laudo.exame ?? laudo.exam ?? laudo.especialidade ?? '';\r\n const especialidadeValue = laudo.especialidade ?? laudo.exame ?? laudo.exam ?? '';\r\n const mostrarDataValue = typeof laudo.hide_date === 'boolean' ? !laudo.hide_date : true;\r\n const mostrarAssinaturaValue = typeof laudo.hide_signature === 'boolean' ? !laudo.hide_signature : true;\r\n\r\n setCampos({\r\n cid: cidValue,\r\n diagnostico: diagnosticoValue,\r\n conclusao: conclusaoValue,\r\n exame: exameValue,\r\n especialidade: especialidadeValue,\r\n mostrarData: mostrarDataValue,\r\n mostrarAssinatura: mostrarAssinaturaValue\r\n });\r\n\r\n // Paciente: não sobrescrever se já existe preSelectedPatient ou pacienteSelecionado\r\n if (!pacienteSelecionado) {\r\n const pacienteFromLaudo = laudo.paciente ?? laudo.patient ?? null;\r\n if (pacienteFromLaudo) {\r\n setPacienteSelecionado(pacienteFromLaudo);\r\n } else if (laudo.patient_id && listaPacientes && listaPacientes.length) {\r\n const found = listaPacientes.find(p => String(p.id) === String(laudo.patient_id));\r\n if (found) setPacienteSelecionado(found);\r\n }\r\n }\r\n\r\n // preencher solicitanteId/prazo quando existe laudo (edição)\r\n // preferimos manter o solicitanteId como o user id; se o laudo tiver requested_by que pareça um id, usamos ele\r\n const possibleRequestedById = laudo.requested_by ?? laudo.created_by ?? null;\r\n if (possibleRequestedById && typeof possibleRequestedById === 'string' && possibleRequestedById.length > 5) {\r\n setSolicitanteId(possibleRequestedById);\r\n } else {\r\n setSolicitanteId(user?.id || \"\");\r\n }\r\n\r\n const dueRaw = laudo.due_at ?? laudo.prazo ?? laudo.dueDate ?? laudo.data ?? null;\r\n if (dueRaw) {\r\n try {\r\n const d = new Date(dueRaw);\r\n if (!isNaN(d.getTime())) {\r\n setPrazoDate(d.toISOString().slice(0,10));\r\n setPrazoTime(d.toTimeString().slice(0,5));\r\n }\r\n } catch (e) {\r\n // ignore invalid date\r\n }\r\n }\r\n\r\n // assinatura: aceitar vários campos possíveis\r\n const sig = laudo.assinaturaImg ?? laudo.signature_image ?? laudo.signature ?? laudo.sign_image ?? null;\r\n if (sig) setAssinaturaImg(sig);\r\n }\r\n }, [laudo, isNewLaudo, pacienteSelecionado, listaPacientes, user]);\r\n\r\n // Histórico para desfazer/refazer\r\n const [history, setHistory] = useState([]);\r\n const [historyIndex, setHistoryIndex] = useState(-1);\r\n\r\n // Atualiza histórico ao digitar\r\n useEffect(() => {\r\n if (history[historyIndex] !== content) {\r\n const newHistory = history.slice(0, historyIndex + 1);\r\n setHistory([...newHistory, content]);\r\n setHistoryIndex(newHistory.length);\r\n }\r\n // eslint-disable-next-line\r\n }, [content]);\r\n\r\n const handleUndo = () => {\r\n if (historyIndex > 0) {\r\n setContent(history[historyIndex - 1]);\r\n setHistoryIndex(historyIndex - 1);\r\n }\r\n };\r\n const handleRedo = () => {\r\n if (historyIndex < history.length - 1) {\r\n setContent(history[historyIndex + 1]);\r\n setHistoryIndex(historyIndex + 1);\r\n }\r\n };\r\n\r\n // Formatação avançada\r\n const formatText = (type: string, value?: any) => {\r\n const textarea = document.querySelector('textarea') as HTMLTextAreaElement;\r\n if (!textarea) return;\r\n const start = textarea.selectionStart;\r\n const end = textarea.selectionEnd;\r\n const selectedText = textarea.value.substring(start, end);\r\n let formattedText = \"\";\r\n switch(type) {\r\n case \"bold\":\r\n formattedText = selectedText ? `**${selectedText}**` : \"**texto em negrito**\";\r\n break;\r\n case \"italic\":\r\n formattedText = selectedText ? `*${selectedText}*` : \"*texto em itálico*\";\r\n break;\r\n case \"underline\":\r\n formattedText = selectedText ? `__${selectedText}__` : \"__texto sublinhado__\";\r\n break;\r\n case \"list-ul\":\r\n formattedText = selectedText ? selectedText.split('\\n').map(l => `• ${l}`).join('\\n') : \"• item da lista\";\r\n break;\r\n case \"list-ol\":\r\n formattedText = selectedText ? selectedText.split('\\n').map((l,i) => `${i+1}. ${l}`).join('\\n') : \"1. item da lista\";\r\n break;\r\n case \"indent\":\r\n formattedText = selectedText ? selectedText.split('\\n').map(l => ` ${l}`).join('\\n') : \" \";\r\n break;\r\n case \"outdent\":\r\n formattedText = selectedText ? selectedText.split('\\n').map(l => l.replace(/^\\s{1,4}/, \"\")).join('\\n') : \"\";\r\n break;\r\n case \"align-left\":\r\n formattedText = selectedText ? `[left]${selectedText}[/left]` : \"[left]Texto à esquerda[/left]\";\r\n break;\r\n case \"align-center\":\r\n formattedText = selectedText ? `[center]${selectedText}[/center]` : \"[center]Texto centralizado[/center]\";\r\n break;\r\n case \"align-right\":\r\n formattedText = selectedText ? `[right]${selectedText}[/right]` : \"[right]Texto à direita[/right]\";\r\n break;\r\n case \"align-justify\":\r\n formattedText = selectedText ? `[justify]${selectedText}[/justify]` : \"[justify]Texto justificado[/justify]\";\r\n break;\r\n case \"font-size\":\r\n formattedText = selectedText ? `[size=${value}]${selectedText}[/size]` : `[size=${value}]Texto tamanho ${value}[/size]`;\r\n break;\r\n case \"font-family\":\r\n formattedText = selectedText ? `[font=${value}]${selectedText}[/font]` : `[font=${value}]${value}[/font]`;\r\n break;\r\n case \"font-color\":\r\n formattedText = selectedText ? `[color=${value}]${selectedText}[/color]` : `[color=${value}]${value}[/color]`;\r\n break;\r\n default:\r\n return;\r\n }\r\n const newText = textarea.value.substring(0, start) + formattedText + textarea.value.substring(end);\r\n setContent(newText);\r\n };\r\n\r\n const insertTemplate = (template: string) => {\r\n setContent((prev: string) => prev ? `${prev}\\n\\n${template}` : template);\r\n };\r\n\r\n const handleImageUpload = (e: React.ChangeEvent) => {\r\n const files = Array.from(e.target.files || []);\r\n files.forEach(file => {\r\n const reader = new FileReader();\r\n reader.onload = (e) => {\r\n setImagens(prev => [...prev, {\r\n id: Date.now() + Math.random(),\r\n name: file.name,\r\n url: e.target?.result,\r\n type: file.type\r\n }]);\r\n };\r\n reader.readAsDataURL(file);\r\n });\r\n };\r\n\r\n const processContent = (content: string) => {\r\n return content\r\n .replace(/\\*\\*(.*?)\\*\\*/g, '$1')\r\n .replace(/\\*(.*?)\\*/g, '$1')\r\n .replace(/__(.*?)__/g, '$1')\r\n .replace(/\\[left\\]([\\s\\S]*?)\\[\\/left\\]/g, '
$1
')\r\n .replace(/\\[center\\]([\\s\\S]*?)\\[\\/center\\]/g, '
$1
')\r\n .replace(/\\[right\\]([\\s\\S]*?)\\[\\/right\\]/g, '
$1
')\r\n .replace(/\\[justify\\]([\\s\\S]*?)\\[\\/justify\\]/g, '
$1
')\r\n .replace(/\\[size=(\\d+)\\]([\\s\\S]*?)\\[\\/size\\]/g, '$2')\r\n .replace(/\\[font=([^\\]]+)\\]([\\s\\S]*?)\\[\\/font\\]/g, '$2')\r\n .replace(/\\[color=([^\\]]+)\\]([\\s\\S]*?)\\[\\/color\\]/g, '$2')\r\n .replace(/{{sexo_paciente}}/g, pacienteSelecionado?.sexo || laudo?.paciente?.sexo || '[SEXO]')\r\n .replace(/{{diagnostico}}/g, campos.diagnostico || '[DIAGNÓSTICO]')\r\n .replace(/{{conclusao}}/g, campos.conclusao || '[CONCLUSÃO]')\r\n .replace(/\\n/g, '
');\r\n };\r\n\r\n return (\r\n
\r\n
\r\n {/* Header */}\r\n
\r\n
\r\n
\r\n

\r\n {isNewLaudo ? \"Novo Laudo Médico\" : \"Editar Laudo Existente\"}\r\n

\r\n {isNewLaudo ? (\r\n

\r\n Crie um novo laudo selecionando um paciente\r\n

\r\n ) : (\r\n

\r\n Paciente: {getPatientName(pacienteSelecionado) || getPatientName(laudo?.paciente) || getPatientName(laudo) || '-'} | CPF: {getReportPatientCpf(laudo) ?? laudo?.patient_cpf ?? '-'} | {laudo?.especialidade}\r\n

\r\n )}\r\n
\r\n \r\n
\r\n\r\n {/* Seleção de Paciente (apenas para novos laudos) */}\r\n {isNewLaudo && (\r\n
\r\n {!pacienteSelecionado ? (\r\n
\r\n \r\n \r\n
\r\n ) : (\r\n
\r\n
\r\n
{getPatientName(pacienteSelecionado)}
\r\n
\r\n {getPatientCpf(pacienteSelecionado) ? `CPF: ${getPatientCpf(pacienteSelecionado)} | ` : ''}\r\n {pacienteSelecionado?.birth_date ? `Nascimento: ${pacienteSelecionado.birth_date}` : (getPatientAge(pacienteSelecionado) ? `Idade: ${getPatientAge(pacienteSelecionado)} anos` : '')}\r\n {getPatientSex(pacienteSelecionado) ? ` | Sexo: ${getPatientSex(pacienteSelecionado)}` : ''}\r\n
\r\n
\r\n {!preSelectedPatient && (\r\n setPacienteSelecionado(null)}\r\n >\r\n Trocar Paciente\r\n \r\n )}\r\n
\r\n )}\r\n {/* Novos campos: Solicitante e Prazo */}\r\n
\r\n
\r\n \r\n {/* Mostrar o nome do usuário logado de forma estática (não editável) */}\r\n \r\n \r\n
\r\n
\r\n \r\n
\r\n setPrazoDate(e.target.value)} />\r\n setPrazoTime(e.target.value)} />\r\n
\r\n

Defina a data e hora do prazo (opcional).

\r\n
\r\n
\r\n
\r\n )}\r\n
\r\n\r\n {/* Tabs */}\r\n
\r\n {/* Informações tab removed - only Editor/Imagens/Campos/Pré-visualização remain */}\r\n setActiveTab(\"editor\")}\r\n className={`px-4 py-2 text-sm font-medium border-b-2 transition-colors ${\r\n activeTab === \"editor\"\r\n ? \"border-blue-500 text-blue-600\"\r\n : \"border-transparent text-gray-600 dark:text-muted-foreground dark:hover:text-foreground dark:hover:bg-blue-900\"\r\n }`}\r\n style={{\r\n backgroundColor: activeTab === \"editor\" ? undefined : \"transparent\"\r\n }}\r\n onMouseEnter={(e) => {\r\n if (activeTab !== \"editor\") {\r\n e.currentTarget.style.backgroundColor = \"transparent\";\r\n e.currentTarget.style.color = \"#4B5563\";\r\n }\r\n }}\r\n onMouseLeave={(e) => {\r\n if (activeTab !== \"editor\") {\r\n e.currentTarget.style.backgroundColor = \"transparent\";\r\n e.currentTarget.style.color = \"#4B5563\";\r\n }\r\n }}\r\n >\r\n \r\n Editor\r\n \r\n setActiveTab(\"imagens\")}\r\n className={`px-4 py-2 text-sm font-medium border-b-2 transition-colors ${\r\n activeTab === \"imagens\"\r\n ? \"border-blue-500 text-blue-600\"\r\n : \"border-transparent text-gray-600 dark:text-muted-foreground dark:hover:text-foreground dark:hover:bg-blue-900\"\r\n }`}\r\n style={{\r\n backgroundColor: activeTab === \"imagens\" ? undefined : \"transparent\"\r\n }}\r\n onMouseEnter={(e) => {\r\n if (activeTab !== \"imagens\") {\r\n e.currentTarget.style.backgroundColor = \"transparent\";\r\n e.currentTarget.style.color = \"#4B5563\";\r\n }\r\n }}\r\n onMouseLeave={(e) => {\r\n if (activeTab !== \"imagens\") {\r\n e.currentTarget.style.backgroundColor = \"transparent\";\r\n e.currentTarget.style.color = \"#4B5563\";\r\n }\r\n }}\r\n >\r\n \r\n Imagens ({imagens.length})\r\n \r\n setActiveTab(\"campos\")}\r\n className={`px-4 py-2 text-sm font-medium border-b-2 transition-colors ${\r\n activeTab === \"campos\"\r\n ? \"border-blue-500 text-blue-600\"\r\n : \"border-transparent text-gray-600 dark:text-muted-foreground dark:hover:text-foreground dark:hover:bg-blue-900\"\r\n }`}\r\n style={{\r\n backgroundColor: activeTab === \"campos\" ? undefined : \"transparent\"\r\n }}\r\n onMouseEnter={(e) => {\r\n if (activeTab !== \"campos\") {\r\n e.currentTarget.style.backgroundColor = \"transparent\";\r\n e.currentTarget.style.color = \"#4B5563\";\r\n }\r\n }}\r\n onMouseLeave={(e) => {\r\n if (activeTab !== \"campos\") {\r\n e.currentTarget.style.backgroundColor = \"transparent\";\r\n e.currentTarget.style.color = \"#4B5563\";\r\n }\r\n }}\r\n >\r\n \r\n Campos\r\n \r\n setShowPreview(!showPreview)}\r\n className={`px-4 py-2 text-sm font-medium border-b-2 transition-colors ${\r\n showPreview\r\n ? \"border-green-500 text-green-600\"\r\n : \"border-transparent text-gray-600 dark:text-muted-foreground dark:hover:text-foreground dark:hover:bg-blue-900\"\r\n }`}\r\n style={{\r\n backgroundColor: !showPreview ? \"transparent\" : undefined\r\n }}\r\n onMouseEnter={(e) => {\r\n if (!showPreview) {\r\n e.currentTarget.style.backgroundColor = \"transparent\";\r\n e.currentTarget.style.color = \"#4B5563\";\r\n }\r\n }}\r\n onMouseLeave={(e) => {\r\n if (!showPreview) {\r\n e.currentTarget.style.backgroundColor = \"transparent\";\r\n e.currentTarget.style.color = \"#4B5563\";\r\n }\r\n }}\r\n >\r\n \r\n {showPreview ? \"Ocultar\" : \"Pré-visualização\"}\r\n \r\n
\r\n\r\n {/* Content */}\r\n
\r\n {/* Left Panel */}\r\n
\r\n {/* 'Informações' section removed to keep editor-only experience */}\r\n\r\n {activeTab === \"editor\" && (\r\n
\r\n {/* Toolbar */}\r\n
\r\n
\r\n {/* Tamanho da fonte */}\r\n \r\n formatText('font-size', e.target.value)}\r\n className=\"w-14 border rounded px-1 py-0.5 text-xs mr-2\"\r\n title=\"Tamanho da fonte\"\r\n />\r\n {/* Família da fonte */}\r\n \r\n formatText('font-family', e.target.value)}\r\n className=\"border rounded px-1 py-0.5 text-xs mr-2 bg-white text-gray-900 dark:bg-gray-800 dark:text-white\"\r\n style={{ minWidth: 140, fontWeight: 500 }}\r\n title=\"Família da fonte\"\r\n >\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n {/* Cor da fonte */}\r\n \r\n formatText('font-color', e.target.value)}\r\n className=\"w-6 h-6 border rounded mr-2\"\r\n title=\"Cor da fonte\"\r\n />\r\n {/* Alinhamento */}\r\n \r\n \r\n \r\n \r\n {/* Listas */}\r\n \r\n \r\n {/* Recuo */}\r\n \r\n \r\n {/* Desfazer/Refazer */}\r\n \r\n
\r\n {templates.map((template, idx) => (\r\n insertTemplate(template)}\r\n >\r\n {template.substring(0, 30)}...\r\n \r\n ))}\r\n
\r\n
\r\n
\r\n\r\n {/* Editor */}\r\n
\r\n setContent(e.target.value)}\r\n placeholder=\"Digite o conteúdo do laudo aqui. Use ** para negrito, * para itálico, para sublinhado.\"\r\n className=\"h-full min-h-[400px] resize-none scrollbar-thin scrollbar-thumb-blue-400 scrollbar-track-blue-100\"\r\n style={{ maxHeight: 400, overflow: 'auto' }}\r\n />\r\n
\r\n
\r\n )}\r\n\r\n {activeTab === \"imagens\" && (\r\n
\r\n
\r\n \r\n \r\n
\r\n \r\n
\r\n {imagens.map((img) => (\r\n
\r\n {img.type.startsWith('image/') ? (\r\n {img.name}\r\n\r\n ) : (\r\n
\r\n \r\n
\r\n )}\r\n

{img.name}

\r\n setImagens(prev => prev.filter(i => i.id !== img.id))}\r\n >\r\n Remover\r\n \r\n
\r\n ))}\r\n
\r\n
\r\n )}\r\n\r\n {activeTab === \"campos\" && (\r\n
\r\n
\r\n \r\n setCampos(prev => ({ ...prev, cid: e.target.value }))}\r\n placeholder=\"Ex: M25.5, I10, etc.\"\r\n />\r\n
\r\n
\r\n \r\n setCampos(prev => ({ ...prev, exame: e.target.value }))}\r\n placeholder=\"Exame realizado\"\r\n />\r\n
\r\n
\r\n \r\n setCampos(prev => ({ ...prev, diagnostico: e.target.value }))}\r\n placeholder=\"Diagnóstico principal\"\r\n rows={3}\r\n />\r\n
\r\n
\r\n \r\n setCampos(prev => ({ ...prev, conclusao: e.target.value }))}\r\n placeholder=\"Conclusão do laudo\"\r\n rows={3}\r\n />\r\n
\r\n
\r\n
\r\n setCampos(prev => ({ ...prev, mostrarData: e.target.checked }))}\r\n />\r\n \r\n
\r\n
\r\n setCampos(prev => ({ ...prev, mostrarAssinatura: e.target.checked }))}\r\n />\r\n \r\n
\r\n
\r\n {/* Assinatura Digital removida dos campos */}\r\n
\r\n )}\r\n
\r\n\r\n {/* Preview Panel */}\r\n {showPreview && (\r\n
\r\n
\r\n

Pré-visualização do Laudo

\r\n
\r\n
\r\n
\r\n {/* Header do Laudo */}\r\n
\r\n

\r\n LAUDO MÉDICO {campos.especialidade ? `- ${campos.especialidade.toUpperCase()}` : ''}\r\n

\r\n {campos.exame && (\r\n

{campos.exame}

\r\n )}\r\n {campos.cid && (\r\n

CID: {campos.cid}

\r\n )}\r\n {campos.diagnostico && (\r\n

Diagnóstico: {campos.diagnostico}

\r\n )}\r\n {campos.conclusao && (\r\n

Conclusão: {campos.conclusao}

\r\n )}\r\n {campos.mostrarData && (\r\n

\r\n Data: {new Date().toLocaleDateString('pt-BR')}\r\n

\r\n )}\r\n
\r\n\r\n {/* Dados do Paciente */}\r\n {(isNewLaudo ? pacienteSelecionado : laudo?.paciente) && (\r\n
\r\n

Dados do Paciente:

\r\n {isNewLaudo && pacienteSelecionado ? (\r\n <>\r\n

Nome: {getPatientName(pacienteSelecionado)}

\r\n

ID: {getPatientId(pacienteSelecionado)}

\r\n

CPF: {getPatientCpf(pacienteSelecionado)}

\r\n

Idade: {getPatientAge(pacienteSelecionado)} anos

\r\n

Sexo: {getPatientSex(pacienteSelecionado)}

\r\n

CID: {campos.cid || '---'}

\r\n

Diagnóstico: {campos.diagnostico || '---'}

\r\n

Conclusão: {campos.conclusao || '---'}

\r\n \r\n ) : (\r\n <>\r\n

Nome: {getPatientName(laudo?.paciente)}

\r\n

ID: {getPatientId(laudo?.paciente)}

\r\n

CPF: {getPatientCpf(laudo?.paciente)}

\r\n

Idade: {getPatientAge(laudo?.paciente)} anos

\r\n

Sexo: {getPatientSex(laudo?.paciente)}

\r\n

CID: {campos.cid || laudo?.cid || '---'}

\r\n

Diagnóstico: {campos.diagnostico || laudo?.diagnostico || '---'}

\r\n

Conclusão: {campos.conclusao || laudo?.conclusao || '---'}

\r\n \r\n )}\r\n
\r\n )}\r\n\r\n {/* Conteúdo */}\r\n
\r\n
\r\n
\r\n\r\n {/* Imagens */}\r\n {imagens.length > 0 && (\r\n
\r\n

Imagens:

\r\n
\r\n {imagens.map((img) => (\r\n {img.name}\r\n\r\n ))}\r\n
\r\n
\r\n )}\r\n\r\n {/* Assinatura Digital em tempo real */}\r\n {campos.mostrarAssinatura && (\r\n
\r\n {assinaturaImg && assinaturaImg.length > 30 ? (\r\n \"Assinatura\r\n ) : (\r\n
Assine no campo ao lado para visualizar aqui.
\r\n )}\r\n
\r\n

{((profileData as any)?.nome || (profileData as any)?.nome_social) || user?.name || 'Squad-20'}

\r\n {(((profileData as any)?.crm) || ((user?.profile as any)?.crm)) ? (\r\n // Ensure we render a single 'CRM ' prefix followed by the raw number\r\n

CRM {(((profileData as any)?.crm) || (user?.profile as any)?.crm).toString().replace(/^(?:CRM\\s*)+/i, '').trim()}

\r\n ) : null}\r\n
\r\n )}\r\n
\r\n
\r\n
\r\n )}\r\n
\r\n\r\n {/* Footer */}\r\n
\r\n
\r\n
\r\n Este editor permite escrever relatórios de forma livre, com formatação de texto rica.\r\n
\r\n
\r\n \r\n {/* botão 'Salvar Rascunho' removido por não ser utilizado */}\r\n {\r\n try {\r\n const userId = user?.id || '00000000-0000-0000-0000-000000000001';\r\n // compor due_at a partir dos campos de data/hora, se fornecidos\r\n let composedDueAt = undefined;\r\n if (prazoDate) {\r\n // if time not provided, default to 23:59\r\n const t = prazoTime || '23:59';\r\n composedDueAt = new Date(`${prazoDate}T${t}:00`).toISOString();\r\n }\r\n\r\n const payload = {\r\n patient_id: pacienteSelecionado?.id,\r\n order_number: '',\r\n exam: campos.exame || '',\r\n diagnosis: campos.diagnostico || '',\r\n conclusion: campos.conclusao || '',\r\n cid_code: campos.cid || '',\r\n content_html: content,\r\n content_json: {},\r\n // status intentionally omitted — não enviar 'draft'\r\n requested_by: solicitanteId || userId,\r\n due_at: composedDueAt ?? new Date().toISOString(),\r\n hide_date: !campos.mostrarData,\r\n hide_signature: !campos.mostrarAssinatura,\r\n };\r\n\r\n if (isNewLaudo) {\r\n if (createNewReport) {\r\n const created = await createNewReport(payload as any);\r\n if (onSaved) onSaved(created);\r\n }\r\n } else {\r\n // Atualizar laudo existente: confirmar e enviar apenas diff\r\n const targetId = laudo?.id ?? laudo?.order_number ?? null;\r\n if (!targetId) throw new Error('ID do laudo ausente, não é possível atualizar');\r\n\r\n // Montar objeto contendo somente campos alterados\r\n const original = laudo || {};\r\n const candidate: any = {\r\n patient_id: payload.patient_id,\r\n order_number: payload.order_number,\r\n exam: payload.exam,\r\n diagnosis: payload.diagnosis,\r\n conclusion: payload.conclusion,\r\n cid_code: payload.cid_code,\r\n content_html: payload.content_html,\r\n // content_json intentionally left as full replacement if changed\r\n // status omitted on purpose\r\n requested_by: payload.requested_by,\r\n due_at: payload.due_at,\r\n hide_date: payload.hide_date,\r\n hide_signature: payload.hide_signature,\r\n };\r\n\r\n const diff: any = {};\r\n for (const k of Object.keys(candidate)) {\r\n const val = candidate[k];\r\n const origVal = original[k];\r\n // Considerar string/undefined equivalence\r\n if (typeof val === 'string') {\r\n if ((origVal ?? '') !== (val ?? '')) diff[k] = val;\r\n } else if (typeof val === 'boolean') {\r\n if (origVal !== val) diff[k] = val;\r\n } else if (val !== undefined && val !== null) {\r\n if (JSON.stringify(origVal) !== JSON.stringify(val)) diff[k] = val;\r\n }\r\n }\r\n\r\n if (Object.keys(diff).length === 0) {\r\n toast({ title: 'Nada a atualizar', description: 'Nenhuma alteração detectada.', variant: 'default' });\r\n } else {\r\n const ok = window.confirm('Deseja realmente atualizar este laudo? As alterações serão enviadas ao servidor.');\r\n if (!ok) return;\r\n if (updateExistingReport) {\r\n const updated = await updateExistingReport(String(targetId), diff as any);\r\n if (onSaved) onSaved(updated);\r\n }\r\n }\r\n }\r\n\r\n if (reloadReports) {\r\n await reloadReports();\r\n }\r\n\r\n toast({\r\n title: isNewLaudo ? 'Laudo criado com sucesso!' : 'Laudo atualizado com sucesso!',\r\n description: isNewLaudo ? 'O laudo foi liberado e salvo.' : 'As alterações foram salvas.',\r\n variant: 'default',\r\n });\r\n onClose();\r\n } catch (err) {\r\n toast({\r\n title: isNewLaudo ? 'Erro ao criar laudo' : 'Erro ao atualizar laudo',\r\n description: (err && typeof err === 'object' && 'message' in err) ? (err as any).message : String(err) || 'Tente novamente.',\r\n variant: 'destructive',\r\n });\r\n }\r\n }}\r\n >\r\n {isNewLaudo ? \"Liberar Laudo\" : \"Atualizar Laudo\"}\r\n \r\n
\r\n
\r\n
\r\n
\r\n
\r\n );\r\n }\r\n\r\n \r\n const renderComunicacaoSection = () => (\r\n
\r\n

Comunicação com o Paciente

\r\n
\r\n
\r\n
\r\n \r\n \r\n
\r\n
\r\n \r\n \r\n
\r\n
\r\n
\r\n
\r\n \r\n

03/09/2025

\r\n
\r\n
\r\n \r\n

Pendente

\r\n
\r\n
\r\n
\r\n \r\n
\r\n

\"Ok, obrigado pelo lembrete!\"

\r\n

03/09/2025 14:30

\r\n
\r\n
\r\n
\r\n \r\n
\r\n
\r\n
\r\n );\r\n\r\n \r\n const renderPerfilSection = () => (\r\n
\r\n
\r\n

Meu Perfil

\r\n {!isEditingProfile ? (\r\n \r\n ) : (\r\n
\r\n \r\n \r\n
\r\n )}\r\n
\r\n\r\n
\r\n {/* Informações Pessoais */}\r\n
\r\n

Informações Pessoais

\r\n \r\n
\r\n \r\n

{profileData.nome}

\r\n Este campo não pode ser alterado\r\n
\r\n\r\n
\r\n \r\n {isEditingProfile ? (\r\n handleProfileChange('email', e.target.value)}\r\n />\r\n ) : (\r\n

{profileData.email}

\r\n )}\r\n
\r\n\r\n
\r\n \r\n {isEditingProfile ? (\r\n handleProfileChange('telefone', e.target.value)}\r\n />\r\n ) : (\r\n

{profileData.telefone}

\r\n )}\r\n
\r\n\r\n
\r\n \r\n

{profileData.crm}

\r\n Este campo não pode ser alterado\r\n
\r\n\r\n
\r\n \r\n {isEditingProfile ? (\r\n handleProfileChange('especialidade', e.target.value)}\r\n />\r\n ) : (\r\n

{profileData.especialidade}

\r\n )}\r\n
\r\n
\r\n\r\n {/* Endereço e Contato */}\r\n
\r\n

Endereço e Contato

\r\n \r\n
\r\n \r\n {isEditingProfile ? (\r\n handleProfileChange('endereco', e.target.value)}\r\n />\r\n ) : (\r\n

{profileData.endereco}

\r\n )}\r\n
\r\n\r\n
\r\n \r\n {isEditingProfile ? (\r\n handleProfileChange('cidade', e.target.value)}\r\n />\r\n ) : (\r\n

{profileData.cidade}

\r\n )}\r\n
\r\n\r\n
\r\n \r\n {isEditingProfile ? (\r\n handleProfileChange('cep', e.target.value)}\r\n />\r\n ) : (\r\n

{profileData.cep}

\r\n )}\r\n
\r\n\r\n {/* Biografia removida: não é um campo no registro de médico */}\r\n
\r\n
\r\n\r\n {/* Foto do Perfil */}\r\n
\r\n

Foto do Perfil

\r\n
\r\n \r\n \r\n {profileData.nome.split(' ').map(n => n[0]).join('').toUpperCase()}\r\n \r\n \r\n {isEditingProfile && (\r\n
\r\n \r\n

\r\n Formatos aceitos: JPG, PNG (máx. 2MB)\r\n

\r\n
\r\n )}\r\n
\r\n
\r\n
\r\n );\r\n\r\n \r\n const renderActiveSection = () => {\r\n switch (activeSection) {\r\n case 'calendario':\r\n return renderCalendarioSection();\r\n case 'pacientes':\r\n return (\r\n
\r\n

Pacientes

\r\n
\r\n \r\n \r\n \r\n Nome\r\n CPF\r\n Idade\r\n \r\n \r\n \r\n {pacientes.map((paciente) => (\r\n \r\n {paciente.nome}\r\n {paciente.cpf}\r\n {getPatientAge(paciente) ? `${getPatientAge(paciente)} anos` : '-'}\r\n \r\n ))}\r\n \r\n
\r\n
\r\n
\r\n );\r\n case 'laudos':\r\n return renderLaudosSection();\r\n case 'comunicacao':\r\n return renderComunicacaoSection();\r\n case 'perfil':\r\n return renderPerfilSection();\r\n default:\r\n return renderCalendarioSection();\r\n }\r\n };\r\n\r\n return (\r\n \r\n
\r\n
\r\n
\r\n \r\n \r\n \r\n \r\n \r\n \r\n
\r\n

Conta do profissional

\r\n

{profileData.nome}

\r\n

{(profileData.crm ? `CRM: ${profileData.crm}` : '') + (profileData.especialidade ? ` • ${profileData.especialidade}` : '')}

\r\n {user?.email && (\r\n

Logado como: {user.email}

\r\n )}\r\n
\r\n
\r\n
\r\n \r\n \r\n \r\n
\r\n
\r\n \r\n
\r\n {}\r\n \r\n\r\n
\r\n
\r\n

Área do Profissional de Saúde

\r\n
\r\n

Bem-vindo à sua área exclusiva.

\r\n\r\n {renderActiveSection()}\r\n
\r\n
\r\n\r\n {}\r\n {showPopup && (\r\n
\r\n\r\n
\r\n\r\n {step === 1 && (\r\n <>\r\n

Selecionar Paciente

\r\n

\r\n Data: {selectedDate ? new Date(selectedDate + 'T00:00:00').toLocaleDateString('pt-BR') : 'Não selecionada'}\r\n

\r\n setNewEvent({ ...newEvent, title: value })}\r\n >\r\n \r\n \r\n \r\n \r\n {pacientes && pacientes.map((paciente) => (\r\n \r\n {paciente.nome} - {paciente.cpf}\r\n \r\n ))}\r\n \r\n \r\n
\r\n setShowPopup(false)}\r\n variant=\"outline\"\r\n className=\"flex-1 hover:bg-blue-50 dark:hover:bg-accent dark:hover:text-accent-foreground\"\r\n >\r\n Cancelar\r\n \r\n \r\n Próximo\r\n \r\n
\r\n \r\n )}\r\n\r\n {step === 2 && (\r\n <>\r\n

Tipo da Consulta

\r\n setNewEvent({ ...newEvent, type: value })}\r\n >\r\n \r\n \r\n \r\n \r\n {Object.keys(colorsByType).map((type) => (\r\n \r\n {type}\r\n \r\n ))}\r\n \r\n \r\n
\r\n setStep(1)}\r\n variant=\"outline\"\r\n className=\"flex-1\"\r\n >\r\n Voltar\r\n \r\n \r\n Próximo\r\n \r\n
\r\n \r\n )}\r\n\r\n {step === 3 && (\r\n <>\r\n

Horário da Consulta

\r\n setNewEvent({ ...newEvent, time: e.target.value })}\r\n className=\"mb-4\"\r\n />\r\n
\r\n setStep(2)}\r\n variant=\"outline\"\r\n className=\"flex-1\"\r\n >\r\n Voltar\r\n \r\n \r\n {editingEvent ? \"Salvar\" : \"Agendar\"}\r\n \r\n
\r\n \r\n )}\r\n
\r\n
\r\n )}\r\n\r\n {}\r\n {showActionModal && selectedEvent && (\r\n
\r\n
\r\n

\r\n Consulta de {selectedEvent.title}\r\n

\r\n

\r\n {selectedEvent.extendedProps.type} às {selectedEvent.extendedProps.time}\r\n

\r\n\r\n
\r\n \r\n \r\n Editar\r\n \r\n \r\n \r\n Excluir\r\n \r\n
\r\n\r\n setShowActionModal(false)}\r\n variant=\"outline\"\r\n className=\"w-full mt-2 hover:bg-blue-50 dark:hover:bg-accent dark:hover:text-accent-foreground\"\r\n >\r\n Cancelar\r\n \r\n
\r\n
\r\n )}\r\n
\r\n
\r\n );\r\n};\r\n\r\nconst getShortId = (id?: string) => {\r\n if (!id) return '-';\r\n try {\r\n return id.length > 10 ? `${id.slice(0, 8)}...` : id;\r\n } catch (e) {\r\n return id;\r\n }\r\n};\r\n\r\nexport default ProfissionalPage;","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\app\\resultados\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\app\\sobre\\page.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\ProtectedRoute.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\about-section.tsx","messages":[{"ruleId":"@next/next/no-img-element","severity":1,"message":"Using `` could result in slower LCP and higher bandwidth. Consider using `` from `next/image` or a custom image loader to automatically optimize images. This may incur additional usage or cost from your provider. See: https://nextjs.org/docs/messages/no-img-element","line":15,"column":15,"nodeType":"JSXOpeningElement","endLine":19,"endColumn":17}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { Card } from \"@/components/ui/card\"\r\nimport { Lightbulb, CheckCircle } from \"lucide-react\"\r\n\r\nexport function AboutSection() {\r\n const values = [\"Inovação\", \"Segurança\", \"Discrição\", \"Transparência\", \"Agilidade\"]\r\n\r\n return (\r\n
\r\n
\r\n
\r\n {}\r\n
\r\n {}\r\n
\r\n \r\n
\r\n\r\n {}\r\n \r\n
\r\n
\r\n \r\n
\r\n
\r\n

NOSSO OBJETIVO

\r\n

\r\n Nosso compromisso é garantir qualidade, segurança e sigilo em cada atendimento, unindo tecnologia à\r\n responsabilidade médica.\r\n

\r\n
\r\n
\r\n
\r\n
\r\n\r\n {}\r\n
\r\n
\r\n
\r\n SOBRE NÓS\r\n
\r\n

\r\n Experimente o futuro do gerenciamento dos seus atendimentos médicos\r\n

\r\n
\r\n\r\n
\r\n

\r\n Somos uma plataforma inovadora que conecta pacientes e médicos de forma prática, segura e humanizada.\r\n Nosso objetivo é simplificar o processo de emissão e acompanhamento de laudos médicos, oferecendo um\r\n ambiente online confiável e acessível.\r\n

\r\n

\r\n Aqui, os pacientes podem registrar suas informações de saúde e solicitar laudos de forma rápida,\r\n enquanto os médicos têm acesso a ferramentas que facilitam a análise, validação e emissão dos\r\n documentos.\r\n

\r\n
\r\n\r\n
\r\n

Nossos valores

\r\n
\r\n {values.map((value, index) => (\r\n
\r\n \r\n {value}\r\n
\r\n ))}\r\n
\r\n
\r\n
\r\n
\r\n
\r\n
\r\n )\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\admin\\AssignmentForm.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\agenda\\FooterAgenda.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\agenda\\HeaderAgenda.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\agendamento\\AgendaCalendar.tsx","messages":[{"ruleId":"react/no-unescaped-entities","severity":2,"message":"`'` can be escaped with `'`, `‘`, `'`, `’`.","line":185,"column":22,"nodeType":"JSXText","messageId":"unescapedEntityAlts","suggestions":[{"messageId":"replaceWithAlt","data":{"alt":"'"},"fix":{"range":[6609,6688],"text":"\r\n Atalhos: 'C' para calendário, 'F' para fila de espera\r\n "},"desc":"Replace with `'`."},{"messageId":"replaceWithAlt","data":{"alt":"‘"},"fix":{"range":[6609,6688],"text":"\r\n Atalhos: ‘C' para calendário, 'F' para fila de espera\r\n "},"desc":"Replace with `‘`."},{"messageId":"replaceWithAlt","data":{"alt":"'"},"fix":{"range":[6609,6688],"text":"\r\n Atalhos: 'C' para calendário, 'F' para fila de espera\r\n "},"desc":"Replace with `'`."},{"messageId":"replaceWithAlt","data":{"alt":"’"},"fix":{"range":[6609,6688],"text":"\r\n Atalhos: ’C' para calendário, 'F' para fila de espera\r\n "},"desc":"Replace with `’`."}]},{"ruleId":"react/no-unescaped-entities","severity":2,"message":"`'` can be escaped with `'`, `‘`, `'`, `’`.","line":185,"column":24,"nodeType":"JSXText","messageId":"unescapedEntityAlts","suggestions":[{"messageId":"replaceWithAlt","data":{"alt":"'"},"fix":{"range":[6609,6688],"text":"\r\n Atalhos: 'C' para calendário, 'F' para fila de espera\r\n "},"desc":"Replace with `'`."},{"messageId":"replaceWithAlt","data":{"alt":"‘"},"fix":{"range":[6609,6688],"text":"\r\n Atalhos: 'C‘ para calendário, 'F' para fila de espera\r\n "},"desc":"Replace with `‘`."},{"messageId":"replaceWithAlt","data":{"alt":"'"},"fix":{"range":[6609,6688],"text":"\r\n Atalhos: 'C' para calendário, 'F' para fila de espera\r\n "},"desc":"Replace with `'`."},{"messageId":"replaceWithAlt","data":{"alt":"’"},"fix":{"range":[6609,6688],"text":"\r\n Atalhos: 'C’ para calendário, 'F' para fila de espera\r\n "},"desc":"Replace with `’`."}]},{"ruleId":"react/no-unescaped-entities","severity":2,"message":"`'` can be escaped with `'`, `‘`, `'`, `’`.","line":185,"column":43,"nodeType":"JSXText","messageId":"unescapedEntityAlts","suggestions":[{"messageId":"replaceWithAlt","data":{"alt":"'"},"fix":{"range":[6609,6688],"text":"\r\n Atalhos: 'C' para calendário, 'F' para fila de espera\r\n "},"desc":"Replace with `'`."},{"messageId":"replaceWithAlt","data":{"alt":"‘"},"fix":{"range":[6609,6688],"text":"\r\n Atalhos: 'C' para calendário, ‘F' para fila de espera\r\n "},"desc":"Replace with `‘`."},{"messageId":"replaceWithAlt","data":{"alt":"'"},"fix":{"range":[6609,6688],"text":"\r\n Atalhos: 'C' para calendário, 'F' para fila de espera\r\n "},"desc":"Replace with `'`."},{"messageId":"replaceWithAlt","data":{"alt":"’"},"fix":{"range":[6609,6688],"text":"\r\n Atalhos: 'C' para calendário, ’F' para fila de espera\r\n "},"desc":"Replace with `’`."}]},{"ruleId":"react/no-unescaped-entities","severity":2,"message":"`'` can be escaped with `'`, `‘`, `'`, `’`.","line":185,"column":45,"nodeType":"JSXText","messageId":"unescapedEntityAlts","suggestions":[{"messageId":"replaceWithAlt","data":{"alt":"'"},"fix":{"range":[6609,6688],"text":"\r\n Atalhos: 'C' para calendário, 'F' para fila de espera\r\n "},"desc":"Replace with `'`."},{"messageId":"replaceWithAlt","data":{"alt":"‘"},"fix":{"range":[6609,6688],"text":"\r\n Atalhos: 'C' para calendário, 'F‘ para fila de espera\r\n "},"desc":"Replace with `‘`."},{"messageId":"replaceWithAlt","data":{"alt":"'"},"fix":{"range":[6609,6688],"text":"\r\n Atalhos: 'C' para calendário, 'F' para fila de espera\r\n "},"desc":"Replace with `'`."},{"messageId":"replaceWithAlt","data":{"alt":"’"},"fix":{"range":[6609,6688],"text":"\r\n Atalhos: 'C' para calendário, 'F’ para fila de espera\r\n "},"desc":"Replace with `’`."}]}],"suppressedMessages":[],"errorCount":4,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"\r\n'use client';\r\n\r\nimport { useState } from 'react';\r\nimport { ChevronLeft, ChevronRight, Plus, Clock, User, Calendar as CalendarIcon } from 'lucide-react';\r\n\r\ninterface Appointment {\r\n id: string;\r\n patient: string;\r\n time: string;\r\n duration: number;\r\n type: 'consulta' | 'exame' | 'retorno';\r\n status: 'confirmed' | 'pending' | 'absent';\r\n professional: string;\r\n notes: string;\r\n}\r\n\r\ninterface Professional {\r\n id: string;\r\n name: string;\r\n specialty: string;\r\n}\r\n\r\ninterface AgendaCalendarProps {\r\n professionals: Professional[];\r\n appointments: Appointment[];\r\n onAddAppointment: () => void;\r\n onEditAppointment: (appointment: Appointment) => void;\r\n}\r\n\r\nexport default function AgendaCalendar({ \r\n professionals, \r\n appointments, \r\n onAddAppointment, \r\n onEditAppointment \r\n}: AgendaCalendarProps) {\r\n const [view, setView] = useState<'day' | 'week' | 'month'>('week');\r\n const [selectedProfessional, setSelectedProfessional] = useState('all');\r\n const [currentDate, setCurrentDate] = useState(new Date());\r\n\r\n const timeSlots = Array.from({ length: 11 }, (_, i) => {\r\n const hour = i + 8; // Das 8h às 18h\r\n return [`${hour.toString().padStart(2, '0')}:00`, `${hour.toString().padStart(2, '0')}:30`];\r\n }).flat();\r\n\r\n const getStatusColor = (status: string) => {\r\n switch (status) {\r\n case 'confirmed': return 'bg-green-100 border-green-500 text-green-800';\r\n case 'pending': return 'bg-yellow-100 border-yellow-500 text-yellow-800';\r\n case 'absent': return 'bg-red-100 border-red-500 text-red-800';\r\n default: return 'bg-gray-100 border-gray-500 text-gray-800';\r\n }\r\n };\r\n\r\n const getTypeIcon = (type: string) => {\r\n switch (type) {\r\n case 'consulta': return '🩺';\r\n case 'exame': return '📋';\r\n case 'retorno': return '↩️';\r\n default: return '📅';\r\n }\r\n };\r\n\r\n const formatDate = (date: Date) => {\r\n return date.toLocaleDateString('pt-BR', { \r\n weekday: 'long', \r\n day: 'numeric', \r\n month: 'long', \r\n year: 'numeric' \r\n });\r\n };\r\n\r\n const navigateDate = (direction: 'prev' | 'next') => {\r\n const newDate = new Date(currentDate);\r\n if (view === 'day') {\r\n newDate.setDate(newDate.getDate() + (direction === 'next' ? 1 : -1));\r\n } else if (view === 'week') {\r\n newDate.setDate(newDate.getDate() + (direction === 'next' ? 7 : -7));\r\n } else {\r\n newDate.setMonth(newDate.getMonth() + (direction === 'next' ? 1 : -1));\r\n }\r\n setCurrentDate(newDate);\r\n };\r\n\r\n const goToToday = () => {\r\n setCurrentDate(new Date());\r\n };\r\n\r\n \r\n const filteredAppointments = selectedProfessional === 'all' \r\n ? appointments \r\n : appointments.filter(app => app.professional === selectedProfessional);\r\n\r\n return (\r\n
\r\n
\r\n
\r\n

Agenda

\r\n \r\n
\r\n \r\n \r\n
\r\n setView('day')}\r\n className={`px-3 py-2 text-sm font-medium rounded-l-md ${\r\n view === 'day' \r\n ? 'bg-blue-100 text-blue-700 border border-blue-300' \r\n : 'bg-white text-gray-700 border border-gray-300'\r\n }`}\r\n >\r\n Dia\r\n \r\n setView('week')}\r\n className={`px-3 py-2 text-sm font-medium -ml-px ${\r\n view === 'week' \r\n ? 'bg-blue-100 text-blue-700 border border-blue-300' \r\n : 'bg-white text-gray-700 border border-gray-300'\r\n }`}\r\n >\r\n Semana\r\n \r\n setView('month')}\r\n className={`px-3 py-2 text-sm font-medium -ml-px rounded-r-md ${\r\n view === 'month' \r\n ? 'bg-blue-100 text-blue-700 border border-blue-300' \r\n : 'bg-white text-gray-700 border border-gray-300'\r\n }`}\r\n >\r\n Mês\r\n \r\n
\r\n \r\n \r\n
\r\n
\r\n
\r\n\r\n
\r\n
\r\n
\r\n \r\n

\r\n {formatDate(currentDate)}\r\n

\r\n \r\n \r\n
\r\n
\r\n Atalhos: 'C' para calendário, 'F' para fila de espera\r\n
\r\n
\r\n
\r\n\r\n {}\r\n {view !== 'month' && (\r\n
\r\n
\r\n
\r\n
\r\n
\r\n Hora\r\n
\r\n {timeSlots.map(time => (\r\n
\r\n {time}\r\n
\r\n ))}\r\n
\r\n \r\n
\r\n
\r\n {currentDate.toLocaleDateString('pt-BR', { weekday: 'long' })}\r\n
\r\n
\r\n {timeSlots.map(time => (\r\n
\r\n ))}\r\n \r\n {filteredAppointments.map(app => {\r\n const [date, timeStr] = app.time.split('T');\r\n const [hours, minutes] = timeStr.split(':');\r\n const hour = parseInt(hours);\r\n const minute = parseInt(minutes);\r\n \r\n return (\r\n onEditAppointment(app)}\r\n >\r\n
\r\n
\r\n
\r\n \r\n {app.patient}\r\n
\r\n
\r\n \r\n {hours}:{minutes} - {app.type} {getTypeIcon(app.type)}\r\n
\r\n
\r\n {professionals.find(p => p.id === app.professional)?.name}\r\n
\r\n
\r\n
\r\n {app.status === 'confirmed' ? 'confirmado' : app.status === 'pending' ? 'pendente' : 'ausente'}\r\n
\r\n
\r\n
\r\n );\r\n })}\r\n
\r\n
\r\n
\r\n
\r\n
\r\n )}\r\n\r\n {}\r\n {view === 'month' && (\r\n
\r\n
\r\n {filteredAppointments.map(app => {\r\n const [date, timeStr] = app.time.split('T');\r\n const [hours, minutes] = timeStr.split(':');\r\n \r\n return (\r\n
\r\n
\r\n
\r\n \r\n {app.patient}\r\n
\r\n
\r\n \r\n {hours}:{minutes} - {app.type} {getTypeIcon(app.type)}\r\n
\r\n
\r\n {professionals.find(p => p.id === app.professional)?.name}\r\n
\r\n
\r\n {app.notes && (\r\n
\r\n {app.notes}\r\n
\r\n )}\r\n
\r\n \r\n
\r\n
\r\n );\r\n })}\r\n
\r\n
\r\n )}\r\n
\r\n );\r\n}","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\agendamento\\AppointmentModal.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\agendamento\\ListaEspera.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\agendamento\\index.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\credentials-dialog.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\dashboard\\header.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\dashboard\\sidebar.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\footer.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\forms\\calendar-registration-form.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\forms\\doctor-registration-form.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\forms\\patient-registration-form.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\header.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\hero-section.tsx","messages":[{"ruleId":"@next/next/no-img-element","severity":1,"message":"Using `` could result in slower LCP and higher bandwidth. Consider using `` from `next/image` or a custom image loader to automatically optimize images. This may incur additional usage or cost from your provider. See: https://nextjs.org/docs/messages/no-img-element","line":49,"column":15,"nodeType":"JSXOpeningElement","endLine":53,"endColumn":17}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"import { Button } from \"@/components/ui/button\"\r\nimport { Shield, Clock, Users } from \"lucide-react\"\r\nimport Link from \"next/link\"\r\n\r\nexport function HeroSection() {\r\n return (\r\n
\r\n
\r\n
\r\n {}\r\n
\r\n
\r\n
\r\n APROXIMANDO MÉDICOS E PACIENTES\r\n
\r\n

\r\n Segurança, Confiabilidade e{\" \"}\r\n Rapidez\r\n

\r\n
\r\n

Experimente o futuro dos agendamentos.

\r\n

Encontre profissionais capacitados e marque já sua consulta.

\r\n
\r\n
\r\n\r\n {}\r\n
\r\n \r\n \r\n Sou Profissional de Saúde\r\n \r\n
\r\n
\r\n\r\n {}\r\n
\r\n
\r\n \r\n
\r\n
\r\n
\r\n\r\n {}\r\n
\r\n
\r\n
\r\n \r\n
\r\n
\r\n

Laudos digitais e padronizados

\r\n
\r\n
\r\n\r\n
\r\n
\r\n \r\n
\r\n
\r\n

Notificações automáticas ao paciente

\r\n
\r\n
\r\n\r\n
\r\n
\r\n \r\n
\r\n
\r\n

LGPD: controle de acesso e consentimento

\r\n
\r\n
\r\n
\r\n
\r\n
\r\n )\r\n}\r\n","usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\simple-theme-toggle.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\theme-provider.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\theme-toggle.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\ui\\accordion.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\ui\\alert-dialog.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\ui\\alert.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\ui\\aspect-ratio.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\ui\\avatar.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\ui\\badge.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\ui\\breadcrumb.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\ui\\button.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\ui\\calendar.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\ui\\card.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\ui\\carousel.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\ui\\chart.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\ui\\checkbox.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\ui\\collapsible.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\ui\\command.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\ui\\context-menu.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\ui\\dialog.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\ui\\drawer.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\ui\\dropdown-menu.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\ui\\form.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\ui\\hover-card.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\ui\\input-otp.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\ui\\input.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\ui\\label.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\ui\\menubar.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\ui\\navigation-menu.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\ui\\pagination.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\ui\\popover.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\ui\\progress.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\ui\\radio-group.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\ui\\resizable.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\ui\\scroll-area.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\ui\\select.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\ui\\separator.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\ui\\sheet.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\ui\\sidebar.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\ui\\skeleton.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\ui\\slider.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\ui\\sonner.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\ui\\switch.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\ui\\table.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\ui\\tabs.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\ui\\textarea.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\ui\\toast.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\ui\\toaster.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\ui\\toggle-group.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\ui\\toggle.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\ui\\tooltip.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\ui\\use-mobile.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\components\\ui\\use-toast.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\eslint.config.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\hooks\\UseAgenda.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\hooks\\use-force-default-theme.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\hooks\\use-mobile.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\hooks\\use-toast.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\hooks\\useAuth.tsx","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\hooks\\useReports.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\lib\\api.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\lib\\assignment.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\lib\\auth.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\lib\\config.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\lib\\debug-utils.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\lib\\env-config.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\lib\\http.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\lib\\jwt.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\lib\\mocks\\appointment-mocks.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\lib\\reports.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\lib\\utils.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\next.config.mjs","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\postcss.config.mjs","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\public\\forward-client-logs.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\src\\app\\api\\assign-role\\route.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\types\\auth.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\types\\react-signature-canvas.d.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"C:\\Users\\Gabri\\Desktop\\riseup-squad20\\susconecta\\types\\report-types.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]}]