From 9b250a41a033ff5391a144a66bffbd544d79a5e3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=9C=EB=8F=99=ED=98=84?= <hando1220@ajou.ac.kr>
Date: Mon, 10 Mar 2025 16:31:07 +0900
Subject: [PATCH] =?UTF-8?q?feat:=20=ED=94=84=EB=A1=9C=EC=A0=9D=ED=8A=B8=20?=
 =?UTF-8?q?=EB=AA=A9=EB=A1=9D=20API=20=EC=97=B0=EB=8F=99?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 src/components/app-sidebar.tsx      |  9 ++++-----
 src/components/project-switcher.tsx | 20 +++++++++++++-------
 src/components/site-header.tsx      |  6 +++---
 src/stores/authStore.ts             | 17 ++++++++++++++---
 src/types/project.ts                |  4 ++++
 5 files changed, 38 insertions(+), 18 deletions(-)
 create mode 100644 src/types/project.ts

diff --git a/src/components/app-sidebar.tsx b/src/components/app-sidebar.tsx
index 5f90f3f..9db571b 100644
--- a/src/components/app-sidebar.tsx
+++ b/src/components/app-sidebar.tsx
@@ -17,7 +17,6 @@ import {
 import { ProjectSwitcher } from '@/components/project-switcher';
 
 const data = {
-  projects: ['aolda_edu', 'proxy_manager', 'blog'],
   menus: [
     {
       title: '웹 프록시 서버',
@@ -58,15 +57,15 @@ const data = {
 };
 
 export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
-  const { token } = useAuthStore();
+  const { token, selectedProject } = useAuthStore();
   const location = useLocation();
   const selected = location.pathname.split('/')[1] || '';
 
   return (
     <Sidebar className="top-(--header-height) h-[calc(100svh-var(--header-height))]!" {...props}>
-      {token ? (
+      {token && selectedProject ? (
         <SidebarHeader>
-          <ProjectSwitcher projects={data.projects} defaultProject={data.projects[0]} />
+          <ProjectSwitcher />
         </SidebarHeader>
       ) : null}
       <SidebarContent>
@@ -78,7 +77,7 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
                 {item.items.map((item) => (
                   <SidebarMenuItem key={item.title}>
                     <SidebarMenuButton asChild isActive={selected === item.url}>
-                      <Link to={item.url} aria-disabled={token === null}>
+                      <Link to={item.url} aria-disabled={!token || !selectedProject}>
                         <item.icon />
                         {item.title}
                       </Link>
diff --git a/src/components/project-switcher.tsx b/src/components/project-switcher.tsx
index 485b5ba..1a78082 100644
--- a/src/components/project-switcher.tsx
+++ b/src/components/project-switcher.tsx
@@ -1,4 +1,4 @@
-import * as React from 'react';
+import { useAuthStore } from '@/stores/authStore';
 import { Check, ChevronsUpDown, GalleryVerticalEnd } from 'lucide-react';
 import {
   DropdownMenu,
@@ -8,8 +8,8 @@ import {
 } from '@/components/ui/dropdown-menu';
 import { SidebarMenu, SidebarMenuButton, SidebarMenuItem } from '@/components/ui/sidebar';
 
-export function ProjectSwitcher({ projects, defaultProject }: { projects: string[]; defaultProject: string }) {
-  const [selectedProject, setSelectedProject] = React.useState(defaultProject);
+export function ProjectSwitcher() {
+  const { projects, selectedProject, setSelectedProject } = useAuthStore();
 
   return (
     <SidebarMenu>
@@ -24,16 +24,22 @@ export function ProjectSwitcher({ projects, defaultProject }: { projects: string
                 <GalleryVerticalEnd className="size-4" />
               </div>
               <div className="flex flex-col gap-0.5 leading-none">
-                <span className="font-semibold">Project</span>
-                <span className="">{selectedProject}</span>
+                {selectedProject ? (
+                  <>
+                    <span className="font-semibold">Project</span>
+                    <span>{selectedProject.name}</span>
+                  </>
+                ) : (
+                  <span className="text-muted-foreground">프로젝트를 선택해주세요</span>
+                )}
               </div>
               <ChevronsUpDown className="ml-auto" />
             </SidebarMenuButton>
           </DropdownMenuTrigger>
           <DropdownMenuContent className="w-[--radix-dropdown-menu-trigger-width]" align="start">
             {projects.map((project) => (
-              <DropdownMenuItem key={project} onSelect={() => setSelectedProject(project)}>
-                {project} {project === selectedProject && <Check className="ml-auto" />}
+              <DropdownMenuItem key={project.id} onSelect={() => setSelectedProject(project)}>
+                {project.name} {project.id === selectedProject?.id && <Check className="ml-auto" />}
               </DropdownMenuItem>
             ))}
           </DropdownMenuContent>
diff --git a/src/components/site-header.tsx b/src/components/site-header.tsx
index ef6ec73..9085730 100644
--- a/src/components/site-header.tsx
+++ b/src/components/site-header.tsx
@@ -16,7 +16,7 @@ import {
 export function SiteHeader() {
   const navigate = useNavigate();
   const { toggleSidebar } = useSidebar();
-  const { token, logout } = useAuthStore();
+  const { token, username, logout } = useAuthStore();
 
   const handleLogout = () => {
     logout();
@@ -36,11 +36,11 @@ export function SiteHeader() {
           <DropdownMenu>
             <DropdownMenuTrigger asChild className="ml-auto cursor-pointer">
               <Avatar className="w-10 h-10 border">
-                <AvatarFallback>{'admin'.charAt(0)}</AvatarFallback>
+                <AvatarFallback>{username.charAt(0)}</AvatarFallback>
               </Avatar>
             </DropdownMenuTrigger>
             <DropdownMenuContent align="end">
-              <DropdownMenuLabel>닉네임</DropdownMenuLabel>
+              <DropdownMenuLabel>{username}</DropdownMenuLabel>
               <DropdownMenuSeparator />
               <DropdownMenuItem onClick={handleLogout} className="cursor-pointer">
                 <LogOut />
diff --git a/src/stores/authStore.ts b/src/stores/authStore.ts
index 28d337e..4dfbc7c 100644
--- a/src/stores/authStore.ts
+++ b/src/stores/authStore.ts
@@ -1,8 +1,14 @@
 import { create } from 'zustand';
 import { persist } from 'zustand/middleware';
+import { Project } from '@/types/project';
 
 export interface AuthStore {
   token: string | null;
+  username: string;
+  isAdmin: boolean;
+  projects: Project[];
+  selectedProject: Project | null;
+  setSelectedProject: (project: Project) => void;
   login: (username: string, password: string) => void;
   logout: () => void;
 }
@@ -11,6 +17,11 @@ export const useAuthStore = create<AuthStore>()(
   persist(
     (set) => ({
       token: null,
+      username: '',
+      isAdmin: false,
+      projects: [],
+      selectedProject: null,
+      setSelectedProject: (project) => set({ selectedProject: project }),
       login: async (username, password) => {
         const response = await fetch('/api/auth/login', {
           method: 'POST',
@@ -23,11 +34,11 @@ export const useAuthStore = create<AuthStore>()(
         }
 
         const token = response.headers.get('X-Subject-Token')!;
-        console.log(token);
+        const { isAdmin, projects } = await response.json();
 
-        set({ token });
+        set({ token, username, isAdmin, projects, selectedProject: projects[0] });
       },
-      logout: () => set({ token: null }),
+      logout: () => set({ token: null, username: '', isAdmin: false, projects: [], selectedProject: null }),
     }),
     { name: 'authStorage' }
   )
diff --git a/src/types/project.ts b/src/types/project.ts
new file mode 100644
index 0000000..d7e3291
--- /dev/null
+++ b/src/types/project.ts
@@ -0,0 +1,4 @@
+export interface Project {
+  id: string;
+  name: string;
+}
-- 
GitLab