style(storybook): refine patterns data and layout stories
This commit is contained in:
1 parent
3bebe8a660
commit
b33ca2a157
12 files changed
+168
-152
No files matched your search
@@ -180,3 +180,15 @@
|
||||
}
|
||||
|
||||
/* Compact geometry is shared by all viewports. Dashboard's native scroll frame preserves its minimum width. */
|
||||
|
||||
/* Representative page only: the Dashboard grows with its content instead of being fixed to 100dvh, so the content surface has no
|
||||
inner vertical scrollbar and the Storybook page itself scrolls. The body's `flex: 1 1 0` is a vertical share of the fixed height,
|
||||
so it becomes content-sized; `dashboard-main` keeps its own `flex: 1 1 0`, which is its horizontal fill inside the body row.
|
||||
The package's rules (fixed height, overflow-y: auto) stay as they are for operating screens; the horizontal scroll frame
|
||||
(min-width 960px) is unchanged. */
|
||||
.dashboard-all-page [data-slot="dashboard-scroll"] {
|
||||
height: auto;
|
||||
}
|
||||
.dashboard-all-page [data-slot="dashboard-body"] {
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
@@ -429,7 +429,7 @@ const frame = (height?: number): CSSProperties | undefined => (height ? ({ "--hm
|
||||
// story composition here prevents the export from becoming a second hand-written UI.
|
||||
export function DashboardAll() {
|
||||
return (
|
||||
<StoryPage>
|
||||
<StoryPage className="dashboard-all-page">
|
||||
<StoryPageHeader title="Dashboard" />
|
||||
<StorySection title="Overview">
|
||||
<DashboardScreen mode="compact" />
|
||||
|
||||
@@ -155,15 +155,18 @@ export const All: Story = {
|
||||
render: () => (
|
||||
<StoryPage>
|
||||
<StoryPageHeader title="Data Table" />
|
||||
<StorySection title="Overview">
|
||||
<Basic />
|
||||
</StorySection>
|
||||
<StorySection title="기본 목록 검색"><WithFilterBar extended={false} /></StorySection>
|
||||
<StorySection title="추가 조건 목록 검색"><WithFilterBar /></StorySection>
|
||||
<StorySection title="Advanced Search"><WithFilterBar /></StorySection>
|
||||
</StoryPage>
|
||||
),
|
||||
}
|
||||
|
||||
// The other two usage types that used to be stacked on the representative page; kept as separate Stories.
|
||||
// Plain table with column visibility (selection, sorting, row actions, pagination).
|
||||
export const Overview: Story = { tags: ["verification"], render: () => <Basic /> }
|
||||
|
||||
// Basic list search: status + keyword search, centered above the table.
|
||||
export const BasicSearch: Story = { tags: ["verification"], name: "Basic Search", render: () => <WithFilterBar extended={false} /> }
|
||||
|
||||
// Combination Story: Filter Bar (search, status, department, date, reset) filtering the Data Table rows.
|
||||
export const WithFilterBarStory: Story = { tags: ["verification"], name: "With Filter Bar", render: () => <WithFilterBar /> }
|
||||
|
||||
|
||||
@@ -90,7 +90,7 @@ export const All: Story = {
|
||||
render: () => (
|
||||
<StoryPage>
|
||||
<StoryPageHeader title="Filter Bar" />
|
||||
<StorySection title="Overview">
|
||||
<StorySection title="Basic">
|
||||
<Full />
|
||||
</StorySection>
|
||||
</StoryPage>
|
||||
|
||||
@@ -46,6 +46,7 @@ import {
|
||||
TooltipContent,
|
||||
TooltipTrigger,
|
||||
} from "@hm-design/ui"
|
||||
import { StateBoard } from "@/components/story-board"
|
||||
import { StoryPage, StoryPageHeader, StorySection } from "@/components/story-page"
|
||||
|
||||
// GNB is a composition pattern. Layout visual reference is the ui-viz GNB (green 70px bar, centered menu, Beige current item joined to
|
||||
@@ -308,12 +309,14 @@ function Bar({
|
||||
}
|
||||
|
||||
// The bar with the content that follows it (the GNB Mode page owns this part; here it only shows the join).
|
||||
function Page({ children, label }: { children: ReactNode; label?: string }) {
|
||||
// `compact` is for the representative page only: it trims the connected area's height and padding (the 12px join and rounded corners stay).
|
||||
const compactContentStyle: CSSProperties = { ...contentStyle, minHeight: 0, padding: 16 }
|
||||
function Page({ children, label, compact = false }: { children: ReactNode; label?: string; compact?: boolean }) {
|
||||
return (
|
||||
<div>
|
||||
{children}
|
||||
<div style={contentStyle}>
|
||||
<p className="text-sm text-muted-foreground">{label ?? "연결된 콘텐츠 영역: GNB는 자기 높이와 12px 접점까지만 차지합니다."}</p>
|
||||
<div style={compact ? compactContentStyle : contentStyle}>
|
||||
{label === "" ? null : <p className="text-sm text-muted-foreground">{label ?? "연결된 콘텐츠 영역: GNB는 자기 높이와 12px 접점까지만 차지합니다."}</p>}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
@@ -426,50 +429,60 @@ const meta = { title: "Patterns/Layout/GNB", component: GNB, parameters: { layou
|
||||
export default meta
|
||||
type Story = StoryObj<typeof meta>
|
||||
|
||||
// Representative Story: the green bar with a current item, an open 2-depth panel, long labels, a logo, the optional utility icons and
|
||||
// sticky. Menus and the user menu open for real. Narrow the page to 390px for the mobile bar (hamburger → Sheet).
|
||||
// Each GNB fills its specimen parent inside the same Inner as the page and section headings.
|
||||
// The representative page names the connected area only; the verification Stories keep the default 12px join note.
|
||||
const contentLabel = "콘텐츠 영역"
|
||||
// Representative Story: Variants (Default, Long Labels) and States (Navigation Menu Open). Menus and the user menu open for real.
|
||||
// Narrow the page to 390px for the mobile bar (hamburger → Sheet). Each GNB fills its specimen parent inside the same Inner as the
|
||||
// page and section headings. The connected-area note shows once (Default); the verification Stories keep the default 12px join note.
|
||||
// With Logo, Utilities, Sticky, GNB Sidebar and Brown Sidebar live in their own verification Stories.
|
||||
export const All: Story = {
|
||||
name: "GNB",
|
||||
render: () => (
|
||||
<StoryPage>
|
||||
<StoryPageHeader title="GNB" />
|
||||
<StorySection title="Overview">
|
||||
<Page label={contentLabel}>
|
||||
<Bar />
|
||||
</Page>
|
||||
<StorySection title="Variants">
|
||||
<StateBoard
|
||||
states={[
|
||||
{
|
||||
id: "default",
|
||||
label: "Default",
|
||||
wide: true,
|
||||
children: (
|
||||
<Page compact label="콘텐츠 영역">
|
||||
<Bar />
|
||||
</Page>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: "long-labels",
|
||||
label: "Long Labels",
|
||||
wide: true,
|
||||
children: (
|
||||
<Page compact label="">
|
||||
<Bar items={longNavItems} person={longUser} eyebrow="한맥기술 인프라 통합 시스템" brand="스마트 시설물 통합 관리 시스템" />
|
||||
</Page>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</StorySection>
|
||||
<StorySection title="States · Navigation Menu Open">
|
||||
<div style={{ minHeight: 360 }}>
|
||||
<Page label="프로젝트 > 진행 중 프로젝트">
|
||||
<Bar initial="projects/active" openMenu="projects" />
|
||||
</Page>
|
||||
</div>
|
||||
{/* The open 2-depth panel ends 238px below the bar's top; the wrapper reserves that room so it never overlaps the page below. */}
|
||||
<StorySection title="States">
|
||||
<StateBoard
|
||||
states={[
|
||||
{
|
||||
id: "navigation-menu-open",
|
||||
label: "Navigation Menu Open",
|
||||
wide: true,
|
||||
children: (
|
||||
<div style={{ minHeight: 248 }}>
|
||||
<Page compact label="프로젝트 > 진행 중 프로젝트">
|
||||
<Bar initial="projects/active" openMenu="projects" />
|
||||
</Page>
|
||||
</div>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</StorySection>
|
||||
<StorySection title="Variants · Long Labels">
|
||||
<Page label={contentLabel}>
|
||||
<Bar items={longNavItems} person={longUser} eyebrow="한맥기술 인프라 통합 시스템" brand="스마트 시설물 통합 관리 시스템" />
|
||||
</Page>
|
||||
</StorySection>
|
||||
<StorySection title="Variants · With Logo">
|
||||
<Page label={contentLabel}>
|
||||
<Bar logo={testLogo} />
|
||||
</Page>
|
||||
</StorySection>
|
||||
<StorySection title="Variants · Utilities">
|
||||
<Page label={contentLabel}>
|
||||
<Bar utilities />
|
||||
</Page>
|
||||
</StorySection>
|
||||
<StorySection title="Usage · Sticky">
|
||||
<StickyPage />
|
||||
</StorySection>
|
||||
<StorySection title="Usage · GNB Sidebar">
|
||||
<SidebarPage showCurrent={false} />
|
||||
</StorySection>
|
||||
<StorySection title="Usage · Brown 콘텐츠 LNB"><SidebarPage color="brown" showCurrent={false} /></StorySection>
|
||||
</StoryPage>
|
||||
),
|
||||
}
|
||||
@@ -477,6 +490,8 @@ export const All: Story = {
|
||||
export const Default: Story = { tags: ["verification"], render: () => <Page><Bar /></Page> }
|
||||
export const Sticky: Story = { tags: ["verification"], render: () => <StickyPage /> }
|
||||
export const Sidebar: Story = { tags: ["verification"], name: "GNB Sidebar", render: () => <SidebarPage fullHeight /> }
|
||||
// Brown content LNB: same condition the representative page used to show (560px frame, no route readout).
|
||||
export const BrownSidebar: Story = { tags: ["verification"], name: "Brown Sidebar", render: () => <SidebarPage color="brown" showCurrent={false} /> }
|
||||
export const NavigationMenuOpen: Story = {
|
||||
tags: ["verification"],
|
||||
name: "Navigation Menu Open",
|
||||
|
||||
@@ -619,28 +619,6 @@ const mobileViewport = {
|
||||
globals: { viewport: { value: "hmMobile", isRotated: false } },
|
||||
}
|
||||
|
||||
// Representative-board preview of a Story at a fixed viewport. The LNB/bar switch is a viewport media query, so the board embeds the
|
||||
// verification Story (the same `Standalone` composition) in an iframe of that width instead of rebuilding it in a narrow container.
|
||||
// Each iframe is its own document, so the closed Sheets do not share a portal or focus with the board.
|
||||
function ViewportPreview({ story, label, width, height, args }: { story: string; label: string; width: number; height: number; args?: string }) {
|
||||
return (
|
||||
<figure className="grid min-w-0 content-start gap-2">
|
||||
<figcaption className="text-sm text-muted-foreground">
|
||||
{label} · {width}px
|
||||
</figcaption>
|
||||
<div className="max-w-full overflow-x-auto">
|
||||
<iframe
|
||||
title={`${label} ${width}px`}
|
||||
src={`iframe.html?id=patterns-layout-lnb--${story}&viewMode=story${args ? `&args=${args}` : ""}`}
|
||||
width={width}
|
||||
height={height}
|
||||
className="box-content block border border-black/10"
|
||||
/>
|
||||
</div>
|
||||
</figure>
|
||||
)
|
||||
}
|
||||
|
||||
// `hm-lnb-story` (display: contents, see lnb.stories.css) scopes the 212px min width and 14px menu font to this Story only.
|
||||
const meta = { title: "Patterns/Layout/LNB", component: LNB, decorators: [(Story) => <div className="hm-lnb-story"><Story /></div>], parameters: { layout: "fullscreen", controls: { disable: true } } } satisfies Meta<typeof LNB>
|
||||
export default meta
|
||||
@@ -653,19 +631,11 @@ export const All: Story = {
|
||||
render: () => (
|
||||
<StoryPage>
|
||||
<StoryPageHeader title="LNB" />
|
||||
<StorySection title="Overview">
|
||||
<StateBoard
|
||||
min={560}
|
||||
states={[
|
||||
{ id: "selected", label: "Selected", children: <ResponsiveSpecimen nodes={simpleNav()} initial="overview" initialOpen={{ tasks: true }} dividers={false} /> },
|
||||
{ id: "third-selected", label: "3뎁스 · Selected", children: <ResponsiveSpecimen nodes={thirdLevelNav()} initial="projects/design/tokens" initialOpen={{ projects: true, "projects/design": true }} dividers={false} /> },
|
||||
]}
|
||||
/>
|
||||
</StorySection>
|
||||
<StorySection title="States">
|
||||
<StateBoard
|
||||
min={560}
|
||||
states={[
|
||||
{ id: "selected", label: "Selected", children: <ResponsiveSpecimen nodes={simpleNav()} initial="overview" initialOpen={{ tasks: true }} dividers={false} /> },
|
||||
{ id: "expanded", label: "Expanded", children: <ResponsiveSpecimen nodes={simpleNav()} initial={null} initialOpen={{ tasks: true }} /> },
|
||||
{ id: "states-third-selected", label: "3뎁스 · Selected", children: <ResponsiveSpecimen nodes={thirdLevelNav()} initial="projects/design/tokens" initialOpen={{ projects: true, "projects/design": true }} /> },
|
||||
]}
|
||||
@@ -680,25 +650,12 @@ export const All: Story = {
|
||||
]}
|
||||
/>
|
||||
</StorySection>
|
||||
<StorySection title="Usage · Scroll">
|
||||
<StateBoard
|
||||
min={560}
|
||||
states={[
|
||||
{ id: "scroll", label: "Scroll", children: <ResponsiveSpecimen nodes={simpleNav(false, true)} initial="overview" initialOpen={{ tasks: true }} height={460} dividers={false} /> },
|
||||
{ id: "third-scroll", label: "3뎁스 · Scroll", children: <ResponsiveSpecimen nodes={thirdLevelNav(true, true)} initial="projects/design/layout" initialOpen={{ projects: true, "projects/design": true }} longLabels height={460} dividers={false} /> },
|
||||
]}
|
||||
/>
|
||||
</StorySection>
|
||||
<StorySection title="Usage · Standalone 반응형">
|
||||
<div className="flex flex-wrap items-start gap-6">
|
||||
<ViewportPreview story="standalone-desktop" label="Desktop" width={1024} height={640} args="dividers:!false" />
|
||||
<ViewportPreview story="standalone-mobile" label="Mobile" width={390} height={640} />
|
||||
</div>
|
||||
</StorySection>
|
||||
</StoryPage>
|
||||
),
|
||||
}
|
||||
|
||||
// Usage examples live in their own verification Stories: Scroll, Third Level Scroll, LNB Standalone Desktop / Mobile (and Mobile Menu Open).
|
||||
|
||||
export const Default: Story = { tags: ["verification"], render: () => <Menu initial={null} groupOpen={false} /> }
|
||||
export const Selected: Story = { tags: ["verification"], render: () => <Menu dividers={false} /> }
|
||||
export const Expanded: Story = { tags: ["verification"], render: () => <Menu initial={null} /> }
|
||||
@@ -754,8 +711,7 @@ export const WithGNB: Story = {
|
||||
|
||||
// LNB without a GNB, responsive. Desktop (>= 1024px): the column. Mobile (<= 1023px): the column is hidden and the connected-content
|
||||
// column becomes a full-width stack whose first row opens a Sheet with the same tree, selected item and every depth.
|
||||
// `dividers` is a Story arg (default true: the Story itself keeps its group dividers). The representative board's Desktop preview
|
||||
// opens this Story with `&args=dividers:!false` (see ViewportPreview) to show it without them.
|
||||
// `dividers` is a Story arg (default true: the Story itself keeps its group dividers).
|
||||
export const StandaloneDesktop: StoryObj<{ dividers: boolean }> = {
|
||||
tags: ["verification"],
|
||||
name: "LNB Standalone Desktop",
|
||||
|
||||
@@ -33,12 +33,12 @@ export const All: Story = {
|
||||
render: () => (
|
||||
<StoryPage>
|
||||
<StoryPageHeader title="Loading Screen" />
|
||||
<StorySection title="Overview">
|
||||
<LoadingScreen />
|
||||
</StorySection>
|
||||
<StorySection title="Variants">
|
||||
{/* Default spans the full width; the other two share an equal 2-column row (1 column below 2 × 560px). */}
|
||||
<StateBoard
|
||||
min={560}
|
||||
states={[
|
||||
{ id: "default", label: "Default", wide: true, children: <LoadingScreen /> },
|
||||
{ id: "label", label: "Custom label", children: <LoadingScreen>데이터를 불러오는 중</LoadingScreen> },
|
||||
{ id: "narrow", label: "Narrow parent (240px)", children: <div style={{ width: 240 }}><LoadingScreen /></div> },
|
||||
]}
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
Table, TableBody, TableCell, TableHead, TableHeader, TableRow,
|
||||
Tabs, TabsContent, TabsList, TabsTrigger,
|
||||
} from "@hm-design/ui"
|
||||
import { StateBoard } from "@/components/story-board"
|
||||
import { StoryPage, StoryPageHeader, StorySection } from "@/components/story-page"
|
||||
|
||||
const meta = {
|
||||
@@ -27,62 +28,91 @@ type Story = StoryObj<typeof meta>
|
||||
|
||||
// Content composition only. The consuming screen owns navigation and action handlers.
|
||||
// Dashboard's minimum width and internal navigation belong to DashboardLayout.
|
||||
// The Breadcrumb header and the task Tabs / Table are shared by the representative page (header only) and the Full Page Story.
|
||||
function BreadcrumbHeader() {
|
||||
return (
|
||||
<PageHeader>
|
||||
<PageHeaderContent>
|
||||
<Breadcrumb>
|
||||
<BreadcrumbList>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink href="#home">홈</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink href="#projects">프로젝트</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbPage>업무 현황</BreadcrumbPage>
|
||||
</BreadcrumbItem>
|
||||
</BreadcrumbList>
|
||||
</Breadcrumb>
|
||||
<PageHeaderTitle>업무 현황</PageHeaderTitle>
|
||||
<PageHeaderDescription>프로젝트별 업무 진행 상황을 확인하고 조회합니다.</PageHeaderDescription>
|
||||
</PageHeaderContent>
|
||||
<PageHeaderActions>
|
||||
<Button type="button" variant="primary" appearance="brand" size="default">
|
||||
신규 등록
|
||||
</Button>
|
||||
</PageHeaderActions>
|
||||
</PageHeader>
|
||||
)
|
||||
}
|
||||
|
||||
function TaskTabs() {
|
||||
return (
|
||||
<Tabs defaultValue="mine" className="min-w-0">
|
||||
<TabsList aria-label="업무 목록 보기">
|
||||
<TabsTrigger value="mine">내 업무</TabsTrigger>
|
||||
<TabsTrigger value="completed">완료 업무</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="mine" className="px-0 py-6">
|
||||
<Table aria-label="내 업무">
|
||||
<TableHeader><TableRow><TableHead scope="col">업무</TableHead><TableHead scope="col">마감일</TableHead><TableHead scope="col">상태</TableHead></TableRow></TableHeader>
|
||||
<TableBody>
|
||||
<TableRow><TableCell>화면 설계서 검토</TableCell><TableCell>10월 14일</TableCell><TableCell>검토</TableCell></TableRow>
|
||||
<TableRow><TableCell>고객 요구사항 정리</TableCell><TableCell>10월 16일</TableCell><TableCell>진행</TableCell></TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TabsContent>
|
||||
<TabsContent value="completed" className="px-0 py-6">
|
||||
<Table aria-label="완료 업무">
|
||||
<TableHeader><TableRow><TableHead scope="col">업무</TableHead><TableHead scope="col">완료일</TableHead><TableHead scope="col">상태</TableHead></TableRow></TableHeader>
|
||||
<TableBody><TableRow><TableCell>프로젝트 기본 정보 등록</TableCell><TableCell>10월 9일</TableCell><TableCell>완료</TableCell></TableRow></TableBody>
|
||||
</Table>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
)
|
||||
}
|
||||
|
||||
export const All: Story = {
|
||||
name: "Page Header",
|
||||
render: () => (
|
||||
<StoryPage>
|
||||
<StoryPageHeader title="Page Header" />
|
||||
<StorySection title="Overview">
|
||||
<PageHeader><PageHeaderContent><PageHeaderTitle>업무 현황</PageHeaderTitle></PageHeaderContent></PageHeader>
|
||||
<StorySection title="Variants">
|
||||
<StateBoard
|
||||
states={[
|
||||
{ id: "basic", label: "Basic", wide: true, children: <PageHeader><PageHeaderContent><PageHeaderTitle>업무 현황</PageHeaderTitle></PageHeaderContent></PageHeader> },
|
||||
{ id: "with-breadcrumb", label: "With Breadcrumb", wide: true, children: <BreadcrumbHeader /> },
|
||||
]}
|
||||
/>
|
||||
</StorySection>
|
||||
</StoryPage>
|
||||
),
|
||||
}
|
||||
|
||||
// The former representative page's full work screen (Breadcrumb header + Tabs + Table), kept unchanged as its own Story.
|
||||
export const FullPage: Story = {
|
||||
tags: ["verification"],
|
||||
name: "Full Page",
|
||||
render: () => (
|
||||
<StoryPage>
|
||||
<StoryPageHeader title="Page Header" />
|
||||
<StorySection title="Breadcrumb 포함형">
|
||||
<PageHeader>
|
||||
<PageHeaderContent>
|
||||
<Breadcrumb>
|
||||
<BreadcrumbList>
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink href="#home">홈</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbLink href="#projects">프로젝트</BreadcrumbLink>
|
||||
</BreadcrumbItem>
|
||||
<BreadcrumbSeparator />
|
||||
<BreadcrumbItem>
|
||||
<BreadcrumbPage>업무 현황</BreadcrumbPage>
|
||||
</BreadcrumbItem>
|
||||
</BreadcrumbList>
|
||||
</Breadcrumb>
|
||||
<PageHeaderTitle>업무 현황</PageHeaderTitle>
|
||||
<PageHeaderDescription>프로젝트별 업무 진행 상황을 확인하고 조회합니다.</PageHeaderDescription>
|
||||
</PageHeaderContent>
|
||||
<PageHeaderActions>
|
||||
<Button type="button" variant="primary" appearance="brand" size="default">
|
||||
신규 등록
|
||||
</Button>
|
||||
</PageHeaderActions>
|
||||
</PageHeader>
|
||||
<Tabs defaultValue="mine" className="min-w-0">
|
||||
<TabsList aria-label="업무 목록 보기">
|
||||
<TabsTrigger value="mine">내 업무</TabsTrigger>
|
||||
<TabsTrigger value="completed">완료 업무</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="mine" className="px-0 py-6">
|
||||
<Table aria-label="내 업무">
|
||||
<TableHeader><TableRow><TableHead scope="col">업무</TableHead><TableHead scope="col">마감일</TableHead><TableHead scope="col">상태</TableHead></TableRow></TableHeader>
|
||||
<TableBody>
|
||||
<TableRow><TableCell>화면 설계서 검토</TableCell><TableCell>10월 14일</TableCell><TableCell>검토</TableCell></TableRow>
|
||||
<TableRow><TableCell>고객 요구사항 정리</TableCell><TableCell>10월 16일</TableCell><TableCell>진행</TableCell></TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TabsContent>
|
||||
<TabsContent value="completed" className="px-0 py-6">
|
||||
<Table aria-label="완료 업무">
|
||||
<TableHeader><TableRow><TableHead scope="col">업무</TableHead><TableHead scope="col">완료일</TableHead><TableHead scope="col">상태</TableHead></TableRow></TableHeader>
|
||||
<TableBody><TableRow><TableCell>프로젝트 기본 정보 등록</TableCell><TableCell>10월 9일</TableCell><TableCell>완료</TableCell></TableRow></TableBody>
|
||||
</Table>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
<BreadcrumbHeader />
|
||||
<TaskTabs />
|
||||
</StorySection>
|
||||
</StoryPage>
|
||||
),
|
||||
|
||||
Reference in new issue
Block a user