Skip to content
Snippets Groups Projects
Commit 9b250a41 authored by 한동현's avatar 한동현
Browse files

feat: 프로젝트 목록 API 연동

parent b3096d94
No related branches found
No related tags found
No related merge requests found
......@@ -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>
......
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">
{selectedProject ? (
<>
<span className="font-semibold">Project</span>
<span className="">{selectedProject}</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>
......
......@@ -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 />
......
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' }
)
......
export interface Project {
id: string;
name: string;
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment