Feat: 워크스페이스 레이아웃 기반으로 Admin 사이트 레이아웃 구현

This commit is contained in:
정현조 2024-09-05 17:58:07 +09:00
parent a0dc26719b
commit 250cd864c5
4 changed files with 162 additions and 0 deletions

View File

@ -0,0 +1,44 @@
import '@/index.css';
import { Meta, StoryObj } from '@storybook/react';
import AdminLayout from './index';
import { BrowserRouter as Router } from 'react-router-dom';
import { Workspace } from '@/types';
const meta: Meta<typeof AdminLayout> = {
title: 'Layout/AdminLayout',
component: AdminLayout,
parameters: {
layout: 'fullscreen',
},
};
export default meta;
type Story = StoryObj<typeof AdminLayout>;
const workspace: Workspace = {
id: 1,
name: 'Workspace Alpha',
projects: [
{
id: 1,
name: 'Project Alpha',
type: 'Segmentation',
children: [],
},
{
id: 2,
name: 'Project Beta',
type: 'Classification',
children: [],
},
],
};
export const Default: Story = {
render: () => (
<Router>
<AdminLayout workspace={workspace} />
</Router>
),
};

View File

@ -0,0 +1,32 @@
import { Outlet } from 'react-router-dom';
import Header from '../Header';
import { ResizablePanelGroup, ResizablePanel } from '../ui/resizable';
import AdminProjectSidebar from '../AdminProjectSidebar';
import AdminMenuSidebar from '../AdminMenuSidebar';
import { Workspace } from '@/types';
interface AdminLayoutProps {
workspace: Workspace;
}
export default function AdminLayout({ workspace }: AdminLayoutProps) {
return (
<>
<Header className="fixed left-0 top-0" />
<div className="mt-16 h-[calc(100vh-64px)] w-screen">
<ResizablePanelGroup direction="horizontal">
<AdminProjectSidebar
workspaceName={workspace.name}
projects={workspace.projects}
/>
<ResizablePanel className="flex w-full items-center">
<main className="h-full grow">
<Outlet />
</main>
</ResizablePanel>
<AdminMenuSidebar />
</ResizablePanelGroup>
</div>
</>
);
}

View File

@ -0,0 +1,32 @@
import { useNavigate } from 'react-router-dom';
import { cn } from '@/lib/utils';
export default function AdminMenuSidebar() {
const navigate = useNavigate();
const menuItems = [
{ label: '작업', path: '/admin/tasks' },
{ label: '리뷰', path: '/admin/reviews' },
{ label: '멤버 관리', path: '/admin/members' },
];
return (
<div className="flex h-full w-[280px] flex-col border-l border-gray-300 bg-gray-100 p-4">
<h2 className="mb-4 text-lg font-semibold text-gray-800"></h2>
<div className="flex flex-col gap-2">
{menuItems.map((item) => (
<button
key={item.label}
className={cn(
'cursor-pointer rounded-md px-3 py-2 text-left text-gray-700 hover:bg-gray-200',
'transition-colors focus:bg-gray-300 focus:outline-none'
)}
onClick={() => navigate(item.path)}
>
{item.label}
</button>
))}
</div>
</div>
);
}

View File

@ -0,0 +1,54 @@
import { ResizablePanel, ResizableHandle } from '../ui/resizable';
import { Project } from '@/types';
import { useNavigate } from 'react-router-dom';
import { SquarePen } from 'lucide-react';
import { Button } from '../ui/button';
interface AdminProjectSidebarProps {
workspaceName: string;
projects: Project[];
}
export default function AdminProjectSidebar({ workspaceName, projects }: AdminProjectSidebarProps): JSX.Element {
const navigate = useNavigate();
return (
<>
<ResizablePanel
minSize={15}
maxSize={35}
defaultSize={20}
className="flex h-full flex-col bg-gray-100"
>
<header className="body flex w-full items-center gap-2 p-2">
<h1 className="w-full overflow-hidden text-ellipsis whitespace-nowrap text-xl font-bold text-gray-900">
{workspaceName}
</h1>
<button>
<SquarePen size={16} />
</button>
<Button
variant="outline"
size="xs"
className="caption border-gray-800 bg-gray-100"
onClick={() => console.log('New project')}
>
</Button>
</header>
<div className="flex flex-col gap-2 p-4">
{projects.map((project) => (
<button
key={project.id}
className="rounded-md px-3 py-2 text-left hover:bg-gray-200"
onClick={() => navigate(`/project/${project.id}`)}
>
{project.name}
</button>
))}
</div>
</ResizablePanel>
<ResizableHandle className="bg-gray-300" />
</>
);
}