From 498d6c80c10a1092e0ec256f2a8aff8e0a562708 Mon Sep 17 00:00:00 2001 From: M-Gabrielly Date: Tue, 7 Oct 2025 05:26:13 -0300 Subject: [PATCH] feature(refactor): reorganize project structure and routes --- eslint.config.js | 67 +- package-lock.json | 49 +- package.json | 2 + pnpm-lock.yaml | 6 + .../calendar/index.css | 0 .../calendar/page.tsx | 2 - .../consultas/page.tsx | 0 .../dashboard/page.tsx | 0 .../dashboard/relatorios/page.tsx | 0 .../doutores/page.tsx | 0 src/app/{ => (admin)}/financeiro/page.tsx | 0 src/app/{(main-routes) => (admin)}/layout.tsx | 8 +- .../pacientes/loading.tsx | 0 .../pacientes/page.tsx | 0 src/app/{ => (admin)}/procedimento/page.tsx | 0 src/app/{ => (marketing)}/sobre/page.tsx | 4 +- src/app/(paciente)/layout.tsx | 11 + src/app/(paciente)/paciente/page.tsx | 92 + src/app/(profissional)/layout.tsx | 11 + .../{profissional => (profissional)}/page.tsx | 5 +- src/app/(profissional)/profissional/page.tsx | 3514 +++++++++++++++++ src/app/agenda/page.tsx | 1 + src/app/layout.tsx | 2 +- src/app/login-admin/page.tsx | 124 - src/app/login-paciente/page.tsx | 122 - src/app/login/page.tsx | 142 +- src/app/paciente/page.tsx | 95 - src/app/page.tsx | 4 +- .../forms/doctor-registration-form.tsx | 1 - .../forms/patient-registration-form.tsx | 29 +- src/components/hero-section.tsx | 12 +- .../{ => layout}/ProtectedRoute.tsx | 0 .../SimpleThemeToggle.tsx} | 0 .../ThemeProvider.tsx} | 0 .../ThemeToggle.tsx} | 0 .../header.tsx => layout/app/Header.tsx} | 5 +- .../sidebar.tsx => layout/app/Sidebar.tsx} | 32 +- .../marketing/Footer.tsx} | 3 - .../marketing/Header.tsx} | 59 +- src/features/autenticacao/api/index.ts | 179 + src/features/pacientes/api/index.ts | 141 +- src/features/profissionais/api/index.ts | 168 + src/features/profissionais/types/index.ts | 9 +- src/lib/api.ts | 590 --- src/lib/http.ts | 17 +- src/lib/utils.ts | 25 + src/types/auth.ts | 6 +- tsconfig.json | 9 +- 48 files changed, 4370 insertions(+), 1176 deletions(-) rename src/app/{(main-routes) => (admin)}/calendar/index.css (100%) rename src/app/{(main-routes) => (admin)}/calendar/page.tsx (98%) rename src/app/{(main-routes) => (admin)}/consultas/page.tsx (100%) rename src/app/{(main-routes) => (admin)}/dashboard/page.tsx (100%) rename src/app/{(main-routes) => (admin)}/dashboard/relatorios/page.tsx (100%) rename src/app/{(main-routes) => (admin)}/doutores/page.tsx (100%) rename src/app/{ => (admin)}/financeiro/page.tsx (100%) rename src/app/{(main-routes) => (admin)}/layout.tsx (68%) rename src/app/{(main-routes) => (admin)}/pacientes/loading.tsx (100%) rename src/app/{(main-routes) => (admin)}/pacientes/page.tsx (100%) rename src/app/{ => (admin)}/procedimento/page.tsx (100%) rename src/app/{ => (marketing)}/sobre/page.tsx (69%) create mode 100644 src/app/(paciente)/layout.tsx create mode 100644 src/app/(paciente)/paciente/page.tsx create mode 100644 src/app/(profissional)/layout.tsx rename src/app/{profissional => (profissional)}/page.tsx (99%) create mode 100644 src/app/(profissional)/profissional/page.tsx delete mode 100644 src/app/login-admin/page.tsx delete mode 100644 src/app/login-paciente/page.tsx delete mode 100644 src/app/paciente/page.tsx rename src/components/{ => layout}/ProtectedRoute.tsx (100%) rename src/components/{simple-theme-toggle.tsx => layout/SimpleThemeToggle.tsx} (100%) rename src/components/{theme-provider.tsx => layout/ThemeProvider.tsx} (100%) rename src/components/{theme-toggle.tsx => layout/ThemeToggle.tsx} (100%) rename src/components/{dashboard/header.tsx => layout/app/Header.tsx} (96%) rename src/components/{dashboard/sidebar.tsx => layout/app/Sidebar.tsx} (75%) rename src/components/{footer.tsx => layout/marketing/Footer.tsx} (97%) rename src/components/{header.tsx => layout/marketing/Header.tsx} (58%) create mode 100644 src/features/autenticacao/api/index.ts create mode 100644 src/features/profissionais/api/index.ts delete mode 100644 src/lib/api.ts diff --git a/eslint.config.js b/eslint.config.js index 90b0b03..18c13e3 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,19 +1,19 @@ // eslint.config.js -import globals from "globals"; -import tseslint from "typescript-eslint"; -import eslint from "@eslint/js"; -import nextPlugin from "@next/eslint-plugin-next"; -import unicornPlugin from "eslint-plugin-unicorn"; -import prettierConfig from "eslint-config-prettier"; +import globals from 'globals'; +import tseslint from 'typescript-eslint'; +import eslint from '@eslint/js'; +import nextPlugin from '@next/eslint-plugin-next'; +import unicornPlugin from 'eslint-plugin-unicorn'; +import prettierConfig from 'eslint-config-prettier'; export default [ eslint.configs.recommended, ...tseslint.configs.recommended, { - files: ["**/*.{js,mjs,cjs,ts,jsx,tsx}"], + files: ['**/*.{js,mjs,cjs,ts,jsx,tsx}'], plugins: { - "@next/next": nextPlugin, - "unicorn": unicornPlugin, + '@next/next': nextPlugin, + unicorn: unicornPlugin, }, languageOptions: { globals: { @@ -22,14 +22,53 @@ export default [ }, parser: tseslint.parser, parserOptions: { - project: "./tsconfig.json", + project: './tsconfig.json', }, }, rules: { ...nextPlugin.configs.recommended.rules, - ...nextPlugin.configs["core-web-vitals"].rules, - ...unicornPlugin.configs.recommended.rules, - } + ...nextPlugin.configs['core-web-vitals'].rules, + 'unicorn/prevent-abbreviations': 'off', + 'unicorn/no-null': 'off', + 'unicorn/prefer-string-replace-all': 'off', + 'unicorn/prefer-string-slice': 'off', + 'unicorn/prefer-number-properties': 'off', + 'unicorn/no-array-reduce': 'off', + 'unicorn/no-array-for-each': 'off', + 'unicorn/prefer-global-this': 'off', + 'unicorn/no-useless-undefined': 'off', + 'unicorn/explicit-length-check': 'off', + 'unicorn/consistent-existence-index-check': 'off', + 'unicorn/prefer-ternary': 'off', + 'unicorn/numeric-separators-style': 'off', + 'unicorn/filename-case': [ + 'error', + { + cases: { + camelCase: true, + pascalCase: true, + kebabCase: true, + }, + }, + ], + 'unicorn/prefer-add-event-listener': 'off', + 'unicorn/prefer-spread': 'off', + 'unicorn/consistent-function-scoping': 'off', + 'unicorn/no-document-cookie': 'off', + 'unicorn/no-negated-condition': 'off', + 'unicorn/prefer-code-point': 'off', + 'unicorn/prefer-single-call': 'off', + '@typescript-eslint/no-unused-vars': [ + 'warn', + { + argsIgnorePattern: '^_', + varsIgnorePattern: '^_', + caughtErrorsIgnorePattern: '^_', + }, + ], + '@typescript-eslint/no-explicit-any': 'off', + 'prefer-const': 'off', + }, }, prettierConfig, -]; \ No newline at end of file +]; diff --git a/package-lock.json b/package-lock.json index bb4783c..ce0bf32 100644 --- a/package-lock.json +++ b/package-lock.json @@ -69,6 +69,7 @@ }, "devDependencies": { "@eslint/js": "^9.36.0", + "@next/eslint-plugin-next": "^15.5.4", "@tailwindcss/postcss": "^4.1.9", "@types/node": "^22", "@types/react": "^18", @@ -79,6 +80,7 @@ "eslint-config-next": "^15.5.4", "eslint-config-prettier": "^10.1.8", "eslint-plugin-unicorn": "^61.0.2", + "globals": "^16.4.0", "next": "^15.5.4", "postcss": "^8.5", "tailwindcss": "^4.1.9", @@ -284,6 +286,19 @@ "concat-map": "0.0.1" } }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@eslint/eslintrc/node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -5399,19 +5414,6 @@ "eslint": ">=9.29.0" } }, - "node_modules/eslint-plugin-unicorn/node_modules/globals": { - "version": "16.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz", - "integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/eslint-scope": { "version": "8.4.0", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", @@ -5917,9 +5919,9 @@ } }, "node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "version": "16.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-16.4.0.tgz", + "integrity": "sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==", "dev": true, "license": "MIT", "engines": { @@ -9402,21 +9404,6 @@ "funding": { "url": "https://github.com/sponsors/colinhacks" } - }, - "node_modules/@next/swc-win32-ia32-msvc": { - "version": "14.2.16", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.16.tgz", - "integrity": "sha512-jhPl3nN0oKEshJBNDAo0etGMzv0j3q3VYorTSFqH1o3rwv1MQRdor27u1zhkgsHPNeY1jxcgyx1ZsCkDD1IHgg==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } } } } diff --git a/package.json b/package.json index d0eacfd..f6710c0 100644 --- a/package.json +++ b/package.json @@ -71,6 +71,7 @@ }, "devDependencies": { "@eslint/js": "^9.36.0", + "@next/eslint-plugin-next": "^15.5.4", "@tailwindcss/postcss": "^4.1.9", "@types/node": "^22", "@types/react": "^18", @@ -81,6 +82,7 @@ "eslint-config-next": "^15.5.4", "eslint-config-prettier": "^10.1.8", "eslint-plugin-unicorn": "^61.0.2", + "globals": "^16.4.0", "next": "^15.5.4", "postcss": "^8.5", "tailwindcss": "^4.1.9", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6b1edf6..6393a05 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -186,6 +186,9 @@ importers: '@eslint/js': specifier: ^9.36.0 version: 9.36.0 + '@next/eslint-plugin-next': + specifier: ^15.5.4 + version: 15.5.4 '@tailwindcss/postcss': specifier: ^4.1.9 version: 4.1.13 @@ -216,6 +219,9 @@ importers: eslint-plugin-unicorn: specifier: ^61.0.2 version: 61.0.2(eslint@9.36.0(jiti@2.5.1)) + globals: + specifier: ^16.4.0 + version: 16.4.0 next: specifier: ^15.5.4 version: 15.5.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) diff --git a/src/app/(main-routes)/calendar/index.css b/src/app/(admin)/calendar/index.css similarity index 100% rename from src/app/(main-routes)/calendar/index.css rename to src/app/(admin)/calendar/index.css diff --git a/src/app/(main-routes)/calendar/page.tsx b/src/app/(admin)/calendar/page.tsx similarity index 98% rename from src/app/(main-routes)/calendar/page.tsx rename to src/app/(admin)/calendar/page.tsx index b5a7947..f862ece 100644 --- a/src/app/(main-routes)/calendar/page.tsx +++ b/src/app/(admin)/calendar/page.tsx @@ -8,8 +8,6 @@ import dayGridPlugin from "@fullcalendar/daygrid"; import interactionPlugin from "@fullcalendar/interaction"; import timeGridPlugin from "@fullcalendar/timegrid"; import { EventInput } from "@fullcalendar/core/index.js"; -import { Sidebar } from "@/components/dashboard/sidebar"; -import { PagesHeader } from "@/components/dashboard/header"; import { Button } from "@/components/ui/button"; import { mockAppointments, diff --git a/src/app/(main-routes)/consultas/page.tsx b/src/app/(admin)/consultas/page.tsx similarity index 100% rename from src/app/(main-routes)/consultas/page.tsx rename to src/app/(admin)/consultas/page.tsx diff --git a/src/app/(main-routes)/dashboard/page.tsx b/src/app/(admin)/dashboard/page.tsx similarity index 100% rename from src/app/(main-routes)/dashboard/page.tsx rename to src/app/(admin)/dashboard/page.tsx diff --git a/src/app/(main-routes)/dashboard/relatorios/page.tsx b/src/app/(admin)/dashboard/relatorios/page.tsx similarity index 100% rename from src/app/(main-routes)/dashboard/relatorios/page.tsx rename to src/app/(admin)/dashboard/relatorios/page.tsx diff --git a/src/app/(main-routes)/doutores/page.tsx b/src/app/(admin)/doutores/page.tsx similarity index 100% rename from src/app/(main-routes)/doutores/page.tsx rename to src/app/(admin)/doutores/page.tsx diff --git a/src/app/financeiro/page.tsx b/src/app/(admin)/financeiro/page.tsx similarity index 100% rename from src/app/financeiro/page.tsx rename to src/app/(admin)/financeiro/page.tsx diff --git a/src/app/(main-routes)/layout.tsx b/src/app/(admin)/layout.tsx similarity index 68% rename from src/app/(main-routes)/layout.tsx rename to src/app/(admin)/layout.tsx index 6269b21..2a72c53 100644 --- a/src/app/(main-routes)/layout.tsx +++ b/src/app/(admin)/layout.tsx @@ -1,8 +1,8 @@ import type React from "react"; -import ProtectedRoute from "@/components/ProtectedRoute"; -import { Sidebar } from "@/components/dashboard/sidebar"; -import { SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar"; -import { PagesHeader } from "@/components/dashboard/header"; +import ProtectedRoute from "@/components/layout/ProtectedRoute"; +import { Sidebar } from "@/components/layout/app/Sidebar"; +import { SidebarProvider } from "@/components/ui/sidebar"; +import { PagesHeader } from "@/components/layout/app/Header"; export default function MainRoutesLayout({ children, diff --git a/src/app/(main-routes)/pacientes/loading.tsx b/src/app/(admin)/pacientes/loading.tsx similarity index 100% rename from src/app/(main-routes)/pacientes/loading.tsx rename to src/app/(admin)/pacientes/loading.tsx diff --git a/src/app/(main-routes)/pacientes/page.tsx b/src/app/(admin)/pacientes/page.tsx similarity index 100% rename from src/app/(main-routes)/pacientes/page.tsx rename to src/app/(admin)/pacientes/page.tsx diff --git a/src/app/procedimento/page.tsx b/src/app/(admin)/procedimento/page.tsx similarity index 100% rename from src/app/procedimento/page.tsx rename to src/app/(admin)/procedimento/page.tsx diff --git a/src/app/sobre/page.tsx b/src/app/(marketing)/sobre/page.tsx similarity index 69% rename from src/app/sobre/page.tsx rename to src/app/(marketing)/sobre/page.tsx index 4ce80eb..e163efa 100644 --- a/src/app/sobre/page.tsx +++ b/src/app/(marketing)/sobre/page.tsx @@ -1,6 +1,6 @@ -import { Header } from "@/components/header" +import { Header } from "@/components/layout/marketing/Header" import { AboutSection } from "@/components/about-section" -import { Footer } from "@/components/footer" +import { Footer } from "@/components/layout/marketing/Footer" export default function AboutPage() { return ( diff --git a/src/app/(paciente)/layout.tsx b/src/app/(paciente)/layout.tsx new file mode 100644 index 0000000..05a5e0a --- /dev/null +++ b/src/app/(paciente)/layout.tsx @@ -0,0 +1,11 @@ +'use client' +import type { ReactNode } from 'react' +import ProtectedRoute from '@/components/layout/ProtectedRoute' + +export default function PacienteLayout({ children }: { children: ReactNode }) { + return ( + + {children} + + ) +} diff --git a/src/app/(paciente)/paciente/page.tsx b/src/app/(paciente)/paciente/page.tsx new file mode 100644 index 0000000..7cd6aff --- /dev/null +++ b/src/app/(paciente)/paciente/page.tsx @@ -0,0 +1,92 @@ +'use client' +import { useAuth } from '@/hooks/useAuth' +import { Button } from '@/components/ui/button' +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' +import { User, LogOut, Home } from 'lucide-react' +import Link from 'next/link' + +export default function PacientePage() { + const { logout, user } = useAuth() + + const handleLogout = async () => { + console.log('[PACIENTE] Iniciando logout...') + await logout() + } + + return ( +
+ + +
+ +
+ + Portal do Paciente + +

