diff --git a/app/cadastro/page.tsx b/app/cadastro/page.tsx index 7b6fb88..843a490 100644 --- a/app/cadastro/page.tsx +++ b/app/cadastro/page.tsx @@ -5,15 +5,15 @@ import { Calendar, Clock, User, Shield, Stethoscope, Receipt, IdCard } from "lu export default function HomePage() { return ( -
+
-

Central de Operações
+

Central de Operações

MedConnect

-

+

Gerencie suas consultas médicas de forma simples e eficiente

@@ -21,21 +21,21 @@ export default function HomePage() {
- + Área do Paciente Tenha o controle total da sua saúde: agende, consulte o histórico e informações pessoais
-
+
Agendar consultas
-
+
Ver histórico de consultas
-
+
Gerenciar dados pessoais
@@ -54,21 +54,21 @@ export default function HomePage() {
-
+
Gerenciar consultas
-
+
Cadastrar pacientes
-
+
Controlar agenda
- + @@ -81,21 +81,21 @@ export default function HomePage() {
-
+
Gerenciar agenda
-
+
Ver pacientes
-
+
Histórico de atendimentos
- + @@ -108,21 +108,21 @@ export default function HomePage() {
-
+
Relatórios gerenciais
-
+
Configurações do sistema
-
+
Gestão de usuários
- + @@ -135,21 +135,21 @@ export default function HomePage() {
-
+
Relatórios financeiros
-
+
Faturamento
-
+
Controle de pagamentos
- + diff --git a/app/context/AccessibilityContext.tsx b/app/context/AccessibilityContext.tsx new file mode 100644 index 0000000..f63e18e --- /dev/null +++ b/app/context/AccessibilityContext.tsx @@ -0,0 +1,99 @@ +"use client"; + +import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react'; + +type Theme = 'light' | 'dark'; +type Contrast = 'normal' | 'high'; + +interface AccessibilityContextProps { + theme: Theme; + setTheme: (theme: Theme) => void; + contrast: Contrast; + setContrast: (contrast: Contrast) => void; + fontSize: number; + increaseFontSize: () => void; + decreaseFontSize: () => void; + resetFontSize: () => void; +} + +const AccessibilityContext = createContext(undefined); + +export const AccessibilityProvider = ({ children }: { children: ReactNode }) => { + const [theme, setThemeState] = useState(() => { + if (typeof window === 'undefined') return 'light'; + return (localStorage.getItem('accessibility-theme') as Theme) || 'light'; + }); + const [contrast, setContrastState] = useState(() => { + if (typeof window === 'undefined') return 'normal'; + return (localStorage.getItem('accessibility-contrast') as Contrast) || 'normal'; + }); + const [fontSize, setFontSize] = useState(() => { + if (typeof window === 'undefined') return 16; + const storedSize = localStorage.getItem('accessibility-font-size'); + return storedSize ? parseFloat(storedSize) : 16; + }); + + useEffect(() => { + const root = document.documentElement; + + // Theme + root.classList.remove('light', 'dark'); + root.classList.add(theme); + localStorage.setItem('accessibility-theme', theme); + + // Contrast + root.classList.remove('normal-contrast', 'high-contrast'); + root.classList.add(contrast === 'high' ? 'high-contrast' : 'normal-contrast'); + localStorage.setItem('accessibility-contrast', contrast); + + // Font Size + root.style.fontSize = `${fontSize}px`; + localStorage.setItem('accessibility-font-size', fontSize.toString()); + + }, [theme, contrast, fontSize]); + + const setTheme = (newTheme: Theme) => { + setThemeState(newTheme); + }; + + const setContrast = (newContrast: Contrast) => { + setContrastState(newContrast); + }; + + const increaseFontSize = () => { + setFontSize(prev => Math.min(prev + 2, 24)); // Cap at 24px + }; + + const decreaseFontSize = () => { + setFontSize(prev => Math.max(prev - 2, 12)); // Cap at 12px + }; + + const resetFontSize = () => { + setFontSize(16); + }; + + return ( + + {children} + + ); +}; + +export const useAccessibility = () => { + const context = useContext(AccessibilityContext); + if (context === undefined) { + throw new Error('useAccessibility must be used within an AccessibilityProvider'); + } + return context; +}; diff --git a/app/doctor/dashboard/page.tsx b/app/doctor/dashboard/page.tsx new file mode 100644 index 0000000..d9615f2 --- /dev/null +++ b/app/doctor/dashboard/page.tsx @@ -0,0 +1,91 @@ +import DoctorLayout from "@/components/doctor-layout" +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" +import { Button } from "@/components/ui/button" +import { Calendar, Clock, User, Plus } from "lucide-react" +import Link from "next/link" + +export default function PatientDashboard() { + return ( + +
+
+

Dashboard

+

Bem-vindo ao seu portal de consultas médicas

+
+ +
+ + + Próxima Consulta + + + +
02 out
+

Dr. Silva - 14:30

+
+
+ + + + Consultas Este Mês + + + +
4
+

4 agendadas

+
+
+ + + + Perfil + + + +
100%
+

Dados completos

+
+
+
+ +
+ + + Ações Rápidas + Acesse rapidamente as principais funcionalidades + + + + + + + + + + + Próximas Consultas + Suas consultas agendadas + + +
+
+
+

Dr. João Santos

+

Cardiologia

+
+
+

02 out

+

14:30

+
+
+
+
+
+
+
+
+ ) +} diff --git a/app/doctor/login/page.tsx b/app/doctor/login/page.tsx index 2e40bd2..c9cf646 100644 --- a/app/doctor/login/page.tsx +++ b/app/doctor/login/page.tsx @@ -1,157 +1,11 @@ -"use client" +// Caminho: app/(doctor)/login/page.tsx -import type React from "react" +import { LoginForm } from "@/components/LoginForm"; -import { useState } from "react" -import { useRouter } from "next/navigation" -import { Button } from "@/components/ui/button" -import { Input } from "@/components/ui/input" -import { Label } from "@/components/ui/label" -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" -import { Separator } from "@/components/ui/separator" -import { useToast } from "@/hooks/use-toast" -import { Eye, EyeOff, Mail, Lock, Stethoscope, Loader2 } from "lucide-react" -import Link from "next/link" - -interface LoginForm { - email: string - password: string -} - -export default function DoctorLogin() { - const [form, setForm] = useState({ email: "", password: "" }) - const [showPassword, setShowPassword] = useState(false) - const [isLoading, setIsLoading] = useState(false) - const router = useRouter() - const { toast } = useToast() - - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault() - setIsLoading(true) - - // Simular autenticação - setTimeout(() => { - if (form.email && form.password) { - const doctorData = { - id: "1", - name: "Dr. João Santos", - email: form.email, - phone: "(11) 98888-8888", - cpf: "987.654.321-00", - crm: "CRM/SP 123456", - specialty: "Cardiologia", - department: "Cardiologia", - permissions: ["view_patients", "manage_appointments", "create_reports"], - } - - localStorage.setItem("doctorData", JSON.stringify(doctorData)) - localStorage.setItem("userType", "doctor") - - toast({ - title: "Login realizado com sucesso!", - description: "Bem-vindo ao sistema, " + doctorData.name, - }) - - router.push("/doctor/medicos") - } else { - toast({ - title: "Erro no login", - description: "Por favor, preencha todos os campos.", - variant: "destructive", - }) - } - setIsLoading(false) - }, 1500) - } - - return ( -
- - -
- -
-
- Área do Médico - Acesse o sistema médico -
-
- - -
-
- -
- - setForm({ ...form, email: e.target.value })} - className="pl-10 h-11 border-gray-200 focus:border-green-500 focus:ring-green-500" - required - /> -
-
- -
- -
- - setForm({ ...form, password: e.target.value })} - className="pl-10 pr-10 h-11 border-gray-200 focus:border-green-500 focus:ring-green-500" - required - /> - -
-
- - -
- -
- - - ou - -
- -
- - Voltar à página inicial - -
-
-
-
- ) +export default function DoctorLoginPage() { + return ( +
+ +
+ ); } diff --git a/app/doctor/medicos/consultas/page.tsx b/app/doctor/medicos/consultas/page.tsx index 57c658c..b8edfda 100644 --- a/app/doctor/medicos/consultas/page.tsx +++ b/app/doctor/medicos/consultas/page.tsx @@ -2,7 +2,7 @@ import type React from "react"; import { useState, useEffect } from "react"; -import DoctorLayout from "@/components/doctor-layout"; +import DoctorLayout from "@/components/doctor-layout"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Clock, Calendar as CalendarIcon, MapPin, Phone, User, X, RefreshCw } from "lucide-react"; @@ -19,7 +19,7 @@ const APPOINTMENTS_STORAGE_KEY = "clinic-appointments"; interface LocalStorageAppointment { id: number; patientName: string; - doctor: string; + doctor: string; specialty: string; date: string; // Data no formato YYYY-MM-DD time: string; // Hora no formato HH:MM @@ -28,13 +28,13 @@ interface LocalStorageAppointment { phone: string; } -const LOGGED_IN_DOCTOR_NAME = "Dr. João Santos"; +const LOGGED_IN_DOCTOR_NAME = "Dr. João Santos"; // Função auxiliar para comparar se duas datas (Date objects) são o mesmo dia const isSameDay = (date1: Date, date2: Date) => { return date1.getFullYear() === date2.getFullYear() && - date1.getMonth() === date2.getMonth() && - date1.getDate() === date2.getDate(); + date1.getMonth() === date2.getMonth() && + date1.getDate() === date2.getDate(); }; // --- COMPONENTE PRINCIPAL --- @@ -43,10 +43,10 @@ export default function DoctorAppointmentsPage() { const [allAppointments, setAllAppointments] = useState([]); const [filteredAppointments, setFilteredAppointments] = useState([]); const [isLoading, setIsLoading] = useState(true); - + // NOVO ESTADO 1: Armazena os dias com consultas (para o calendário) const [bookedDays, setBookedDays] = useState([]); - + // NOVO ESTADO 2: Armazena a data selecionada no calendário const [selectedCalendarDate, setSelectedCalendarDate] = useState(new Date()); @@ -58,20 +58,20 @@ export default function DoctorAppointmentsPage() { useEffect(() => { if (selectedCalendarDate) { const dateString = format(selectedCalendarDate, 'yyyy-MM-dd'); - + // Filtra a lista completa de agendamentos pela data selecionada const todayAppointments = allAppointments .filter(app => app.date === dateString) .sort((a, b) => a.time.localeCompare(b.time)); // Ordena por hora - + setFilteredAppointments(todayAppointments); } else { // Se nenhuma data estiver selecionada (ou se for limpa), mostra todos (ou os de hoje) const todayDateString = format(new Date(), 'yyyy-MM-dd'); const todayAppointments = allAppointments .filter(app => app.date === todayDateString) - .sort((a, b) => a.time.localeCompare(b.time)); - + .sort((a, b) => a.time.localeCompare(b.time)); + setFilteredAppointments(todayAppointments); } }, [allAppointments, selectedCalendarDate]); @@ -87,7 +87,7 @@ export default function DoctorAppointmentsPage() { // 1. EXTRAI E PREPARA AS DATAS PARA O CALENDÁRIO const uniqueBookedDates = Array.from(new Set(appointmentsToShow.map(app => app.date))); - + // Converte YYYY-MM-DD para objetos Date, garantindo que o tempo seja meia-noite (00:00:00) const dateObjects = uniqueBookedDates.map(dateString => new Date(dateString + 'T00:00:00')); @@ -122,12 +122,12 @@ export default function DoctorAppointmentsPage() { const storedAppointmentsRaw = localStorage.getItem(APPOINTMENTS_STORAGE_KEY); const allAppts: LocalStorageAppointment[] = storedAppointmentsRaw ? JSON.parse(storedAppointmentsRaw) : []; - const updatedAppointments = allAppts.map(app => + const updatedAppointments = allAppts.map(app => app.id === id ? { ...app, status: "cancelada" as const } : app ); localStorage.setItem(APPOINTMENTS_STORAGE_KEY, JSON.stringify(updatedAppointments)); - loadAppointments(); + loadAppointments(); toast.info(`Consulta cancelada com sucesso.`); }; @@ -135,8 +135,8 @@ export default function DoctorAppointmentsPage() { toast.info(`Reagendamento da Consulta ID: ${id}. Navegar para a página de agendamento.`); }; - const displayDate = selectedCalendarDate ? - new Date(selectedCalendarDate).toLocaleDateString("pt-BR", {weekday: 'long', day: '2-digit', month: 'long'}) : + const displayDate = selectedCalendarDate ? + new Date(selectedCalendarDate).toLocaleDateString("pt-BR", { weekday: 'long', day: '2-digit', month: 'long' }) : "Selecione uma data"; @@ -158,7 +158,7 @@ export default function DoctorAppointmentsPage() { {/* NOVO LAYOUT DE DUAS COLUNAS */}
- + {/* COLUNA 1: CALENDÁRIO */}
@@ -176,10 +176,10 @@ export default function DoctorAppointmentsPage() { onSelect={setSelectedCalendarDate} initialFocus // A CHAVE DO HIGHLIGHT: Passa o array de datas agendadas - modifiers={{ booked: bookedDays }} + modifiers={{ booked: bookedDays }} // Define o estilo CSS para o modificador 'booked' - modifiersClassNames={{ - booked: "bg-blue-600 text-white aria-selected:!bg-blue-700 hover:!bg-blue-700/90" + modifiersClassNames={{ + booked: "bg-blue-600 text-white aria-selected:!bg-blue-700 hover:!bg-blue-700/90" }} className="rounded-md border p-2" /> @@ -233,23 +233,23 @@ export default function DoctorAppointmentsPage() {
- {appointment.phone || "N/A"} + {appointment.phone || "N/A"}
{showActions && (
- - diff --git a/app/finance/login/page.tsx b/app/finance/login/page.tsx index a27ba24..d00e41c 100644 --- a/app/finance/login/page.tsx +++ b/app/finance/login/page.tsx @@ -1,155 +1,12 @@ -"use client" +// Caminho: app/(finance)/login/page.tsx -import type React from "react" +import { LoginForm } from "@/components/LoginForm"; -import { useState } from "react" -import { useRouter } from "next/navigation" -import { Button } from "@/components/ui/button" -import { Input } from "@/components/ui/input" -import { Label } from "@/components/ui/label" -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" -import { Separator } from "@/components/ui/separator" -import { useToast } from "@/hooks/use-toast" -import { Eye, EyeOff, Mail, Lock, Stethoscope, Loader2, Receipt } from "lucide-react" -import Link from "next/link" - -interface LoginForm { - email: string - password: string -} - -export default function DoctorLogin() { - const [form, setForm] = useState({ email: "", password: "" }) - const [showPassword, setShowPassword] = useState(false) - const [isLoading, setIsLoading] = useState(false) - const router = useRouter() - const { toast } = useToast() - - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault() - setIsLoading(true) - - // Simular autenticação - setTimeout(() => { - if (form.email && form.password) { - const financierData = { - id: "1", - name: "Thiago Nigro", - email: form.email, - phone: "(11) 98888-8888", - cpf: "987.654.321-00", - department: "Financeiro", - permissions: ["view_reports", "manage_finances", "create_reports"], - } - - localStorage.setItem("financierData", JSON.stringify(financierData)) - localStorage.setItem("userType", "financier") - - toast({ - title: "Login realizado com sucesso!", - description: "Bem-vindo ao sistema, " + financierData.name, - }) - - router.push("/finance/home") - } else { - toast({ - title: "Erro no login", - description: "Por favor, preencha todos os campos.", - variant: "destructive", - }) - } - setIsLoading(false) - }, 1500) - } - - return ( -
- - -
- -
-
- Área do Médico - Acesse o sistema médico -
-
- - -
-
- -
- - setForm({ ...form, email: e.target.value })} - className="pl-10 h-11 border-gray-200 focus:border-green-500 focus:ring-green-500" - required - /> -
-
- -
- -
- - setForm({ ...form, password: e.target.value })} - className="pl-10 pr-10 h-11 border-gray-200 focus:border-green-500 focus:ring-green-500" - required - /> - -
-
- - -
- -
- - - ou - -
- -
- - Voltar à página inicial - -
-
-
-
- ) +export default function FinanceLoginPage() { + return ( + // Fundo com gradiente laranja, como no seu código original +
+ +
+ ); } diff --git a/app/globals.css b/app/globals.css index 8384a1f..d0b1400 100644 --- a/app/globals.css +++ b/app/globals.css @@ -74,6 +74,36 @@ --sidebar-ring: oklch(0.439 0 0); } +.high-contrast { + --background: oklch(0 0 0); + --foreground: oklch(1 0.5 100); + --card: oklch(0 0 0); + --card-foreground: oklch(1 0.5 100); + --popover: oklch(0 0 0); + --popover-foreground: oklch(1 0.5 100); + --primary: oklch(1 0.5 100); + --primary-foreground: oklch(0 0 0); + --secondary: oklch(0 0 0); + --secondary-foreground: oklch(1 0.5 100); + --muted: oklch(0 0 0); + --muted-foreground: oklch(1 0.5 100); + --accent: oklch(0 0 0); + --accent-foreground: oklch(1 0.5 100); + --destructive: oklch(0.5 0.3 30); + --destructive-foreground: oklch(0 0 0); + --border: oklch(1 0.5 100); + --input: oklch(0 0 0); + --ring: oklch(1 0.5 100); + --sidebar: oklch(0 0 0); + --sidebar-foreground: oklch(1 0.5 100); + --sidebar-primary: oklch(1 0.5 100); + --sidebar-primary-foreground: oklch(0 0 0); + --sidebar-accent: oklch(0 0 0); + --sidebar-accent-foreground: oklch(1 0.5 100); + --sidebar-border: oklch(1 0.5 100); + --sidebar-ring: oklch(1 0.5 100); +} + @theme inline { --font-sans: var(--font-geist-sans); --font-mono: var(--font-geist-mono); @@ -121,5 +151,6 @@ } body { @apply bg-background text-foreground; + transition: background-color 0.3s, color 0.3s; } } \ No newline at end of file diff --git a/app/layout.tsx b/app/layout.tsx index 3becb5e..797f31e 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,31 +1,32 @@ -import type { Metadata } from 'next' -import { GeistSans } from 'geist/font/sans' -import { GeistMono } from 'geist/font/mono' -import { Analytics } from '@vercel/analytics/next' -import './globals.css' +import type { Metadata } from "next"; +import { GeistSans } from "geist/font/sans"; +import { GeistMono } from "geist/font/mono"; +import { Analytics } from "@vercel/analytics/next"; +import "./globals.css"; +import { Toaster } from "@/components/ui/toaster"; // [PASSO 1.2] - Importando o nosso provider -import { AppointmentsProvider } from './context/AppointmentsContext' - -export const metadata: Metadata = { - title: 'Clinic App', - description: 'Created with v0', - generator: 'v0.app', -} +import { AppointmentsProvider } from "./context/AppointmentsContext"; +import { AccessibilityProvider } from "./context/AccessibilityContext"; +import { AccessibilityModal } from "@/components/accessibility-modal"; +import { ThemeInitializer } from "@/components/theme-initializer"; export default function RootLayout({ - children, + children, }: Readonly<{ - children: React.ReactNode + children: React.ReactNode; }>) { - return ( - - - {/* [PASSO 1.2] - Envolvendo a aplicação com o provider */} - - {children} - - - - - ) -} \ No newline at end of file + return ( + + + {/* [PASSO 1.2] - Envolvendo a aplicação com o provider */} + + + {children} + + + + + + + ); +} diff --git a/app/manager/dashboard/page.tsx b/app/manager/dashboard/page.tsx new file mode 100644 index 0000000..6e1dd10 --- /dev/null +++ b/app/manager/dashboard/page.tsx @@ -0,0 +1,113 @@ +import ManagerLayout from "@/components/manager-layout" +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" +import { Button } from "@/components/ui/button" +import { Calendar, Clock, User, Plus } from "lucide-react" +import Link from "next/link" + +export default function ManagerDashboard() { + return ( + +
+
+

