Merge pull request #37 from m1guelmcf/fix/avatar-profile-sync
fix: loop infinito no perfil e sincronia de avatar na sidebar
This commit is contained in:
commit
097222df79
@ -1,18 +1,17 @@
|
|||||||
// ARQUIVO COMPLETO PARA: app/patient/profile/page.tsx
|
// Caminho: app/patient/profile/page.tsx
|
||||||
|
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState, useEffect, useRef } from "react";
|
import { useState, useEffect, useRef } from "react";
|
||||||
import Sidebar from "@/components/Sidebar";
|
import Sidebar from "@/components/Sidebar";
|
||||||
import { useAuthLayout } from "@/hooks/useAuthLayout";
|
import { useAuthLayout } from "@/hooks/useAuthLayout";
|
||||||
import { patientsService } from "@/services/patientsApi.mjs";
|
import { patientsService } from "@/services/patientsApi.mjs";
|
||||||
|
import { usersService } from "@/services/usersApi.mjs"; // Adicionado import
|
||||||
import { api } from "@/services/api.mjs";
|
import { api } from "@/services/api.mjs";
|
||||||
|
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { Textarea } from "@/components/ui/textarea";
|
|
||||||
import { User, Mail, Phone, Calendar, Upload } from "lucide-react";
|
import { User, Mail, Phone, Calendar, Upload } from "lucide-react";
|
||||||
import { toast } from "@/hooks/use-toast";
|
import { toast } from "@/hooks/use-toast";
|
||||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
|
||||||
@ -40,43 +39,63 @@ export default function PatientProfile() {
|
|||||||
const [isSaving, setIsSaving] = useState(false);
|
const [isSaving, setIsSaving] = useState(false);
|
||||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
const getInitials = (name: string) => {
|
||||||
console.log("PatientProfile MONTADO");
|
if (!name) return "U";
|
||||||
}, []);
|
return name
|
||||||
|
.split(" ")
|
||||||
|
.map((n) => n[0])
|
||||||
useEffect(() => {
|
.slice(0, 2)
|
||||||
const userId = user?.id;
|
.join("")
|
||||||
if (!userId) return;
|
.toUpperCase();
|
||||||
|
|
||||||
const fetchPatientDetails = async () => {
|
|
||||||
try {
|
|
||||||
const patientDetails = await patientsService.getById(userId);
|
|
||||||
setPatientData({
|
|
||||||
name: patientDetails.full_name || user.name,
|
|
||||||
email: user.email,
|
|
||||||
phone: patientDetails.phone_mobile || "",
|
|
||||||
cpf: patientDetails.cpf || "",
|
|
||||||
birthDate: patientDetails.birth_date || "",
|
|
||||||
cep: patientDetails.cep || "",
|
|
||||||
street: patientDetails.street || "",
|
|
||||||
number: patientDetails.number || "",
|
|
||||||
city: patientDetails.city || "",
|
|
||||||
avatarFullUrl: user.avatarFullUrl,
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
console.error("Erro ao buscar detalhes do paciente:", error);
|
|
||||||
toast({
|
|
||||||
title: "Erro",
|
|
||||||
description: "Não foi possível carregar seus dados completos.",
|
|
||||||
variant: "destructive",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
fetchPatientDetails();
|
// Função auxiliar para construir URL do avatar
|
||||||
}, [user?.id, user?.name, user?.email, user?.avatarFullUrl]);
|
const buildAvatarUrl = (path: string | null | undefined) => {
|
||||||
|
if (!path) return undefined;
|
||||||
|
const baseUrl = "https://yuanqfswhberkoevtmfr.supabase.co";
|
||||||
|
const cleanPath = path.startsWith('/') ? path.slice(1) : path;
|
||||||
|
const separator = cleanPath.includes('?') ? '&' : '?';
|
||||||
|
return `${baseUrl}/storage/v1/object/avatars/${cleanPath}${separator}t=${new Date().getTime()}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (user?.id) {
|
||||||
|
const loadData = async () => {
|
||||||
|
try {
|
||||||
|
// 1. Busca dados médicos (Tabela Patients)
|
||||||
|
const patientDetails = await patientsService.getById(user.id);
|
||||||
|
|
||||||
|
// 2. Busca dados de sistema frescos (Tabela Profiles via getMe)
|
||||||
|
// Isso garante que pegamos o avatar real do banco, não do cache local
|
||||||
|
const userSystemData = await usersService.getMe();
|
||||||
|
|
||||||
|
const freshAvatarPath = userSystemData?.profile?.avatar_url;
|
||||||
|
const freshAvatarUrl = buildAvatarUrl(freshAvatarPath);
|
||||||
|
|
||||||
|
setPatientData({
|
||||||
|
name: patientDetails.full_name || user.name,
|
||||||
|
email: user.email,
|
||||||
|
phone: patientDetails.phone_mobile || "",
|
||||||
|
cpf: patientDetails.cpf || "",
|
||||||
|
birthDate: patientDetails.birth_date || "",
|
||||||
|
cep: patientDetails.cep || "",
|
||||||
|
street: patientDetails.street || "",
|
||||||
|
number: patientDetails.number || "",
|
||||||
|
city: patientDetails.city || "",
|
||||||
|
avatarFullUrl: freshAvatarUrl, // Usa a URL fresca do banco
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Erro ao buscar detalhes:", error);
|
||||||
|
toast({
|
||||||
|
title: "Erro",
|
||||||
|
description: "Não foi possível carregar seus dados completos.",
|
||||||
|
variant: "destructive",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
loadData();
|
||||||
|
}
|
||||||
|
}, [user?.id, user?.email, user?.name]); // Removi user.avatarFullUrl para não depender do cache
|
||||||
|
|
||||||
const handleInputChange = (
|
const handleInputChange = (
|
||||||
field: keyof PatientProfileData,
|
field: keyof PatientProfileData,
|
||||||
@ -85,6 +104,27 @@ export default function PatientProfile() {
|
|||||||
setPatientData((prev) => (prev ? { ...prev, [field]: value } : null));
|
setPatientData((prev) => (prev ? { ...prev, [field]: value } : null));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const updateLocalSession = (updates: { full_name?: string; avatar_url?: string }) => {
|
||||||
|
try {
|
||||||
|
const storedUserString = localStorage.getItem("user_info");
|
||||||
|
if (storedUserString) {
|
||||||
|
const storedUser = JSON.parse(storedUserString);
|
||||||
|
|
||||||
|
if (!storedUser.user_metadata) storedUser.user_metadata = {};
|
||||||
|
if (updates.full_name) storedUser.user_metadata.full_name = updates.full_name;
|
||||||
|
if (updates.avatar_url) storedUser.user_metadata.avatar_url = updates.avatar_url;
|
||||||
|
|
||||||
|
if (!storedUser.profile) storedUser.profile = {};
|
||||||
|
if (updates.full_name) storedUser.profile.full_name = updates.full_name;
|
||||||
|
if (updates.avatar_url) storedUser.profile.avatar_url = updates.avatar_url;
|
||||||
|
|
||||||
|
localStorage.setItem("user_info", JSON.stringify(storedUser));
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Erro ao atualizar sessão local:", e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const handleSave = async () => {
|
const handleSave = async () => {
|
||||||
if (!patientData || !user) return;
|
if (!patientData || !user) return;
|
||||||
setIsSaving(true);
|
setIsSaving(true);
|
||||||
@ -99,12 +139,22 @@ export default function PatientProfile() {
|
|||||||
number: patientData.number,
|
number: patientData.number,
|
||||||
city: patientData.city,
|
city: patientData.city,
|
||||||
};
|
};
|
||||||
|
|
||||||
await patientsService.update(user.id, patientPayload);
|
await patientsService.update(user.id, patientPayload);
|
||||||
|
await api.patch(`/rest/v1/profiles?id=eq.${user.id}`, {
|
||||||
|
full_name: patientData.name,
|
||||||
|
});
|
||||||
|
|
||||||
|
updateLocalSession({ full_name: patientData.name });
|
||||||
|
|
||||||
toast({
|
toast({
|
||||||
title: "Sucesso!",
|
title: "Sucesso!",
|
||||||
description: "Seus dados foram atualizados.",
|
description: "Seus dados foram atualizados. A página será recarregada.",
|
||||||
});
|
});
|
||||||
|
|
||||||
setIsEditing(false);
|
setIsEditing(false);
|
||||||
|
setTimeout(() => window.location.reload(), 1000);
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Erro ao salvar dados:", error);
|
console.error("Erro ao salvar dados:", error);
|
||||||
toast({
|
toast({
|
||||||
@ -128,9 +178,6 @@ export default function PatientProfile() {
|
|||||||
if (!file || !user) return;
|
if (!file || !user) return;
|
||||||
|
|
||||||
const fileExt = file.name.split(".").pop();
|
const fileExt = file.name.split(".").pop();
|
||||||
|
|
||||||
// *** A CORREÇÃO ESTÁ AQUI ***
|
|
||||||
// O caminho salvo no banco de dados não deve conter o nome do bucket.
|
|
||||||
const filePath = `${user.id}/avatar.${fileExt}`;
|
const filePath = `${user.id}/avatar.${fileExt}`;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -139,15 +186,21 @@ export default function PatientProfile() {
|
|||||||
avatar_url: filePath,
|
avatar_url: filePath,
|
||||||
});
|
});
|
||||||
|
|
||||||
const newFullUrl = `https://yuanqfswhberkoevtmfr.supabase.co/storage/v1/object/public/avatars/${filePath}?t=${new Date().getTime()}`;
|
const newFullUrl = buildAvatarUrl(filePath);
|
||||||
|
|
||||||
setPatientData((prev) =>
|
setPatientData((prev) =>
|
||||||
prev ? { ...prev, avatarFullUrl: newFullUrl } : null
|
prev ? { ...prev, avatarFullUrl: newFullUrl } : null
|
||||||
);
|
);
|
||||||
|
|
||||||
|
updateLocalSession({ avatar_url: filePath });
|
||||||
|
|
||||||
toast({
|
toast({
|
||||||
title: "Sucesso!",
|
title: "Sucesso!",
|
||||||
description: "Sua foto de perfil foi atualizada.",
|
description: "Sua foto de perfil foi atualizada.",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
setTimeout(() => window.location.reload(), 1000);
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Erro no upload do avatar:", error);
|
console.error("Erro no upload do avatar:", error);
|
||||||
toast({
|
toast({
|
||||||
@ -161,7 +214,9 @@ export default function PatientProfile() {
|
|||||||
if (isAuthLoading || !patientData) {
|
if (isAuthLoading || !patientData) {
|
||||||
return (
|
return (
|
||||||
<Sidebar>
|
<Sidebar>
|
||||||
<div>Carregando seus dados...</div>
|
<div className="flex items-center justify-center h-full">
|
||||||
|
<p className="text-muted-foreground">Carregando seus dados...</p>
|
||||||
|
</div>
|
||||||
</Sidebar>
|
</Sidebar>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -320,22 +375,20 @@ export default function PatientProfile() {
|
|||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="space-y-4">
|
<CardContent className="space-y-4">
|
||||||
<div className="flex items-center space-x-3">
|
<div className="flex items-center space-x-3">
|
||||||
<div className="relative">
|
<div className="relative group">
|
||||||
<Avatar
|
<Avatar
|
||||||
className="w-16 h-16 cursor-pointer"
|
className="w-16 h-16 cursor-pointer border-2 border-transparent group-hover:border-blue-500 transition-all"
|
||||||
onClick={handleAvatarClick}
|
onClick={handleAvatarClick}
|
||||||
>
|
>
|
||||||
<AvatarImage src={patientData.avatarFullUrl} />
|
<AvatarImage src={patientData.avatarFullUrl} className="object-cover" />
|
||||||
<AvatarFallback className="text-2xl">
|
<AvatarFallback className="text-2xl bg-gray-200 text-gray-700 font-bold">
|
||||||
{patientData.name
|
{getInitials(patientData.name)}
|
||||||
.split(" ")
|
|
||||||
.map((n) => n[0])
|
|
||||||
.join("")}
|
|
||||||
</AvatarFallback>
|
</AvatarFallback>
|
||||||
</Avatar>
|
</Avatar>
|
||||||
<div
|
<div
|
||||||
className="absolute bottom-0 right-0 bg-primary text-primary-foreground rounded-full p-1 cursor-pointer hover:bg-primary/80"
|
className="absolute bottom-0 right-0 bg-blue-600 text-white rounded-full p-1.5 cursor-pointer hover:bg-blue-700 shadow-md transition-colors"
|
||||||
onClick={handleAvatarClick}
|
onClick={handleAvatarClick}
|
||||||
|
title="Alterar foto"
|
||||||
>
|
>
|
||||||
<Upload className="w-3 h-3" />
|
<Upload className="w-3 h-3" />
|
||||||
</div>
|
</div>
|
||||||
@ -344,12 +397,12 @@ export default function PatientProfile() {
|
|||||||
ref={fileInputRef}
|
ref={fileInputRef}
|
||||||
onChange={handleAvatarUpload}
|
onChange={handleAvatarUpload}
|
||||||
className="hidden"
|
className="hidden"
|
||||||
accept="image/png, image/jpeg"
|
accept="image/png, image/jpeg, image/jpg"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<p className="font-medium">{patientData.name}</p>
|
<p className="font-medium text-lg">{patientData.name}</p>
|
||||||
<p className="text-sm text-muted-foreground">Paciente</p>
|
<p className="text-sm text-gray-500">Paciente</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-3 pt-4 border-t">
|
<div className="space-y-3 pt-4 border-t">
|
||||||
@ -380,4 +433,4 @@ export default function PatientProfile() {
|
|||||||
</div>
|
</div>
|
||||||
</Sidebar>
|
</Sidebar>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -6,6 +6,7 @@ import { useRouter, usePathname } from "next/navigation";
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import Cookies from "js-cookie";
|
import Cookies from "js-cookie";
|
||||||
import { api } from "@/services/api.mjs";
|
import { api } from "@/services/api.mjs";
|
||||||
|
import { usersService } from "@/services/usersApi.mjs"; // Importando usersService
|
||||||
import { useAccessibility } from "@/app/context/AccessibilityContext";
|
import { useAccessibility } from "@/app/context/AccessibilityContext";
|
||||||
|
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
@ -46,6 +47,7 @@ interface UserData {
|
|||||||
full_name: string;
|
full_name: string;
|
||||||
phone_mobile: string;
|
phone_mobile: string;
|
||||||
role: string;
|
role: string;
|
||||||
|
avatar_url?: string;
|
||||||
};
|
};
|
||||||
identities: {
|
identities: {
|
||||||
identity_id: string;
|
identity_id: string;
|
||||||
@ -71,8 +73,18 @@ export default function Sidebar({ children }: SidebarProps) {
|
|||||||
const [role, setRole] = useState<string>();
|
const [role, setRole] = useState<string>();
|
||||||
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
|
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
|
||||||
const [showLogoutDialog, setShowLogoutDialog] = useState(false);
|
const [showLogoutDialog, setShowLogoutDialog] = useState(false);
|
||||||
|
const [avatarFullUrl, setAvatarFullUrl] = useState<string | undefined>(undefined);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
|
|
||||||
|
// Função auxiliar para construir URL
|
||||||
|
const buildAvatarUrl = (path: string) => {
|
||||||
|
if (!path) return undefined;
|
||||||
|
const baseUrl = "https://yuanqfswhberkoevtmfr.supabase.co";
|
||||||
|
const cleanPath = path.startsWith('/') ? path.slice(1) : path;
|
||||||
|
const separator = cleanPath.includes('?') ? '&' : '?';
|
||||||
|
return `${baseUrl}/storage/v1/object/avatars/${cleanPath}${separator}t=${new Date().getTime()}`;
|
||||||
|
};
|
||||||
const { theme, contrast } = useAccessibility();
|
const { theme, contrast } = useAccessibility();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -80,31 +92,82 @@ export default function Sidebar({ children }: SidebarProps) {
|
|||||||
const token = localStorage.getItem("token");
|
const token = localStorage.getItem("token");
|
||||||
|
|
||||||
if (userInfoString && token) {
|
if (userInfoString && token) {
|
||||||
const userInfo = JSON.parse(userInfoString);
|
try {
|
||||||
|
const userInfo = JSON.parse(userInfoString);
|
||||||
|
|
||||||
|
// 1. Tenta pegar o avatar do cache local
|
||||||
|
let rawAvatarPath =
|
||||||
|
userInfo.profile?.avatar_url ||
|
||||||
|
userInfo.user_metadata?.avatar_url ||
|
||||||
|
userInfo.app_metadata?.avatar_url ||
|
||||||
|
"";
|
||||||
|
|
||||||
|
// Configura estado inicial com o que tem no cache
|
||||||
|
setUserData({
|
||||||
|
id: userInfo.id ?? "",
|
||||||
|
email: userInfo.email ?? "",
|
||||||
|
app_metadata: {
|
||||||
|
user_role: userInfo.app_metadata?.user_role ?? "patient",
|
||||||
|
},
|
||||||
|
user_metadata: {
|
||||||
|
cpf: userInfo.user_metadata?.cpf ?? "",
|
||||||
|
email_verified: userInfo.user_metadata?.email_verified ?? false,
|
||||||
|
full_name: userInfo.user_metadata?.full_name || userInfo.profile?.full_name || "Usuário",
|
||||||
|
phone_mobile: userInfo.user_metadata?.phone_mobile ?? "",
|
||||||
|
role: userInfo.user_metadata?.role ?? "",
|
||||||
|
avatar_url: rawAvatarPath,
|
||||||
|
},
|
||||||
|
identities: userInfo.identities ?? [],
|
||||||
|
is_anonymous: userInfo.is_anonymous ?? false,
|
||||||
|
});
|
||||||
|
|
||||||
|
setRole(userInfo.user_metadata?.role);
|
||||||
|
|
||||||
|
if (rawAvatarPath) {
|
||||||
|
setAvatarFullUrl(buildAvatarUrl(rawAvatarPath));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. AUTO-REPARO: Se não tiver avatar ou profile no cache, busca na API e atualiza
|
||||||
|
if (!rawAvatarPath || !userInfo.profile) {
|
||||||
|
console.log("[Sidebar] Cache incompleto. Buscando dados frescos...");
|
||||||
|
usersService.getMe().then((freshData) => {
|
||||||
|
if (freshData && freshData.profile) {
|
||||||
|
const freshAvatar = freshData.profile.avatar_url;
|
||||||
|
|
||||||
|
// Atualiza o objeto local
|
||||||
|
const updatedUserInfo = {
|
||||||
|
...userInfo,
|
||||||
|
profile: freshData.profile, // Injeta o profile completo
|
||||||
|
user_metadata: {
|
||||||
|
...userInfo.user_metadata,
|
||||||
|
avatar_url: freshAvatar || userInfo.user_metadata.avatar_url
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Salva no localStorage para a próxima vez
|
||||||
|
localStorage.setItem("user_info", JSON.stringify(updatedUserInfo));
|
||||||
|
console.log("[Sidebar] LocalStorage sincronizado com sucesso.");
|
||||||
|
|
||||||
|
// Atualiza visualmente se achou um avatar novo
|
||||||
|
if (freshAvatar && freshAvatar !== rawAvatarPath) {
|
||||||
|
setAvatarFullUrl(buildAvatarUrl(freshAvatar));
|
||||||
|
// Atualiza o userData também para refletir no tooltip
|
||||||
|
setUserData(prev => prev ? ({
|
||||||
|
...prev,
|
||||||
|
user_metadata: {
|
||||||
|
...prev.user_metadata,
|
||||||
|
avatar_url: freshAvatar
|
||||||
|
}
|
||||||
|
}) : undefined);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}).catch(err => console.error("[Sidebar] Falha no auto-reparo:", err));
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
console.error("Erro ao processar dados do usuário na Sidebar:", e);
|
||||||
|
}
|
||||||
|
|
||||||
setUserData({
|
|
||||||
id: userInfo.id ?? "",
|
|
||||||
email: userInfo.email ?? "",
|
|
||||||
app_metadata: {
|
|
||||||
user_role: userInfo.app_metadata?.user_role ?? "patient",
|
|
||||||
},
|
|
||||||
user_metadata: {
|
|
||||||
cpf: userInfo.user_metadata?.cpf ?? "",
|
|
||||||
email_verified: userInfo.user_metadata?.email_verified ?? false,
|
|
||||||
full_name: userInfo.user_metadata?.full_name ?? "",
|
|
||||||
phone_mobile: userInfo.user_metadata?.phone_mobile ?? "",
|
|
||||||
role: userInfo.user_metadata?.role ?? "",
|
|
||||||
},
|
|
||||||
identities:
|
|
||||||
userInfo.identities?.map((identity: any) => ({
|
|
||||||
identity_id: identity.identity_id ?? "",
|
|
||||||
id: identity.id ?? "",
|
|
||||||
user_id: identity.user_id ?? "",
|
|
||||||
provider: identity.provider ?? "",
|
|
||||||
})) ?? [],
|
|
||||||
is_anonymous: userInfo.is_anonymous ?? false,
|
|
||||||
});
|
|
||||||
setRole(userInfo.user_metadata?.role);
|
|
||||||
} else {
|
} else {
|
||||||
router.push("/login");
|
router.push("/login");
|
||||||
}
|
}
|
||||||
@ -129,6 +192,7 @@ export default function Sidebar({ children }: SidebarProps) {
|
|||||||
try {
|
try {
|
||||||
await api.logout();
|
await api.logout();
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
console.error("Erro ao fazer logout", error);
|
||||||
} finally {
|
} finally {
|
||||||
localStorage.removeItem("user_info");
|
localStorage.removeItem("user_info");
|
||||||
localStorage.removeItem("token");
|
localStorage.removeItem("token");
|
||||||
@ -303,6 +367,7 @@ export default function Sidebar({ children }: SidebarProps) {
|
|||||||
sidebarCollapsed={sidebarCollapsed}
|
sidebarCollapsed={sidebarCollapsed}
|
||||||
handleLogout={handleLogout}
|
handleLogout={handleLogout}
|
||||||
isActive={role !== "paciente"}
|
isActive={role !== "paciente"}
|
||||||
|
avatarUrl={avatarFullUrl}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -33,6 +33,7 @@ interface Props {
|
|||||||
sidebarCollapsed: boolean;
|
sidebarCollapsed: boolean;
|
||||||
handleLogout: () => void;
|
handleLogout: () => void;
|
||||||
isActive: boolean;
|
isActive: boolean;
|
||||||
|
avatarUrl?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function SidebarUserSection({
|
export default function SidebarUserSection({
|
||||||
@ -40,6 +41,7 @@ export default function SidebarUserSection({
|
|||||||
sidebarCollapsed,
|
sidebarCollapsed,
|
||||||
handleLogout,
|
handleLogout,
|
||||||
isActive,
|
isActive,
|
||||||
|
avatarUrl,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const pathname = usePathname();
|
const pathname = usePathname();
|
||||||
const menuItems: any[] = [
|
const menuItems: any[] = [
|
||||||
@ -56,6 +58,18 @@ export default function SidebarUserSection({
|
|||||||
{ href: "/patient/reports", icon: ClipboardPlus, label: "Meus Laudos" },
|
{ href: "/patient/reports", icon: ClipboardPlus, label: "Meus Laudos" },
|
||||||
{ href: "/patient/profile", icon: SquareUser, label: "Meus Dados" },
|
{ href: "/patient/profile", icon: SquareUser, label: "Meus Dados" },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// Função auxiliar para obter iniciais
|
||||||
|
const getInitials = (name: string) => {
|
||||||
|
if (!name) return "U";
|
||||||
|
return name
|
||||||
|
.split(" ")
|
||||||
|
.map((n) => n[0])
|
||||||
|
.slice(0, 2)
|
||||||
|
.join("")
|
||||||
|
.toUpperCase();
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="border-t p-4 mt-auto">
|
<div className="border-t p-4 mt-auto">
|
||||||
{/* POPUP DE INFORMAÇÕES DO USUÁRIO */}
|
{/* POPUP DE INFORMAÇÕES DO USUÁRIO */}
|
||||||
@ -67,12 +81,13 @@ export default function SidebarUserSection({
|
|||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<Avatar>
|
<Avatar>
|
||||||
<AvatarImage src="/placeholder.svg?height=40&width=40" />
|
<AvatarImage
|
||||||
<AvatarFallback>
|
src={avatarUrl}
|
||||||
{userData.user_metadata.full_name
|
alt={userData.user_metadata.full_name}
|
||||||
.split(" ")
|
className="object-cover"
|
||||||
.map((n) => n[0])
|
/>
|
||||||
.join("")}
|
<AvatarFallback className="text-black bg-gray-200 font-semibold">
|
||||||
|
{getInitials(userData.user_metadata.full_name)}
|
||||||
</AvatarFallback>
|
</AvatarFallback>
|
||||||
</Avatar>
|
</Avatar>
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user