+ Bem-vindo ao seu espaço pessoal +

+
+ + + {/* Informações do Paciente */} +
+

+ Maria Silva Santos +

+

+ CPF: 123.456.789-00 +

+

+ Idade: 35 anos +

+
+ + {/* Informações do Login */} +
+
+

+ Conectado como: +

+

+ {user?.email || 'paciente@example.com'} +

+

+ Tipo de usuário: Paciente +

+
+
+ + {/* Botão Voltar ao Início */} + + + {/* Botão de Logout */} + + + {/* Informação adicional */} +
+

+ Em breve, mais funcionalidades estarão disponíveis +

+
+
+
+
+ ) +} diff --git a/src/app/(profissional)/layout.tsx b/src/app/(profissional)/layout.tsx new file mode 100644 index 0000000..4bd26e2 --- /dev/null +++ b/src/app/(profissional)/layout.tsx @@ -0,0 +1,11 @@ +'use client' +import type { ReactNode } from 'react' +import ProtectedRoute from '@/components/layout/ProtectedRoute' + +export default function ProfissionalLayout({ children }: { children: ReactNode }) { + return ( + + {children} + + ) +} diff --git a/src/app/profissional/page.tsx b/src/app/(profissional)/page.tsx similarity index 99% rename from src/app/profissional/page.tsx rename to src/app/(profissional)/page.tsx index d81f419..07b158f 100644 --- a/src/app/profissional/page.tsx +++ b/src/app/(profissional)/page.tsx @@ -3,7 +3,6 @@ import React, { useState, useRef, useEffect } from "react"; import SignatureCanvas from "react-signature-canvas"; import Link from "next/link"; -import ProtectedRoute from "@/components/ProtectedRoute"; import { useAuth } from "@/hooks/useAuth"; import { buscarPacientes } from "@/lib/api"; import { Button } from "@/components/ui/button"; @@ -3251,8 +3250,7 @@ Nevo melanocítico benigno. Seguimento clínico recomendado. }; return ( - -
+
@@ -3510,7 +3508,6 @@ Nevo melanocítico benigno. Seguimento clínico recomendado.
)}
- ); }; diff --git a/src/app/(profissional)/profissional/page.tsx b/src/app/(profissional)/profissional/page.tsx new file mode 100644 index 0000000..07b158f --- /dev/null +++ b/src/app/(profissional)/profissional/page.tsx @@ -0,0 +1,3514 @@ +"use client"; + +import React, { useState, useRef, useEffect } from "react"; +import SignatureCanvas from "react-signature-canvas"; +import Link from "next/link"; +import { useAuth } from "@/hooks/useAuth"; +import { buscarPacientes } from "@/lib/api"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { Textarea } from "@/components/ui/textarea"; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar" +import { User, FolderOpen, X, Users, MessageSquare, ClipboardList, Plus, Edit, Trash2, ChevronLeft, ChevronRight, Clock, FileCheck, Upload, Download, Eye, History, Stethoscope, Pill, Activity, Search } from "lucide-react" +import { Calendar as CalendarIcon, FileText, Settings } from "lucide-react"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; + + +import dynamic from "next/dynamic"; +import dayGridPlugin from "@fullcalendar/daygrid"; +import timeGridPlugin from "@fullcalendar/timegrid"; +import interactionPlugin from "@fullcalendar/interaction"; +import ptBrLocale from "@fullcalendar/core/locales/pt-br"; + +const FullCalendar = dynamic(() => import("@fullcalendar/react"), { + ssr: false, +}); + +const pacientes = [ + { nome: "Ana Souza", cpf: "123.456.789-00", idade: 42, statusLaudo: "Finalizado" }, + { nome: "Bruno Lima", cpf: "987.654.321-00", idade: 33, statusLaudo: "Pendente" }, + { nome: "Carla Menezes", cpf: "111.222.333-44", idade: 67, statusLaudo: "Rascunho" }, +]; + +const medico = { + nome: "Dr. Carlos Andrade", + identificacao: "CRM 000000 • Cardiologia e Dermatologia", + fotoUrl: "", +} + + +const colorsByType = { + Rotina: "#4dabf7", + Cardiologia: "#f76c6c", + Otorrino: "#f7b84d", + Pediatria: "#6cf78b", + Dermatologia: "#9b59b6", + Oftalmologia: "#2ecc71" +}; + +const ProfissionalPage = () => { + const { logout, user } = useAuth(); + const [activeSection, setActiveSection] = useState('calendario'); + const [pacienteSelecionado, setPacienteSelecionado] = useState(null); + + // Estados para edição de laudo + const [isEditingLaudoForPatient, setIsEditingLaudoForPatient] = useState(false); + const [patientForLaudo, setPatientForLaudo] = useState(null); + + // Estados para o perfil do médico + const [isEditingProfile, setIsEditingProfile] = useState(false); + const [profileData, setProfileData] = useState({ + nome: "Dr. Carlos Andrade", + email: user?.email || "carlos.andrade@hospital.com", + telefone: "(11) 99999-9999", + endereco: "Rua das Flores, 123 - Centro", + cidade: "São Paulo", + cep: "01234-567", + crm: "CRM 000000", + especialidade: "Cardiologia e Dermatologia", + biografia: "Médico especialista em cardiologia e dermatologia com mais de 15 anos de experiência em tratamentos clínicos e cirúrgicos." + }); + + // Estados para relatórios médicos + const [relatorioMedico, setRelatorioMedico] = useState({ + pacienteNome: "", + pacienteCpf: "", + pacienteIdade: "", + profissionalNome: medico.nome, + profissionalCrm: medico.identificacao, + motivoRelatorio: "", + historicoClinico: "", + sinaisSintomas: "", + examesRealizados: "", + resultadosExames: "", + diagnosticos: "", + prognostico: "", + tratamentosRealizados: "", + recomendacoes: "", + dataRelatorio: new Date().toISOString().split('T')[0] + }); + const [relatoriosMedicos, setRelatoriosMedicos] = useState([]); + const [editandoRelatorio, setEditandoRelatorio] = useState(null); + + // Estados para funcionalidades do prontuário + const [consultasRegistradas, setConsultasRegistradas] = useState([]); + const [historicoMedico, setHistoricoMedico] = useState([]); + const [prescricoesMedicas, setPrescricoesMedicas] = useState([]); + const [examesSolicitados, setExamesSolicitados] = useState([]); + const [diagnosticos, setDiagnosticos] = useState([]); + const [evolucaoQuadro, setEvolucaoQuadro] = useState([]); + const [anexos, setAnexos] = useState([]); + const [abaProntuarioAtiva, setAbaProntuarioAtiva] = useState('nova-consulta'); + + // Estados para campos principais da consulta + const [consultaAtual, setConsultaAtual] = useState({ + dataConsulta: new Date().toISOString().split('T')[0], + anamnese: "", + exameFisico: "", + hipotesesDiagnosticas: "", + condutaMedica: "", + prescricoes: "", + retornoAgendado: "", + cid10: "" + }); + + const [events, setEvents] = useState([ + + { + id: 1, + title: "Ana Souza", + type: "Cardiologia", + time: "09:00", + date: new Date().toISOString().split('T')[0], + pacienteId: "123.456.789-00", + color: colorsByType.Cardiologia + }, + { + id: 2, + title: "Bruno Lima", + type: "Cardiologia", + time: "10:30", + date: new Date().toISOString().split('T')[0], + pacienteId: "987.654.321-00", + color: colorsByType.Cardiologia + }, + { + id: 3, + title: "Carla Menezes", + type: "Dermatologia", + time: "14:00", + date: new Date().toISOString().split('T')[0], + pacienteId: "111.222.333-44", + color: colorsByType.Dermatologia + } + ]); + const [editingEvent, setEditingEvent] = useState(null); + const [showPopup, setShowPopup] = useState(false); + const [showActionModal, setShowActionModal] = useState(false); + const [step, setStep] = useState(1); + const [newEvent, setNewEvent] = useState({ + title: "", + type: "", + time: "", + pacienteId: "" + }); + const [selectedDate, setSelectedDate] = useState(null); + const [selectedEvent, setSelectedEvent] = useState(null); + const [currentCalendarDate, setCurrentCalendarDate] = useState(new Date()); + + const handleSave = (event: React.MouseEvent) => { + event.preventDefault(); + console.log("Laudo salvo!"); + window.scrollTo({ top: 0, behavior: "smooth" }); + }; + + const handleAbrirProntuario = (paciente: any) => { + setPacienteSelecionado(paciente); + + const pacienteLaudo = document.getElementById('pacienteLaudo') as HTMLInputElement; + if (pacienteLaudo) pacienteLaudo.value = paciente.nome; + + const destinatario = document.getElementById('destinatario') as HTMLInputElement; + if (destinatario) destinatario.value = `${paciente.nome} - ${paciente.cpf}`; + + const prontuarioSection = document.getElementById('prontuario-paciente'); + if (prontuarioSection) { + prontuarioSection.scrollIntoView({ behavior: 'smooth' }); + } + }; + + const handleFecharProntuario = () => { + setPacienteSelecionado(null); + }; + + const handleEditarLaudo = (paciente: any) => { + setPatientForLaudo(paciente); + setIsEditingLaudoForPatient(true); + setActiveSection('laudos'); + }; + + + const navigateDate = (direction: 'prev' | 'next') => { + const newDate = new Date(currentCalendarDate); + newDate.setDate(newDate.getDate() + (direction === 'next' ? 1 : -1)); + setCurrentCalendarDate(newDate); + }; + + const goToToday = () => { + setCurrentCalendarDate(new Date()); + }; + + const formatDate = (date: Date) => { + return date.toLocaleDateString('pt-BR', { + weekday: 'long', + day: 'numeric', + month: 'long', + year: 'numeric' + }); + }; + + // Filtrar eventos do dia atual + const getTodayEvents = () => { + const today = currentCalendarDate.toISOString().split('T')[0]; + return events + .filter(event => event.date === today) + .sort((a, b) => a.time.localeCompare(b.time)); + }; + + const getStatusColor = (type: string) => { + return colorsByType[type as keyof typeof colorsByType] || "#4dabf7"; + }; + + // Funções para o perfil + const handleProfileChange = (field: string, value: string) => { + setProfileData(prev => ({ + ...prev, + [field]: value + })); + }; + + const handleSaveProfile = () => { + setIsEditingProfile(false); + alert('Perfil atualizado com sucesso!'); + }; + + const handleCancelEdit = () => { + setIsEditingProfile(false); + }; + + // Funções para relatórios médicos + const handleRelatorioChange = (field: string, value: string) => { + setRelatorioMedico(prev => ({ + ...prev, + [field]: value + })); + }; + + const handleSalvarRelatorio = () => { + if (!relatorioMedico.pacienteNome || !relatorioMedico.motivoRelatorio) { + alert('Por favor, preencha pelo menos o nome do paciente e o motivo do relatório.'); + return; + } + + const novoRelatorio = { + ...relatorioMedico, + id: Date.now(), + dataGeracao: new Date().toLocaleString() + }; + + if (editandoRelatorio) { + setRelatoriosMedicos(prev => + prev.map(rel => rel.id === editandoRelatorio.id ? novoRelatorio : rel) + ); + setEditandoRelatorio(null); + alert('Relatório médico atualizado com sucesso!'); + } else { + setRelatoriosMedicos(prev => [novoRelatorio, ...prev]); + alert('Relatório médico salvo com sucesso!'); + } + + // Limpar formulário + setRelatorioMedico({ + pacienteNome: "", + pacienteCpf: "", + pacienteIdade: "", + profissionalNome: medico.nome, + profissionalCrm: medico.identificacao, + motivoRelatorio: "", + historicoClinico: "", + sinaisSintomas: "", + examesRealizados: "", + resultadosExames: "", + diagnosticos: "", + prognostico: "", + tratamentosRealizados: "", + recomendacoes: "", + dataRelatorio: new Date().toISOString().split('T')[0] + }); + }; + + const handleEditarRelatorio = (relatorio: any) => { + setRelatorioMedico(relatorio); + setEditandoRelatorio(relatorio); + }; + + const handleExcluirRelatorio = (id: number) => { + if (confirm('Tem certeza que deseja excluir este relatório médico?')) { + setRelatoriosMedicos(prev => prev.filter(rel => rel.id !== id)); + alert('Relatório médico excluído com sucesso!'); + } + }; + + const handleCancelarEdicaoRelatorio = () => { + setEditandoRelatorio(null); + setRelatorioMedico({ + pacienteNome: "", + pacienteCpf: "", + pacienteIdade: "", + profissionalNome: medico.nome, + profissionalCrm: medico.identificacao, + motivoRelatorio: "", + historicoClinico: "", + sinaisSintomas: "", + examesRealizados: "", + resultadosExames: "", + diagnosticos: "", + prognostico: "", + tratamentosRealizados: "", + recomendacoes: "", + dataRelatorio: new Date().toISOString().split('T')[0] + }); + }; + + + const handleDateClick = (arg: any) => { + setSelectedDate(arg.dateStr); + setNewEvent({ title: "", type: "", time: "", pacienteId: "" }); + setStep(1); + setEditingEvent(null); + setShowPopup(true); + }; + + + const handleAddEvent = () => { + const paciente = pacientes.find(p => p.nome === newEvent.title); + const eventToAdd = { + id: Date.now(), + title: newEvent.title, + type: newEvent.type, + time: newEvent.time, + date: selectedDate || currentCalendarDate.toISOString().split('T')[0], + pacienteId: paciente ? paciente.cpf : "", + color: colorsByType[newEvent.type as keyof typeof colorsByType] || "#4dabf7" + }; + setEvents((prev) => [...prev, eventToAdd]); + setShowPopup(false); + }; + + + const handleEditEvent = () => { + setEvents((prevEvents) => + prevEvents.map((ev) => + ev.id.toString() === editingEvent.id.toString() + ? { + ...ev, + title: newEvent.title, + type: newEvent.type, + time: newEvent.time, + color: colorsByType[newEvent.type as keyof typeof colorsByType] || "#4dabf7" + } + : ev + ) + ); + setEditingEvent(null); + setShowPopup(false); + setShowActionModal(false); + }; + + + const handleNextStep = () => { + if (step < 3) setStep(step + 1); + else editingEvent ? handleEditEvent() : handleAddEvent(); + }; + + + const handleEventClick = (clickInfo: any) => { + setSelectedEvent(clickInfo.event); + setShowActionModal(true); + }; + + + const handleDeleteEvent = () => { + if (!selectedEvent) return; + setEvents((prevEvents) => + prevEvents.filter((ev: any) => ev.id.toString() !== selectedEvent.id.toString()) + ); + setShowActionModal(false); + }; + + + const handleStartEdit = () => { + if (!selectedEvent) return; + setEditingEvent(selectedEvent); + setNewEvent({ + title: selectedEvent.title, + type: selectedEvent.extendedProps.type, + time: selectedEvent.extendedProps.time, + pacienteId: selectedEvent.extendedProps.pacienteId || "" + }); + setStep(1); + setShowActionModal(false); + setShowPopup(true); + }; + + + const renderEventContent = (eventInfo: any) => { + const bg = eventInfo.event.backgroundColor || eventInfo.event.extendedProps?.color || "#4dabf7"; + + return ( +
+ {eventInfo.event.title} + + {eventInfo.event.extendedProps.type} + + {eventInfo.event.extendedProps.time} +
+ ); + }; + + + const renderCalendarioSection = () => { + const todayEvents = getTodayEvents(); + + return ( +
+
+

Agenda do Dia

+
+ + {/* Navegação de Data */} +
+
+ +

+ {formatDate(currentCalendarDate)} +

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

Nenhuma consulta agendada para este dia

+

Agenda livre para este dia

+
+ ) : ( + todayEvents.map((appointment) => { + const paciente = pacientes.find(p => p.nome === appointment.title); + return ( +
+
+
+
+
+
+ + {appointment.title} +
+ {paciente && ( +
+ CPF: {paciente.cpf} • {paciente.idade} anos +
+ )} +
+
+
+ + {appointment.time} +
+
+
+ {appointment.type} +
+
+
+
+ +
+ Ver informações do paciente +
+
+
+ +
+
+
+ ); + }) + )} +
+
+ ); + }; + + + function PacientesSection({ + handleAbrirProntuario, + setActiveSection, + }: { + handleAbrirProntuario: (paciente: any) => void; + setActiveSection: (section: string) => void; + }) { + return ( +
+

Gerenciamento de Pacientes

+ + + + {/* Tabela de pacientes padrão */} +
+

Pacientes Recentes

+ + + + Paciente + CPF + Idade + Status do laudo + Ações + + + + {pacientes.map((paciente) => ( + + {paciente.nome} + {paciente.cpf} + {paciente.idade} + {paciente.statusLaudo} + +
+
+ +
+ Ver informações do paciente +
+
+
+ +
+
+
+ ))} +
+
+
+
+ ); + }; + + + const renderProntuarioSection = () => ( +
+
+

Prontuário do Paciente

+ + {/* Informações do Paciente Selecionado */} + {pacienteSelecionado && ( +
+
+

Dados do Paciente

+
+ + +
+
+
+
+ Nome: +

{pacienteSelecionado.nome}

+
+
+ CPF: +

{pacienteSelecionado.cpf}

+
+
+ Idade: +

{pacienteSelecionado.idade} anos

+
+
+
+ )} + + {/* Seletor de Paciente */} + {!pacienteSelecionado && ( +
+
+
+ +

Selecionar Paciente

+

Escolha um paciente para visualizar o prontuário completo

+
+ +
+ + +
+
+ + {/* Cards de pacientes para seleção rápida */} +
+

Ou selecione rapidamente:

+
+ {pacientes.map((paciente) => ( +
setPacienteSelecionado(paciente)} + className="border rounded-lg p-4 hover:shadow-md hover:border-primary transition-all cursor-pointer group" + > +
+
+ +
+
+

{paciente.nome}

+

CPF: {paciente.cpf}

+

{paciente.idade} anos

+
+
+
+ + {paciente.statusLaudo} + + +
+
+ ))} +
+
+
+ )} + + {/* Tabs de Navegação do Prontuário */} + {pacienteSelecionado && ( +
+ +
+ )} + + {/* Conteúdo das Abas */} + {pacienteSelecionado && ( +
+ {abaProntuarioAtiva === 'nova-consulta' && renderNovaConsultaTab()} + {abaProntuarioAtiva === 'consultas' && renderConsultasTab()} + {abaProntuarioAtiva === 'historico' && renderHistoricoTab()} + {abaProntuarioAtiva === 'prescricoes' && renderPrescricoesTab()} + {abaProntuarioAtiva === 'exames' && renderExamesTab()} + {abaProntuarioAtiva === 'diagnosticos' && renderDiagnosticosTab()} + {abaProntuarioAtiva === 'evolucao' && renderEvolucaoTab()} + {abaProntuarioAtiva === 'anexos' && renderAnexosTab()} +
+ )} +
+
+ ); + + // Função para alterar campos da consulta atual + const handleConsultaChange = (field: string, value: string) => { + setConsultaAtual(prev => ({ + ...prev, + [field]: value + })); + }; + + // Função para salvar a consulta + const handleSalvarConsulta = () => { + if (!consultaAtual.anamnese || !consultaAtual.exameFisico) { + alert('Por favor, preencha os campos que são obrigatórios.'); + return; + } + + const novaConsulta = { + ...consultaAtual, + id: Date.now(), + paciente: pacienteSelecionado?.nome, + dataCriacao: new Date().toLocaleString(), + profissional: medico.nome + }; + + setConsultasRegistradas(prev => [novaConsulta, ...prev]); + + setConsultaAtual({ + dataConsulta: new Date().toISOString().split('T')[0], + anamnese: "", + exameFisico: "", + hipotesesDiagnosticas: "", + condutaMedica: "", + prescricoes: "", + retornoAgendado: "", + cid10: "" + }); + + alert('Consulta registrada com sucesso!'); + }; + + // Funções para renderizar cada aba do prontuário + const renderNovaConsultaTab = () => ( +
+
+

Registrar Nova Consulta

+
+ + +
+
+ +
+ {/* Data da Consulta */} +
+
+ + handleConsultaChange('dataConsulta', e.target.value)} + className="w-full" + /> +
+ +
+ + handleConsultaChange('cid10', e.target.value)} + placeholder="Ex: I10, E11, etc." + className="w-full" + /> +
+
+ + {/* Anamnese */} +
+ +