Dashboard

+

Bem-vindo ao seu portal de consultas médicas

+
+ +
+ + + Relatórios gerenciais + + + +
3
+

2 não lidos, 1 lido

+
+
+ + + + Gestão de usuários + + + +
João Marques
+

fez login a 13min

+
+
+ + + + Perfil + + + +
100%
+

Dados completos

+
+
+
+ +
+ + + Ações Rápidas + Acesse rapidamente as principais funcionalidades + + + + + + + + + + + + + + + + + Gestão de Médicos + Médicos online + + +
+
+
+

Dr. Silva

+

Cardiologia

+
+
+

On-line

+

+
+
+
+
+

Dra. Santos

+

Dermatologia

+
+
+

Off-line

+

Visto as 8:33

+
+
+
+
+
+
+
+
+ ) +} diff --git a/app/manager/login/page.tsx b/app/manager/login/page.tsx index d5347ce..db3aadc 100644 --- a/app/manager/login/page.tsx +++ b/app/manager/login/page.tsx @@ -1,154 +1,12 @@ -"use client" +// Caminho: app/(manager)/login/page.tsx -import type React from "react" +import { LoginForm } from "@/components/LoginForm"; -import { useState } from "react" -import { useRouter } from "next/navigation" -import { Button } from "@/components/ui/button" -import { Input } from "@/components/ui/input" -import { Label } from "@/components/ui/label" -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" -import { Separator } from "@/components/ui/separator" -import { useToast } from "@/hooks/use-toast" -import { Eye, EyeOff, Mail, Lock, Stethoscope, Loader2, IdCard } from "lucide-react" -import Link from "next/link" - -interface LoginForm { - email: string - password: string -} - -export default function ManagerLogin() { - const [form, setForm] = useState({ email: "", password: "" }) - const [showPassword, setShowPassword] = useState(false) - const [isLoading, setIsLoading] = useState(false) - const router = useRouter() - const { toast } = useToast() - - const handleSubmit = async (e: React.FormEvent) => { - e.preventDefault() - setIsLoading(true) - - setTimeout(() => { - if (form.email && form.password) { - const managerData = { - id: "1", - name: "Arthur Cavalcante", - email: form.email, - phone: "(11) 98888-8888", - cpf: "987.654.321-00", - department: "Gerente", - permissions: ["manage_user", "manage_doctors", "create_reports"], - } - - localStorage.setItem("managerData", JSON.stringify(managerData)) - localStorage.setItem("userType", "manager") - - toast({ - title: "Login realizado com sucesso!", - description: "Bem-vindo ao sistema, " + managerData.name, - }) - - router.push("/manager/home") - } else { - toast({ - title: "Erro no login", - description: "Por favor, preencha todos os campos.", - variant: "destructive", - }) - } - setIsLoading(false) - }, 1500) - } - - return ( -
- - -
- -
-
- Área do Gestor - Acesse o sistema médico -
-
- - -
-
- -
- - setForm({ ...form, email: e.target.value })} - className="pl-10 h-11 border-gray-200 focus:border-blue-500 focus:ring-blue-500" - required - /> -
-
- -
- -
- - setForm({ ...form, password: e.target.value })} - className="pl-10 pr-10 h-11 border-gray-200 focus:border-green-500 focus:ring-green-500" - required - /> - -
-
- - -
- -
- - - ou - -
- -
- - Voltar à página inicial - -
-
-
-
- ) +export default function ManagerLoginPage() { + return ( + // Mantemos o seu plano de fundo original +
+ +
+ ); } diff --git a/app/manager/usuario/[id]/editar/page.tsx b/app/manager/usuario/[id]/editar/page.tsx new file mode 100644 index 0000000..50cb953 --- /dev/null +++ b/app/manager/usuario/[id]/editar/page.tsx @@ -0,0 +1,279 @@ +"use client" + +import { useState, useEffect } from "react" +import { useRouter, useParams } from "next/navigation" +import Link from "next/link" +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" +import { Label } from "@/components/ui/label" +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" +import { Save, Loader2, ArrowLeft } from "lucide-react" +import ManagerLayout from "@/components/manager-layout" + +// Mock user service for demonstration. Replace with your actual API service. +const usersService = { + getById: async (id: string): Promise => { + console.log(`API Call: Fetching user with ID ${id}`); + await new Promise(resolve => setTimeout(resolve, 500)); + // This mock finds a user from a predefined list. + const mockUsers = [ + { id: 1, full_name: 'Alice Admin', email: 'alice.admin@example.com', phone: '(11) 98765-4321', role: 'admin' }, + { id: 2, full_name: 'Bruno Gestor', email: 'bruno.g@example.com', phone: '(21) 91234-5678', role: 'gestor' }, + { id: 3, full_name: 'Dr. Carlos Médico', email: 'carlos.med@example.com', phone: null, role: 'medico' }, + { id: 4, full_name: 'Daniela Secretaria', email: 'daniela.sec@example.com', phone: '(31) 99999-8888', role: 'secretaria' }, + { id: 5, full_name: 'Eduardo Usuário', email: 'edu.user@example.com', phone: '(41) 98888-7777', role: 'user' }, + ]; + const user = mockUsers.find(u => u.id.toString() === id); + if (!user) throw new Error("Usuário não encontrado."); + return user; + }, + update: async (id: string, payload: any): Promise => { + console.log(`API Call: Updating user ${id} with payload:`, payload); + await new Promise(resolve => setTimeout(resolve, 1000)); + // To simulate an error (e.g., duplicate email), you could throw an error here: + // if (payload.email === 'bruno.g@example.com') throw new Error("Este e-mail já está em uso por outro usuário."); + } +}; + +// Interface for the user form data +interface UserFormData { + nomeCompleto: string; + email: string; + telefone: string; + papel: string; + password?: string; // Optional for password updates +} + +// Default state for the form +const defaultFormData: UserFormData = { + nomeCompleto: '', + email: '', + telefone: '', + papel: '', + password: '', +}; + +// Helper functions for phone formatting +const cleanNumber = (value: string): string => value.replace(/\D/g, ''); +const formatPhone = (value: string): string => { + const cleaned = cleanNumber(value).substring(0, 11); + if (cleaned.length > 10) { + return cleaned.replace(/(\d{2})(\d{5})(\d{4})/, '($1) $2-$3'); + } + return cleaned.replace(/(\d{2})(\d{4})(\d{4})/, '($1) $2-$3'); +}; + +export default function EditarUsuarioPage() { + const router = useRouter(); + const params = useParams(); + const id = Array.isArray(params.id) ? params.id[0] : params.id; + + const [formData, setFormData] = useState(defaultFormData); + const [loading, setLoading] = useState(true); + const [isSaving, setIsSaving] = useState(false); + const [error, setError] = useState(null); + + // Map API field names to our form field names + const apiToFormMap: { [key: string]: keyof UserFormData } = { + 'full_name': 'nomeCompleto', + 'email': 'email', + 'phone': 'telefone', + 'role': 'papel' + }; + + // Fetch user data when the component mounts + useEffect(() => { + if (!id) return; + + const fetchUser = async () => { + try { + const data = await usersService.getById(id); + if (!data) { + setError("Usuário não encontrado."); + setLoading(false); + return; + } + + const initialData: Partial = {}; + Object.keys(data).forEach(key => { + const formKey = apiToFormMap[key]; + if (formKey) { + initialData[formKey] = data[key] === null ? '' : String(data[key]); + } + }); + + setFormData(prev => ({ ...prev, ...initialData })); + } catch (e: any) { + console.error("Erro ao carregar dados do usuário:", e); + setError(e.message || "Não foi possível carregar os dados do usuário."); + } finally { + setLoading(false); + } + }; + fetchUser(); + }, [id]); + + const handleInputChange = (key: keyof UserFormData, value: string) => { + const updatedValue = key === 'telefone' ? formatPhone(value) : value; + setFormData((prev) => ({ ...prev, [key]: updatedValue })); + }; + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + setError(null); + setIsSaving(true); + + if (!id) { + setError("ID do usuário ausente."); + setIsSaving(false); + return; + } + + // Prepare payload for the API + const payload: { [key: string]: any } = { + full_name: formData.nomeCompleto, + email: formData.email, + phone: formData.telefone.trim() || null, + role: formData.papel, + }; + + // Only include the password in the payload if it has been changed + if (formData.password && formData.password.trim() !== '') { + payload.password = formData.password; + } + + try { + await usersService.update(id, payload); + router.push("/manager/usuario"); + } catch (e: any) { + console.error("Erro ao salvar o usuário:", e); + setError(e.message || "Ocorreu um erro inesperado ao atualizar."); + } finally { + setIsSaving(false); + } + }; + + if (loading) { + return ( + +
+ +

