{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "console-record-detail", "title": "Console Record Detail", "description": "A dense CRM and ERP record detail layout with command bar, identity header, accessible tabs, facts, activity, related records, context rail, and lifecycle states", "dependencies": ["@base-ui/react", "daisyui", "lucide-react"], "files": [ { "path": "registry/default/blocks/console-record-detail/console-record-detail.tsx", "content": "import { AlertTriangle, ChevronRight, CircleAlert, LoaderCircle, Pencil, Save, X } from 'lucide-react';\nimport type { ComponentPropsWithRef, ReactNode } from 'react';\nimport { cn } from '@/lib/utils';\n\nexport type ConsoleRecordDetailPageProps = ComponentPropsWithRef<'article'> & {\n\tcommandBar?: ReactNode;\n\tfooter?: ReactNode;\n\theader: ReactNode;\n};\n\nexport function ConsoleRecordDetailPage({\n\tchildren,\n\tclassName,\n\tcommandBar,\n\tfooter,\n\theader,\n\t...props\n}: ConsoleRecordDetailPageProps) {\n\treturn (\n\t\t
\n\t\t\t{hasRenderableNode(commandBar) ? commandBar : null}\n\t\t\t{header}\n\t\t\t
{children}
\n\t\t\t{hasRenderableNode(footer) ? (\n\t\t\t\t\n\t\t\t) : null}\n\t\t
\n\t);\n}\n\nexport type ConsoleRecordCommandBarProps = ComponentPropsWithRef<'div'> & {\n\tend?: ReactNode;\n\tlabel?: string;\n\tsticky?: boolean;\n};\n\nexport function ConsoleRecordCommandBar({\n\tchildren,\n\tclassName,\n\tend,\n\tlabel = '记录操作',\n\tsticky,\n\t...props\n}: ConsoleRecordCommandBarProps) {\n\treturn (\n\t\t\n\t\t\t
{children}
\n\t\t\t{hasRenderableNode(end) ?
{end}
: null}\n\t\t\n\t);\n}\n\nexport type ConsoleRecordEditMessages = {\n\tcancel: string;\n\tedit: string;\n\tsave: string;\n\tsaving: string;\n};\n\nexport type ConsoleRecordEditActionsProps = Omit, 'children'> & {\n\tdirty: boolean;\n\tediting: boolean;\n\tmessages?: Partial;\n\tonCancel: () => void;\n\tonEdit: () => void;\n\tonSave: () => void;\n\tsaving?: boolean;\n};\n\nconst defaultEditMessages: ConsoleRecordEditMessages = {\n\tcancel: '取消',\n\tedit: '编辑',\n\tsave: '保存',\n\tsaving: '保存中',\n};\n\nexport function ConsoleRecordEditActions({\n\tclassName,\n\tdirty,\n\tediting,\n\tmessages: messageOverrides,\n\tonCancel,\n\tonEdit,\n\tonSave,\n\tsaving = false,\n\t...props\n}: ConsoleRecordEditActionsProps) {\n\tconst messages = { ...defaultEditMessages, ...messageOverrides };\n\treturn (\n\t\t
\n\t\t\t\n\t\t\t{editing ? (\n\t\t\t\t\n\t\t\t) : null}\n\t\t\t\n\t\t
\n\t);\n}\n\nexport type ConsoleRecordHeaderProps = Omit, 'title'> & {\n\tactions?: ReactNode;\n\tavatar?: ReactNode;\n\tbadges?: ReactNode;\n\teyebrow?: ReactNode;\n\tmetadata?: ReactNode;\n\tstatus?: ReactNode;\n\tsubtitle?: ReactNode;\n\ttitle: ReactNode;\n};\n\nexport function ConsoleRecordHeader({\n\tactions,\n\tavatar,\n\tbadges,\n\tclassName,\n\teyebrow,\n\tmetadata,\n\tstatus,\n\tsubtitle,\n\ttitle,\n\t...props\n}: ConsoleRecordHeaderProps) {\n\treturn (\n\t\t
\n\t\t\t
\n\t\t\t\t{hasRenderableNode(avatar) ? (\n\t\t\t\t\t
\n\t\t\t\t\t\t{avatar}\n\t\t\t\t\t
\n\t\t\t\t) : null}\n\t\t\t\t
\n\t\t\t\t\t{hasRenderableNode(eyebrow) ? (\n\t\t\t\t\t\t
{eyebrow}
\n\t\t\t\t\t) : null}\n\t\t\t\t\t
\n\t\t\t\t\t\t

{title}

\n\t\t\t\t\t\t{hasRenderableNode(status) ?
{status}
: null}\n\t\t\t\t\t
\n\t\t\t\t\t{hasRenderableNode(subtitle) ? (\n\t\t\t\t\t\t
{subtitle}
\n\t\t\t\t\t) : null}\n\t\t\t\t\t{hasRenderableNode(badges) ?
{badges}
: null}\n\t\t\t\t\t{hasRenderableNode(metadata) ? (\n\t\t\t\t\t\t
{metadata}
\n\t\t\t\t\t) : null}\n\t\t\t\t
\n\t\t\t\t{hasRenderableNode(actions) ? (\n\t\t\t\t\t
{actions}
\n\t\t\t\t) : null}\n\t\t\t
\n\t\t
\n\t);\n}\n\nexport type ConsoleRecordContentLayoutProps = ComponentPropsWithRef<'div'> & {\n\taside?: ReactNode;\n\tasideLabel?: string;\n\tasideWidth?: string;\n\tmain: ReactNode;\n};\n\nexport function ConsoleRecordContentLayout({\n\taside,\n\tasideLabel = '记录上下文',\n\tasideWidth = '20rem',\n\tclassName,\n\tmain,\n\tstyle,\n\t...props\n}: ConsoleRecordContentLayoutProps) {\n\treturn (\n\t\t\n\t\t\t
{main}
\n\t\t\t{hasRenderableNode(aside) ? (\n\t\t\t\t\n\t\t\t\t\t{aside}\n\t\t\t\t\n\t\t\t) : null}\n\t\t\n\t);\n}\n\nexport type ConsoleRecordSectionProps = Omit, 'title'> & {\n\tactions?: ReactNode;\n\tdescription?: ReactNode;\n\ttitle: ReactNode;\n};\n\nexport function ConsoleRecordSection({\n\tactions,\n\tchildren,\n\tclassName,\n\tdescription,\n\ttitle,\n\t...props\n}: ConsoleRecordSectionProps) {\n\treturn (\n\t\t
\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t

{title}

\n\t\t\t\t\t{hasRenderableNode(description) ? (\n\t\t\t\t\t\t
{description}
\n\t\t\t\t\t) : null}\n\t\t\t\t
\n\t\t\t\t{hasRenderableNode(actions) ?
{actions}
: null}\n\t\t\t
\n\t\t\t
{children}
\n\t\t
\n\t);\n}\n\nexport type ConsoleRecordFact = {\n\tdescription?: ReactNode;\n\tid: string;\n\tlabel: ReactNode;\n\tvalue: ReactNode;\n};\n\nexport type ConsoleRecordFactListProps = Omit, 'children'> & {\n\tcolumns?: 1 | 2 | 3;\n\tempty?: ReactNode;\n\temptyLabel?: string;\n\tfacts: ConsoleRecordFact[];\n};\n\nconst factGridClasses = {\n\t1: 'grid-cols-1',\n\t2: 'grid-cols-1 md:grid-cols-2',\n\t3: 'grid-cols-1 md:grid-cols-2 2xl:grid-cols-3',\n} as const;\n\nexport function ConsoleRecordFactList({\n\tclassName,\n\tcolumns = 2,\n\tempty = '暂无信息',\n\temptyLabel = '信息',\n\tfacts,\n\t...props\n}: ConsoleRecordFactListProps) {\n\tif (facts.length === 0) {\n\t\treturn (\n\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t
{emptyLabel}
\n\t\t\t\t\t
{empty}
\n\t\t\t\t
\n\t\t\t
\n\t\t);\n\t}\n\treturn (\n\t\t
\n\t\t\t{facts.map((fact) => (\n\t\t\t\t
\n\t\t\t\t\t
{fact.label}
\n\t\t\t\t\t
\n\t\t\t\t\t\t
{fact.value}
\n\t\t\t\t\t\t{hasRenderableNode(fact.description) ? (\n\t\t\t\t\t\t\t
{fact.description}
\n\t\t\t\t\t\t) : null}\n\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t))}\n\t\t
\n\t);\n}\n\nexport type ConsoleRecordActivity = {\n\tactor?: ReactNode;\n\tdateTime?: string;\n\tdescription?: ReactNode;\n\ticon?: ReactNode;\n\tid: string;\n\ttime?: ReactNode;\n\ttitle: ReactNode;\n};\n\nexport type ConsoleRecordActivityListProps = ComponentPropsWithRef<'ol'> & {\n\tactivities: ConsoleRecordActivity[];\n\tempty?: ReactNode;\n};\n\nexport function ConsoleRecordActivityList({\n\tactivities,\n\tclassName,\n\tempty = '暂无活动',\n\t...props\n}: ConsoleRecordActivityListProps) {\n\tif (activities.length === 0) {\n\t\treturn (\n\t\t\t
    \n\t\t\t\t
  1. {empty}
  2. \n\t\t\t
\n\t\t);\n\t}\n\treturn (\n\t\t
    \n\t\t\t{activities.map((activity, index) => (\n\t\t\t\t
  1. \n\t\t\t\t\t
    \n\t\t\t\t\t\t{index < activities.length - 1 ? (\n\t\t\t\t\t\t\t
    \n\t\t\t\t\t
    \n\t\t\t\t\t\t
    \n\t\t\t\t\t\t\t
    {activity.title}
    \n\t\t\t\t\t\t\t{hasRenderableNode(activity.time) ? (\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t
    \n\t\t\t\t\t\t{hasRenderableNode(activity.description) ? (\n\t\t\t\t\t\t\t
    {activity.description}
    \n\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t{hasRenderableNode(activity.actor) ? (\n\t\t\t\t\t\t\t
    {activity.actor}
    \n\t\t\t\t\t\t) : null}\n\t\t\t\t\t
    \n\t\t\t\t
  2. \n\t\t\t))}\n\t\t
\n\t);\n}\n\nexport type ConsoleRecordRelatedItem = {\n\tactions?: ReactNode;\n\thref?: string;\n\tid: string;\n\tmeta?: ReactNode;\n\tstatus?: ReactNode;\n\tsubtitle?: ReactNode;\n\ttitle: ReactNode;\n};\n\nexport type ConsoleRecordRelatedListProps = ComponentPropsWithRef<'ul'> & {\n\tempty?: ReactNode;\n\titems: ConsoleRecordRelatedItem[];\n};\n\nexport function ConsoleRecordRelatedList({\n\tclassName,\n\tempty = '暂无关联记录',\n\titems,\n\t...props\n}: ConsoleRecordRelatedListProps) {\n\tif (items.length === 0) {\n\t\treturn (\n\t\t\t
    \n\t\t\t\t
  • {empty}
  • \n\t\t\t
\n\t\t);\n\t}\n\treturn (\n\t\t
    \n\t\t\t{items.map((item) => {\n\t\t\t\tconst content = (\n\t\t\t\t\t<>\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t{item.title}\n\t\t\t\t\t\t\t\t{hasRenderableNode(item.status) ? item.status : null}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t{hasRenderableNode(item.subtitle) ? (\n\t\t\t\t\t\t\t\t{item.subtitle}\n\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t\t{hasRenderableNode(item.meta) ? (\n\t\t\t\t\t\t\t\t{item.meta}\n\t\t\t\t\t\t\t) : null}\n\t\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t);\n\t\t\t\treturn (\n\t\t\t\t\t
  • \n\t\t\t\t\t\t
    \n\t\t\t\t\t\t\t{item.href ? (\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t{content}\n\t\t\t\t\t\t\t\t\t
    \n\t\t\t\t\t\t{hasRenderableNode(item.actions) ?
    {item.actions}
    : null}\n\t\t\t\t\t
  • \n\t\t\t\t);\n\t\t\t})}\n\t\t
\n\t);\n}\n\nexport type ConsoleRecordStateProps = Omit, 'title'> & {\n\tactions?: ReactNode;\n\tdescription?: ReactNode;\n\ticon?: ReactNode;\n\tstate?: 'loading' | 'empty' | 'error';\n\ttitle?: ReactNode;\n};\n\nconst stateDefaults = {\n\tloading: { icon: , title: '正在加载记录' },\n\tempty: { icon: , title: '未找到记录' },\n\terror: { icon: , title: '无法加载记录' },\n} as const;\n\nexport function ConsoleRecordState({\n\tactions,\n\tclassName,\n\tdescription,\n\ticon,\n\tstate = 'empty',\n\ttitle,\n\t...props\n}: ConsoleRecordStateProps) {\n\tconst defaults = stateDefaults[state];\n\treturn (\n\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\t{icon ?? defaults.icon}\n\t\t\t\t
\n\t\t\t\t

{title ?? defaults.title}

\n\t\t\t\t{hasRenderableNode(description) ? (\n\t\t\t\t\t
{description}
\n\t\t\t\t) : null}\n\t\t\t\t{hasRenderableNode(actions) ?
{actions}
: null}\n\t\t\t\n\t\t\n\t);\n}\n\nfunction hasRenderableNode(value: ReactNode) {\n\treturn value !== undefined && value !== null && value !== false;\n}\n", "type": "registry:component", "target": "@components/console-record-detail/console-record-detail.tsx" }, { "path": "registry/default/blocks/console-record-detail/console-record-tabs.tsx", "content": "'use client';\n\nimport { Tabs } from '@base-ui/react/tabs';\nimport type { ComponentPropsWithRef, ReactNode } from 'react';\nimport { cn } from '@/lib/utils';\n\nexport type ConsoleRecordTab = {\n\tbadge?: ReactNode;\n\tcontent: ReactNode;\n\tdisabled?: boolean;\n\ticon?: ReactNode;\n\tlabel: ReactNode;\n\tvalue: string;\n};\n\nexport type ConsoleRecordTabsProps = Omit<\n\tComponentPropsWithRef,\n\t'children' | 'defaultValue' | 'onValueChange' | 'value'\n> & {\n\tdefaultValue?: string | null;\n\tkeepMounted?: boolean;\n\tlistLabel?: string;\n\tonValueChange?: (value: string | null, eventDetails: Tabs.Root.ChangeEventDetails) => void;\n\tpanelClassName?: string;\n\ttabs: ConsoleRecordTab[];\n\tvalue?: string | null;\n};\n\nexport function ConsoleRecordTabs({\n\tclassName,\n\tdefaultValue,\n\tkeepMounted = true,\n\tlistLabel = '记录视图',\n\tonValueChange,\n\tpanelClassName,\n\ttabs,\n\tvalue,\n\t...props\n}: ConsoleRecordTabsProps) {\n\tconst initialValue = defaultValue === undefined ? tabs.find((tab) => !tab.disabled)?.value : defaultValue;\n\tconst rootClassName =\n\t\ttypeof className === 'function'\n\t\t\t? (state: Tabs.Root.State) => cn('min-h-0', className(state))\n\t\t\t: cn('min-h-0', className);\n\treturn (\n\t\t\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\t{tabs.map((tab) => (\n\t\t\t\t\t\t\n\t\t\t\t\t\t\t{hasRenderableNode(tab.icon) ? {tab.icon} : null}\n\t\t\t\t\t\t\t{tab.label}\n\t\t\t\t\t\t\t{hasRenderableNode(tab.badge) ? {tab.badge} : null}\n\t\t\t\t\t\t\n\t\t\t\t\t))}\n\t\t\t\t\n\t\t\t
\n\t\t\t{tabs.map((tab) => (\n\t\t\t\t\n\t\t\t\t\t{tab.content}\n\t\t\t\t\n\t\t\t))}\n\t\t\n\t);\n}\n\nfunction hasRenderableNode(value: ReactNode) {\n\treturn value !== undefined && value !== null && value !== false;\n}\n", "type": "registry:component", "target": "@components/console-record-detail/console-record-tabs.tsx" }, { "path": "registry/default/blocks/console-record-detail/index.ts", "content": "export * from './console-record-detail';\nexport * from './console-record-tabs';\n", "type": "registry:component", "target": "@components/console-record-detail/index.ts" } ], "type": "registry:block" }