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