698 lines
26 KiB
TypeScript
698 lines
26 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useState } from "react";
|
|
import { useRouter } from "next/navigation";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Alert, AlertDescription } from "@/components/ui/alert";
|
|
import { Badge } from "@/components/ui/badge";
|
|
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
|
import { Label } from "@/components/ui/label";
|
|
import { Input } from "@/components/ui/input";
|
|
import { UploadAvatar } from "@/components/ui/upload-avatar";
|
|
import { AlertCircle, ArrowLeft, CheckCircle, XCircle } from "lucide-react";
|
|
import { getUserInfoById } from "@/lib/api";
|
|
import { useAuth } from "@/hooks/useAuth";
|
|
import { formatTelefone, formatCEP, validarCEP, buscarCEP } from "@/lib/utils";
|
|
|
|
interface UserProfile {
|
|
user: {
|
|
id: string;
|
|
email: string;
|
|
created_at: string;
|
|
last_sign_in_at: string | null;
|
|
email_confirmed_at: string | null;
|
|
};
|
|
profile: {
|
|
id: string;
|
|
full_name: string | null;
|
|
email: string | null;
|
|
phone: string | null;
|
|
avatar_url: string | null;
|
|
cep?: string | null;
|
|
street?: string | null;
|
|
number?: string | null;
|
|
complement?: string | null;
|
|
neighborhood?: string | null;
|
|
city?: string | null;
|
|
state?: string | null;
|
|
disabled: boolean;
|
|
created_at: string;
|
|
updated_at: string;
|
|
} | null;
|
|
roles: string[];
|
|
permissions: {
|
|
isAdmin: boolean;
|
|
isManager: boolean;
|
|
isDoctor: boolean;
|
|
isSecretary: boolean;
|
|
isAdminOrManager: boolean;
|
|
};
|
|
}
|
|
|
|
export default function PerfilPage() {
|
|
const router = useRouter();
|
|
const { user: authUser, updateUserProfile } = useAuth();
|
|
const [userInfo, setUserInfo] = useState<UserProfile | null>(null);
|
|
const [loading, setLoading] = useState(true);
|
|
const [error, setError] = useState<string | null>(null);
|
|
const [isEditing, setIsEditing] = useState(false);
|
|
const [editingData, setEditingData] = useState<{
|
|
phone?: string;
|
|
full_name?: string;
|
|
avatar_url?: string;
|
|
cep?: string;
|
|
street?: string;
|
|
number?: string;
|
|
complement?: string;
|
|
neighborhood?: string;
|
|
city?: string;
|
|
state?: string;
|
|
}>({});
|
|
const [cepLoading, setCepLoading] = useState(false);
|
|
const [cepValid, setCepValid] = useState<boolean | null>(null);
|
|
|
|
useEffect(() => {
|
|
async function loadUserInfo() {
|
|
try {
|
|
setLoading(true);
|
|
|
|
if (!authUser?.id) {
|
|
throw new Error("ID do usuário não encontrado");
|
|
}
|
|
|
|
console.log('[PERFIL] Chamando getUserInfoById com ID:', authUser.id);
|
|
|
|
// Para admin/gestor, usar getUserInfoById com o ID do usuário logado
|
|
const info = await getUserInfoById(authUser.id);
|
|
console.log('[PERFIL] Sucesso ao carregar info:', info);
|
|
setUserInfo(info as UserProfile);
|
|
setError(null);
|
|
} catch (err: any) {
|
|
console.error('[PERFIL] Erro ao carregar:', err);
|
|
setError(err?.message || "Erro ao carregar informações do perfil");
|
|
setUserInfo(null);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}
|
|
|
|
if (authUser) {
|
|
console.log('[PERFIL] useEffect acionado, authUser:', authUser);
|
|
loadUserInfo();
|
|
}
|
|
}, [authUser]);
|
|
|
|
if (authUser?.userType !== 'administrador') {
|
|
return (
|
|
<div className="flex flex-col h-screen">
|
|
<div className="flex-1 p-6">
|
|
<Alert variant="destructive">
|
|
<AlertCircle className="h-4 w-4" />
|
|
<AlertDescription>
|
|
Você não tem permissão para acessar esta página.
|
|
</AlertDescription>
|
|
</Alert>
|
|
<Button
|
|
variant="outline"
|
|
onClick={() => router.back()}
|
|
className="mt-4 hover:bg-primary/10 hover:text-primary dark:hover:bg-accent dark:hover:text-accent-foreground"
|
|
>
|
|
<ArrowLeft className="h-4 w-4 mr-2" />
|
|
Voltar
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (loading) {
|
|
return (
|
|
<div className="flex flex-col h-screen">
|
|
<div className="flex-1 p-6">
|
|
<div className="space-y-4">
|
|
<div className="h-20 bg-muted rounded-lg animate-pulse" />
|
|
<div className="h-64 bg-muted rounded-lg animate-pulse" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (error) {
|
|
return (
|
|
<div className="flex flex-col h-screen">
|
|
<div className="flex-1 p-6">
|
|
<Alert variant="destructive">
|
|
<AlertCircle className="h-4 w-4" />
|
|
<AlertDescription>{error}</AlertDescription>
|
|
</Alert>
|
|
<Button
|
|
variant="outline"
|
|
onClick={() => window.location.reload()}
|
|
className="mt-4"
|
|
>
|
|
Tentar Novamente
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (!userInfo) {
|
|
return (
|
|
<div className="flex flex-col h-screen">
|
|
<div className="flex-1 p-6">
|
|
<Alert>
|
|
<AlertCircle className="h-4 w-4" />
|
|
<AlertDescription>
|
|
Nenhuma informação de perfil disponível.
|
|
</AlertDescription>
|
|
</Alert>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const getInitials = (name: string | null | undefined) => {
|
|
if (!name) return "AD";
|
|
return name
|
|
.split(" ")
|
|
.map((n) => n[0])
|
|
.join("")
|
|
.toUpperCase()
|
|
.slice(0, 2);
|
|
};
|
|
|
|
const handleEditClick = () => {
|
|
if (!isEditing && userInfo) {
|
|
setEditingData({
|
|
full_name: userInfo.profile?.full_name || "",
|
|
phone: userInfo.profile?.phone || "",
|
|
avatar_url: userInfo.profile?.avatar_url || "",
|
|
cep: userInfo.profile?.cep || "",
|
|
street: userInfo.profile?.street || "",
|
|
number: userInfo.profile?.number || "",
|
|
complement: userInfo.profile?.complement || "",
|
|
neighborhood: userInfo.profile?.neighborhood || "",
|
|
city: userInfo.profile?.city || "",
|
|
state: userInfo.profile?.state || "",
|
|
});
|
|
// Se já existe CEP, marcar como válido
|
|
if (userInfo.profile?.cep) {
|
|
setCepValid(true);
|
|
}
|
|
}
|
|
setIsEditing(!isEditing);
|
|
};
|
|
|
|
const handleSaveEdit = async () => {
|
|
try {
|
|
// Aqui você implementaria a chamada para atualizar o perfil
|
|
console.log('[PERFIL] Salvando alterações:', editingData);
|
|
// await atualizarPerfil(userInfo?.user.id, editingData);
|
|
setIsEditing(false);
|
|
setUserInfo((prev) =>
|
|
prev ? {
|
|
...prev,
|
|
profile: prev.profile ? {
|
|
...prev.profile,
|
|
full_name: editingData.full_name || prev.profile.full_name,
|
|
phone: editingData.phone || prev.profile.phone,
|
|
avatar_url: editingData.avatar_url || prev.profile.avatar_url,
|
|
cep: editingData.cep || prev.profile.cep,
|
|
street: editingData.street || prev.profile.street,
|
|
number: editingData.number || prev.profile.number,
|
|
complement: editingData.complement || prev.profile.complement,
|
|
neighborhood: editingData.neighborhood || prev.profile.neighborhood,
|
|
city: editingData.city || prev.profile.city,
|
|
state: editingData.state || prev.profile.state,
|
|
} : null,
|
|
} : null
|
|
);
|
|
|
|
// Also update global auth profile so header/avatar updates immediately
|
|
try {
|
|
if (typeof updateUserProfile === 'function') {
|
|
updateUserProfile({
|
|
// Persist common keys used across the app
|
|
foto_url: editingData.avatar_url || undefined,
|
|
telefone: editingData.phone || undefined
|
|
});
|
|
} else {
|
|
// Fallback: try to persist directly to localStorage so next reload shows it
|
|
try {
|
|
const raw = localStorage.getItem('auth_user')
|
|
if (raw) {
|
|
const u = JSON.parse(raw)
|
|
u.profile = u.profile || {}
|
|
if (editingData.avatar_url) { u.profile.foto_url = editingData.avatar_url; u.profile.avatar_url = editingData.avatar_url }
|
|
if (editingData.phone) u.profile.telefone = editingData.phone
|
|
localStorage.setItem('auth_user', JSON.stringify(u))
|
|
}
|
|
} catch (_e) {}
|
|
}
|
|
} catch (err) {
|
|
console.warn('[PERFIL] Falha ao sincronizar profile global:', err)
|
|
}
|
|
} catch (err: any) {
|
|
console.error('[PERFIL] Erro ao salvar:', err);
|
|
}
|
|
};
|
|
|
|
const handleCancelEdit = () => {
|
|
setIsEditing(false);
|
|
setEditingData({});
|
|
setCepValid(null);
|
|
};
|
|
|
|
const handleCepChange = async (cepValue: string) => {
|
|
// Formatar CEP
|
|
const formatted = formatCEP(cepValue);
|
|
setEditingData({...editingData, cep: formatted});
|
|
|
|
// Validar CEP
|
|
const isValid = validarCEP(cepValue);
|
|
setCepValid(isValid ? null : false); // null = não validado ainda, false = inválido
|
|
|
|
if (isValid) {
|
|
setCepLoading(true);
|
|
try {
|
|
const resultado = await buscarCEP(cepValue);
|
|
if (resultado) {
|
|
setCepValid(true);
|
|
// Preencher campos automaticamente
|
|
setEditingData(prev => ({
|
|
...prev,
|
|
street: resultado.street,
|
|
neighborhood: resultado.neighborhood,
|
|
city: resultado.city,
|
|
state: resultado.state,
|
|
}));
|
|
console.log('[PERFIL] CEP preenchido com sucesso:', resultado);
|
|
} else {
|
|
setCepValid(false);
|
|
}
|
|
} catch (err) {
|
|
console.error('[PERFIL] Erro ao buscar CEP:', err);
|
|
setCepValid(false);
|
|
} finally {
|
|
setCepLoading(false);
|
|
}
|
|
}
|
|
};
|
|
|
|
const handlePhoneChange = (phoneValue: string) => {
|
|
const formatted = formatTelefone(phoneValue);
|
|
setEditingData({...editingData, phone: formatted});
|
|
};
|
|
|
|
return (
|
|
<div className="flex flex-col h-screen bg-background">
|
|
<div className="flex-1 overflow-y-auto">
|
|
<div className="p-6 space-y-6">
|
|
{/* Header com Título e Botão */}
|
|
<div className="flex items-center justify-between">
|
|
<div>
|
|
<h2 className="text-3xl font-bold">Meu Perfil</h2>
|
|
<p className="text-muted-foreground mt-1">Bem-vindo à sua área exclusiva.</p>
|
|
</div>
|
|
{!isEditing ? (
|
|
<Button
|
|
className="bg-blue-600 hover:bg-blue-700"
|
|
onClick={handleEditClick}
|
|
>
|
|
Editar Perfil
|
|
</Button>
|
|
) : (
|
|
<div className="flex gap-2">
|
|
<Button
|
|
className="bg-green-600 hover:bg-green-700"
|
|
onClick={handleSaveEdit}
|
|
>
|
|
✓ Salvar
|
|
</Button>
|
|
<Button
|
|
variant="outline"
|
|
onClick={handleCancelEdit}
|
|
>
|
|
✕ Cancelar
|
|
</Button>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Grid de 2 colunas */}
|
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
|
{/* Coluna Esquerda - Informações Pessoais */}
|
|
<div className="lg:col-span-2 space-y-6">
|
|
{/* Informações Pessoais */}
|
|
<div className="border border-border rounded-lg p-6">
|
|
<h3 className="text-lg font-semibold mb-4">Informações Pessoais</h3>
|
|
|
|
<div className="space-y-4">
|
|
{/* Nome Completo */}
|
|
<div>
|
|
<Label className="text-sm font-medium text-muted-foreground">
|
|
Nome Completo
|
|
</Label>
|
|
{isEditing ? (
|
|
<Input
|
|
value={editingData.full_name || ""}
|
|
onChange={(e) => setEditingData({...editingData, full_name: e.target.value})}
|
|
className="mt-2"
|
|
/>
|
|
) : (
|
|
<>
|
|
<div className="mt-2 p-3 bg-muted rounded text-foreground font-medium">
|
|
{userInfo.profile?.full_name || "Não preenchido"}
|
|
</div>
|
|
<p className="text-xs text-muted-foreground mt-1">
|
|
Este campo não pode ser alterado
|
|
</p>
|
|
</>
|
|
)}
|
|
</div>
|
|
|
|
{/* Email */}
|
|
<div>
|
|
<Label className="text-sm font-medium text-muted-foreground">
|
|
Email
|
|
</Label>
|
|
<div className="mt-2 p-3 bg-muted rounded text-foreground">
|
|
{userInfo.user.email}
|
|
</div>
|
|
<p className="text-xs text-muted-foreground mt-1">
|
|
Este campo não pode ser alterado
|
|
</p>
|
|
</div>
|
|
|
|
{/* UUID */}
|
|
<div>
|
|
<Label className="text-sm font-medium text-muted-foreground">
|
|
UUID
|
|
</Label>
|
|
<div className="mt-2 p-3 bg-muted rounded text-foreground font-mono text-xs break-all">
|
|
{userInfo.user.id}
|
|
</div>
|
|
<p className="text-xs text-muted-foreground mt-1">
|
|
Este campo não pode ser alterado
|
|
</p>
|
|
</div>
|
|
|
|
{/* Permissões */}
|
|
<div>
|
|
<Label className="text-sm font-medium text-muted-foreground">
|
|
Permissões
|
|
</Label>
|
|
<div className="mt-2 flex flex-wrap gap-2">
|
|
{userInfo.roles && userInfo.roles.length > 0 ? (
|
|
userInfo.roles.map((role) => (
|
|
<Badge key={role} variant="outline">
|
|
{role}
|
|
</Badge>
|
|
))
|
|
) : (
|
|
<span className="text-sm text-muted-foreground">
|
|
Nenhuma permissão atribuída
|
|
</span>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Endereço e Contato */}
|
|
<div className="border border-border rounded-lg p-6">
|
|
<h3 className="text-lg font-semibold mb-4">Endereço e Contato</h3>
|
|
|
|
<div className="space-y-4">
|
|
{/* Telefone */}
|
|
<div>
|
|
<Label className="text-sm font-medium text-muted-foreground">
|
|
Telefone
|
|
</Label>
|
|
{isEditing ? (
|
|
<Input
|
|
value={editingData.phone || ""}
|
|
onChange={(e) => handlePhoneChange(e.target.value)}
|
|
className="mt-2"
|
|
placeholder="(00) 00000-0000"
|
|
maxLength={15}
|
|
/>
|
|
) : (
|
|
<div className="mt-2 p-3 bg-muted rounded text-foreground">
|
|
{userInfo.profile?.phone || "Não preenchido"}
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Endereço */}
|
|
<div>
|
|
<Label className="text-sm font-medium text-muted-foreground">
|
|
Logradouro
|
|
</Label>
|
|
{isEditing ? (
|
|
<Input
|
|
value={editingData.street || ""}
|
|
onChange={(e) => setEditingData({...editingData, street: e.target.value})}
|
|
className="mt-2"
|
|
placeholder="Rua, avenida, etc."
|
|
/>
|
|
) : (
|
|
<div className="mt-2 p-3 bg-muted rounded text-foreground">
|
|
{userInfo.profile?.street || "Não preenchido"}
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Número */}
|
|
<div>
|
|
<Label className="text-sm font-medium text-muted-foreground">
|
|
Número
|
|
</Label>
|
|
{isEditing ? (
|
|
<Input
|
|
value={editingData.number || ""}
|
|
onChange={(e) => setEditingData({...editingData, number: e.target.value})}
|
|
className="mt-2"
|
|
placeholder="123"
|
|
/>
|
|
) : (
|
|
<div className="mt-2 p-3 bg-muted rounded text-foreground">
|
|
{userInfo.profile?.number || "Não preenchido"}
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Complemento */}
|
|
<div>
|
|
<Label className="text-sm font-medium text-muted-foreground">
|
|
Complemento
|
|
</Label>
|
|
{isEditing ? (
|
|
<Input
|
|
value={editingData.complement || ""}
|
|
onChange={(e) => setEditingData({...editingData, complement: e.target.value})}
|
|
className="mt-2"
|
|
placeholder="Apto 42, Bloco B, etc."
|
|
/>
|
|
) : (
|
|
<div className="mt-2 p-3 bg-muted rounded text-foreground">
|
|
{userInfo.profile?.complement || "Não preenchido"}
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Bairro */}
|
|
<div>
|
|
<Label className="text-sm font-medium text-muted-foreground">
|
|
Bairro
|
|
</Label>
|
|
{isEditing ? (
|
|
<Input
|
|
value={editingData.neighborhood || ""}
|
|
onChange={(e) => setEditingData({...editingData, neighborhood: e.target.value})}
|
|
className="mt-2"
|
|
placeholder="Vila, bairro, etc."
|
|
/>
|
|
) : (
|
|
<div className="mt-2 p-3 bg-muted rounded text-foreground">
|
|
{userInfo.profile?.neighborhood || "Não preenchido"}
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Cidade */}
|
|
<div>
|
|
<Label className="text-sm font-medium text-muted-foreground">
|
|
Cidade
|
|
</Label>
|
|
{isEditing ? (
|
|
<Input
|
|
value={editingData.city || ""}
|
|
onChange={(e) => setEditingData({...editingData, city: e.target.value})}
|
|
className="mt-2"
|
|
placeholder="São Paulo"
|
|
/>
|
|
) : (
|
|
<div className="mt-2 p-3 bg-muted rounded text-foreground">
|
|
{userInfo.profile?.city || "Não preenchido"}
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Estado */}
|
|
<div>
|
|
<Label className="text-sm font-medium text-muted-foreground">
|
|
Estado
|
|
</Label>
|
|
{isEditing ? (
|
|
<Input
|
|
value={editingData.state || ""}
|
|
onChange={(e) => setEditingData({...editingData, state: e.target.value})}
|
|
className="mt-2"
|
|
placeholder="SP"
|
|
maxLength={2}
|
|
/>
|
|
) : (
|
|
<div className="mt-2 p-3 bg-muted rounded text-foreground">
|
|
{userInfo.profile?.state || "Não preenchido"}
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* CEP */}
|
|
<div>
|
|
<Label className="text-sm font-medium text-muted-foreground">
|
|
CEP
|
|
</Label>
|
|
{isEditing ? (
|
|
<div className="space-y-2">
|
|
<div className="flex gap-2 items-end">
|
|
<div className="flex-1">
|
|
<Input
|
|
value={editingData.cep || ""}
|
|
onChange={(e) => handleCepChange(e.target.value)}
|
|
className="mt-2"
|
|
placeholder="00000-000"
|
|
maxLength={9}
|
|
disabled={cepLoading}
|
|
/>
|
|
</div>
|
|
{cepValid === true && (
|
|
<CheckCircle className="h-5 w-5 text-green-500 mb-2" />
|
|
)}
|
|
{cepValid === false && (
|
|
<XCircle className="h-5 w-5 text-red-500 mb-2" />
|
|
)}
|
|
</div>
|
|
{cepLoading && (
|
|
<p className="text-xs text-muted-foreground">Buscando CEP...</p>
|
|
)}
|
|
{cepValid === false && (
|
|
<p className="text-xs text-red-500">CEP inválido ou não encontrado</p>
|
|
)}
|
|
{cepValid === true && (
|
|
<p className="text-xs text-green-500">✓ CEP preenchido com sucesso</p>
|
|
)}
|
|
</div>
|
|
) : (
|
|
<div className="mt-2 p-3 bg-muted rounded text-foreground">
|
|
{userInfo.profile?.cep || "Não preenchido"}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Coluna Direita - Foto do Perfil */}
|
|
<div>
|
|
<div className="border border-border rounded-lg p-6">
|
|
<h3 className="text-lg font-semibold mb-4">Foto do Perfil</h3>
|
|
|
|
{isEditing ? (
|
|
<div className="space-y-4">
|
|
<UploadAvatar
|
|
userId={userInfo.user.id}
|
|
currentAvatarUrl={editingData.avatar_url || userInfo.profile?.avatar_url || "/avatars/01.png"}
|
|
onAvatarChange={(newUrl) => {
|
|
setEditingData({...editingData, avatar_url: newUrl})
|
|
try {
|
|
if (typeof updateUserProfile === 'function') {
|
|
updateUserProfile({ foto_url: newUrl })
|
|
} else {
|
|
const raw = localStorage.getItem('auth_user')
|
|
if (raw) {
|
|
const u = JSON.parse(raw)
|
|
u.profile = u.profile || {}
|
|
u.profile.foto_url = newUrl
|
|
u.profile.avatar_url = newUrl
|
|
localStorage.setItem('auth_user', JSON.stringify(u))
|
|
}
|
|
}
|
|
} catch (err) {
|
|
console.warn('[PERFIL] erro ao persistir avatar no auth_user localStorage', err)
|
|
}
|
|
}}
|
|
userName={editingData.full_name || userInfo.profile?.full_name || "Usuário"}
|
|
/>
|
|
</div>
|
|
) : (
|
|
<div className="flex flex-col items-center gap-4">
|
|
<Avatar className="h-24 w-24">
|
|
<AvatarImage
|
|
src={userInfo.profile?.avatar_url || "/avatars/01.png"}
|
|
alt={userInfo.profile?.full_name || "Usuário"}
|
|
/>
|
|
<AvatarFallback className="bg-primary text-primary-foreground text-2xl font-bold">
|
|
{getInitials(userInfo.profile?.full_name)}
|
|
</AvatarFallback>
|
|
</Avatar>
|
|
|
|
<div className="text-center space-y-2">
|
|
<p className="text-sm text-muted-foreground">
|
|
{getInitials(userInfo.profile?.full_name)}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Informações de Status */}
|
|
<div className="mt-6 pt-6 border-t border-border space-y-4">
|
|
<div>
|
|
<Label className="text-sm font-medium text-muted-foreground">
|
|
Status
|
|
</Label>
|
|
<div className="mt-2">
|
|
<Badge
|
|
variant={
|
|
userInfo.profile?.disabled ? "destructive" : "default"
|
|
}
|
|
>
|
|
{userInfo.profile?.disabled ? "Desabilitado" : "Ativo"}
|
|
</Badge>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Botão Voltar */}
|
|
<div className="flex gap-3 pb-6">
|
|
<Button
|
|
variant="outline"
|
|
onClick={() => router.back()}
|
|
className="hover:bg-primary/10 hover:text-primary dark:hover:bg-accent dark:hover:text-accent-foreground"
|
|
>
|
|
<ArrowLeft className="h-4 w-4 mr-2" />
|
|
Voltar
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|