Carregando dados do usuário...

+
+
+ ); + } + + return ( + +
+
+
+

+ Editar Usuário: {formData.nomeCompleto} +

+

+ Atualize as informações do usuário (ID: {id}). +

+
+ + + +
+ +
+ {error && ( +
+

Erro na Atualização:

+

{error}

+
+ )} + +
+
+ + handleInputChange("nomeCompleto", e.target.value)} + /> +
+ +
+
+ + handleInputChange("email", e.target.value)} + /> +
+
+ + handleInputChange("password", e.target.value)} + placeholder="Deixe em branco para não alterar" + /> +
+
+ +
+
+ + handleInputChange("telefone", e.target.value)} + placeholder="(00) 00000-0000" + maxLength={15} + /> +
+
+ + +
+
+
+ +
+ + + + +
+
+
+
+ ); +} \ No newline at end of file diff --git a/app/manager/usuario/novo/page.tsx b/app/manager/usuario/novo/page.tsx new file mode 100644 index 0000000..781f35d --- /dev/null +++ b/app/manager/usuario/novo/page.tsx @@ -0,0 +1,214 @@ +"use client" + +import { useState } from "react" +import { useRouter } from "next/navigation" +import Link from "next/link" +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" +import { Label } from "@/components/ui/label" +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" +import { Save, Loader2 } from "lucide-react" +import ManagerLayout from "@/components/manager-layout" + +// Mock user service for demonstration. Replace with your actual API service. +const usersService = { + create: async (payload: any) => { + console.log("API Call: Creating user with payload:", payload); + // Simulate network delay + await new Promise(resolve => setTimeout(resolve, 1000)); + // Simulate a success response + return { id: Date.now(), ...payload }; + // To simulate an error, you could throw an error here: + // throw new Error("O e-mail informado já está em uso."); + } +}; + +// Define the structure for our form data +interface UserFormData { + email: string; + password: string; + nomeCompleto: string; + telefone: string; + papel: string; // e.g., 'admin', 'gestor', 'medico', etc. +} + +// Define the initial state for the form +const defaultFormData: UserFormData = { + email: '', + password: '', + nomeCompleto: '', + telefone: '', + papel: '', +}; + +// Helper function to remove non-digit characters +const cleanNumber = (value: string): string => value.replace(/\D/g, ''); + +// Helper function to format a phone number +const formatPhone = (value: string): string => { + const cleaned = cleanNumber(value).substring(0, 11); + if (cleaned.length > 10) { + return cleaned.replace(/(\d{2})(\d{5})(\d{4})/, '($1) $2-$3'); + } + return cleaned.replace(/(\d{2})(\d{4})(\d{4})/, '($1) $2-$3'); +}; + +export default function NovoUsuarioPage() { + const router = useRouter(); + const [formData, setFormData] = useState(defaultFormData); + const [isSaving, setIsSaving] = useState(false); + const [error, setError] = useState(null); + + // Handles changes in form inputs + const handleInputChange = (key: keyof UserFormData, value: string) => { + const updatedValue = key === 'telefone' ? formatPhone(value) : value; + setFormData((prev) => ({ ...prev, [key]: updatedValue })); + }; + + // Handles form submission + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + setError(null); + + // Basic validation + if (!formData.email || !formData.password || !formData.nomeCompleto || !formData.papel) { + setError("Por favor, preencha todos os campos obrigatórios."); + return; + } + + setIsSaving(true); + + // Prepare payload for the API + const payload = { + email: formData.email, + password: formData.password, + full_name: formData.nomeCompleto, + phone: formData.telefone.trim() || null, // Send null if empty + role: formData.papel, + }; + + try { + await usersService.create(payload); + // On success, redirect to the main user list page + router.push("/manager/usuario"); + } catch (e: any) { + console.error("Erro ao criar usuário:", e); + setError(e.message || "Ocorreu um erro inesperado. Tente novamente."); + } finally { + setIsSaving(false); + } + }; + + return ( + +
+
+
+

