ai-design-system 0.1.47 → 0.1.49

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/components/ai-elements/code-block.tsx +2 -4
  2. package/components/ai-elements/prompt-input.tsx +7 -12
  3. package/components/ai-elements/queue.tsx +2 -2
  4. package/components/blocks/DataTable/DataTable.tsx +2 -2
  5. package/components/blocks/index.ts +0 -11
  6. package/components/composites/AppHeader/AppHeader.tsx +5 -1
  7. package/components/composites/AppHeader/interfaces.ts +1 -1
  8. package/components/composites/ApprovalCard/ApprovalCard.tsx +11 -11
  9. package/components/composites/CommentBox/CommentBox.tsx +3 -3
  10. package/components/{blocks → composites}/DashboardHeader/DashboardHeader.tsx +2 -0
  11. package/components/composites/DataTable/DragHandleCell.tsx +2 -2
  12. package/components/composites/DataTable/EnhancedDataTable.behaviors.stories.tsx +1 -1
  13. package/components/composites/DataTable/EnhancedDataTable.tsx +20 -9
  14. package/components/composites/DataTable/InlineEditCell.tsx +0 -2
  15. package/components/composites/DataTable/RowDetailDrawer.tsx +2 -3
  16. package/components/composites/DataTable/StatusCell.tsx +4 -4
  17. package/components/composites/DataTable/index.ts +0 -2
  18. package/components/composites/DocumentTabBar/DocumentTabBar.tsx +5 -4
  19. package/components/composites/EmptyState/EmptyState.stories.tsx +26 -0
  20. package/components/composites/EmptyState/EmptyState.tsx +38 -0
  21. package/components/composites/EmptyState/index.ts +1 -0
  22. package/components/composites/ProjectSwitcher/ProjectSwitcher.tsx +105 -0
  23. package/components/composites/ProjectSwitcher/index.ts +1 -0
  24. package/components/{blocks → composites}/PromptInput/PromptInput.stories.tsx +1 -1
  25. package/components/composites/StateNode/StateNode.tsx +5 -5
  26. package/components/composites/TransitionNode/TransitionNode.tsx +4 -4
  27. package/components/composites/TriggerNode/TriggerNode.tsx +2 -2
  28. package/components/composites/WorkflowToolbar/WorkflowToolbar.tsx +3 -4
  29. package/components/composites/index.ts +24 -0
  30. package/components/features/DashboardFeature/DashboardFeature.behaviors.stories.tsx +4 -0
  31. package/components/features/DashboardFeature/DashboardFeature.mocks.ts +1 -1
  32. package/components/features/DashboardFeature/DashboardFeature.stories.tsx +21 -0
  33. package/components/features/DashboardFeature/DashboardFeature.tsx +95 -24
  34. package/components/features/DashboardFeature/useDashboardFeature.d.ts +21 -0
  35. package/components/features/DashboardFeature/useDashboardFeature.mock.ts +39 -30
  36. package/components/features/PageLayout/PageLayout.stories.tsx +33 -10
  37. package/components/features/PageLayout/PageLayout.tsx +1 -1
  38. package/components/features/PageLayout/useDashboardIntegration.mock.ts +108 -0
  39. package/components/features/RefinementPanel/RefinementPanel.tsx +1 -1
  40. package/components/features/WorkflowBuilder/WorkflowBuilder.tsx +3 -6
  41. package/components/index.ts +4 -3
  42. package/components/primitives/Icon/Icon.tsx +48 -1
  43. package/dist/index.cjs +731 -228
  44. package/dist/index.cjs.map +1 -1
  45. package/dist/index.css +28 -0
  46. package/dist/index.d.ts +123 -4
  47. package/dist/index.js +730 -230
  48. package/dist/index.js.map +1 -1
  49. package/package.json +1 -1
  50. /package/components/{blocks → composites}/DashboardChart/DashboardChart.tsx +0 -0
  51. /package/components/{blocks → composites}/DashboardChart/index.ts +0 -0
  52. /package/components/{blocks → composites}/DashboardHeader/index.ts +0 -0
  53. /package/components/{blocks → composites}/LayoutProvider/LayoutProvider.tsx +0 -0
  54. /package/components/{blocks → composites}/LayoutProvider/index.ts +0 -0
  55. /package/components/{blocks → composites}/PromptInput/PromptInput.tsx +0 -0
  56. /package/components/{blocks → composites}/PromptInput/index.ts +0 -0
