forked from RiseUP/riseup-squad21
feat(admin, patient): implementa criação condicional e corrige layouts
Refatora o formulário de criação de usuários no painel do manager para lidar com a lógica de múltiplos endpoints, diferenciando a criação de médicos das demais roles. - Adiciona campos condicionais para CRM e especialidade na UI. - Implementa a chamada ao endpoint `/functions/v1/create-doctor` para a role "medico". - Ajusta o payload para o endpoint `/create-user-with-password` para as outras roles. fix(patient): corrige renderização duplicada do layout nas páginas de agendamento e consultas, removendo o wrapper redundante do `PatientLayout`. refactor(services): ajusta os serviços `doctorsApi` e `usersApi` para alinhar com os schemas de dados corretos da API.
This commit is contained in:
parent
50fd9141ce
commit
f8f5f8214a
@ -1,3 +1,5 @@
|
|||||||
|
// /app/manager/usuario/novo/page.tsx
|
||||||
|
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
@ -9,7 +11,8 @@ import { Label } from "@/components/ui/label";
|
|||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||||
import { Save, Loader2, Pause } from "lucide-react";
|
import { Save, Loader2, Pause } from "lucide-react";
|
||||||
import ManagerLayout from "@/components/manager-layout";
|
import ManagerLayout from "@/components/manager-layout";
|
||||||
import { usersService } from "services/usersApi.mjs";
|
import { usersService } from "@/services/usersApi.mjs";
|
||||||
|
import { doctorsService } from "@/services/doctorsApi.mjs"; // Importação adicionada
|
||||||
import { login } from "services/api.mjs";
|
import { login } from "services/api.mjs";
|
||||||
|
|
||||||
interface UserFormData {
|
interface UserFormData {
|
||||||
@ -20,6 +23,10 @@ interface UserFormData {
|
|||||||
senha: string;
|
senha: string;
|
||||||
confirmarSenha: string;
|
confirmarSenha: string;
|
||||||
cpf: string;
|
cpf: string;
|
||||||
|
// Novos campos para Médico
|
||||||
|
crm: string;
|
||||||
|
crm_uf: string;
|
||||||
|
specialty: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const defaultFormData: UserFormData = {
|
const defaultFormData: UserFormData = {
|
||||||
@ -30,6 +37,10 @@ const defaultFormData: UserFormData = {
|
|||||||
senha: "",
|
senha: "",
|
||||||
confirmarSenha: "",
|
confirmarSenha: "",
|
||||||
cpf: "",
|
cpf: "",
|
||||||
|
// Valores iniciais para campos de Médico
|
||||||
|
crm: "",
|
||||||
|
crm_uf: "",
|
||||||
|
specialty: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
const cleanNumber = (value: string): string => value.replace(/\D/g, "");
|
const cleanNumber = (value: string): string => value.replace(/\D/g, "");
|
||||||
@ -47,7 +58,13 @@ export default function NovoUsuarioPage() {
|
|||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
|
||||||
const handleInputChange = (key: keyof UserFormData, value: string) => {
|
const handleInputChange = (key: keyof UserFormData, value: string) => {
|
||||||
const updatedValue = key === "telefone" ? formatPhone(value) : value;
|
let updatedValue = value;
|
||||||
|
if (key === "telefone") {
|
||||||
|
updatedValue = formatPhone(value);
|
||||||
|
} else if (key === "crm_uf") {
|
||||||
|
// Converte UF para maiúsculas
|
||||||
|
updatedValue = value.toUpperCase();
|
||||||
|
}
|
||||||
setFormData((prev) => ({ ...prev, [key]: updatedValue }));
|
setFormData((prev) => ({ ...prev, [key]: updatedValue }));
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -65,22 +82,56 @@ export default function NovoUsuarioPage() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validação adicional para Médico
|
||||||
|
if (formData.papel === "medico") {
|
||||||
|
if (!formData.crm || !formData.crm_uf) {
|
||||||
|
setError("Para a função 'Médico', o CRM e a UF do CRM são obrigatórios.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setIsSaving(true);
|
setIsSaving(true);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const payload = {
|
if (formData.papel === "medico") {
|
||||||
full_name: formData.nomeCompleto,
|
// Lógica para criação de Médico
|
||||||
email: formData.email.trim().toLowerCase(),
|
const doctorPayload = {
|
||||||
phone: formData.telefone || null,
|
email: formData.email.trim().toLowerCase(),
|
||||||
role: formData.papel,
|
full_name: formData.nomeCompleto,
|
||||||
password: formData.senha,
|
cpf: formData.cpf,
|
||||||
cpf: formData.cpf,
|
crm: formData.crm,
|
||||||
};
|
crm_uf: formData.crm_uf,
|
||||||
|
specialty: formData.specialty || null,
|
||||||
|
phone_mobile: formData.telefone || null, // Usando phone_mobile conforme o schema
|
||||||
|
};
|
||||||
|
|
||||||
console.log("📤 Enviando payload:");
|
console.log("📤 Enviando payload para Médico:");
|
||||||
console.log(payload);
|
console.log(doctorPayload);
|
||||||
|
|
||||||
await usersService.create_user(payload);
|
// Chamada ao endpoint específico para criação de médico
|
||||||
|
await doctorsService.create(doctorPayload);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// Lógica para criação de Outras Roles
|
||||||
|
const isPatient = formData.papel === "paciente";
|
||||||
|
|
||||||
|
const userPayload = {
|
||||||
|
email: formData.email.trim().toLowerCase(),
|
||||||
|
password: formData.senha,
|
||||||
|
full_name: formData.nomeCompleto,
|
||||||
|
phone: formData.telefone || null,
|
||||||
|
role: formData.papel,
|
||||||
|
cpf: formData.cpf,
|
||||||
|
create_patient_record: isPatient, // true se a role for 'paciente'
|
||||||
|
phone_mobile: isPatient ? formData.telefone || null : undefined, // Enviar phone_mobile se for paciente
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log("📤 Enviando payload para Usuário Comum:");
|
||||||
|
console.log(userPayload);
|
||||||
|
|
||||||
|
// Chamada ao endpoint padrão para criação de usuário
|
||||||
|
await usersService.create_user(userPayload);
|
||||||
|
}
|
||||||
|
|
||||||
router.push("/manager/usuario");
|
router.push("/manager/usuario");
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
@ -91,6 +142,8 @@ export default function NovoUsuarioPage() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const isMedico = formData.papel === "medico";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ManagerLayout>
|
<ManagerLayout>
|
||||||
<div className="w-full h-full p-4 md:p-8 flex justify-center items-start">
|
<div className="w-full h-full p-4 md:p-8 flex justify-center items-start">
|
||||||
@ -140,6 +193,27 @@ export default function NovoUsuarioPage() {
|
|||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Campos Condicionais para Médico */}
|
||||||
|
{isMedico && (
|
||||||
|
<>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label htmlFor="crm">CRM *</Label>
|
||||||
|
<Input id="crm" value={formData.crm} onChange={(e) => handleInputChange("crm", e.target.value)} placeholder="Número do CRM" required />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label htmlFor="crm_uf">UF do CRM *</Label>
|
||||||
|
<Input id="crm_uf" value={formData.crm_uf} onChange={(e) => handleInputChange("crm_uf", e.target.value)} placeholder="Ex: SP" maxLength={2} required />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="space-y-2 md:col-span-2">
|
||||||
|
<Label htmlFor="specialty">Especialidade (opcional)</Label>
|
||||||
|
<Input id="specialty" value={formData.specialty} onChange={(e) => handleInputChange("specialty", e.target.value)} placeholder="Ex: Cardiologia" />
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{/* Fim dos Campos Condicionais */}
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="senha">Senha *</Label>
|
<Label htmlFor="senha">Senha *</Label>
|
||||||
<Input id="senha" type="password" value={formData.senha} onChange={(e) => handleInputChange("senha", e.target.value)} placeholder="Mínimo 8 caracteres" minLength={8} required />
|
<Input id="senha" type="password" value={formData.senha} onChange={(e) => handleInputChange("senha", e.target.value)} placeholder="Mínimo 8 caracteres" minLength={8} required />
|
||||||
|
|||||||
@ -182,8 +182,8 @@ export default function PatientAppointments() {
|
|||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<div className="flex justify-between items-center">
|
<div className="flex justify-between items-center">
|
||||||
<div>
|
<div>
|
||||||
<h1 className="text-3xl font-bold text-gray-900">Minhas Consultas</h1>
|
<h1 className="text-3xl font-bold text-foreground">Minhas Consultas</h1>
|
||||||
<p className="text-gray-600">Veja, reagende ou cancele suas consultas</p>
|
<p className="text-muted-foreground">Veja, reagende ou cancele suas consultas</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -244,7 +244,13 @@ export default function PatientAppointments() {
|
|||||||
</Card>
|
</Card>
|
||||||
))
|
))
|
||||||
) : (
|
) : (
|
||||||
<p className="text-gray-600">Você ainda não possui consultas agendadas.</p>
|
<Card className="p-6 text-center">
|
||||||
|
<CalendarDays className="mx-auto h-12 w-12 text-muted-foreground mb-4" />
|
||||||
|
<CardTitle className="text-xl">Nenhuma Consulta Encontrada</CardTitle>
|
||||||
|
<CardDescription className="mt-2">
|
||||||
|
Você ainda não possui consultas agendadas. Use o menu "Agendar Consulta" para começar.
|
||||||
|
</CardDescription>
|
||||||
|
</Card>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -134,8 +134,8 @@ export default function ScheduleAppointment() {
|
|||||||
{/* Médico */}
|
{/* Médico */}
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="doctor">Médico</Label>
|
<Label htmlFor="doctor">Médico</Label>
|
||||||
<Select value={selectedDoctor} onValueChange={setSelectedDoctor}>
|
<Select value={selectedDoctor} onValueChange={setSelectedDoctor} disabled={loading}>
|
||||||
<SelectTrigger>
|
<SelectTrigger id="doctor">
|
||||||
<SelectValue placeholder="Selecione um médico" />
|
<SelectValue placeholder="Selecione um médico" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
@ -168,7 +168,7 @@ export default function ScheduleAppointment() {
|
|||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="time">Horário</Label>
|
<Label htmlFor="time">Horário</Label>
|
||||||
<Select value={selectedTime} onValueChange={setSelectedTime}>
|
<Select value={selectedTime} onValueChange={setSelectedTime}>
|
||||||
<SelectTrigger>
|
<SelectTrigger id="time">
|
||||||
<SelectValue placeholder="Selecione um horário" />
|
<SelectValue placeholder="Selecione um horário" />
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
|
|||||||
@ -223,7 +223,7 @@ export default function PatientLayout({ children }: PatientLayoutProps) {
|
|||||||
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-muted-foreground w-4 h-4" />
|
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-muted-foreground w-4 h-4" />
|
||||||
<Input
|
<Input
|
||||||
placeholder="Buscar paciente"
|
placeholder="Buscar paciente"
|
||||||
className="pl-10 bg-background border-border"
|
className="pl-10 bg-background border-input"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -3,7 +3,10 @@ import { api } from "./api.mjs";
|
|||||||
export const doctorsService = {
|
export const doctorsService = {
|
||||||
list: () => api.get("/rest/v1/doctors"),
|
list: () => api.get("/rest/v1/doctors"),
|
||||||
getById: (id) => api.get(`/rest/v1/doctors?id=eq.${id}`).then(data => data[0]),
|
getById: (id) => api.get(`/rest/v1/doctors?id=eq.${id}`).then(data => data[0]),
|
||||||
create: (data) => api.post("/functions/v1/create-doctor", data),
|
async create(data) {
|
||||||
|
// Esta é a função usada no page.tsx para criar médicos
|
||||||
|
return await api.post("/functions/v1/create-doctor", data);
|
||||||
|
},
|
||||||
update: (id, data) => api.patch(`/rest/v1/doctors?id=eq.${id}`, data),
|
update: (id, data) => api.patch(`/rest/v1/doctors?id=eq.${id}`, data),
|
||||||
delete: (id) => api.delete(`/rest/v1/doctors?id=eq.${id}`),
|
delete: (id) => api.delete(`/rest/v1/doctors?id=eq.${id}`),
|
||||||
};
|
};
|
||||||
@ -1,5 +1,3 @@
|
|||||||
// SUBSTITUA O OBJETO INTEIRO EM services/usersApi.mjs
|
|
||||||
|
|
||||||
import { api } from "./api.mjs";
|
import { api } from "./api.mjs";
|
||||||
|
|
||||||
export const usersService = {
|
export const usersService = {
|
||||||
@ -19,6 +17,7 @@ export const usersService = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async create_user(data) {
|
async create_user(data) {
|
||||||
|
// Esta é a função usada no page.tsx para criar usuários que não são médicos
|
||||||
return await api.post(`/functions/v1/create-user-with-password`, data);
|
return await api.post(`/functions/v1/create-user-with-password`, data);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user