From 0c16743230a11b88aa64497e29bf4488701563d2 Mon Sep 17 00:00:00 2001 From: Sosokker Date: Fri, 14 Feb 2025 08:06:27 +0700 Subject: [PATCH] feat: fetch user profile on sidebar --- frontend/api/config.ts | 7 ++--- frontend/api/user.ts | 22 +++++++++++++ frontend/components/sidebar/app-sidebar.tsx | 33 +++++++++++++++++--- frontend/public/avatars/avatar.webp | Bin 0 -> 7176 bytes frontend/types.ts | 11 +++++++ 5 files changed, 65 insertions(+), 8 deletions(-) create mode 100644 frontend/api/user.ts create mode 100644 frontend/public/avatars/avatar.webp diff --git a/frontend/api/config.ts b/frontend/api/config.ts index a48150b..68c107d 100644 --- a/frontend/api/config.ts +++ b/frontend/api/config.ts @@ -1,4 +1,5 @@ import axios from "axios"; +import Cookies from "js-cookie"; const axiosInstance = axios.create({ baseURL: process.env.NEXT_PUBLIC_BACKEND_URL || "http://localhost:8000", @@ -9,7 +10,7 @@ const axiosInstance = axios.create({ axiosInstance.interceptors.request.use( (config) => { - const token = localStorage.getItem("token"); + const token = Cookies.get("token"); if (token) { config.headers.Authorization = `Bearer ${token}`; } @@ -20,9 +21,7 @@ axiosInstance.interceptors.request.use( axiosInstance.interceptors.response.use( (response) => response, - (error) => { - return Promise.reject(error); - } + (error) => Promise.reject(error) ); export default axiosInstance; diff --git a/frontend/api/user.ts b/frontend/api/user.ts new file mode 100644 index 0000000..50b5669 --- /dev/null +++ b/frontend/api/user.ts @@ -0,0 +1,22 @@ +import axios from "axios"; +import axiosInstance from "./config"; +import { User } from "@/types"; + +export interface UserDataOutput { + user: User; +} + +/** + * Fetches the data for the authenticated user. + */ +export async function fetchUserMe(): Promise { + try { + const response = await axiosInstance.get("/user/me"); + return response.data; + } catch (error: any) { + if (axios.isAxiosError(error)) { + throw new Error(error.response?.data?.message || "Failed to fetch user data."); + } + throw error; + } +} diff --git a/frontend/components/sidebar/app-sidebar.tsx b/frontend/components/sidebar/app-sidebar.tsx index a2a1186..5faacd1 100644 --- a/frontend/components/sidebar/app-sidebar.tsx +++ b/frontend/components/sidebar/app-sidebar.tsx @@ -19,12 +19,14 @@ import { NavProjects } from "./nav-projects"; import { NavUser } from "./nav-user"; import { TeamSwitcher } from "./team-switcher"; import { Sidebar, SidebarContent, SidebarFooter, SidebarHeader, SidebarRail } from "@/components/ui/sidebar"; +import { useEffect } from "react"; +import { fetchUserMe } from "@/api/user"; const data = { user: { name: "shadcn", email: "m@example.com", - avatar: "/avatars/shadcn.jpg", + avatar: "/avatars/avatar.webp", }, teams: [ { @@ -134,6 +136,31 @@ const data = { }; export function AppSidebar({ ...props }: React.ComponentProps) { + const [user, setUser] = React.useState<{ name: string; email: string; avatar: string }>({ + name: "", + email: "", + avatar: "/avatars/avatar.webp", + }); + const [loading, setLoading] = React.useState(true); + const [error, setError] = React.useState(""); + + useEffect(() => { + async function getUser() { + try { + const data = await fetchUserMe(); + let to_set = user; + to_set.name = data.user.UUID; + to_set.email = data.user.Email; + setUser(to_set); + } catch (err: any) { + setError(err.message); + } finally { + setLoading(false); + } + } + getUser(); + }, []); + return ( @@ -143,9 +170,7 @@ export function AppSidebar({ ...props }: React.ComponentProps) { - - - + {loading ? "Loading..." : error ? error : } ); diff --git a/frontend/public/avatars/avatar.webp b/frontend/public/avatars/avatar.webp new file mode 100644 index 0000000000000000000000000000000000000000..20f5a9e7aaaafd1e3309bcb7b8d4e20af9cb49db GIT binary patch literal 7176 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