@startsimpli/ui 0.4.21 → 0.4.23
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.
- package/package.json +2 -2
- package/src/components/workflows/WorkflowEmptyState.tsx +256 -0
- package/src/components/workflows/WorkflowListComposer.tsx +644 -0
- package/src/components/workflows/WorkflowSettingsCard.tsx +208 -0
- package/src/components/workflows/WorkflowStatsOverview.tsx +175 -0
- package/src/components/workflows/WorkflowTemplateGallery.tsx +608 -0
- package/src/components/workflows/WorkflowVersionHistory.tsx +151 -0
- package/src/components/workflows/index.ts +33 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useMemo } from 'react';
|
|
4
|
+
import { Hash, User, StickyNote, Boxes } from 'lucide-react';
|
|
5
|
+
import { cn } from '../../lib/utils';
|
|
6
|
+
import { Badge } from '../ui/badge';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Minimal, in-package shape of a workflow version. Mirrors the subset of the
|
|
10
|
+
* backend version record the history view actually reads — kept local so the
|
|
11
|
+
* component carries no app type coupling.
|
|
12
|
+
*/
|
|
13
|
+
export interface WorkflowVersion {
|
|
14
|
+
uuid: string;
|
|
15
|
+
versionNumber: number;
|
|
16
|
+
timestamp: string;
|
|
17
|
+
isPublished?: boolean;
|
|
18
|
+
versionNote?: string | null;
|
|
19
|
+
createdBy?: {
|
|
20
|
+
firstName?: string | null;
|
|
21
|
+
lastName?: string | null;
|
|
22
|
+
email: string;
|
|
23
|
+
} | null;
|
|
24
|
+
/** Opaque workflow definition; only `nodes.length` is read for display. */
|
|
25
|
+
workflowData?: Record<string, unknown> | null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface WorkflowVersionHistoryProps {
|
|
29
|
+
versions: WorkflowVersion[];
|
|
30
|
+
/**
|
|
31
|
+
* Formats a version's ISO timestamp for display. Supplied by the consumer
|
|
32
|
+
* so this component stays free of date/locale dependencies. Defaults to the
|
|
33
|
+
* raw timestamp string when omitted.
|
|
34
|
+
*/
|
|
35
|
+
formatTimestamp?: (timestamp: string) => string;
|
|
36
|
+
className?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function WorkflowVersionHistory({
|
|
40
|
+
versions,
|
|
41
|
+
formatTimestamp,
|
|
42
|
+
className,
|
|
43
|
+
}: WorkflowVersionHistoryProps) {
|
|
44
|
+
const sorted = useMemo(
|
|
45
|
+
() => [...versions].sort((a, b) => b.versionNumber - a.versionNumber),
|
|
46
|
+
[versions]
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
if (versions.length === 0) {
|
|
50
|
+
return (
|
|
51
|
+
<div className={cn('text-center py-12', className)}>
|
|
52
|
+
<Hash className="h-12 w-12 mx-auto mb-3 text-muted-foreground" />
|
|
53
|
+
<h3 className="text-lg font-semibold mb-2">No Versions Yet</h3>
|
|
54
|
+
<p className="text-muted-foreground max-w-sm mx-auto">
|
|
55
|
+
No versions published yet. Use the canvas toolbar to publish a
|
|
56
|
+
version.
|
|
57
|
+
</p>
|
|
58
|
+
</div>
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const formatTime = formatTimestamp ?? ((t: string) => t);
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<ol className={cn('relative space-y-6', className)}>
|
|
66
|
+
{sorted.map((version, index) => {
|
|
67
|
+
const label = version.versionNote
|
|
68
|
+
? `v${version.versionNumber} — ${version.versionNote}`
|
|
69
|
+
: `v${version.versionNumber}`;
|
|
70
|
+
|
|
71
|
+
const nodeCount =
|
|
72
|
+
version.workflowData &&
|
|
73
|
+
Array.isArray(
|
|
74
|
+
(version.workflowData as Record<string, unknown>).nodes
|
|
75
|
+
)
|
|
76
|
+
? (
|
|
77
|
+
(version.workflowData as Record<string, unknown>)
|
|
78
|
+
.nodes as unknown[]
|
|
79
|
+
).length
|
|
80
|
+
: null;
|
|
81
|
+
|
|
82
|
+
const isLast = index === sorted.length - 1;
|
|
83
|
+
|
|
84
|
+
return (
|
|
85
|
+
<li key={version.uuid} className="relative pl-8">
|
|
86
|
+
{/* Timeline rail + dot */}
|
|
87
|
+
{!isLast && (
|
|
88
|
+
<span
|
|
89
|
+
className="absolute left-[7px] top-5 bottom-[-1.5rem] w-px bg-border"
|
|
90
|
+
aria-hidden="true"
|
|
91
|
+
/>
|
|
92
|
+
)}
|
|
93
|
+
<span
|
|
94
|
+
className={cn(
|
|
95
|
+
'absolute left-0 top-1 h-3.5 w-3.5 rounded-full border-2',
|
|
96
|
+
version.isPublished
|
|
97
|
+
? 'border-green-500 bg-green-500'
|
|
98
|
+
: 'border-muted-foreground bg-background'
|
|
99
|
+
)}
|
|
100
|
+
aria-hidden="true"
|
|
101
|
+
/>
|
|
102
|
+
|
|
103
|
+
<div className="flex items-center justify-between gap-2">
|
|
104
|
+
<span className="font-medium text-sm">{label}</span>
|
|
105
|
+
<span className="text-xs text-muted-foreground">
|
|
106
|
+
{formatTime(version.timestamp)}
|
|
107
|
+
</span>
|
|
108
|
+
</div>
|
|
109
|
+
|
|
110
|
+
<div className="mt-2 space-y-2 text-sm">
|
|
111
|
+
{version.isPublished && (
|
|
112
|
+
<div>
|
|
113
|
+
<Badge
|
|
114
|
+
variant="outline"
|
|
115
|
+
className="border-green-200 bg-green-50 text-green-700 dark:border-green-800 dark:bg-green-950/30 dark:text-green-400"
|
|
116
|
+
>
|
|
117
|
+
Published
|
|
118
|
+
</Badge>
|
|
119
|
+
</div>
|
|
120
|
+
)}
|
|
121
|
+
{version.createdBy && (
|
|
122
|
+
<div className="flex items-center gap-2 text-muted-foreground">
|
|
123
|
+
<User className="h-3.5 w-3.5 shrink-0" />
|
|
124
|
+
<span>
|
|
125
|
+
{version.createdBy.firstName
|
|
126
|
+
? `${version.createdBy.firstName} ${version.createdBy.lastName ?? ''}`.trim()
|
|
127
|
+
: version.createdBy.email}
|
|
128
|
+
</span>
|
|
129
|
+
</div>
|
|
130
|
+
)}
|
|
131
|
+
{version.versionNote && (
|
|
132
|
+
<div className="flex items-start gap-2 text-muted-foreground">
|
|
133
|
+
<StickyNote className="h-3.5 w-3.5 shrink-0 mt-0.5" />
|
|
134
|
+
<span>{version.versionNote}</span>
|
|
135
|
+
</div>
|
|
136
|
+
)}
|
|
137
|
+
{nodeCount !== null && (
|
|
138
|
+
<div className="flex items-center gap-2 text-muted-foreground">
|
|
139
|
+
<Boxes className="h-3.5 w-3.5 shrink-0" />
|
|
140
|
+
<span>
|
|
141
|
+
{nodeCount} node{nodeCount !== 1 ? 's' : ''}
|
|
142
|
+
</span>
|
|
143
|
+
</div>
|
|
144
|
+
)}
|
|
145
|
+
</div>
|
|
146
|
+
</li>
|
|
147
|
+
);
|
|
148
|
+
})}
|
|
149
|
+
</ol>
|
|
150
|
+
);
|
|
151
|
+
}
|
|
@@ -76,3 +76,36 @@ export { useNodeStatusOverlay } from './hooks/useNodeStatusOverlay';
|
|
|
76
76
|
|
|
77
77
|
// Category icon resolver (lucide)
|
|
78
78
|
export { getCategoryIcon } from './node-icons';
|
|
79
|
+
|
|
80
|
+
// Page composers — zero-coupling workflow UI (empty-state guidance + version history)
|
|
81
|
+
export { WorkflowEmptyState, useWorkflowEmptyState } from './WorkflowEmptyState';
|
|
82
|
+
export type { WorkflowEmptyStateProps } from './WorkflowEmptyState';
|
|
83
|
+
export { WorkflowVersionHistory } from './WorkflowVersionHistory';
|
|
84
|
+
export type {
|
|
85
|
+
WorkflowVersion,
|
|
86
|
+
WorkflowVersionHistoryProps,
|
|
87
|
+
} from './WorkflowVersionHistory';
|
|
88
|
+
|
|
89
|
+
// Page composers — workflow settings, stats, list, and template gallery
|
|
90
|
+
export { WorkflowSettingsCard } from './WorkflowSettingsCard';
|
|
91
|
+
export type { WorkflowSettingsCardProps } from './WorkflowSettingsCard';
|
|
92
|
+
export { WorkflowStatsOverview } from './WorkflowStatsOverview';
|
|
93
|
+
export type {
|
|
94
|
+
WorkflowStats,
|
|
95
|
+
WorkflowStatsOverviewProps,
|
|
96
|
+
} from './WorkflowStatsOverview';
|
|
97
|
+
export { WorkflowListComposer } from './WorkflowListComposer';
|
|
98
|
+
export type {
|
|
99
|
+
WorkflowListItem,
|
|
100
|
+
WorkflowCreateInput,
|
|
101
|
+
WorkflowListComposerProps,
|
|
102
|
+
} from './WorkflowListComposer';
|
|
103
|
+
export {
|
|
104
|
+
WorkflowTemplateGallery,
|
|
105
|
+
WORKFLOW_TEMPLATES,
|
|
106
|
+
} from './WorkflowTemplateGallery';
|
|
107
|
+
export type {
|
|
108
|
+
WorkflowTemplate,
|
|
109
|
+
WorkflowTemplateDefinition,
|
|
110
|
+
WorkflowTemplateGalleryProps,
|
|
111
|
+
} from './WorkflowTemplateGallery';
|