본문으로 건너뛰기

예제

Resource 표

표와 카드 예제를 함께 보고 더 보기에서 보기와 카드 열 수를 바꾸며, 같은 행 동작과 컬렉션 상태를 확인합니다.

공개 계약NxButtonResourcePrimaryCellResourceRowActionsMenuResourceStatusBadgeResourceTableResourceTextCell

실행 미리 보기

실시간

Resource 컬렉션

조회 상태를 전환한 뒤 더 보기에서 표 또는 카드를 선택하고 카드 열 수와 너비를 조절해 보세요.

ResourceTable

마지막 작업: 없음

프리셋을 선택하거나 프레임 오른쪽 아래 모서리를 드래그하세요.

표 보기
설비담당자상태작업
MC-001Line A

Jin Lee

Line A

Ready
MC-014Line B

Mina Park

Line B

Maintenance
MC-021Line C

Sora Kim

Line C

Scheduled
카드 보기
MC-001Line A
담당자

Jin Lee

Line A

상태
Ready
MC-014Line B
담당자

Mina Park

Line B

상태
Maintenance
MC-021Line C
담당자

Sora Kim

Line C

상태
Scheduled

복사 가능한 소스

코드 예시
TSX
import { useMemo, useState } from "react";

import type { ResourceTableColumn } from "@nexia/sdk";
import {
    NxButton,
    ResourcePrimaryCell,
    ResourceRowActionsMenu,
    ResourceStatusBadge,
    ResourceTable,
    ResourceTextCell,
} from "@nexia/sdk/host";

type TableState = "data" | "empty" | "noResults" | "loading" | "error";
type MachineRow = {
    id: string;
    name: string;
    code: string;
    line: string;
    owner: string;
    status: string;
};

const rows: MachineRow[] = [
    {
        id: "cnc-a1",
        name: "CNC A-1",
        code: "MC-001",
        line: "Line A",
        owner: "Jin Lee",
        status: "ready",
    },
    {
        id: "press-b2",
        name: "Press B-2",
        code: "MC-014",
        line: "Line B",
        owner: "Mina Park",
        status: "maintenance",
    },
];

export function ResourceTableExample() {
    const [state, setState] = useState<TableState>("data");
    const [lastAction, setLastAction] = useState("None");
    const columns = useMemo<ResourceTableColumn<MachineRow>[]>(
        () => [
            {
                key: "machine",
                header: "Machine",
                size: "primary",
                cell: (row) => (
                    <ResourcePrimaryCell
                        resource={{
                            type: "manufacturing.machine",
                            id: row.id,
                            label: row.name,
                        }}
                        label={row.name}
                        meta={[
                            { kind: "text", key: "code", label: row.code },
                            { kind: "badge", key: "line", label: row.line },
                        ]}
                        onActivate={() => setLastAction(`View ${row.name}`)}
                    />
                ),
            },
            {
                key: "owner",
                header: "Owner",
                size: "text",
                priority: 2,
                defaultVisible: false,
                cell: (row) => (
                    <ResourceTextCell
                        primary={row.owner}
                        secondary={row.line}
                    />
                ),
            },
            {
                key: "status",
                header: "Status",
                size: "meta",
                cell: (row) => <ResourceStatusBadge status={row.status} />,
            },
            {
                key: "actions",
                header: "Actions",
                size: "actions",
                hideable: false,
                cell: (row) => (
                    <ResourceRowActionsMenu
                        label={`Actions: ${row.name}`}
                        items={[
                            {
                                id: "view",
                                label: "View",
                                onSelect: () =>
                                    setLastAction(`View ${row.name}`),
                            },
                            {
                                id: "edit",
                                label: "Edit",
                                onSelect: () =>
                                    setLastAction(`Edit ${row.name}`),
                            },
                            {
                                id: "schedule-maintenance",
                                label: "Schedule maintenance",
                                onSelect: () =>
                                    setLastAction(
                                        `Schedule maintenance for ${row.name}`
                                    ),
                            },
                            {
                                id: "deactivate",
                                label: "Deactivate",
                                destructive: true,
                                onSelect: () =>
                                    setLastAction(`Deactivate ${row.name}`),
                            },
                        ]}
                    />
                ),
            },
        ],
        []
    );
    const renderCard = (row: MachineRow, index: number) => {
        const primary = columns.find((column) => column.key === "machine");
        const actions = columns.find((column) => column.key === "actions");
        const details = columns.filter(
            (column) => column.key !== "machine" && column.key !== "actions"
        );

        return (
            <div className="space-y-3">
                <div className="flex items-start justify-between gap-2">
                    {primary?.cell(row, index)}
                    <div className="shrink-0">
                        {actions?.cell(row, index)}
                    </div>
                </div>
                <dl className="space-y-2">
                    {details.map((column) => (
                        <div key={column.key}>
                            <dt className="text-sm text-text-tertiary">
                                {column.header}
                            </dt>
                            <dd>{column.cell(row, index)}</dd>
                        </div>
                    ))}
                </dl>
            </div>
        );
    };
    const noResults = state === "noResults";

    return (
        <div className="grid gap-4">
            <div className="flex flex-wrap gap-2">
                {(
                    ["data", "empty", "noResults", "loading", "error"] as const
                ).map((item) => (
                    <NxButton
                        key={item}
                        size="xs"
                        variant={state === item ? "primary" : "secondary"}
                        onClick={() => setState(item)}
                    >
                        {item}
                    </NxButton>
                ))}
            </div>
            <p role="status" className="text-sm text-text-secondary">
                Last action: {lastAction}
            </p>
            {(["table", "cards"] as const).map((defaultView) => (
                <ResourceTable
                    key={defaultView}
                    tableId={`developer-example.production-machines.${defaultView}`}
                    label={
                        defaultView === "table" ? "Table view" : "Card view"
                    }
                    showLabel
                    rows={state === "data" ? rows : []}
                    columns={columns}
                    renderCard={renderCard}
                    defaultView={defaultView}
                    getKey={(row) => row.id}
                    toolbar={{}}
                    loading={state === "loading"}
                    error={state === "error"}
                    errorDescription="Machines could not be loaded."
                    onRetry={() => setState("data")}
                    hasActiveQuery={noResults}
                    emptyState={{
                        kind: "empty",
                        title: "No machines yet",
                        description: "Create a machine to start planning capacity.",
                    }}
                />
            ))}
        </div>
    );
}