@dilipod/ui 0.4.2 → 0.4.3
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/button.d.ts +4 -0
- package/dist/components/button.d.ts.map +1 -1
- package/dist/components/scenarios-manager.d.ts +4 -1
- package/dist/components/scenarios-manager.d.ts.map +1 -1
- package/dist/index.js +256 -196
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +251 -191
- package/dist/index.mjs.map +1 -1
- package/package.json +12 -2
- package/src/__tests__/button.test.tsx +171 -0
- package/src/__tests__/scenarios-manager.test.tsx +260 -0
- package/src/__tests__/setup.ts +31 -0
- package/src/components/button.tsx +50 -25
- package/src/components/scenarios-manager.tsx +83 -26
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
3
|
import * as React from 'react'
|
|
4
|
-
import { Plus, PencilSimple, Trash, Warning, CheckCircle, Question, Lightning } from '@phosphor-icons/react'
|
|
4
|
+
import { Plus, PencilSimple, Trash, Warning, CheckCircle, Question, Lightning, Check } from '@phosphor-icons/react'
|
|
5
5
|
import { cn } from '../lib/utils'
|
|
6
6
|
import { Button } from './button'
|
|
7
7
|
import { Badge } from './badge'
|
|
@@ -37,12 +37,15 @@ export interface ScenariosManagerProps {
|
|
|
37
37
|
onAdd: (scenario: Omit<Scenario, 'id'>) => Promise<void>
|
|
38
38
|
onUpdate: (id: string, scenario: Omit<Scenario, 'id'>) => Promise<void>
|
|
39
39
|
onDelete: (id: string) => Promise<void>
|
|
40
|
+
onComplete?: () => Promise<void>
|
|
40
41
|
suggestions?: ScenarioSuggestion[]
|
|
41
42
|
isLoading?: boolean
|
|
43
|
+
isComplete?: boolean
|
|
44
|
+
minScenariosToComplete?: number
|
|
42
45
|
className?: string
|
|
43
46
|
}
|
|
44
47
|
|
|
45
|
-
// Type configuration
|
|
48
|
+
// Type configuration - using Dilipod brand colors
|
|
46
49
|
const typeConfig: Record<ScenarioType, {
|
|
47
50
|
label: string
|
|
48
51
|
icon: React.ElementType
|
|
@@ -60,8 +63,8 @@ const typeConfig: Record<ScenarioType, {
|
|
|
60
63
|
default_behavior: {
|
|
61
64
|
label: 'Handle it',
|
|
62
65
|
icon: CheckCircle,
|
|
63
|
-
color: 'text-
|
|
64
|
-
bgColor: 'bg-
|
|
66
|
+
color: 'text-[var(--cyan)]',
|
|
67
|
+
bgColor: 'bg-[var(--cyan)]/10',
|
|
65
68
|
description: 'Proceed automatically using this rule',
|
|
66
69
|
},
|
|
67
70
|
quality_check: {
|
|
@@ -74,8 +77,8 @@ const typeConfig: Record<ScenarioType, {
|
|
|
74
77
|
edge_case: {
|
|
75
78
|
label: 'Watch out',
|
|
76
79
|
icon: Question,
|
|
77
|
-
color: 'text-
|
|
78
|
-
bgColor: 'bg-
|
|
80
|
+
color: 'text-muted-foreground',
|
|
81
|
+
bgColor: 'bg-muted',
|
|
79
82
|
description: 'Common mistakes or tricky situations',
|
|
80
83
|
},
|
|
81
84
|
}
|
|
@@ -94,21 +97,21 @@ function ScenarioCard({
|
|
|
94
97
|
const Icon = config.icon
|
|
95
98
|
|
|
96
99
|
return (
|
|
97
|
-
<div className="group relative border border-
|
|
100
|
+
<div className="group relative border border-border rounded-sm p-4 hover:border-[var(--cyan)]/30 hover:bg-[var(--cyan)]/[0.02] transition-all">
|
|
98
101
|
<div className="flex items-start justify-between gap-3">
|
|
99
102
|
<div className="flex items-start gap-3 flex-1 min-w-0">
|
|
100
|
-
<div className={cn('w-
|
|
101
|
-
<Icon size={
|
|
103
|
+
<div className={cn('w-9 h-9 rounded-sm flex items-center justify-center shrink-0', config.bgColor)}>
|
|
104
|
+
<Icon size={18} weight="fill" className={config.color} />
|
|
102
105
|
</div>
|
|
103
106
|
<div className="flex-1 min-w-0">
|
|
104
|
-
<div className="flex items-center gap-2 mb-1">
|
|
105
|
-
<Badge variant="outline" size="sm">{config.label}</Badge>
|
|
107
|
+
<div className="flex items-center gap-2 mb-1.5">
|
|
108
|
+
<Badge variant="outline" size="sm" className="font-medium">{config.label}</Badge>
|
|
106
109
|
</div>
|
|
107
|
-
<p className="text-sm text-[var(--black)]
|
|
108
|
-
|
|
110
|
+
<p className="text-sm text-[var(--black)]">
|
|
111
|
+
<span className="font-medium">When:</span> {scenario.situation}
|
|
109
112
|
</p>
|
|
110
113
|
<p className="text-sm text-muted-foreground mt-1">
|
|
111
|
-
|
|
114
|
+
<span className="text-[var(--black)] font-medium">Action:</span> {scenario.action}
|
|
112
115
|
</p>
|
|
113
116
|
</div>
|
|
114
117
|
</div>
|
|
@@ -116,7 +119,7 @@ function ScenarioCard({
|
|
|
116
119
|
<Button
|
|
117
120
|
variant="ghost"
|
|
118
121
|
size="icon"
|
|
119
|
-
className="h-8 w-8"
|
|
122
|
+
className="h-8 w-8 text-muted-foreground hover:text-[var(--black)]"
|
|
120
123
|
onClick={onEdit}
|
|
121
124
|
>
|
|
122
125
|
<PencilSimple size={16} />
|
|
@@ -124,7 +127,7 @@ function ScenarioCard({
|
|
|
124
127
|
<Button
|
|
125
128
|
variant="ghost"
|
|
126
129
|
size="icon"
|
|
127
|
-
className="h-8 w-8 text-
|
|
130
|
+
className="h-8 w-8 text-muted-foreground hover:text-red-600 hover:bg-red-50"
|
|
128
131
|
onClick={onDelete}
|
|
129
132
|
>
|
|
130
133
|
<Trash size={16} />
|
|
@@ -145,21 +148,18 @@ function SuggestionChip({
|
|
|
145
148
|
onAdd: () => void
|
|
146
149
|
disabled?: boolean
|
|
147
150
|
}) {
|
|
148
|
-
const config = typeConfig[suggestion.type]
|
|
149
|
-
const Icon = config.icon
|
|
150
|
-
|
|
151
151
|
return (
|
|
152
152
|
<button
|
|
153
153
|
type="button"
|
|
154
154
|
onClick={onAdd}
|
|
155
155
|
disabled={disabled}
|
|
156
156
|
className={cn(
|
|
157
|
-
'inline-flex items-center gap-
|
|
157
|
+
'inline-flex items-center gap-1.5 px-3 py-1.5 rounded-sm border border-dashed border-border',
|
|
158
158
|
'text-sm text-muted-foreground hover:border-[var(--cyan)] hover:text-[var(--cyan)] hover:bg-[var(--cyan)]/5',
|
|
159
|
-
'transition-
|
|
159
|
+
'transition-all disabled:opacity-50 disabled:cursor-not-allowed'
|
|
160
160
|
)}
|
|
161
161
|
>
|
|
162
|
-
<Plus size={14} />
|
|
162
|
+
<Plus size={14} weight="bold" />
|
|
163
163
|
{suggestion.situation}
|
|
164
164
|
</button>
|
|
165
165
|
)
|
|
@@ -276,13 +276,19 @@ export function ScenariosManager({
|
|
|
276
276
|
onAdd,
|
|
277
277
|
onUpdate,
|
|
278
278
|
onDelete,
|
|
279
|
+
onComplete,
|
|
279
280
|
suggestions = [],
|
|
280
281
|
isLoading,
|
|
282
|
+
isComplete = false,
|
|
283
|
+
minScenariosToComplete = 1,
|
|
281
284
|
className,
|
|
282
285
|
}: ScenariosManagerProps) {
|
|
283
286
|
const [dialogOpen, setDialogOpen] = React.useState(false)
|
|
284
287
|
const [editingScenario, setEditingScenario] = React.useState<Scenario | null>(null)
|
|
285
288
|
const [deletingId, setDeletingId] = React.useState<string | null>(null)
|
|
289
|
+
const [isCompleting, setIsCompleting] = React.useState(false)
|
|
290
|
+
|
|
291
|
+
const canComplete = scenarios.length >= minScenariosToComplete && !isComplete && onComplete
|
|
286
292
|
|
|
287
293
|
const handleAddClick = () => {
|
|
288
294
|
setEditingScenario(null)
|
|
@@ -315,6 +321,16 @@ export function ScenariosManager({
|
|
|
315
321
|
await onAdd(suggestion)
|
|
316
322
|
}
|
|
317
323
|
|
|
324
|
+
const handleComplete = async () => {
|
|
325
|
+
if (!onComplete) return
|
|
326
|
+
setIsCompleting(true)
|
|
327
|
+
try {
|
|
328
|
+
await onComplete()
|
|
329
|
+
} finally {
|
|
330
|
+
setIsCompleting(false)
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
|
|
318
334
|
// Filter out suggestions that already exist
|
|
319
335
|
const filteredSuggestions = suggestions.filter(
|
|
320
336
|
(s) => !scenarios.some(
|
|
@@ -361,20 +377,22 @@ export function ScenariosManager({
|
|
|
361
377
|
|
|
362
378
|
{/* Empty state */}
|
|
363
379
|
{scenarios.length === 0 && (
|
|
364
|
-
<div className="border border-dashed border-
|
|
365
|
-
<
|
|
380
|
+
<div className="border border-dashed border-border rounded-sm p-8 text-center">
|
|
381
|
+
<div className="w-12 h-12 rounded-sm bg-muted flex items-center justify-center mx-auto mb-3">
|
|
382
|
+
<Lightning size={24} className="text-muted-foreground" />
|
|
383
|
+
</div>
|
|
366
384
|
<p className="text-sm text-muted-foreground mb-4">
|
|
367
385
|
No scenarios yet. Add rules for how the worker should handle edge cases.
|
|
368
386
|
</p>
|
|
369
387
|
<Button variant="outline" size="sm" onClick={handleAddClick}>
|
|
370
|
-
<Plus size={16} className="mr-1" />
|
|
388
|
+
<Plus size={16} className="mr-1.5" />
|
|
371
389
|
Add your first scenario
|
|
372
390
|
</Button>
|
|
373
391
|
</div>
|
|
374
392
|
)}
|
|
375
393
|
|
|
376
394
|
{/* Suggestions */}
|
|
377
|
-
{filteredSuggestions.length > 0 && (
|
|
395
|
+
{filteredSuggestions.length > 0 && !isComplete && (
|
|
378
396
|
<div className="pt-2">
|
|
379
397
|
<p className="text-xs text-muted-foreground mb-2">Suggested scenarios:</p>
|
|
380
398
|
<div className="flex flex-wrap gap-2">
|
|
@@ -390,6 +408,45 @@ export function ScenariosManager({
|
|
|
390
408
|
</div>
|
|
391
409
|
)}
|
|
392
410
|
|
|
411
|
+
{/* Complete button */}
|
|
412
|
+
{canComplete && (
|
|
413
|
+
<div className="pt-4 border-t border-border">
|
|
414
|
+
<div className="flex items-center justify-between gap-4 bg-[var(--cyan)]/5 rounded-sm p-4 -mx-1">
|
|
415
|
+
<div>
|
|
416
|
+
<p className="text-sm font-medium text-[var(--black)]">
|
|
417
|
+
Ready to proceed?
|
|
418
|
+
</p>
|
|
419
|
+
<p className="text-xs text-muted-foreground mt-0.5">
|
|
420
|
+
Mark your scenarios as complete to continue with the onboarding.
|
|
421
|
+
</p>
|
|
422
|
+
</div>
|
|
423
|
+
<Button
|
|
424
|
+
onClick={handleComplete}
|
|
425
|
+
disabled={isCompleting}
|
|
426
|
+
loading={isCompleting}
|
|
427
|
+
size="sm"
|
|
428
|
+
className="shrink-0"
|
|
429
|
+
>
|
|
430
|
+
<Check size={16} className="mr-1.5" />
|
|
431
|
+
Mark complete
|
|
432
|
+
</Button>
|
|
433
|
+
</div>
|
|
434
|
+
</div>
|
|
435
|
+
)}
|
|
436
|
+
|
|
437
|
+
{/* Complete state */}
|
|
438
|
+
{isComplete && (
|
|
439
|
+
<div className="pt-4 border-t border-border">
|
|
440
|
+
<div className="flex items-center gap-2 text-[var(--cyan)]">
|
|
441
|
+
<CheckCircle size={16} weight="fill" />
|
|
442
|
+
<p className="text-sm font-medium">Scenarios completed</p>
|
|
443
|
+
</div>
|
|
444
|
+
<p className="text-xs text-muted-foreground mt-1">
|
|
445
|
+
You can still add or edit scenarios while we build your worker.
|
|
446
|
+
</p>
|
|
447
|
+
</div>
|
|
448
|
+
)}
|
|
449
|
+
|
|
393
450
|
{/* Dialog */}
|
|
394
451
|
<ScenarioDialog
|
|
395
452
|
open={dialogOpen}
|