@@ -3,7 +3,7 @@
3
3
  import { Button } from "@/components/ui/button";
4
4
  import { cn } from "@/lib/utils";
5
5
  import type { Element } from "hast";
6
- import { CheckIcon, CopyIcon } from "lucide-react";
6
+ import { CheckIcon } from "lucide-react";
7
7
  import {
8
8
  type ComponentProps,
9
9
  createContext,
@@ -163,8 +163,6 @@ export const CodeBlockCopyButton = ({
163
163
  }
164
164
  };
165
165
 
166
- const Icon = isCopied ? CheckIcon : CopyIcon;
167
-
168
166
  return (
169
167
  <Button
170
168
  className={cn("shrink-0", className)}
@@ -173,7 +171,7 @@ export const CodeBlockCopyButton = ({
173
171
  variant="ghost"
174
172
  {...props}
175
173
  >
176
- {children ?? <Icon size={14} />}
174
+ {children ?? <CheckIcon size={14} />}
177
175
  </Button>
178
176
  );
179
177
  };
@@ -37,26 +37,21 @@ import {
37
37
  import { cn } from "@/lib/utils";
38
38
  import type { ChatStatus, FileUIPart } from "ai";
39
39
  import {
40
- ImageIcon,
41
40
  Loader2Icon,
42
- MicIcon,
43
- PaperclipIcon,
44
- PlusIcon,
45
41
  SendIcon,
46
42
  SquareIcon,
47
43
  XIcon,
48
44
  } from "lucide-react";
45
+ import React from "react";
49
46
  import { nanoid } from "nanoid";
50
47
  import {
51
48
  type ChangeEvent,
52
49
  type ChangeEventHandler,
53
- Children,
54
50
  type ClipboardEventHandler,
55
51
  type ComponentProps,
56
52
  createContext,
57
53
  type FormEvent,
58
54
  type FormEventHandler,
59
- Fragment,
60
55
  type HTMLAttributes,
61
56
  type KeyboardEventHandler,
62
57
  type PropsWithChildren,
@@ -295,7 +290,7 @@ export function PromptInputAttachment({
295
290
  />
296
291
  ) : (
297
292
  <div className="flex size-5 items-center justify-center text-muted-foreground">
298
- <PaperclipIcon className="size-3" />
293
+ <div className="size-3" />
299
294
  </div>
300
295
  )}
301
296
  </div>
@@ -365,7 +360,7 @@ export function PromptInputAttachments({
365
360
  }
366
361
 
367
362
  return attachments.files.map((file) => (
368
- <Fragment key={file.id}>{children(file)}</Fragment>
363
+ <React.Fragment key={file.id}>{children(file)}</React.Fragment>
369
364
  ));
370
365
  }
371
366
 
@@ -389,7 +384,7 @@ export const PromptInputActionAddAttachments = ({
389
384
  attachments.openFileDialog();
390
385
  }}
391
386
  >
392
- <ImageIcon className="mr-2 size-4" /> {label}
387
+ {label}
393
388
  </DropdownMenuItem>
394
389
  );
395
390
  };
@@ -900,7 +895,7 @@ export const PromptInputButton = ({
900
895
  ...props
901
896
  }: PromptInputButtonProps) => {
902
897
  const newSize =
903
- size ?? (Children.count(props.children) > 1 ? "sm" : "icon-sm");
898
+ size ?? (React.Children.count(props.children) > 1 ? "sm" : "icon-sm");
904
899
 
905
900
  return (
906
901
  <InputGroupButton
@@ -927,7 +922,7 @@ export const PromptInputActionMenuTrigger = ({
927
922
  }: PromptInputActionMenuTriggerProps) => (
928
923
  <DropdownMenuTrigger asChild>
929
924
  <PromptInputButton className={className} {...props}>
930
- {children ?? <PlusIcon className="size-4" />}
925
+ {children}
931
926
  </PromptInputButton>
932
927
  </DropdownMenuTrigger>
933
928
  );
@@ -1150,7 +1145,7 @@ export const PromptInputSpeechButton = ({
1150
1145
  onClick={toggleListening}
1151
1146
  {...props}
1152
1147
  >
1153
- <MicIcon className="size-4" />
1148
+ <div className="size-4" />
1154
1149
  </PromptInputButton>
1155
1150
  );
1156
1151
  };
@@ -8,7 +8,7 @@ import {
8
8
  } from "@/components/ui/collapsible";
9
9
  import { ScrollArea } from "@/components/ui/scroll-area";
10
10
  import { cn } from "@/lib/utils";
11
- import { ChevronDownIcon, PaperclipIcon } from "lucide-react";
11
+ import { ChevronDownIcon } from "lucide-react";
12
12
  import type { ComponentProps } from "react";
13
13
 
14
14
  export type QueueMessagePart = {
@@ -174,7 +174,7 @@ export const QueueItemFile = ({
174
174
  )}
175
175
  {...props}
176
176
  >
177
- <PaperclipIcon size={12} />
177
+ <div className="size-3" />
178
178
  <span className="max-w-[100px] truncate">{children}</span>
179
179
  </span>
180
180
  );
@@ -1,5 +1,4 @@
1
1
  import * as React from "react"
2
- import { Plus } from "lucide-react"
3
2
 
4
3
  import {
5
4
  EnhancedDataTable,
@@ -8,6 +7,7 @@ import {
8
7
  } from "@/components/composites/DataTable"
9
8
  import type { DynamicTableSchema } from "ui-schema-contracts"
10
9
  import { Button } from "@/components/primitives/Button"
10
+ import { Icon } from "@/components/primitives/Icon"
11
11
 
12
12
  export interface DataTableProps {
13
13
  rows: DashboardRow[]
@@ -29,7 +29,7 @@ export const DataTable = React.memo<DataTableProps>(({ rows, tableSchema, handle
29
29
  rightActions={
30
30
  handlers?.onAddSection ? (
31
31
  <Button variant="outline" size="sm" className="h-8" onClick={handlers.onAddSection}>
32
- <Plus className="size-4" />
32
+ <Icon name="plus" size="sm" />
33
33
  <span className="hidden lg:inline">Add Section</span>
34
34
  </Button>
35
35
  ) : null
@@ -10,26 +10,15 @@ export type { DocumentEditorWithCommentsProps } from './DocumentEditorWithCommen
10
10
  export { FileChangeQueue } from './FileChangeQueue'
11
11
  export type { FileChangeQueueProps } from './FileChangeQueue'
12
12
 
13
- export { PromptInput } from './PromptInput'
14
- export type { PromptInputBlockProps } from './PromptInput'
15
-
16
13
  // Blocks
17
- export * from './LayoutProvider'
18
14
  export * from './SectionLayout'
19
- export type { LayoutProviderProps } from './LayoutProvider'
20
15
 
21
16
  export { WorkflowCanvas, getLayoutedElements } from './WorkflowCanvas'
22
17
  export type { WorkflowCanvasProps, WorkflowNode, WorkflowEdge, WorkflowNodeData } from './WorkflowCanvas'
23
18
 
24
- export { DashboardHeader } from './DashboardHeader'
25
- export type { DashboardHeaderProps } from './DashboardHeader'
26
-
27
19
  export { DashboardMetrics } from './DashboardMetrics'
28
20
  export type { DashboardMetricsProps } from './DashboardMetrics'
29
21
 
30
- export { DashboardChart } from './DashboardChart'
31
- export type { DashboardChartProps } from './DashboardChart'
32
-
33
22
  export { DataTable } from './DataTable'
34
23
  export type { DataTableProps } from './DataTable'
35
24
 
@@ -22,7 +22,11 @@ export const AppHeader = React.memo<AppHeaderProps>(({
22
22
  {showSidebarToggle && showTitle && title && (
23
23
  <Separator orientation="vertical" className="mx-2 data-[orientation=vertical]:h-4" />
24
24
  )}
25
- {showTitle && title && <h1 className="max-w-[28rem] truncate text-base font-medium">{title}</h1>}
25
+ {showTitle && title && (
26
+ typeof title === 'string'
27
+ ? <h1 className="max-w-[28rem] truncate text-base font-medium">{title}</h1>
28
+ : title
29
+ )}
26
30
  </div>
27
31
 
28
32
  <div className="justify-self-center">
@@ -6,7 +6,7 @@ export interface TabItem {
6
6
  }
7
7
 
8
8
  export interface AppHeaderProps {
9
- title?: string;
9
+ title?: React.ReactNode;
10
10
  actions?: React.ReactNode;
11
11
  tabs?: TabItem[];
12
12
  defaultTab?: string;
@@ -2,7 +2,7 @@
2
2
 
3
3
  import * as React from "react";
4
4
  import { useState } from "react";
5
- import { Check, Pencil, X, ChevronLeft, ChevronRight } from "lucide-react";
5
+ import { Icon } from "@/components/primitives/Icon";
6
6
  import { Input } from "@/components/primitives/Input";
7
7
  import { Textarea } from "@/components/primitives/Textarea";
8
8
  import { Label } from "@/components/primitives/Label";
@@ -222,7 +222,7 @@ export const ApprovalCard = React.memo<ApprovalCardProps>(
222
222
  disabled={currentQuestionIndex === 0}
223
223
  className="hover:text-foreground disabled:opacity-30 p-0.5 transition-colors"
224
224
  >
225
- <ChevronLeft className="h-4 w-4" />
225
+ <Icon name="chevron-left" size="sm" />
226
226
  </button>
227
227
  <span className="font-medium">{currentQuestionIndex + 1} of {questions.length}</span>
228
228
  <button
@@ -233,7 +233,7 @@ export const ApprovalCard = React.memo<ApprovalCardProps>(
233
233
  disabled={currentQuestionIndex === questions.length - 1}
234
234
  className="hover:text-foreground disabled:opacity-30 p-0.5 transition-colors"
235
235
  >
236
- <ChevronRight className="h-4 w-4" />
236
+ <Icon name="chevron-right" size="sm" />
237
237
  </button>
238
238
  </div>
239
239
  )}
@@ -343,13 +343,13 @@ export const ApprovalCard = React.memo<ApprovalCardProps>(
343
343
  >
344
344
  {currentQuestionIndex === questions.length - 1 ? (
345
345
  <>
346
- <Check className="h-4 w-4 mr-2" />
346
+ <Icon name="check" size="sm" className="mr-2" />
347
347
  {submitButtonText}
348
348
  </>
349
349
  ) : (
350
350
  <>
351
351
  Next
352
- <ChevronRight className="h-4 w-4 ml-2" />
352
+ <Icon name="chevron-right" size="sm" className="ml-2" />
353
353
  </>
354
354
  )}
355
355
  </ConfirmationAction>
@@ -370,7 +370,7 @@ export const ApprovalCard = React.memo<ApprovalCardProps>(
370
370
  {isEditing ? (
371
371
  <div className="space-y-4">
372
372
  <div className="flex items-center gap-2 text-sm font-medium text-muted-foreground">
373
- <Pencil className="h-4 w-4" />
373
+ <Icon name="pencil" size="sm" />
374
374
  <span>
375
375
  Edit —{" "}
376
376
  <code className="font-mono text-xs bg-muted px-1.5 py-0.5 rounded">
@@ -435,7 +435,7 @@ export const ApprovalCard = React.memo<ApprovalCardProps>(
435
435
  }}
436
436
  disabled={isProcessing}
437
437
  >
438
- <Check className="h-4 w-4 mr-2" />
438
+ <Icon name="check" size="sm" className="mr-2" />
439
439
  Save & Approve
440
440
  </ConfirmationAction>
441
441
  </ConfirmationActions>
@@ -511,7 +511,7 @@ export const ApprovalCard = React.memo<ApprovalCardProps>(
511
511
  }}
512
512
  disabled={isProcessing}
513
513
  >
514
- <X className="h-4 w-4 mr-2" />
514
+ <Icon name="x" size="sm" className="mr-2" />
515
515
  Confirm Rejection
516
516
  </ConfirmationAction>
517
517
  </>
@@ -524,7 +524,7 @@ export const ApprovalCard = React.memo<ApprovalCardProps>(
524
524
  onClick={() => setShowRejectInput(true)}
525
525
  disabled={isProcessing}
526
526
  >
527
- <X className="h-4 w-4 mr-2" />
527
+ <Icon name="x" size="sm" className="mr-2" />
528
528
  {skipButtonText}
529
529
  </ConfirmationAction>
530
530
  )}
@@ -535,7 +535,7 @@ export const ApprovalCard = React.memo<ApprovalCardProps>(
535
535
  onClick={() => setIsEditing(true)}
536
536
  disabled={isProcessing}
537
537
  >
538
- <Pencil className="h-4 w-4 mr-2" />
538
+ <Icon name="pencil" size="sm" className="mr-2" />
539
539
  Edit
540
540
  </ConfirmationAction>
541
541
  )}
@@ -545,7 +545,7 @@ export const ApprovalCard = React.memo<ApprovalCardProps>(
545
545
  onClick={onApprove}
546
546
  disabled={isProcessing}
547
547
  >
548
- <Check className="h-4 w-4 mr-2" />
548
+ <Icon name="check" size="sm" className="mr-2" />
549
549
  {submitButtonText}
550
550
  </ConfirmationAction>
551
551
  </>
@@ -56,7 +56,7 @@ import {
56
56
  InputGroupTextarea,
57
57
  InputGroupButton,
58
58
  } from '@/components/primitives/InputGroup'
59
- import { Send } from 'lucide-react'
59
+ import { Icon } from '@/components/primitives/Icon'
60
60
  import { formatCommentDate } from '@/utils/ai-editor'
61
61
  import type { CommentBoxProps, Comment } from '@/types/ai-editor'
62
62
  import { cn } from '@/lib/utils'
@@ -132,7 +132,7 @@ const CommentThreadDisplay = React.memo<{
132
132
  }
133
133
  }}
134
134
  >
135
- <Send className="h-4 w-4" />
135
+ <Icon name="send" size="sm" />
136
136
  </InputGroupButton>
137
137
  </InputGroup>
138
138
  </ConversationContent>
@@ -208,7 +208,7 @@ export const CommentBox = React.memo<CommentBoxProps>(
208
208
  }
209
209
  }}
210
210
  >
211
- <Send className="h-4 w-4" />
211
+ <Icon name="send" size="sm" />
212
212
  </InputGroupButton>
213
213
  </InputGroup>
214
214
  </div>
@@ -18,3 +18,5 @@ export const DashboardHeader = React.memo<DashboardHeaderProps>(({ ctaLabel = "Q
18
18
  })
19
19
 
20
20
  DashboardHeader.displayName = "DashboardHeader"
21
+
22
+
@@ -1,7 +1,7 @@
1
1
  import { useSortable } from "@dnd-kit/sortable"
2
- import { GripVertical } from "lucide-react"
3
2
 
4
3
  import { Button } from "@/components/primitives/Button"
4
+ import { Icon } from "@/components/primitives/Icon"
5
5
 
6
6
  export interface DragHandleCellProps {
7
7
  id: number | string
@@ -19,7 +19,7 @@ export function DragHandleCell({ id }: DragHandleCellProps) {
19
19
  className="size-7 text-muted-foreground hover:bg-transparent"
20
20
  aria-label="Drag to reorder"
21
21
  >
22
- <GripVertical className="size-3" />
22
+ <Icon name="grip-vertical" size="xs" />
23
23
  </Button>
24
24
  )
25
25
  }
@@ -19,7 +19,7 @@ const tableSchema = dynamicTableSchema.parse({
19
19
  enablePagination: true,
20
20
  enableRowSelection: true,
21
21
  columns: [
22
- { key: "header", label: "Header", sortable: true, hideable: false },
22
+ { key: "header", label: "Header", renderType: "link", sortable: true, hideable: false },
23
23
  { key: "type", label: "Type", sortable: true },
24
24
  { key: "status", label: "Status", renderType: "badge", sortable: true },
25
25
  { key: "target", label: "Target", align: "right", sortable: true },
@@ -11,7 +11,7 @@ import {
11
11
  } from "@dnd-kit/core"
12
12
  import { restrictToVerticalAxis } from "@dnd-kit/modifiers"
13
13
  import { SortableContext, verticalListSortingStrategy } from "@dnd-kit/sortable"
14
- import { MoreVertical, ChevronDown, ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight, Columns3, Plus } from "lucide-react"
14
+ import { Icon } from "@/components/primitives/Icon"
15
15
  import { flexRender, type ColumnDef } from "@tanstack/react-table"
16
16
  import type { DynamicTableSchema, TableColumn } from "ui-schema-contracts"
17
17
 
@@ -35,6 +35,7 @@ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@
35
35
 
36
36
  import { DragHandleCell } from "./DragHandleCell"
37
37
  import { DraggableRow } from "./DraggableRow"
38
+ import { RowDetailDrawer } from "./RowDetailDrawer"
38
39
  import { createTableSelectColumn } from "./TableSelectColumn"
39
40
  import type {
40
41
  DashboardPaginationState,
@@ -318,6 +319,16 @@ export function EnhancedDataTable({
318
319
  const rawValue = row.original[column.key]
319
320
  const rowId = String(toRowId(row.original, tableSchema))
320
321
  const isRowEditing = editingRowId === rowId
322
+ if (column.renderType === "link") {
323
+ return (
324
+ <div className={alignClass(column.align)}>
325
+ <RowDetailDrawer
326
+ item={row.original}
327
+ onChange={(rowId, key, value) => handleInlineSave(rowId, String(key), value)}
328
+ />
329
+ </div>
330
+ )
331
+ }
321
332
  if (isRowEditing && (column.editable ?? false) && (column.inputType ?? "none") !== "none") {
322
333
  return renderEditableCell(row.original, column, rawValue)
323
334
  }
@@ -336,7 +347,7 @@ export function EnhancedDataTable({
336
347
  <DropdownMenu>
337
348
  <DropdownMenuTrigger asChild>
338
349
  <Button variant="ghost" className="flex size-8 text-muted-foreground data-[state=open]:bg-muted" size="icon">
339
- <MoreVertical className="size-4" />
350
+ <Icon name="more-vertical" size="sm" />
340
351
  <span className="sr-only">Open menu</span>
341
352
  </Button>
342
353
  </DropdownMenuTrigger>
@@ -470,10 +481,10 @@ export function EnhancedDataTable({
470
481
  <DropdownMenu>
471
482
  <DropdownMenuTrigger asChild>
472
483
  <Button variant="outline" size="sm" className="h-8">
473
- <Columns3 className="size-4" />
484
+ <Icon name="columns-3" size="sm" />
474
485
  <span className="hidden lg:inline">Customize Columns</span>
475
486
  <span className="lg:hidden">Columns</span>
476
- <ChevronDown className="size-4" />
487
+ <Icon name="chevron-down" size="sm" />
477
488
  </Button>
478
489
  </DropdownMenuTrigger>
479
490
  <DropdownMenuContent align="end" className="w-56">
@@ -496,7 +507,7 @@ export function EnhancedDataTable({
496
507
  </DropdownMenuContent>
497
508
  </DropdownMenu>
498
509
  <Button variant="outline" size="sm" className="h-8" onClick={onCreateClick}>
499
- <Plus className="size-4" />
510
+ <Icon name="plus" size="sm" />
500
511
  <span>{createButtonLabel}</span>
501
512
  </Button>
502
513
  {rightActions}
@@ -588,7 +599,7 @@ export function EnhancedDataTable({
588
599
  disabled={!canPreviousPage}
589
600
  >
590
601
  <span className="sr-only">Go to first page</span>
591
- <ChevronsLeft className="size-4" />
602
+ <Icon name="chevrons-left" size="sm" />
592
603
  </Button>
593
604
  <Button
594
605
  variant="outline"
@@ -602,7 +613,7 @@ export function EnhancedDataTable({
602
613
  disabled={!canPreviousPage}
603
614
  >
604
615
  <span className="sr-only">Go to previous page</span>
605
- <ChevronLeft className="size-4" />
616
+ <Icon name="chevron-left" size="sm" />
606
617
  </Button>
607
618
  <Button
608
619
  variant="outline"
@@ -616,7 +627,7 @@ export function EnhancedDataTable({
616
627
  disabled={!canNextPage}
617
628
  >
618
629
  <span className="sr-only">Go to next page</span>
619
- <ChevronRight className="size-4" />
630
+ <Icon name="chevron-right" size="sm" />
620
631
  </Button>
621
632
  <Button
622
633
  variant="outline"
@@ -630,7 +641,7 @@ export function EnhancedDataTable({
630
641
  disabled={!canNextPage}
631
642
  >
632
643
  <span className="sr-only">Go to last page</span>
633
- <ChevronsRight className="size-4" />
644
+ <Icon name="chevrons-right" size="sm" />
634
645
  </Button>
635
646
  </div>
636
647
  </div>
@@ -1,5 +1,4 @@
1
1
  import * as React from "react"
2
- import { toast } from "sonner"
3
2
 
4
3
  import { Input } from "@/components/primitives/Input"
5
4
  import { Label } from "@/components/primitives/Label"
@@ -23,7 +22,6 @@ export function InlineEditCell({ row, field, onSave }: InlineEditCellProps) {
23
22
  const formData = new FormData(event.currentTarget)
24
23
  const value = String(formData.get(field) ?? "").trim()
25
24
  onSave(rowId, field, value)
26
- toast.success(`Saved ${field} for ${rowHeader}`)
27
25
  },
28
26
  [field, onSave, rowId, rowHeader]
29
27
  )
@@ -1,8 +1,7 @@
1
1
  import * as React from "react"
2
2
  import { Area, AreaChart, CartesianGrid, XAxis } from "recharts"
3
- import { TrendingUp } from "lucide-react"
4
-
5
3
  import { Button } from "@/components/primitives/Button"
4
+ import { Icon } from "@/components/primitives/Icon"
6
5
  import { ChartContainer, ChartTooltip, ChartTooltipContent, type ChartConfig } from "@/components/primitives/Chart"
7
6
  import {
8
7
  Drawer,
@@ -90,7 +89,7 @@ export function RowDetailDrawer({ item, onChange }: RowDetailDrawerProps) {
90
89
  <Separator />
91
90
  <div className="grid gap-2">
92
91
  <div className="flex gap-2 leading-none font-medium">
93
- Trending up by 5.2% this month <TrendingUp className="size-4" />
92
+ Trending up by 5.2% this month <Icon name="trending-up" size="sm" />
94
93
  </div>
95
94
  <div className="text-muted-foreground">
96
95
  Showing total visitors for the last 6 months. This section mirrors the v4 row detail panel.
@@ -1,5 +1,5 @@
1
1
  import { Badge } from "@/components/primitives/Badge"
2
- import { CheckCircle2, LoaderCircle, CircleSlash } from "lucide-react"
2
+ import { Icon } from "@/components/primitives/Icon"
3
3
 
4
4
  import type { DashboardRow } from "./table-types"
5
5
 
@@ -10,9 +10,9 @@ export interface StatusCellProps {
10
10
  export function StatusCell({ status }: StatusCellProps) {
11
11
  return (
12
12
  <Badge variant="outline" className="gap-1 px-1.5 text-muted-foreground">
13
- {status === "Done" ? <CheckCircle2 className="size-3 text-green-500" /> : null}
14
- {status === "In Process" ? <LoaderCircle className="size-3 animate-spin" /> : null}
15
- {status === "Not Started" ? <CircleSlash className="size-3" /> : null}
13
+ {status === "Done" ? <Icon name="circle-check" size="xs" className="text-green-500" /> : null}
14
+ {status === "In Process" ? <Icon name="loader-2" size="xs" className="animate-spin" /> : null}
15
+ {status === "Not Started" ? <Icon name="circle-slash" size="xs" /> : null}
16
16
  {status as React.ReactNode}
17
17
  </Badge>
18
18
  )
@@ -10,8 +10,6 @@ export { InlineEditCell } from './InlineEditCell'
10
10
  export type { InlineEditCellProps } from './InlineEditCell'
11
11
  export { ReviewerCell } from './ReviewerCell'
12
12
  export type { ReviewerCellProps } from './ReviewerCell'
13
- export { StatusCell } from './StatusCell'
14
- export type { StatusCellProps } from './StatusCell'
15
13
  export { RowDetailDrawer } from './RowDetailDrawer'
16
14
  export type { RowDetailDrawerProps } from './RowDetailDrawer'
17
15
  export { createTableSelectColumn } from './TableSelectColumn'
@@ -7,7 +7,7 @@
7
7
 
8
8
  import React from 'react'
9
9
  import { Tabs, TabsList, TabsTrigger, Button, ScrollArea } from '@/components/primitives'
10
- import { X, Circle } from 'lucide-react'
10
+ import { Icon } from '@/components/primitives/Icon'
11
11
  import { cn } from '@/lib/utils'
12
12
  export interface DocumentFile {
13
13
  id: string
@@ -102,8 +102,9 @@ export const DocumentTabBar = React.memo<DocumentTabBarProps>(
102
102
  <div className="flex items-center gap-2">
103
103
  {/* Dirty indicator */}
104
104
  {tab.isDirty && (
105
- <Circle
106
- className="size-2 fill-primary text-primary flex-shrink-0"
105
+ <Icon
106
+ name="circle"
107
+ className="size-2 fill-primary text-primary"
107
108
  aria-label="unsaved changes"
108
109
  />
109
110
  )}
@@ -128,7 +129,7 @@ export const DocumentTabBar = React.memo<DocumentTabBarProps>(
128
129
  )}
129
130
  aria-label={`Close ${tab.name}`}
130
131
  >
131
- <X className="size-3" />
132
+ <Icon name="x" size="xs" />
132
133
  </Button>
133
134
  </div>
134
135
  ))}
@@ -0,0 +1,26 @@
1
+ import type { Meta, StoryObj } from "@storybook/react"
2
+ import { fn } from "@storybook/test"
3
+
4
+ import { EmptyState } from "./EmptyState"
5
+
6
+ const meta = {
7
+ title: "Composites/EmptyState",
8
+ component: EmptyState,
9
+ parameters: {
10
+ layout: "centered",
11
+ },
12
+ args: {
13
+ onAction: fn(),
14
+ },
15
+ } satisfies Meta<typeof EmptyState>
16
+
17
+ export default meta
18
+ type Story = StoryObj<typeof meta>
19
+
20
+ export const Default: Story = {
21
+ args: {
22
+ title: "Distribute Track",
23
+ description: "Upload your first master to start reaching listeners on Spotify, Apple Music, and more.",
24
+ actionLabel: "Create Release",
25
+ },
26
+ }
@@ -0,0 +1,38 @@
1
+ import * as React from "react"
2
+
3
+ import { Button } from "@/components/primitives/Button"
4
+ import { Icon } from "@/components/primitives/Icon"
5
+
6
+ export interface EmptyStateProps {
7
+ title: string
8
+ description: string
9
+ actionLabel: string
10
+ onAction?: () => void
11
+ className?: string
12
+ }
13
+
14
+ export const EmptyState = React.forwardRef<HTMLDivElement, EmptyStateProps>(
15
+ ({ title, description, actionLabel, onAction, className }, ref) => {
16
+ return (
17
+ <div
18
+ ref={ref}
19
+ className={`flex flex-col items-center justify-center rounded-3xl border border-border bg-card p-10 text-center shadow-md max-w-[420px] w-full mx-auto ${className || ""}`}
20
+ >
21
+ <div className="mx-auto flex h-14 w-14 items-center justify-center rounded-2xl bg-muted/50 mb-6">
22
+ <Icon name="plus" size="lg" className="text-muted-foreground" />
23
+ </div>
24
+ <h3 className="mb-3 text-xl font-semibold tracking-tight text-card-foreground">
25
+ {title}
26
+ </h3>
27
+ <p className="mb-8 text-sm text-muted-foreground leading-relaxed">
28
+ {description}
29
+ </p>
30
+ <Button onClick={onAction} variant="secondary" className="rounded-full px-6 py-5 font-medium">
31
+ {actionLabel}
32
+ </Button>
33
+ </div>
34
+ )
35
+ }
36
+ )
37
+
38
+ EmptyState.displayName = "EmptyState"
@@ -0,0 +1 @@
1
+ export * from "./EmptyState"