import { useMemo, useState } from "react"
import type { Meta, StoryObj } from "@storybook/react-vite"
import {
Card, CardContent, PageHeader, PageHeaderContent, PageHeaderTitle, PageHeaderDescription,
Badge,
Button,
Combobox,
DataTable,
DataTableColumnHeader,
DataTableViewOptions,
DatePicker,
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
FilterBar,
FilterBarActions,
FilterBarSearch,
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
dataTableSelectColumn,
toast,
useDataTable,
type DataTableColumnDef,
} from "@hm-design/ui"
import "../list-screen.css"
import { MoreHorizontalIcon } from "lucide-react"
import { STORY_KICKER, StoryPage, StoryPageHeader, StorySection } from "@/components/story-page"
import { formatDate, sameDay, sampleUsers, statusLabel, type SampleUser } from "./sample-users"
// Data Table is a pattern: the HM Table primitive driven by TanStack Table v9 (useDataTable). Columns and data stay referentially stable
// (module scope / useMemo). Row actions use the HM Dropdown Menu and report through Sonner `toast` (Story composition only).
const statusBadge = (status: SampleUser["status"]) => (
{statusLabel[status]}
)
const columnLabels = { name: "이름", department: "부서", status: "상태", createdAt: "등록일" }
const columns: DataTableColumnDef[] = [
dataTableSelectColumn(),
{ accessorKey: "name", header: ({ column }) => },
{ accessorKey: "department", header: "부서" },
{
accessorKey: "status",
header: "상태",
cell: ({ row }) => statusBadge(row.original.status),
filterFn: "equalsString",
enableSorting: false,
},
{
accessorKey: "createdAt",
header: ({ column }) => ,
cell: ({ row }) => formatDate(row.original.createdAt),
filterFn: (row, columnId, value: Date) => sameDay(row.getValue(columnId), value),
},
{
id: "actions",
enableHiding: false,
cell: ({ row }) => (
toast(`${row.original.name} 보기`)}>보기
toast(`${row.original.name} 수정`)}>수정
toast.error(`${row.original.name} 삭제 요청`)}>
삭제
),
},
]
function Basic({ data = sampleUsers, loading }: { data?: SampleUser[]; loading?: boolean }) {
const table = useDataTable({ columns, data, getRowId: (row) => row.id })
return (
)
}
const statusOptions = [
{ value: "active", label: "활성" },
{ value: "pending", label: "대기" },
{ value: "inactive", label: "비활성" },
]
const departmentOptions = [...new Set(sampleUsers.map((u) => u.department))].map((d) => ({ value: d, label: d }))
// Filter Bar → filter state → Data Table: search is the TanStack global filter, status / date are column filters.
function WithFilterBar({ extended = true }: { extended?: boolean }) {
const data = useMemo(() => sampleUsers, [])
const table = useDataTable({ columns, data, getRowId: (row) => row.id })
const [search, setSearch] = useState("")
const [status, setStatus] = useState("all")
const [date, setDate] = useState()
const [department, setDepartment] = useState()
const apply = () => {
table.setGlobalFilter(search || undefined)
table.getColumn("status")?.setFilterValue(status === "all" ? undefined : status)
table.getColumn("department")?.setFilterValue(department)
table.getColumn("createdAt")?.setFilterValue(date)
}
const reset = () => {
setSearch(""); setStatus("all"); setDate(undefined); setDepartment(undefined)
table.resetGlobalFilter(true); table.resetColumnFilters(true)
}
return
}
const meta = { title: "Patterns/Data/Data Table", parameters: { layout: "padded", controls: { disable: true } } } satisfies Meta
export default meta
type Story = StoryObj
// Representative Story: selection (mixed header), sorting, column visibility, row actions and pagination with page size.
export const All: Story = {
name: "Data Table",
parameters: { layout: "fullscreen" },
render: () => (
),
}
// 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: () => }
export const Loading: Story = { tags: ["verification"], render: () => }
export const Empty: Story = { tags: ["verification"], render: () => }