diff --git a/app/cadastro/page.tsx b/app/cadastro/page.tsx
index 843a490..9c772fb 100644
--- a/app/cadastro/page.tsx
+++ b/app/cadastro/page.tsx
@@ -10,7 +10,7 @@ export default function HomePage() {
Central de Operações
- MedConnect
+ MediConnect
diff --git a/app/manager/usuario/novo/page.tsx b/app/manager/usuario/novo/page.tsx
index e9f50e5..ee73c95 100644
--- a/app/manager/usuario/novo/page.tsx
+++ b/app/manager/usuario/novo/page.tsx
@@ -1,231 +1,259 @@
-"use client"
+"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"
+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";
import { usersService } from "services/usersApi.mjs";
+import { login } from "services/api.mjs";
+// Adicionada a propriedade 'senha' e 'confirmarSenha'
interface UserFormData {
- email: string;
- password: string;
- nomeCompleto: string;
- telefone: string;
- papel: string;
+ email: string;
+ nomeCompleto: string;
+ telefone: string;
+ papel: string;
+ senha: string;
+ confirmarSenha: string; // Novo campo para confirmação
}
const defaultFormData: UserFormData = {
- email: '',
- password: '',
- nomeCompleto: '',
- telefone: '',
- papel: '',
+ email: "",
+ nomeCompleto: "",
+ telefone: "",
+ papel: "",
+ senha: "",
+ confirmarSenha: "",
};
-// Remove todos os caracteres não numéricos
-const cleanNumber = (value: string): string => value.replace(/\D/g, '');
-
-// Definição do requisito mínimo de senha
-const MIN_PASSWORD_LENGTH = 8;
-
-
+// Funções de formatação de telefone
+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;
+ 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 [formData, setFormData] = useState(defaultFormData);
- const [isSaving, setIsSaving] = useState(false);
- const [error, setError] = useState(null);
+ const router = useRouter();
+ const [formData, setFormData] = useState(defaultFormData);
+ const [isSaving, setIsSaving] = useState(false);
+ const [error, setError] = useState(null);
- const handleInputChange = (key: keyof UserFormData, value: string) => {
- const updatedValue = key === 'telefone' ? formatPhone(value) : value;
- setFormData((prev) => ({ ...prev, [key]: updatedValue }));
- };
+ 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);
+ 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;
- }
+ // Validação de campos obrigatórios
+ if (!formData.email || !formData.nomeCompleto || !formData.papel || !formData.senha || !formData.confirmarSenha) {
+ setError("Por favor, preencha todos os campos obrigatórios.");
+ return;
+ }
- // Validação de comprimento mínimo da senha
- if (formData.password.length < MIN_PASSWORD_LENGTH) {
- setError(`A senha deve ter no mínimo ${MIN_PASSWORD_LENGTH} caracteres.`);
- return;
- }
+ // Validação de senhas
+ if (formData.senha !== formData.confirmarSenha) {
+ setError("A Senha e a Confirmação de Senha não coincidem.");
+ return;
+ }
- setIsSaving(true);
+ setIsSaving(true);
- // ----------------------------------------------------------------------
- // CORREÇÃO FINAL: Usa o formato de telefone que o mock API comprovadamente aceitou.
- // ----------------------------------------------------------------------
- const phoneValue = formData.telefone.trim();
+ try {
+ await login();
- // Prepara o payload com os campos obrigatórios
- const payload: any = {
- email: formData.email,
- password: formData.password,
- full_name: formData.nomeCompleto,
- role: formData.papel,
- };
+ const payload = {
+ full_name: formData.nomeCompleto,
+ email: formData.email.trim().toLowerCase(),
+ phone: formData.telefone || null,
+ role: formData.papel,
+ password: formData.senha, // Senha adicionada
+ };
- // Adiciona o telefone APENAS se estiver preenchido, enviando o formato FORMATADO.
- if (phoneValue.length > 0) {
- payload.phone = phoneValue;
- }
- // ----------------------------------------------------------------------
+ console.log("📤 Enviando payload:", payload);
+ await usersService.create_user(payload);
- try {
- await usersService.create_user(payload);
- router.push("/manager/usuario");
- } catch (e: any) {
- console.error("Erro ao criar usuário:", e);
- // Melhorando a mensagem de erro para o usuário final
- const apiErrorMsg = e.message?.includes("500")
- ? "Erro interno do servidor. Verifique os logs do backend ou tente novamente mais tarde. (Possível problema: E-mail já em uso ou falha de conexão.)"
- : e.message || "Ocorreu um erro inesperado. Tente novamente.";
+ router.push("/manager/usuario");
+ } catch (e: any) {
+ console.error("Erro ao criar usuário:", e);
+ const msg =
+ e.message ||
+ "Não foi possível criar o usuário. Verifique os dados e tente novamente.";
+ setError(msg);
+ } finally {
+ setIsSaving(false);
+ }
+ };
- setError(apiErrorMsg);
- } finally {
- setIsSaving(false);
- }
- };
-
- return (
-
-
-
-
-
Novo Usuário
-
- Preencha os dados para cadastrar um novo usuário no sistema.
-
-
-
-
-
-
-
-
+ return (
+
+ {/* Container principal: w-full e centralizado. max-w-screen-lg para evitar expansão excessiva */}
+
+
+
+ {/* Cabeçalho */}
+
+
+
Novo Usuário
+
+ Preencha os dados para cadastrar um novo usuário no sistema.
+
-
- );
-}
+
+
+
+
+
+ {/* Formulário */}
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/app/manager/usuario/page.tsx b/app/manager/usuario/page.tsx
index d126112..660db2a 100644
--- a/app/manager/usuario/page.tsx
+++ b/app/manager/usuario/page.tsx
@@ -1,15 +1,20 @@
+// app/manager/usuario/page.tsx
"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 {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@/components/ui/select";
+import { Plus, Eye, Filter, Loader2 } from "lucide-react";
import {
AlertDialog,
- AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
@@ -17,173 +22,144 @@ import {
AlertDialogHeader,
AlertDialogTitle,
} from "@/components/ui/alert-dialog";
-
+import { api, login } from "services/api.mjs";
import { usersService } from "services/usersApi.mjs";
-interface User {
- user: {
- id: string;
- email: string;
- email_confirmed_at?: string;
- created_at?: string;
- last_sign_in_at?: string;
- };
- profile: {
- id?: string;
- full_name?: string;
- email?: string;
- phone?: string | null;
- avatar_url?: string;
- disabled?: boolean;
- created_at?: string;
- updated_at?: string;
- };
- roles: string[];
- permissions: {
- isAdmin?: boolean;
- isManager?: boolean;
- isDoctor?: boolean;
- isSecretary?: boolean;
- isAdminOrManager?: boolean;
- [key: string]: boolean | undefined;
- };
-}
-
-
interface FlatUser {
- id: string;
- user_id: string;
+ id: string;
+ user_id: string;
full_name?: string;
email: string;
phone?: string | null;
role: string;
}
-
+interface UserInfoResponse {
+ user: any;
+ profile: any;
+ roles: string[];
+ permissions: Record
;
+}
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 [userDetails, setUserDetails] = useState(null);
const [selectedRole, setSelectedRole] = useState("");
const fetchUsers = useCallback(async () => {
- setLoading(true);
- setError(null);
- try {
- const data = await usersService.list_roles(); // já retorna o JSON diretamente
- console.log("Resposta da API list_roles:", data);
+ setLoading(true);
+ setError(null);
+ try {
+ // 1) pega roles
+ const rolesData: any[] = await usersService.list_roles();
+ // Garante que rolesData é array
+ const rolesArray = Array.isArray(rolesData) ? rolesData : [];
- if (Array.isArray(data)) {
- const mappedUsers: FlatUser[] = data.map((item: any) => ({
- id: item.id || (item.user_id ?? ""), // id da linha ou fallback
- user_id: item.user_id || item.id || "", // garante que user_id exista
- full_name: item.full_name || "—",
- email: item.email || "—",
- phone: item.phone ?? "—",
- role: item.role || "—",
- }));
+ // 2) pega todos os profiles de uma vez (para evitar muitos requests)
+ const profilesData: any[] = await api.get(`/rest/v1/profiles?select=id,full_name,email,phone`);
+ const profilesById = new Map();
+ if (Array.isArray(profilesData)) {
+ for (const p of profilesData) {
+ if (p?.id) profilesById.set(p.id, p);
+ }
+ }
- setUsers(mappedUsers);
- } else {
- console.warn("Formato inesperado recebido em list_roles:", data);
+ // 3) mapear roles -> flat users, usando ID específico de cada item
+ const mapped: FlatUser[] = rolesArray.map((roleItem) => {
+ const uid = roleItem.user_id;
+ const profile = profilesById.get(uid);
+ return {
+ id: uid,
+ user_id: uid,
+ full_name: profile?.full_name ?? "—",
+ email: profile?.email ?? "—",
+ phone: profile?.phone ?? "—",
+ role: roleItem.role ?? "—",
+ };
+ });
+
+ setUsers(mapped);
+ console.log("[fetchUsers] mapped count:", mapped.length);
+ } catch (err: any) {
+ console.error("Erro ao buscar usuários:", err);
+ setError("Não foi possível carregar os usuários. Veja console.");
setUsers([]);
+ } finally {
+ setLoading(false);
}
- } catch (err: any) {
- console.error("Erro ao buscar usuários:", err);
- setError("Não foi possível carregar os usuários. Tente novamente.");
- setUsers([]);
- } finally {
- setLoading(false);
- }
-}, []);
+ }, []);
-
useEffect(() => {
- fetchUsers();
+ const init = async () => {
+ try {
+ await login(); // garante token
+ } catch (e) {
+ console.warn("login falhou no init:", e);
+ }
+ await fetchUsers();
+ };
+ init();
}, [fetchUsers]);
-
-
-
-
const openDetailsDialog = async (flatUser: FlatUser) => {
- setDetailsDialogOpen(true);
- setUserDetails(null);
-
- try {
- console.log("Buscando detalhes do user_id:", flatUser.user_id);
- const fullUserData: User = await usersService.full_data(flatUser.user_id);
- setUserDetails(fullUserData);
- } catch (err: any) {
- console.error("Erro ao buscar detalhes do usuário:", err);
- setUserDetails({
- user: {
- id: flatUser.user_id,
- email: flatUser.email || "",
- created_at: "Erro ao Carregar",
- last_sign_in_at: "Erro ao Carregar",
- },
- profile: {
- full_name: flatUser.full_name || "Erro ao Carregar Detalhes",
- phone: flatUser.phone || "—",
- },
- roles: [],
- permissions: {},
- } as any);
- }
-};
-
-
-
- const filteredUsers = selectedRole && selectedRole !== "all"
- ? users.filter((u) => u.role === selectedRole)
- : users;
+ setDetailsDialogOpen(true);
+ setUserDetails(null);
+ try {
+ console.log("[openDetailsDialog] user_id:", flatUser.user_id);
+ const data = await usersService.full_data(flatUser.user_id);
+ console.log("[openDetailsDialog] full_data returned:", data);
+ setUserDetails(data);
+ } catch (err: any) {
+ console.error("Erro ao carregar detalhes:", err);
+ // fallback com dados já conhecidos
+ setUserDetails({
+ user: { id: flatUser.user_id, email: flatUser.email },
+ profile: { full_name: flatUser.full_name, phone: flatUser.phone },
+ roles: [flatUser.role],
+ permissions: {},
+ });
+ }
+ };
+ const filteredUsers =
+ selectedRole && selectedRole !== "all" ? users.filter((u) => u.role === selectedRole) : users;
return (
-
Usuários Cadastrados
-
Gerencie todos os usuários e seus papéis no sistema.
+
Usuários
+
Gerencie usuários.
-
-
+
+
+
+
+ Todos
+ Admin
+ Gestor
+ Médico
+ Secretária
+ Usuário
+
+
-
{loading ? (
@@ -194,11 +170,7 @@ export default function UsersPage() {
{error}
) : filteredUsers.length === 0 ? (
- Nenhum usuário encontrado.{" "}
-
- Adicione um novo
-
- .
+ Nenhum usuário encontrado.
) : (
@@ -209,31 +181,22 @@ export default function UsersPage() {
Nome |
E-mail |
Telefone |
-
Papel |
+
Cargo |
Ações |
- {filteredUsers.map((user) => (
-
-
- | {user.id} |
- {user.full_name || "—"} |
- {user.email || "—"} |
- {user.phone || "—"} |
- {user.role || "—"} |
+ {filteredUsers.map((u) => (
+
+ | {u.id} |
+ {u.full_name} |
+ {u.email} |
+ {u.phone} |
+ {u.role} |
-
-
-
-
+
|
))}
@@ -243,59 +206,31 @@ export default function UsersPage() {
)}
-
-
- {userDetails?.profile?.full_name || userDetails?.user?.email || "Detalhes do Usuário"}
-
+ {userDetails?.profile?.full_name || "Detalhes do Usuário"}
-
- {!userDetails ? (
-
+ {!userDetails ? (
+
Buscando dados completos...
-
- ) : (
-
-
-
ID: {userDetails.user.id}
-
E-mail: {userDetails.user.email}
-
Email confirmado em: {userDetails.user.email_confirmed_at || "—"}
-
Último login: {userDetails.user.last_sign_in_at || "—"}
-
Criado em: {userDetails.user.created_at || "—"}
-
-
-
Nome completo: {userDetails.profile.full_name || "—"}
-
Telefone: {userDetails.profile.phone || "—"}
- {userDetails.profile.avatar_url && (
-
Avatar: 
- )}
-
Conta desativada: {userDetails.profile.disabled ? "Sim" : "Não"}
-
Profile criado em: {userDetails.profile.created_at || "—"}
-
Profile atualizado em: {userDetails.profile.updated_at || "—"}
-
-
-
-
Roles:
-
- {userDetails.roles.map((role, idx) => - {role}
)}
-
-
-
-
Permissões:
-
- {Object.entries(userDetails.permissions).map(([key, value]) => (
- - {key}: {value ? "Sim" : "Não"}
- ))}
-
+ ) : (
+
+
ID: {userDetails.user.id}
+
E-mail: {userDetails.user.email}
+
Nome completo: {userDetails.profile.full_name}
+
Telefone: {userDetails.profile.phone}
+
Roles: {userDetails.roles?.join(", ")}
+
+
Permissões:
+
+ {Object.entries(userDetails.permissions || {}).map(([k,v]) => - {k}: {v ? "Sim" : "Não"}
)}
+
+
-
- )}
-
-
+ )}
@@ -306,4 +241,4 @@ export default function UsersPage() {
);
-}
\ No newline at end of file
+}
diff --git a/app/page.tsx b/app/page.tsx
index 5c3d1f6..c29ad38 100644
--- a/app/page.tsx
+++ b/app/page.tsx
@@ -10,12 +10,12 @@ export default function InicialPage() {
{}
Horário: 08h00 - 21h00
- Email: contato@medconnect.com
+ Email: contato@mediconnect.com
{}
- MedConnect
+ MediConnect
);
diff --git a/components/doctor-layout.tsx b/components/doctor-layout.tsx
index 2d4375d..69df494 100644
--- a/components/doctor-layout.tsx
+++ b/components/doctor-layout.tsx
@@ -135,7 +135,7 @@ export default function DoctorLayout({ children }: PatientLayoutProps) {
- MidConnecta
+ MediConnect
)}