From 3b645402ba203935ef5306bbf566ae7d2e1e0fc8 Mon Sep 17 00:00:00 2001 From: Gabriel Lira Figueira Date: Wed, 5 Nov 2025 18:37:03 -0300 Subject: [PATCH] =?UTF-8?q?refactor(manager):=20unifica=20fluxo=20de=20cri?= =?UTF-8?q?a=C3=A7=C3=A3o=20de=20m=C3=A9dico=20e=20usu=C3=A1rio?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove o componente de formulário de criação de médico (`/manager/home/novo`) que estava duplicado e desatualizado. O botão "Novo Usuário" na página de gerenciamento de médicos (`/manager/home`) foi redirecionado para o formulário genérico e aprimorado em `/manager/usuario/novo`. Essa alteração centraliza toda a lógica de criação de usuários em um único componente, aproveitando a UI condicional já implementada para a role "medico" e simplificando a manutenção do código. --- app/manager/home/page.tsx | 6 - app/secretary/pacientes/novo/page.tsx | 788 +++++--------------------- 2 files changed, 142 insertions(+), 652 deletions(-) diff --git a/app/manager/home/page.tsx b/app/manager/home/page.tsx index 7a39967..0063e7f 100644 --- a/app/manager/home/page.tsx +++ b/app/manager/home/page.tsx @@ -158,12 +158,6 @@ export default function DoctorsPage() {

Médicos Cadastrados

Gerencie todos os profissionais de saúde.

- - - diff --git a/app/secretary/pacientes/novo/page.tsx b/app/secretary/pacientes/novo/page.tsx index 0da8445..a028ea6 100644 --- a/app/secretary/pacientes/novo/page.tsx +++ b/app/secretary/pacientes/novo/page.tsx @@ -1,676 +1,172 @@ +// Caminho: app/(manager)/usuario/novo/page.tsx "use client"; -import type React from "react"; - import { useState } from "react"; -import Link from "next/link"; 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 { Textarea } from "@/components/ui/textarea"; -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; -import { Checkbox } from "@/components/ui/checkbox"; -import { Upload, Plus, X, ChevronDown } from "lucide-react"; -import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible"; -import { useToast } from "@/hooks/use-toast"; -import SecretaryLayout from "@/components/secretary-layout"; -import { patientsService } from "@/services/patientsApi.mjs"; +// O Select foi removido pois não é mais necessário +import { Save, Loader2 } from "lucide-react"; +import ManagerLayout from "@/components/manager-layout"; +// Os imports originais foram mantidos, como solicitado +import { usersService } from "services/usersApi.mjs"; +import { doctorsService } from "services/doctorsApi.mjs"; +import { login } from "services/api.mjs"; -export default function NovoPacientePage() { - const [anexosOpen, setAnexosOpen] = useState(false); - const [anexos, setAnexos] = useState([]); - const [isLoading, setIsLoading] = useState(false); +// Interface simplificada para refletir apenas os campos necessários +interface UserFormData { + email: string; + nomeCompleto: string; + telefone: string; + senha: string; + confirmarSenha: string; + cpf: string; +} + +const defaultFormData: UserFormData = { + email: "", + nomeCompleto: "", + telefone: "", + senha: "", + confirmarSenha: "", + cpf: "", +}; + +const cleanNumber = (value: string): string => value.replace(/\D/g, ""); +const formatPhone = (value: string): string => { + const cleaned = cleanNumber(value).substring(0, 11); + if (cleaned.length === 11) return cleaned.replace(/(\d{2})(\d{5})(\d{4})/, "($1) $2-$3"); + if (cleaned.length === 10) return cleaned.replace(/(\d{2})(\d{4})(\d{4})/, "($1) $2-$3"); + return cleaned; +}; + +export default function NovoUsuarioPage() { const router = useRouter(); - const { toast } = useToast(); + const [formData, setFormData] = useState(defaultFormData); + const [isSaving, setIsSaving] = useState(false); + const [error, setError] = useState(null); - const adicionarAnexo = () => { - setAnexos([...anexos, `Documento ${anexos.length + 1}`]); + const handleInputChange = (key: keyof UserFormData, value: string) => { + const updatedValue = key === "telefone" ? formatPhone(value) : value; + setFormData((prev) => ({ ...prev, [key]: updatedValue })); }; - const removerAnexo = (index: number) => { - setAnexos(anexos.filter((_, i) => i !== index)); - }; - - - const cleanNumber = (value: string): string => value.replace(/\D/g, ''); - - const formatCPF = (value: string): string => { - const cleaned = cleanNumber(value).substring(0, 11); - return cleaned.replace(/(\d{3})(\d{3})(\d{3})(\d{2})/, '$1.$2.$3-$4'); - }; - - const formatCEP = (value: string): string => { - const cleaned = cleanNumber(value).substring(0, 8); - return cleaned.replace(/(\d{5})(\d{3})/, '$1-$2'); - }; - - const formatPhoneMobile = (value: string): string => { - const cleaned = cleanNumber(value).substring(0, 11); - if (cleaned.length > 10) { - return cleaned.replace(/(\d{2})(\d{5})(\d{4})/, '+55 ($1) $2-$3'); - } - return cleaned.replace(/(\d{2})(\d{4})(\d{4})/, '+55 ($1) $2-$3'); - }; - - const handleSubmit = async (e: React.FormEvent) => { + const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); - if (isLoading) return; - setIsLoading(true); - const form = e.currentTarget; - const formData = new FormData(form); + setError(null); - const apiPayload = { - full_name: (formData.get("nome") as string) || "", // obrigatório - social_name: (formData.get("nomeSocial") as string) || undefined, - cpf: (formatCPF(formData.get("cpf") as string)) || "", // obrigatório - email: (formData.get("email") as string) || "", // obrigatório - phone_mobile: (formatPhoneMobile(formData.get("celular") as string)) || "", // obrigatório - birth_date: formData.get("dataNascimento") ? new Date(formData.get("dataNascimento") as string) : undefined, - sex: (formData.get("sexo") as string) || undefined, - blood_type: (formData.get("tipoSanguineo") as string) || undefined, - weight_kg: formData.get("peso") ? parseFloat(formData.get("peso") as string) : undefined, - height_m: formData.get("altura") ? parseFloat(formData.get("altura") as string) : undefined, - cep: (formatCEP(formData.get("cep") as string)) || undefined, - street: (formData.get("endereco") as string) || undefined, - number: (formData.get("numero") as string) || undefined, - complement: (formData.get("complemento") as string) || undefined, - neighborhood: (formData.get("bairro") as string) || undefined, - city: (formData.get("cidade") as string) || undefined, - state: (formData.get("estado") as string) || undefined, - }; - - console.log(apiPayload.email) - console.log(apiPayload.cep) - console.log(apiPayload.phone_mobile) - - const errors: string[] = []; - const fullName = apiPayload.full_name?.trim() || ""; - if (!fullName || fullName.length < 2 || fullName.length > 255) { - errors.push("Nome deve ter entre 2 e 255 caracteres."); - } - - const cpf = apiPayload.cpf || ""; - if (!/^\d{3}\.\d{3}\.\d{3}-\d{2}$/.test(cpf)) { - errors.push("CPF deve estar no formato XXX.XXX.XXX-XX."); - } - - const sex = apiPayload.sex; - const allowedSex = ["Masculino", "Feminino", "outro"]; - if (!sex || !allowedSex.includes(sex)) { - errors.push("Sexo é obrigatório e deve ser masculino, feminino ou outro."); - } - - if (!apiPayload.birth_date) { - errors.push("Data de nascimento é obrigatória."); - } - - const phoneMobile = apiPayload.phone_mobile || ""; - if (phoneMobile && !/^\+55 \(\d{2}\) \d{4,5}-\d{4}$/.test(phoneMobile)) { - errors.push("Celular deve estar no formato +55 (XX) XXXXX-XXXX."); - } - - const cep = apiPayload.cep || ""; - if (cep && !/^\d{5}-\d{3}$/.test(cep)) { - errors.push("CEP deve estar no formato XXXXX-XXX."); - } - - const state = apiPayload.state || ""; - if (state && state.length !== 2) { - errors.push("Estado (UF) deve ter 2 caracteres."); - } - if (errors.length) { - toast({ title: "Corrija os campos", description: errors[0] }); - console.log("campos errados") - setIsLoading(false); + // Validação simplificada + if (!formData.email || !formData.nomeCompleto || !formData.senha || !formData.confirmarSenha || !formData.cpf) { + setError("Por favor, preencha todos os campos obrigatórios."); return; } + if (formData.senha !== formData.confirmarSenha) { + setError("A Senha e a Confirmação de Senha não coincidem."); + return; + } + + setIsSaving(true); + try { - const res = await patientsService.create(apiPayload); - console.log(res) + // Payload agora é fixo para a role 'paciente' + const payload = { + full_name: formData.nomeCompleto, + email: formData.email.trim().toLowerCase(), + phone: formData.telefone || null, + role: "paciente", // Role fixada + password: formData.senha, + cpf: formData.cpf, + }; - let message = "Paciente cadastrado com sucesso"; - try { - if (!res[0].id) { - throw new Error(`${res.error} ${res.message}`|| "A API retornou erro"); - } else { - console.log(message) - } - } catch {} + console.log("📤 Enviando payload para criação de Usuário (Paciente):"); + console.log(payload); - toast({ - title: "Sucesso", - description: message, - }); - router.push("/secretary/pacientes"); - } catch (err: any) { - toast({ - title: "Erro", - description: err?.message || "Não foi possível cadastrar o paciente", - }); + // A chamada original à API foi mantida + await usersService.create_user(payload); + + router.push("/manager/usuario"); + } catch (e: any) { + console.error("Erro ao criar usuário:", e); + setError(e?.message || "Não foi possível criar o usuário. Verifique os dados e tente novamente."); } finally { - setIsLoading(false); + setIsSaving(false); } }; return ( - -
-
-
-

Novo Paciente

-

Cadastre um novo paciente no sistema

-
-
- -
-
-

Dados Pessoais

- -
-
-
- -
- -
- -
-
- - -
-
- - -
-
- -
-
- - -
-
- - -
-
- - -
-
- -
-
- -
- - -
-
-
- - -
-
- - -
-
- -
-
- - -
-
- - -
-
- -
-
- - -
-
- - -
-
- -
- - -
- -
-
- - -
-
- - -
-
- -
-
- - -
-
- - -
-
- -
-
- - -
-
- - -
-
- -
- - -
- -
- - -
- -
- - -
- -
- -