Novo Usuário

+

+ Preencha os dados para cadastrar um novo usuário no sistema. +

+
+ + + +
+ +
+ + {/* Error Message Display */} + {error && ( +
+

Erro no Cadastro:

+

{error}

+
+ )} + +
+
+ + handleInputChange("nomeCompleto", e.target.value)} + placeholder="Nome e Sobrenome" + required + /> +
+ +
+
+ + handleInputChange("email", e.target.value)} + placeholder="exemplo@dominio.com" + required + /> +
+
+ + handleInputChange("password", e.target.value)} + placeholder="••••••••" + required + /> +
+
+ +
+
+ + handleInputChange("telefone", e.target.value)} + placeholder="(00) 00000-0000" + maxLength={15} + /> +
+
+ + +
+
+
+ + {/* Action Buttons */} +
+ + + + +
+
+
+
+ ); +} \ No newline at end of file diff --git a/app/manager/usuario/page.tsx b/app/manager/usuario/page.tsx new file mode 100644 index 0000000..00c4836 --- /dev/null +++ b/app/manager/usuario/page.tsx @@ -0,0 +1,245 @@ +"use client"; + +import React, { useEffect, useState, useCallback } from "react" +import ManagerLayout from "@/components/manager-layout"; +import Link from "next/link" +import { useRouter } from "next/navigation"; +import { Button } from "@/components/ui/button" +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" +import { Plus, Edit, Trash2, Eye, Filter, Loader2 } from "lucide-react" +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, +} from "@/components/ui/alert-dialog" + +// Mock user service for demonstration. Replace with your actual API service. +const usersService = { + list: async (): Promise => { + console.log("API Call: Fetching users..."); + await new Promise(resolve => setTimeout(resolve, 500)); // Simulate network delay + return [ + { id: 1, full_name: 'Alice Admin', email: 'alice.admin@example.com', phone: '(11) 98765-4321', role: 'user' }, + + ]; + }, + delete: async (id: number): Promise => { + console.log(`API Call: Deleting user with ID ${id}`); + await new Promise(resolve => setTimeout(resolve, 700)); + // In a real app, you'd handle potential errors here + } +}; + +// Interface for a User object +interface User { + id: number; + full_name: string; + email: string; + phone: string | null; + role: 'admin' | 'gestor' | 'medico' | 'secretaria' | 'user'; +} + +// Interface for User Details (can be the same as User for this case) +interface UserDetails extends User {} + +export default function UsersPage() { + const router = useRouter(); + + const [users, setUsers] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [detailsDialogOpen, setDetailsDialogOpen] = useState(false); + const [userDetails, setUserDetails] = useState(null); + const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); + const [userToDeleteId, setUserToDeleteId] = useState(null); + + const fetchUsers = useCallback(async () => { + setLoading(true); + setError(null); + try { + const data: User[] = await usersService.list(); + setUsers(data || []); + } catch (e: any) { + console.error("Erro ao carregar lista de usuários:", e); + setError("Não foi possível carregar a lista de usuários. Tente novamente."); + setUsers([]); + } finally { + setLoading(false); + } + }, []); + + useEffect(() => { + fetchUsers(); + }, [fetchUsers]); + + const openDetailsDialog = (user: User) => { + setUserDetails(user); + setDetailsDialogOpen(true); + }; + + const handleDelete = async () => { + if (userToDeleteId === null) return; + + setLoading(true); + try { + await usersService.delete(userToDeleteId); + console.log(`Usuário com ID ${userToDeleteId} excluído com sucesso!`); + setDeleteDialogOpen(false); + setUserToDeleteId(null); + await fetchUsers(); // Refresh the list after deletion + } catch (e) { + console.error("Erro ao excluir:", e); + alert("Erro ao excluir usuário."); + } finally { + setLoading(false); + } + }; + + const openDeleteDialog = (userId: number) => { + setUserToDeleteId(userId); + setDeleteDialogOpen(true); + }; + + const handleEdit = (userId: number) => { + // Assuming the edit page is at a similar path + router.push(`/manager/usuario/${userId}/editar`); + }; + + return ( + +
+
+
+

