From af7de1dd0c7b586bc699b416e0a93413e6045eca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Gustavo?= <166467972+JoaoGustavo-dev@users.noreply.github.com> Date: Tue, 23 Sep 2025 01:23:41 -0300 Subject: [PATCH] add login-screen --- package-lock.json | 51 ++- package.json | 3 +- susconecta/app/(main-routes)/layout.tsx | 21 +- susconecta/app/layout.tsx | 7 +- susconecta/app/login-admin/page.tsx | 123 ++++++ susconecta/app/login/page.tsx | 123 ++++++ susconecta/app/profissional/page.tsx | 243 +++++++++- susconecta/components/ProtectedRoute.tsx | 43 ++ susconecta/components/dashboard/header.tsx | 117 +++-- susconecta/components/header.tsx | 8 +- susconecta/hooks/useAuth.tsx | 109 +++++ susconecta/package-lock.json | 491 +++++++++++++++++++++ 12 files changed, 1274 insertions(+), 65 deletions(-) create mode 100644 susconecta/app/login-admin/page.tsx create mode 100644 susconecta/app/login/page.tsx create mode 100644 susconecta/components/ProtectedRoute.tsx create mode 100644 susconecta/hooks/useAuth.tsx diff --git a/package-lock.json b/package-lock.json index 963283f..fe0f642 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,8 @@ "@headlessui/react": "^2.2.7", "@heroicons/react": "^2.2.0", "date-fns": "^4.1.0", - "react-big-calendar": "^1.19.4" + "react-big-calendar": "^1.19.4", + "react-signature-canvas": "^1.1.0-alpha.2" } }, "node_modules/@babel/runtime": { @@ -266,6 +267,12 @@ "csstype": "^3.0.2" } }, + "node_modules/@types/signature_pad": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/@types/signature_pad/-/signature_pad-2.3.6.tgz", + "integrity": "sha512-v3j92gCQJoxomHhd+yaG4Vsf8tRS/XbzWKqDv85UsqjMGy4zhokuwKe4b6vhbgncKkh+thF+gpz6+fypTtnFqQ==", + "license": "MIT" + }, "node_modules/@types/warning": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.3.tgz", @@ -520,6 +527,36 @@ "react-dom": ">=16.3.0" } }, + "node_modules/react-signature-canvas": { + "version": "1.1.0-alpha.2", + "resolved": "https://registry.npmjs.org/react-signature-canvas/-/react-signature-canvas-1.1.0-alpha.2.tgz", + "integrity": "sha512-tKUNk3Gmh04Ug4K8p5g8Is08BFUKvbXxi0PyetQ/f8OgCBzcx4vqNf9+OArY/TdNdfHtswXQNRwZD6tyELjkjQ==", + "license": "Apache-2.0", + "dependencies": { + "@babel/runtime": "^7.17.9", + "@types/signature_pad": "^2.3.0", + "signature_pad": "^2.3.2", + "trim-canvas": "^0.1.0" + }, + "funding": { + "url": "https://github.com/sponsors/agilgur5" + }, + "peerDependencies": { + "@types/prop-types": "^15.7.3", + "@types/react": "0.14 - 19", + "prop-types": "^15.5.8", + "react": "0.14 - 19", + "react-dom": "0.14 - 19" + }, + "peerDependenciesMeta": { + "@types/prop-types": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, "node_modules/scheduler": { "version": "0.26.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", @@ -527,12 +564,24 @@ "license": "MIT", "peer": true }, + "node_modules/signature_pad": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/signature_pad/-/signature_pad-2.3.2.tgz", + "integrity": "sha512-peYXLxOsIY6MES2TrRLDiNg2T++8gGbpP2yaC+6Ohtxr+a2dzoaqWosWDY9sWqTAAk6E/TyQO+LJw9zQwyu5kA==", + "license": "MIT" + }, "node_modules/tabbable": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==", "license": "MIT" }, + "node_modules/trim-canvas": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/trim-canvas/-/trim-canvas-0.1.2.tgz", + "integrity": "sha512-nd4Ga3iLFV94mdhW9JFMLpQbHUyCQuhFOD71PEAt1NjtMD5wbZctzhX8c3agHNybMR5zXD1XTGoIEWk995E6pQ==", + "license": "Apache-2.0" + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", diff --git a/package.json b/package.json index e0d27d7..e0ec9ff 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "@headlessui/react": "^2.2.7", "@heroicons/react": "^2.2.0", "date-fns": "^4.1.0", - "react-big-calendar": "^1.19.4" + "react-big-calendar": "^1.19.4", + "react-signature-canvas": "^1.1.0-alpha.2" } } diff --git a/susconecta/app/(main-routes)/layout.tsx b/susconecta/app/(main-routes)/layout.tsx index 68be841..d0e759f 100644 --- a/susconecta/app/(main-routes)/layout.tsx +++ b/susconecta/app/(main-routes)/layout.tsx @@ -1,4 +1,5 @@ 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"; @@ -9,14 +10,16 @@ export default function MainRoutesLayout({ children: React.ReactNode; }) { return ( -
- - -
- - {children} -
-
-
+ +
+ + +
+ + {children} +
+
+
+
); } diff --git a/susconecta/app/layout.tsx b/susconecta/app/layout.tsx index f127de6..52ed9d1 100644 --- a/susconecta/app/layout.tsx +++ b/susconecta/app/layout.tsx @@ -1,5 +1,6 @@ import type React from "react" import type { Metadata } from "next" +import { AuthProvider } from "@/hooks/useAuth" import "./globals.css" export const metadata: Metadata = { @@ -17,7 +18,11 @@ export default function RootLayout({ }) { return ( - {children} + + + {children} + + ) } diff --git a/susconecta/app/login-admin/page.tsx b/susconecta/app/login-admin/page.tsx new file mode 100644 index 0000000..8570b02 --- /dev/null +++ b/susconecta/app/login-admin/page.tsx @@ -0,0 +1,123 @@ +'use client' +import { useState } from 'react' +import { useRouter } from 'next/navigation' +import Link from 'next/link' +import { useAuth } from '@/hooks/useAuth' +import { Button } from '@/components/ui/button' +import { Input } from '@/components/ui/input' +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' +import { Alert, AlertDescription } from '@/components/ui/alert' + +export default function LoginAdminPage() { + const [credentials, setCredentials] = useState({ email: '', password: '' }) + const [error, setError] = useState('') + const [loading, setLoading] = useState(false) + const router = useRouter() + const { login } = useAuth() + + const handleLogin = async (e: React.FormEvent) => { + e.preventDefault() + setLoading(true) + setError('') + + // Simular delay de autenticação + await new Promise(resolve => setTimeout(resolve, 1000)) + + // Tentar fazer login usando o contexto com tipo administrador + const success = login(credentials.email, credentials.password, 'administrador') + + if (success) { + // Redirecionar para o dashboard do administrador + setTimeout(() => { + router.push('/dashboard') + + // Fallback: usar window.location se router.push não funcionar + setTimeout(() => { + if (window.location.pathname === '/login-admin') { + window.location.href = '/dashboard' + } + }, 100) + }, 100) + } else { + setError('Email ou senha incorretos') + } + + setLoading(false) + } + + return ( +
+
+
+

+ Login Administrador de Clínica +

+

+ Entre com suas credenciais para acessar o sistema administrativo +

+
+ + + + Acesso Administrativo + + +
+
+ + setCredentials({...credentials, email: e.target.value})} + required + className="mt-1" + /> +
+ +
+ + setCredentials({...credentials, password: e.target.value})} + required + className="mt-1" + /> +
+ + {error && ( + + {error} + + )} + + +
+ +
+ +
+
+
+
+
+ ) +} \ No newline at end of file diff --git a/susconecta/app/login/page.tsx b/susconecta/app/login/page.tsx new file mode 100644 index 0000000..b308b85 --- /dev/null +++ b/susconecta/app/login/page.tsx @@ -0,0 +1,123 @@ +'use client' +import { useState } from 'react' +import { useRouter } from 'next/navigation' +import Link from 'next/link' +import { useAuth } from '@/hooks/useAuth' +import { Button } from '@/components/ui/button' +import { Input } from '@/components/ui/input' +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' +import { Alert, AlertDescription } from '@/components/ui/alert' + +export default function LoginPage() { + const [credentials, setCredentials] = useState({ email: '', password: '' }) + const [error, setError] = useState('') + const [loading, setLoading] = useState(false) + const router = useRouter() + const { login } = useAuth() + + const handleLogin = async (e: React.FormEvent) => { + e.preventDefault() + setLoading(true) + setError('') + + // Simular delay de autenticação + await new Promise(resolve => setTimeout(resolve, 1000)) + + // Tentar fazer login usando o contexto com tipo profissional + const success = login(credentials.email, credentials.password, 'profissional') + + if (success) { + // Redirecionar para a página do profissional + setTimeout(() => { + router.push('/profissional') + + // Fallback: usar window.location se router.push não funcionar + setTimeout(() => { + if (window.location.pathname === '/login') { + window.location.href = '/profissional' + } + }, 100) + }, 100) + } else { + setError('Email ou senha incorretos') + } + + setLoading(false) + } + + return ( +
+
+
+

