@dilipod/ui 0.4.22 → 0.4.24
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/dist/components/flowchart-diagram.d.ts +8 -0
- package/dist/components/flowchart-diagram.d.ts.map +1 -0
- package/dist/components/worker-spec.d.ts +53 -0
- package/dist/components/worker-spec.d.ts.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +501 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +501 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/components/flowchart-diagram.tsx +319 -0
- package/src/components/worker-spec.tsx +544 -0
- package/src/index.ts +8 -0
|
@@ -0,0 +1,544 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import * as React from 'react'
|
|
4
|
+
import { useState } from 'react'
|
|
5
|
+
import {
|
|
6
|
+
Target,
|
|
7
|
+
Crosshair,
|
|
8
|
+
ListNumbers,
|
|
9
|
+
TreeStructure,
|
|
10
|
+
Lightning,
|
|
11
|
+
Wrench,
|
|
12
|
+
Plugs,
|
|
13
|
+
ShieldCheck,
|
|
14
|
+
CaretDown,
|
|
15
|
+
CaretRight,
|
|
16
|
+
Clock,
|
|
17
|
+
TrendUp,
|
|
18
|
+
CurrencyEur,
|
|
19
|
+
Sparkle,
|
|
20
|
+
Lightbulb,
|
|
21
|
+
Robot,
|
|
22
|
+
MagnifyingGlass,
|
|
23
|
+
FilmStrip,
|
|
24
|
+
FileText,
|
|
25
|
+
TextT,
|
|
26
|
+
VideoCamera,
|
|
27
|
+
ImageSquare,
|
|
28
|
+
} from '@phosphor-icons/react'
|
|
29
|
+
import { FlowchartDiagram } from './flowchart-diagram'
|
|
30
|
+
import { Badge } from './badge'
|
|
31
|
+
import {
|
|
32
|
+
Dialog,
|
|
33
|
+
DialogTrigger,
|
|
34
|
+
DialogContent,
|
|
35
|
+
DialogHeader,
|
|
36
|
+
DialogTitle,
|
|
37
|
+
DialogDescription,
|
|
38
|
+
} from './dialog'
|
|
39
|
+
|
|
40
|
+
// ============================================
|
|
41
|
+
// Types
|
|
42
|
+
// ============================================
|
|
43
|
+
|
|
44
|
+
export interface AnalysisSource {
|
|
45
|
+
type: string
|
|
46
|
+
name: string
|
|
47
|
+
size: number
|
|
48
|
+
insights?: string[]
|
|
49
|
+
processing_time_ms?: number
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface AnalysisSummary {
|
|
53
|
+
total_sources: number
|
|
54
|
+
video_count: number
|
|
55
|
+
document_count: number
|
|
56
|
+
image_count: number
|
|
57
|
+
total_insights: number
|
|
58
|
+
analyzed_at: string
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface WorkerSpecDocumentation {
|
|
62
|
+
scope: string | null
|
|
63
|
+
goal: string | null
|
|
64
|
+
steps: Array<{
|
|
65
|
+
step: number
|
|
66
|
+
title: string
|
|
67
|
+
description: string
|
|
68
|
+
tools_used?: string[]
|
|
69
|
+
}> | null
|
|
70
|
+
diagram: string | null
|
|
71
|
+
expected_impact: {
|
|
72
|
+
time_saved_per_occurrence_minutes: number
|
|
73
|
+
frequency: string
|
|
74
|
+
yearly_hours_saved: number
|
|
75
|
+
yearly_cost_savings_euros: number
|
|
76
|
+
qualitative_benefits: string[]
|
|
77
|
+
} | null
|
|
78
|
+
technical_requirements: string[] | null
|
|
79
|
+
integration_points: string[] | null
|
|
80
|
+
edge_cases_handled: Array<{
|
|
81
|
+
scenario: string
|
|
82
|
+
handling: string
|
|
83
|
+
}> | null
|
|
84
|
+
analysis_sources: AnalysisSource[] | null
|
|
85
|
+
analysis_summary: AnalysisSummary | null
|
|
86
|
+
analysis_context: string | null
|
|
87
|
+
version: number
|
|
88
|
+
model_used: string | null
|
|
89
|
+
updated_at: string
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export interface WorkerSpecProps {
|
|
93
|
+
/** The Knowledge Builder final documentation */
|
|
94
|
+
documentation: WorkerSpecDocumentation | null
|
|
95
|
+
/** Optional className for the container */
|
|
96
|
+
className?: string
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// ============================================
|
|
100
|
+
// Frequency labels for impact display
|
|
101
|
+
// ============================================
|
|
102
|
+
|
|
103
|
+
const frequencyLabels: Record<string, string> = {
|
|
104
|
+
multiple_daily: 'occurrence',
|
|
105
|
+
daily: 'day',
|
|
106
|
+
weekly: 'week',
|
|
107
|
+
monthly: 'month',
|
|
108
|
+
quarterly: 'quarter',
|
|
109
|
+
yearly: 'year',
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// ============================================
|
|
113
|
+
// Section Header Component
|
|
114
|
+
// ============================================
|
|
115
|
+
|
|
116
|
+
function SectionHeader({
|
|
117
|
+
icon,
|
|
118
|
+
title,
|
|
119
|
+
count,
|
|
120
|
+
expanded,
|
|
121
|
+
onToggle,
|
|
122
|
+
iconColor = 'text-[var(--cyan)]',
|
|
123
|
+
}: {
|
|
124
|
+
icon: React.ReactNode
|
|
125
|
+
title: string
|
|
126
|
+
count?: number
|
|
127
|
+
expanded: boolean
|
|
128
|
+
onToggle: () => void
|
|
129
|
+
iconColor?: string
|
|
130
|
+
}) {
|
|
131
|
+
return (
|
|
132
|
+
<button
|
|
133
|
+
onClick={onToggle}
|
|
134
|
+
className="flex items-center gap-1.5 text-xs font-semibold text-muted-foreground uppercase tracking-wide hover:text-[var(--black)] transition-colors w-full"
|
|
135
|
+
>
|
|
136
|
+
{expanded ? <CaretDown size={12} /> : <CaretRight size={12} />}
|
|
137
|
+
<span className={iconColor}>{icon}</span>
|
|
138
|
+
{title}
|
|
139
|
+
{count !== undefined && (
|
|
140
|
+
<span className="text-muted-foreground font-normal">({count})</span>
|
|
141
|
+
)}
|
|
142
|
+
</button>
|
|
143
|
+
)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// ============================================
|
|
147
|
+
// Analysis Context Renderer
|
|
148
|
+
// ============================================
|
|
149
|
+
|
|
150
|
+
function AnalysisContextRenderer({ content }: { content: string }) {
|
|
151
|
+
const sections = content.split(/^## /gm).filter(Boolean)
|
|
152
|
+
|
|
153
|
+
const getIcon = (title: string) => {
|
|
154
|
+
if (title.includes('Request')) return <Target size={14} className="text-[var(--cyan)]" />
|
|
155
|
+
if (title.includes('Video')) return <VideoCamera size={14} className="text-[var(--cyan)]" />
|
|
156
|
+
if (title.includes('Document')) return <FileText size={14} className="text-[var(--cyan)]" />
|
|
157
|
+
if (title.includes('Rules')) return <Lightbulb size={14} className="text-amber-500" />
|
|
158
|
+
if (title.includes('Context')) return <TextT size={14} className="text-[var(--cyan)]" />
|
|
159
|
+
return null
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return (
|
|
163
|
+
<div className="space-y-6">
|
|
164
|
+
{sections.map((section, index) => {
|
|
165
|
+
const lines = section.split('\n')
|
|
166
|
+
const title = lines[0]?.trim()
|
|
167
|
+
const body = lines.slice(1).join('\n').trim()
|
|
168
|
+
|
|
169
|
+
if (!body) return null
|
|
170
|
+
|
|
171
|
+
return (
|
|
172
|
+
<div key={index}>
|
|
173
|
+
<h4 className="text-sm font-medium text-[var(--black)] flex items-center gap-2 mb-3">
|
|
174
|
+
{getIcon(title)}
|
|
175
|
+
{title}
|
|
176
|
+
</h4>
|
|
177
|
+
<div className="text-sm text-muted-foreground pl-5 space-y-2 whitespace-pre-line">
|
|
178
|
+
{body}
|
|
179
|
+
</div>
|
|
180
|
+
</div>
|
|
181
|
+
)
|
|
182
|
+
})}
|
|
183
|
+
</div>
|
|
184
|
+
)
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// ============================================
|
|
188
|
+
// Main Component
|
|
189
|
+
// ============================================
|
|
190
|
+
|
|
191
|
+
export function WorkerSpec({ documentation, className }: WorkerSpecProps) {
|
|
192
|
+
const [expandedSections, setExpandedSections] = useState<Set<string>>(
|
|
193
|
+
new Set(['goal', 'scope', 'steps', 'diagram', 'impact', 'requirements', 'edge_cases'])
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
const toggleSection = (section: string) => {
|
|
197
|
+
setExpandedSections(prev => {
|
|
198
|
+
const next = new Set(prev)
|
|
199
|
+
if (next.has(section)) {
|
|
200
|
+
next.delete(section)
|
|
201
|
+
} else {
|
|
202
|
+
next.add(section)
|
|
203
|
+
}
|
|
204
|
+
return next
|
|
205
|
+
})
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Empty state
|
|
209
|
+
if (!documentation) {
|
|
210
|
+
return (
|
|
211
|
+
<div className={className}>
|
|
212
|
+
<div className="flex items-center gap-3 p-6 rounded-sm border border-dashed border-gray-300 bg-gray-50/50">
|
|
213
|
+
<div className="w-10 h-10 rounded-sm bg-gray-100 flex items-center justify-center">
|
|
214
|
+
<Robot size={20} className="text-gray-400" />
|
|
215
|
+
</div>
|
|
216
|
+
<div>
|
|
217
|
+
<h3 className="font-semibold text-[var(--black)]">Worker Spec Pending</h3>
|
|
218
|
+
<p className="text-sm text-muted-foreground">
|
|
219
|
+
The final specification will be generated automatically after the documentation is approved.
|
|
220
|
+
</p>
|
|
221
|
+
</div>
|
|
222
|
+
</div>
|
|
223
|
+
</div>
|
|
224
|
+
)
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const freqLabel = documentation.expected_impact?.frequency
|
|
228
|
+
? frequencyLabels[documentation.expected_impact.frequency] || documentation.expected_impact.frequency
|
|
229
|
+
: 'occurrence'
|
|
230
|
+
|
|
231
|
+
const hasAnalysis = documentation.analysis_context || (documentation.analysis_sources && documentation.analysis_sources.length > 0)
|
|
232
|
+
|
|
233
|
+
return (
|
|
234
|
+
<div className={className}>
|
|
235
|
+
<div className="space-y-5">
|
|
236
|
+
{/* What was analyzed */}
|
|
237
|
+
{hasAnalysis && (
|
|
238
|
+
<div>
|
|
239
|
+
<Dialog>
|
|
240
|
+
<DialogTrigger asChild>
|
|
241
|
+
<button className="flex items-center gap-2 text-sm font-medium text-[var(--cyan)] hover:text-[var(--cyan)]/80 transition-colors">
|
|
242
|
+
<MagnifyingGlass size={16} />
|
|
243
|
+
What was analyzed
|
|
244
|
+
{documentation.analysis_summary && (
|
|
245
|
+
<span className="text-xs text-muted-foreground font-normal">
|
|
246
|
+
({documentation.analysis_summary.total_sources} source{documentation.analysis_summary.total_sources !== 1 ? 's' : ''})
|
|
247
|
+
</span>
|
|
248
|
+
)}
|
|
249
|
+
<CaretRight size={12} />
|
|
250
|
+
</button>
|
|
251
|
+
</DialogTrigger>
|
|
252
|
+
<DialogContent className="max-w-xl max-h-[80vh] overflow-y-auto">
|
|
253
|
+
<DialogHeader>
|
|
254
|
+
<DialogTitle>What was analyzed</DialogTitle>
|
|
255
|
+
{documentation.analysis_summary && (
|
|
256
|
+
<DialogDescription className="flex items-center gap-4 text-xs">
|
|
257
|
+
<span>{documentation.analysis_summary.total_sources} source{documentation.analysis_summary.total_sources !== 1 ? 's' : ''}</span>
|
|
258
|
+
{documentation.analysis_summary.video_count > 0 && (
|
|
259
|
+
<span className="flex items-center gap-1">
|
|
260
|
+
<FilmStrip size={12} />
|
|
261
|
+
{documentation.analysis_summary.video_count} video{documentation.analysis_summary.video_count !== 1 ? 's' : ''}
|
|
262
|
+
</span>
|
|
263
|
+
)}
|
|
264
|
+
{documentation.analysis_summary.document_count > 0 && (
|
|
265
|
+
<span className="flex items-center gap-1">
|
|
266
|
+
<FileText size={12} />
|
|
267
|
+
{documentation.analysis_summary.document_count} doc{documentation.analysis_summary.document_count !== 1 ? 's' : ''}
|
|
268
|
+
</span>
|
|
269
|
+
)}
|
|
270
|
+
</DialogDescription>
|
|
271
|
+
)}
|
|
272
|
+
</DialogHeader>
|
|
273
|
+
|
|
274
|
+
{/* Sources Section */}
|
|
275
|
+
{documentation.analysis_sources && documentation.analysis_sources.length > 0 && (
|
|
276
|
+
<div className="space-y-2">
|
|
277
|
+
<h4 className="text-xs font-medium text-muted-foreground uppercase tracking-wide">Sources</h4>
|
|
278
|
+
<div className="space-y-1.5">
|
|
279
|
+
{documentation.analysis_sources.map((source, i) => (
|
|
280
|
+
<div key={i} className="flex items-center gap-2 text-sm">
|
|
281
|
+
<div className="flex-shrink-0 text-muted-foreground">
|
|
282
|
+
{source.type === 'video' && <FilmStrip size={14} />}
|
|
283
|
+
{(source.type === 'document' || source.type === 'pdf') && <FileText size={14} />}
|
|
284
|
+
{source.type === 'spreadsheet' && <FileText size={14} />}
|
|
285
|
+
{source.type === 'image' && <ImageSquare size={14} />}
|
|
286
|
+
{source.type === 'description' && <TextT size={14} />}
|
|
287
|
+
</div>
|
|
288
|
+
<span className="truncate text-[var(--black)]">{source.name}</span>
|
|
289
|
+
{source.size > 0 && (
|
|
290
|
+
<span className="text-xs text-muted-foreground flex-shrink-0">
|
|
291
|
+
{source.size > 1024 * 1024
|
|
292
|
+
? `${(source.size / 1024 / 1024).toFixed(1)}MB`
|
|
293
|
+
: `${Math.round(source.size / 1024)}KB`}
|
|
294
|
+
</span>
|
|
295
|
+
)}
|
|
296
|
+
</div>
|
|
297
|
+
))}
|
|
298
|
+
</div>
|
|
299
|
+
</div>
|
|
300
|
+
)}
|
|
301
|
+
|
|
302
|
+
{/* Analysis Details Section */}
|
|
303
|
+
{documentation.analysis_context && (
|
|
304
|
+
<div className="border-t border-gray-100 pt-4 space-y-1">
|
|
305
|
+
<h4 className="text-xs font-medium text-muted-foreground uppercase tracking-wide mb-3">Analysis Details</h4>
|
|
306
|
+
<AnalysisContextRenderer content={documentation.analysis_context} />
|
|
307
|
+
</div>
|
|
308
|
+
)}
|
|
309
|
+
</DialogContent>
|
|
310
|
+
</Dialog>
|
|
311
|
+
</div>
|
|
312
|
+
)}
|
|
313
|
+
|
|
314
|
+
{/* Goal */}
|
|
315
|
+
{documentation.goal && (
|
|
316
|
+
<div>
|
|
317
|
+
<SectionHeader
|
|
318
|
+
icon={<Target size={12} weight="fill" />}
|
|
319
|
+
title="Goal"
|
|
320
|
+
expanded={expandedSections.has('goal')}
|
|
321
|
+
onToggle={() => toggleSection('goal')}
|
|
322
|
+
/>
|
|
323
|
+
{expandedSections.has('goal') && (
|
|
324
|
+
<p className="text-sm text-muted-foreground leading-relaxed pl-5 mt-2">
|
|
325
|
+
{documentation.goal}
|
|
326
|
+
</p>
|
|
327
|
+
)}
|
|
328
|
+
</div>
|
|
329
|
+
)}
|
|
330
|
+
|
|
331
|
+
{/* Scope */}
|
|
332
|
+
{documentation.scope && (
|
|
333
|
+
<div>
|
|
334
|
+
<SectionHeader
|
|
335
|
+
icon={<Crosshair size={12} weight="fill" />}
|
|
336
|
+
title="Scope"
|
|
337
|
+
expanded={expandedSections.has('scope')}
|
|
338
|
+
onToggle={() => toggleSection('scope')}
|
|
339
|
+
/>
|
|
340
|
+
{expandedSections.has('scope') && (
|
|
341
|
+
<p className="text-sm text-muted-foreground leading-relaxed pl-5 mt-2">
|
|
342
|
+
{documentation.scope}
|
|
343
|
+
</p>
|
|
344
|
+
)}
|
|
345
|
+
</div>
|
|
346
|
+
)}
|
|
347
|
+
|
|
348
|
+
{/* Detailed Steps */}
|
|
349
|
+
{documentation.steps && documentation.steps.length > 0 && (
|
|
350
|
+
<div>
|
|
351
|
+
<SectionHeader
|
|
352
|
+
icon={<ListNumbers size={12} weight="fill" />}
|
|
353
|
+
title="Steps"
|
|
354
|
+
count={documentation.steps.length}
|
|
355
|
+
expanded={expandedSections.has('steps')}
|
|
356
|
+
onToggle={() => toggleSection('steps')}
|
|
357
|
+
/>
|
|
358
|
+
{expandedSections.has('steps') && (
|
|
359
|
+
<div className="space-y-3 pl-5 mt-2">
|
|
360
|
+
{documentation.steps.map((step, i) => (
|
|
361
|
+
<div key={i} className="flex items-start gap-3">
|
|
362
|
+
<span className="w-6 h-6 rounded-sm bg-[var(--cyan)]/10 flex items-center justify-center text-xs font-bold text-[var(--cyan)] shrink-0 mt-0.5">
|
|
363
|
+
{step.step || i + 1}
|
|
364
|
+
</span>
|
|
365
|
+
<div className="flex-1 min-w-0">
|
|
366
|
+
<p className="text-sm font-medium text-[var(--black)]">{step.title}</p>
|
|
367
|
+
<p className="text-sm text-muted-foreground mt-0.5">{step.description}</p>
|
|
368
|
+
{step.tools_used && step.tools_used.length > 0 && (
|
|
369
|
+
<div className="flex flex-wrap gap-1 mt-1.5">
|
|
370
|
+
{step.tools_used.map((tool, j) => (
|
|
371
|
+
<span key={j} className="inline-flex items-center gap-1 px-2 py-0.5 rounded-sm bg-gray-100 text-[10px] font-medium text-gray-600">
|
|
372
|
+
<Wrench size={10} />
|
|
373
|
+
{tool}
|
|
374
|
+
</span>
|
|
375
|
+
))}
|
|
376
|
+
</div>
|
|
377
|
+
)}
|
|
378
|
+
</div>
|
|
379
|
+
</div>
|
|
380
|
+
))}
|
|
381
|
+
</div>
|
|
382
|
+
)}
|
|
383
|
+
</div>
|
|
384
|
+
)}
|
|
385
|
+
|
|
386
|
+
{/* Workflow Diagram */}
|
|
387
|
+
{documentation.diagram && (
|
|
388
|
+
<div>
|
|
389
|
+
<SectionHeader
|
|
390
|
+
icon={<TreeStructure size={12} weight="fill" />}
|
|
391
|
+
title="Workflow Diagram"
|
|
392
|
+
expanded={expandedSections.has('diagram')}
|
|
393
|
+
onToggle={() => toggleSection('diagram')}
|
|
394
|
+
iconColor="text-purple-500"
|
|
395
|
+
/>
|
|
396
|
+
{expandedSections.has('diagram') && (
|
|
397
|
+
<div className="pl-5 mt-2">
|
|
398
|
+
<div className="bg-white border border-gray-100 rounded-sm p-4 overflow-x-auto">
|
|
399
|
+
<FlowchartDiagram mermaid={documentation.diagram} />
|
|
400
|
+
</div>
|
|
401
|
+
</div>
|
|
402
|
+
)}
|
|
403
|
+
</div>
|
|
404
|
+
)}
|
|
405
|
+
|
|
406
|
+
{/* Expected Impact */}
|
|
407
|
+
{documentation.expected_impact && (
|
|
408
|
+
<div>
|
|
409
|
+
<SectionHeader
|
|
410
|
+
icon={<Lightning size={12} weight="fill" />}
|
|
411
|
+
title="Expected Impact"
|
|
412
|
+
expanded={expandedSections.has('impact')}
|
|
413
|
+
onToggle={() => toggleSection('impact')}
|
|
414
|
+
iconColor="text-purple-500"
|
|
415
|
+
/>
|
|
416
|
+
{expandedSections.has('impact') && (
|
|
417
|
+
<div className="pl-5 mt-2">
|
|
418
|
+
<div className="bg-emerald-50/50 border border-emerald-100 rounded-sm p-4">
|
|
419
|
+
<div className="grid grid-cols-3 gap-3 mb-3">
|
|
420
|
+
<div className="bg-white rounded-sm p-3 border border-emerald-100 text-center">
|
|
421
|
+
<Clock size={18} className="text-emerald-600 mx-auto mb-1" />
|
|
422
|
+
<p className="text-lg font-bold text-[var(--black)]">
|
|
423
|
+
{documentation.expected_impact.time_saved_per_occurrence_minutes} min
|
|
424
|
+
</p>
|
|
425
|
+
<p className="text-[10px] text-muted-foreground">saved per {freqLabel}</p>
|
|
426
|
+
</div>
|
|
427
|
+
<div className="bg-white rounded-sm p-3 border border-emerald-100 text-center">
|
|
428
|
+
<TrendUp size={18} className="text-emerald-600 mx-auto mb-1" />
|
|
429
|
+
<p className="text-lg font-bold text-[var(--black)]">
|
|
430
|
+
{documentation.expected_impact.yearly_hours_saved}h
|
|
431
|
+
</p>
|
|
432
|
+
<p className="text-[10px] text-muted-foreground">saved per year</p>
|
|
433
|
+
</div>
|
|
434
|
+
<div className="bg-white rounded-sm p-3 border border-emerald-100 text-center">
|
|
435
|
+
<CurrencyEur size={18} className="text-emerald-600 mx-auto mb-1" />
|
|
436
|
+
<p className="text-lg font-bold text-[var(--black)]">
|
|
437
|
+
€{documentation.expected_impact.yearly_cost_savings_euros.toLocaleString()}
|
|
438
|
+
</p>
|
|
439
|
+
<p className="text-[10px] text-muted-foreground">estimated yearly savings</p>
|
|
440
|
+
</div>
|
|
441
|
+
</div>
|
|
442
|
+
{documentation.expected_impact.qualitative_benefits && documentation.expected_impact.qualitative_benefits.length > 0 && (
|
|
443
|
+
<div className="space-y-1">
|
|
444
|
+
{documentation.expected_impact.qualitative_benefits.map((benefit, i) => (
|
|
445
|
+
<div key={i} className="flex items-start gap-2 text-sm text-emerald-700">
|
|
446
|
+
<Sparkle size={14} className="text-emerald-500 shrink-0 mt-0.5" weight="fill" />
|
|
447
|
+
{benefit}
|
|
448
|
+
</div>
|
|
449
|
+
))}
|
|
450
|
+
</div>
|
|
451
|
+
)}
|
|
452
|
+
</div>
|
|
453
|
+
</div>
|
|
454
|
+
)}
|
|
455
|
+
</div>
|
|
456
|
+
)}
|
|
457
|
+
|
|
458
|
+
{/* Technical Requirements & Integration Points */}
|
|
459
|
+
{((documentation.technical_requirements && documentation.technical_requirements.length > 0) ||
|
|
460
|
+
(documentation.integration_points && documentation.integration_points.length > 0)) && (
|
|
461
|
+
<div>
|
|
462
|
+
<SectionHeader
|
|
463
|
+
icon={<Plugs size={12} weight="fill" />}
|
|
464
|
+
title="Requirements & Integrations"
|
|
465
|
+
expanded={expandedSections.has('requirements')}
|
|
466
|
+
onToggle={() => toggleSection('requirements')}
|
|
467
|
+
/>
|
|
468
|
+
{expandedSections.has('requirements') && (
|
|
469
|
+
<div className="grid md:grid-cols-2 gap-4 pl-5 mt-2">
|
|
470
|
+
{documentation.technical_requirements && documentation.technical_requirements.length > 0 && (
|
|
471
|
+
<div>
|
|
472
|
+
<p className="text-xs font-medium text-muted-foreground uppercase tracking-wide mb-2">
|
|
473
|
+
Technical Requirements
|
|
474
|
+
</p>
|
|
475
|
+
<ul className="space-y-1.5">
|
|
476
|
+
{documentation.technical_requirements.map((req, i) => (
|
|
477
|
+
<li key={i} className="flex items-start gap-2 text-sm text-muted-foreground">
|
|
478
|
+
<Wrench size={12} className="text-gray-400 shrink-0 mt-1" />
|
|
479
|
+
{req}
|
|
480
|
+
</li>
|
|
481
|
+
))}
|
|
482
|
+
</ul>
|
|
483
|
+
</div>
|
|
484
|
+
)}
|
|
485
|
+
{documentation.integration_points && documentation.integration_points.length > 0 && (
|
|
486
|
+
<div>
|
|
487
|
+
<p className="text-xs font-medium text-muted-foreground uppercase tracking-wide mb-2">
|
|
488
|
+
Integration Points
|
|
489
|
+
</p>
|
|
490
|
+
<ul className="space-y-1.5">
|
|
491
|
+
{documentation.integration_points.map((point, i) => (
|
|
492
|
+
<li key={i} className="flex items-start gap-2 text-sm text-muted-foreground">
|
|
493
|
+
<Plugs size={12} className="text-gray-400 shrink-0 mt-1" />
|
|
494
|
+
{point}
|
|
495
|
+
</li>
|
|
496
|
+
))}
|
|
497
|
+
</ul>
|
|
498
|
+
</div>
|
|
499
|
+
)}
|
|
500
|
+
</div>
|
|
501
|
+
)}
|
|
502
|
+
</div>
|
|
503
|
+
)}
|
|
504
|
+
|
|
505
|
+
{/* Edge Cases Handled */}
|
|
506
|
+
{documentation.edge_cases_handled && documentation.edge_cases_handled.length > 0 && (
|
|
507
|
+
<div>
|
|
508
|
+
<SectionHeader
|
|
509
|
+
icon={<ShieldCheck size={12} weight="fill" />}
|
|
510
|
+
title="Edge Cases Handled"
|
|
511
|
+
count={documentation.edge_cases_handled.length}
|
|
512
|
+
expanded={expandedSections.has('edge_cases')}
|
|
513
|
+
onToggle={() => toggleSection('edge_cases')}
|
|
514
|
+
iconColor="text-amber-500"
|
|
515
|
+
/>
|
|
516
|
+
{expandedSections.has('edge_cases') && (
|
|
517
|
+
<div className="space-y-2 pl-5 mt-2">
|
|
518
|
+
{documentation.edge_cases_handled.map((ec, i) => (
|
|
519
|
+
<div key={i} className="text-sm p-3 bg-gray-50 rounded-sm border border-gray-100">
|
|
520
|
+
<p className="font-medium text-[var(--black)]">{ec.scenario}</p>
|
|
521
|
+
<p className="text-muted-foreground mt-1">→ {ec.handling}</p>
|
|
522
|
+
</div>
|
|
523
|
+
))}
|
|
524
|
+
</div>
|
|
525
|
+
)}
|
|
526
|
+
</div>
|
|
527
|
+
)}
|
|
528
|
+
|
|
529
|
+
{/* Version footer */}
|
|
530
|
+
<div className="pt-3 border-t border-gray-100 flex items-center gap-2 text-xs text-muted-foreground">
|
|
531
|
+
<Badge variant="outline" size="sm">v{documentation.version}</Badge>
|
|
532
|
+
{documentation.model_used && (
|
|
533
|
+
<>
|
|
534
|
+
<span>•</span>
|
|
535
|
+
<span>{documentation.model_used}</span>
|
|
536
|
+
</>
|
|
537
|
+
)}
|
|
538
|
+
<span>•</span>
|
|
539
|
+
<span>Updated {new Date(documentation.updated_at).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })}</span>
|
|
540
|
+
</div>
|
|
541
|
+
</div>
|
|
542
|
+
</div>
|
|
543
|
+
)
|
|
544
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -293,6 +293,14 @@ export type {
|
|
|
293
293
|
WorkflowTemplate
|
|
294
294
|
} from './components/workflow-viewer'
|
|
295
295
|
|
|
296
|
+
// Worker Spec Components
|
|
297
|
+
export { WorkerSpec } from './components/worker-spec'
|
|
298
|
+
export type { WorkerSpecProps, WorkerSpecDocumentation, AnalysisSource, AnalysisSummary } from './components/worker-spec'
|
|
299
|
+
|
|
300
|
+
// Flowchart Diagram Components
|
|
301
|
+
export { FlowchartDiagram } from './components/flowchart-diagram'
|
|
302
|
+
export type { FlowchartDiagramProps } from './components/flowchart-diagram'
|
|
303
|
+
|
|
296
304
|
// Utilities
|
|
297
305
|
export { cn } from './lib/utils'
|
|
298
306
|
|