Usuários Cadastrados

+

Gerencie todos os usuários do sistema.

+
+ + + +
+ + {/* Filters Section */} +
+ + +
+ + {/* Users Table */} +
+ {loading ? ( +
+ + Carregando usuários... +
+ ) : error ? ( +
+ {error} +
+ ) : users.length === 0 ? ( +
+ Nenhum usuário cadastrado. Adicione um novo. +
+ ) : ( +
+ + + + + + + + + + + + {users.map((user) => ( + + + + + + + + ))} + +
Nome CompletoE-mailTelefonePapelAções
{user.full_name}{user.email}{user.phone || "N/A"}{user.role} +
+ + + +
+
+
+ )} +
+ + {/* Delete Confirmation Dialog */} + + + + Confirma a exclusão? + + Esta ação é irreversível e excluirá permanentemente o registro deste usuário. + + + + Cancelar + + {loading ? : null} + Excluir + + + + + + {/* User Details Dialog */} + + + + {userDetails?.full_name} + + {userDetails && ( +
+
+
E-mail: {userDetails.email}
+
Telefone: {userDetails.phone || 'Não informado'}
+
Papel: {userDetails.role}
+
+
+ )} +
+
+ + Fechar + +
+
+
+
+ ); +} \ No newline at end of file diff --git a/app/page.tsx b/app/page.tsx index 75cbf28..b254689 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -6,29 +6,29 @@ import { Button } from "@/components/ui/button" export default function InicialPage() { return ( -
+
{} -
+
Horário: 08h00 - 21h00 Email: contato@medconnect.com
{} -
-

MedConnect

-
diff --git a/app/patient/login/page.tsx b/app/patient/login/page.tsx index 1dd8046..4fc06ab 100644 --- a/app/patient/login/page.tsx +++ b/app/patient/login/page.tsx @@ -1,164 +1,35 @@ -"use client" +// Caminho: app/(patient)/login/page.tsx -import type React from "react" +import Link from "next/link"; +import { LoginForm } from "@/components/LoginForm"; +import { Button } from "@/components/ui/button"; +import { ArrowLeft } from "lucide-react"; -import { useState } from "react" -import { useRouter } from "next/navigation" -import Link from "next/link" -import { Button } from "@/components/ui/button" -import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" -import { Input } from "@/components/ui/input" -import { Label } from "@/components/ui/label" -import { Eye, EyeOff, ArrowLeft, Stethoscope, Mail, Lock } from "lucide-react" +export default function PatientLoginPage() { + return ( +
+
+
+ + + Voltar ao início + +
-export default function PatientLogin() { - const [showPassword, setShowPassword] = useState(false) - const [email, setEmail] = useState("") - const [password, setPassword] = useState("") - const [isLoading, setIsLoading] = useState(false) - const router = useRouter() + + {/* Este bloco é passado como 'children' para o LoginForm */} + + + + - const handleLogin = async (e: React.FormEvent) => { - e.preventDefault() - setIsLoading(true) - - // Simulação de login - em produção, conectar com API real - setTimeout(() => { - if (email && password) { - // Salvar dados do usuário no localStorage para simulação - localStorage.setItem( - "patientData", - JSON.stringify({ - name: "João Silva", - email: email, - phone: "(11) 99999-9999", - cpf: "123.456.789-00", - birthDate: "1990-01-01", - address: "Rua das Flores, 123 - São Paulo, SP", - }), - ) - router.push("/patient/dashboard") - } - setIsLoading(false) - }, 1000) - } - - return ( -
-
-
- - - Voltar ao início - -
- - - -
- + {/* Conteúdo e espaçamento restaurados */} +
+

Problemas para acessar? Entre em contato conosco

+
- Área do Paciente - - Acesse sua conta para gerenciar consultas e laudos - -
- - -
-
- -
- - setEmail(e.target.value)} - className="pl-11 h-12 border-slate-200 focus:border-blue-500 focus:ring-blue-500" - required - /> -
-
- -
- -
- - setPassword(e.target.value)} - className="pl-11 pr-12 h-12 border-slate-200 focus:border-blue-500 focus:ring-blue-500" - required - /> - -
-
- - -
- -
-
-
-
-
-
- Novo por aqui? -
-
-
- - Criar nova conta - -
-
-
-
- -
-

Problemas para acessar? Entre em contato conosco

-
-
- ) + ); } diff --git a/app/patient/schedule/page.tsx b/app/patient/schedule/page.tsx index 859bdc6..a0333ca 100644 --- a/app/patient/schedule/page.tsx +++ b/app/patient/schedule/page.tsx @@ -1,7 +1,7 @@ "use client" import type React from "react" -import { useState } from "react" +import { useState, useEffect, useCallback } from "react" // Importações de componentes omitidas para brevidade, mas estão no código original import PatientLayout from "@/components/patient-layout" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" @@ -11,6 +11,15 @@ import { Label } from "@/components/ui/label" import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select" import { Textarea } from "@/components/ui/textarea" import { Calendar, Clock, User } from "lucide-react" +import { doctorsService } from "services/doctorsApi.mjs"; + +interface Doctor { + id: string; + full_name: string; + specialty: string; + phone_mobile: string; + +} // Chave do LocalStorage, a mesma usada em secretarypage.tsx const APPOINTMENTS_STORAGE_KEY = "clinic-appointments"; @@ -20,13 +29,30 @@ export default function ScheduleAppointment() { const [selectedDate, setSelectedDate] = useState("") const [selectedTime, setSelectedTime] = useState("") const [notes, setNotes] = useState("") + const [doctors, setDoctors] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); - const doctors = [ - { id: "1", name: "Dr. João Silva", specialty: "Cardiologia" }, - { id: "2", name: "Dra. Maria Santos", specialty: "Dermatologia" }, - { id: "3", name: "Dr. Pedro Costa", specialty: "Ortopedia" }, - { id: "4", name: "Dra. Ana Lima", specialty: "Ginecologia" }, - ] + + const fetchDoctors = useCallback(async () => { + setLoading(true); + setError(null); + try { + + const data: Doctor[] = await doctorsService.list(); + setDoctors(data || []); + } catch (e: any) { + console.error("Erro ao carregar lista de médicos:", e); + setError("Não foi possível carregar a lista de médicos. Verifique a conexão com a API."); + setDoctors([]); + } finally { + setLoading(false); + } + }, []); + + useEffect(() => { + fetchDoctors(); + }, [fetchDoctors]); const availableTimes = [ "08:00", @@ -65,7 +91,7 @@ export default function ScheduleAppointment() { const newAppointment = { id: new Date().getTime(), // ID único simples patientName: patientDetails.full_name, - doctor: doctorDetails.name, // Nome completo do médico (necessário para a listagem) + doctor: doctorDetails.full_name, // Nome completo do médico (necessário para a listagem) specialty: doctorDetails.specialty, date: selectedDate, time: selectedTime, @@ -83,7 +109,7 @@ export default function ScheduleAppointment() { // 3. Salva a lista atualizada no LocalStorage localStorage.setItem(APPOINTMENTS_STORAGE_KEY, JSON.stringify(updatedAppointments)); - alert(`Consulta com ${doctorDetails.name} agendada com sucesso!`); + alert(`Consulta com ${doctorDetails.full_name} agendada com sucesso!`); // Limpar o formulário após o sucesso (opcional) setSelectedDoctor(""); @@ -118,7 +144,7 @@ export default function ScheduleAppointment() { {doctors.map((doctor) => ( - {doctor.name} - {doctor.specialty} + {doctor.full_name} - {doctor.specialty} ))} @@ -185,7 +211,7 @@ export default function ScheduleAppointment() { {selectedDoctor && (
- {doctors.find((d) => d.id === selectedDoctor)?.name} + {doctors.find((d) => d.id === selectedDoctor)?.full_name}
)} diff --git a/app/secretary/appointments/page.tsx b/app/secretary/appointments/page.tsx new file mode 100644 index 0000000..2687690 --- /dev/null +++ b/app/secretary/appointments/page.tsx @@ -0,0 +1,283 @@ +"use client"; + +import { useState, useEffect } from "react"; +import SecretaryLayout from "@/components/secretary-layout"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { Button } from "@/components/ui/button"; +import { Badge } from "@/components/ui/badge"; +import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { Textarea } from "@/components/ui/textarea"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; +import { Calendar, Clock, MapPin, Phone, CalendarDays, X, User } from "lucide-react"; +import { toast } from "sonner"; +import Link from "next/link"; + +const APPOINTMENTS_STORAGE_KEY = "clinic-appointments"; + +const initialAppointments = [ + { + id: 1, + patientName: "Carlos Pereira", + doctor: "Dr. João Silva", + specialty: "Cardiologia", + date: "2024-01-15", + time: "14:30", + status: "agendada", + location: "Consultório A - 2º andar", + phone: "(11) 3333-4444", + }, + { + id: 2, + patientName: "Ana Beatriz Costa", + doctor: "Dra. Maria Santos", + specialty: "Dermatologia", + date: "2024-01-22", + time: "10:00", + status: "agendada", + location: "Consultório B - 1º andar", + phone: "(11) 3333-5555", + }, + { + id: 3, + patientName: "Roberto Almeida", + doctor: "Dr. Pedro Costa", + specialty: "Ortopedia", + date: "2024-01-08", + time: "16:00", + status: "realizada", + location: "Consultório C - 3º andar", + phone: "(11) 3333-6666", + }, + { + id: 4, + patientName: "Fernanda Lima", + doctor: "Dra. Ana Lima", + specialty: "Ginecologia", + date: "2024-01-05", + time: "09:30", + status: "realizada", + location: "Consultório D - 2º andar", + phone: "(11) 3333-7777", + }, +]; + +export default function SecretaryAppointments() { + const [appointments, setAppointments] = useState([]); + const [rescheduleModal, setRescheduleModal] = useState(false); + const [cancelModal, setCancelModal] = useState(false); + const [selectedAppointment, setSelectedAppointment] = useState(null); + const [rescheduleData, setRescheduleData] = useState({ date: "", time: "", reason: "" }); + const [cancelReason, setCancelReason] = useState(""); + + useEffect(() => { + const storedAppointments = localStorage.getItem(APPOINTMENTS_STORAGE_KEY); + if (storedAppointments) { + setAppointments(JSON.parse(storedAppointments)); + } else { + setAppointments(initialAppointments); + localStorage.setItem(APPOINTMENTS_STORAGE_KEY, JSON.stringify(initialAppointments)); + } + }, []); + + const updateAppointments = (updatedAppointments: any[]) => { + setAppointments(updatedAppointments); + localStorage.setItem(APPOINTMENTS_STORAGE_KEY, JSON.stringify(updatedAppointments)); + }; + + const handleReschedule = (appointment: any) => { + setSelectedAppointment(appointment); + setRescheduleData({ date: "", time: "", reason: "" }); + setRescheduleModal(true); + }; + + const handleCancel = (appointment: any) => { + setSelectedAppointment(appointment); + setCancelReason(""); + setCancelModal(true); + }; + + const confirmReschedule = () => { + if (!rescheduleData.date || !rescheduleData.time) { + toast.error("Por favor, selecione uma nova data e horário"); + return; + } + const updated = appointments.map((apt) => (apt.id === selectedAppointment.id ? { ...apt, date: rescheduleData.date, time: rescheduleData.time } : apt)); + updateAppointments(updated); + setRescheduleModal(false); + toast.success("Consulta reagendada com sucesso!"); + }; + + const confirmCancel = () => { + if (!cancelReason.trim() || cancelReason.trim().length < 10) { + toast.error("O motivo do cancelamento é obrigatório e deve ter no mínimo 10 caracteres."); + return; + } + const updated = appointments.map((apt) => (apt.id === selectedAppointment.id ? { ...apt, status: "cancelada" } : apt)); + updateAppointments(updated); + setCancelModal(false); + toast.success("Consulta cancelada com sucesso!"); + }; + + const getStatusBadge = (status: string) => { + switch (status) { + case "agendada": + return Agendada; + case "realizada": + return Realizada; + case "cancelada": + return Cancelada; + default: + return {status}; + } + }; + + const timeSlots = ["08:00", "08:30", "09:00", "09:30", "10:00", "10:30", "11:00", "11:30", "14:00", "14:30", "15:00", "15:30", "16:00", "16:30", "17:00", "17:30"]; + + return ( + +
+
+
+

Consultas Agendadas

+

Gerencie as consultas dos pacientes

+
+ + + +
+ +
+ {appointments.length > 0 ? ( + appointments.map((appointment) => ( + + +
+
+ {appointment.doctor} + {appointment.specialty} +
+ {getStatusBadge(appointment.status)} +
+
+ +
+
+
+ + {appointment.patientName} +
+
+ + {new Date(appointment.date).toLocaleDateString("pt-BR", { timeZone: "UTC" })} +
+
+ + {appointment.time} +
+
+
+
+ + {appointment.location} +
+
+ + {appointment.phone} +
+
+
+ + {appointment.status === "agendada" && ( +
+ + +
+ )} +
+
+ )) + ) : ( +

Nenhuma consulta encontrada.

+ )} +
+
+ + + + + Reagendar Consulta + Reagendar consulta com {selectedAppointment?.doctor} para {selectedAppointment?.patientName} + +
+
+ + setRescheduleData((prev) => ({ ...prev, date: e.target.value }))} min={new Date().toISOString().split("T")[0]} /> +
+
+ + +
+
+ +