+ Login Profissional de Saúde +

+

+ Entre com suas credenciais para acessar o sistema +

+
+ + + + Acesso ao Sistema + + +
+
+ + setCredentials({...credentials, email: e.target.value})} + required + className="mt-1" + /> +
+ +
+ + setCredentials({...credentials, password: e.target.value})} + required + className="mt-1" + /> +
+ + {error && ( + + {error} + + )} + + +
+ +
+ +
+
+
+
+
+ ) +} \ No newline at end of file diff --git a/susconecta/app/profissional/page.tsx b/susconecta/app/profissional/page.tsx index a796679..1625bce 100644 --- a/susconecta/app/profissional/page.tsx +++ b/susconecta/app/profissional/page.tsx @@ -5,6 +5,8 @@ import SignatureCanvas from "react-signature-canvas"; import ReactQuill from "react-quill"; import "react-quill/dist/quill.snow.css"; import Link from "next/link"; +import ProtectedRoute from "@/components/ProtectedRoute"; +import { useAuth } from "@/hooks/useAuth"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; @@ -68,8 +70,24 @@ const colorsByType = { }; const ProfissionalPage = () => { + const { logout, userEmail } = useAuth(); const [activeSection, setActiveSection] = useState('calendario'); const [pacienteSelecionado, setPacienteSelecionado] = useState(null); + + // Estados para o perfil do médico + const [isEditingProfile, setIsEditingProfile] = useState(false); + const [profileData, setProfileData] = useState({ + nome: "Dr. Carlos Andrade", + email: userEmail || "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", + biografia: "Médico cardiologista com mais de 15 anos de experiência em cirurgias cardíacas e tratamentos preventivos." + }); + const [events, setEvents] = useState([ { @@ -171,6 +189,23 @@ const ProfissionalPage = () => { 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); + }; + const handleDateClick = (arg: any) => { setSelectedDate(arg.dateStr); @@ -739,9 +774,166 @@ function LaudoEditor() { const renderPerfilSection = () => ( -
-

Página em construção

-
+
+
+

Meu Perfil

+ {!isEditingProfile ? ( + + ) : ( +
+ + +
+ )} +
+ +
+ {/* Informações Pessoais */} +
+

Informações Pessoais

+ +
+ +

{profileData.nome}

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

{profileData.email}

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

{profileData.telefone}

+ )} +
+ +
+ +

{profileData.crm}

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

{profileData.especialidade}

+ )} +
+
+ + {/* Endereço e Contato */} +
+

Endereço e Contato

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

{profileData.endereco}

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

{profileData.cidade}

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

{profileData.cep}

+ )} +
+ +
+ + {isEditingProfile ? ( +