@wealthx/shadcn 1.5.1 → 1.5.2

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 (36) hide show
  1. package/.turbo/turbo-build.log +118 -118
  2. package/CHANGELOG.md +6 -0
  3. package/dist/chunk-G2EWIP2N.mjs +960 -0
  4. package/dist/{chunk-MHHA7QGO.mjs → chunk-ODO6BUOF.mjs} +1 -1
  5. package/dist/chunk-PX4M67XQ.mjs +301 -0
  6. package/dist/{chunk-FYUSF5KO.mjs → chunk-QRVEI6J3.mjs} +1 -1
  7. package/dist/{chunk-42NEC57Y.mjs → chunk-RAKBWNQH.mjs} +272 -3
  8. package/dist/components/ui/{contact-alert-dialog.js → contact-alert-dialog/index.js} +1029 -593
  9. package/dist/components/ui/contact-alert-dialog/index.mjs +31 -0
  10. package/dist/components/ui/file-preview-dialog.js +407 -100
  11. package/dist/components/ui/file-preview-dialog.mjs +3 -1
  12. package/dist/components/ui/kanban-column.js +408 -113
  13. package/dist/components/ui/kanban-column.mjs +3 -2
  14. package/dist/components/ui/opportunity-card.js +383 -88
  15. package/dist/components/ui/opportunity-card.mjs +2 -1
  16. package/dist/components/ui/pipeline-board.js +424 -129
  17. package/dist/components/ui/pipeline-board.mjs +4 -3
  18. package/dist/index.js +3081 -2282
  19. package/dist/index.mjs +39 -35
  20. package/dist/styles.css +1 -1
  21. package/package.json +5 -4
  22. package/src/components/index.tsx +3 -2
  23. package/src/components/ui/contact-alert-dialog/builder-ui.tsx +556 -0
  24. package/src/components/ui/contact-alert-dialog/config.ts +262 -0
  25. package/src/components/ui/contact-alert-dialog/contact-alert-dialog.tsx +214 -0
  26. package/src/components/ui/contact-alert-dialog/index.tsx +15 -0
  27. package/src/components/ui/contact-alert-dialog/types.ts +61 -0
  28. package/src/components/ui/contact-alert-dialog/utils.ts +93 -0
  29. package/src/components/ui/file-preview-dialog.tsx +299 -99
  30. package/src/components/ui/opportunity-card.tsx +328 -1
  31. package/src/styles/styles-css.ts +1 -1
  32. package/tsup.config.ts +1 -1
  33. package/dist/chunk-5WMFKQZ6.mjs +0 -180
  34. package/dist/chunk-Y24TXIFJ.mjs +0 -518
  35. package/dist/components/ui/contact-alert-dialog.mjs +0 -27
  36. package/src/components/ui/contact-alert-dialog.tsx +0 -710
@@ -1,13 +1,17 @@
1
+ import { useState } from "react";
1
2
  import {
2
3
  Phone,
3
4
  Mail,
4
5
  User,
5
6
  Users,
6
7
  Calendar,
8
+ Check,
7
9
  MoreVertical,
8
10
  Clock,
9
11
  ArrowRight,
10
12
  Bot,
13
+ ChevronDown,
14
+ ChevronRight,
11
15
  } from "lucide-react";
12
16
  import { cn } from "@/lib/utils";
13
17
  import { formatCurrency } from "@/lib/format-currency";
@@ -29,6 +33,7 @@ import {
29
33
  AccordionTrigger,
30
34
  } from "@/components/ui/accordion";
31
35
  import { TaskCheckItem } from "@/components/ui/pipeline-primitives";
36
+ import { Progress } from "@/components/ui/progress";
32
37
  import {
33
38
  Tooltip,
34
39
  TooltipContent,
@@ -127,6 +132,12 @@ export interface OpportunityCardProps {
127
132
  draggable?: boolean;
128
133
  onDragStart?: React.DragEventHandler<HTMLDivElement>;
129
134
  className?: string;
135
+ /**
136
+ * Render mode for the card:
137
+ * - `"deal"` (default) — full opportunity info with task accordion.
138
+ * - `"task"` — compact task-first layout (next task, progress, deal ref).
139
+ */
140
+ viewMode?: "deal" | "task";
130
141
  }
131
142
 
132
143
  // ---------------------------------------------------------------------------
@@ -169,11 +180,291 @@ function formatLoanType(type: string): string {
169
180
  .join(" ");
170
181
  }
171
182
 
183
+ // ---------------------------------------------------------------------------
184
+ // TaskViewCard — compact task-first card (viewMode === "task")
185
+ //
186
+ // Three states driven by task progress:
187
+ // A) Has next task → shows task title + AI agent + segmented progress
188
+ // B) All tasks done → shows ✓ message + full green progress bar
189
+ // C) No tasks → shows fallback muted text
190
+ // ---------------------------------------------------------------------------
191
+
192
+ function TaskViewCard({
193
+ customerName,
194
+ loanType,
195
+ loanPurposeLabel,
196
+ amount,
197
+ priority,
198
+ daysSinceColumnChanged,
199
+ warningDays,
200
+ priorityDays,
201
+ tasks = [],
202
+ nextTask,
203
+ onCardClick,
204
+ onViewDetails,
205
+ onTaskToggle,
206
+ onMarkAsDone,
207
+ onMoveToNextStage,
208
+ onChangePriority,
209
+ onDelete,
210
+ onPutOnHold,
211
+ isSubmitting = false,
212
+ draggable = false,
213
+ onDragStart,
214
+ className,
215
+ }: OpportunityCardProps): React.JSX.Element {
216
+ const resolvedPriority = resolvePriority(
217
+ daysSinceColumnChanged,
218
+ warningDays,
219
+ priorityDays,
220
+ priority,
221
+ );
222
+ const priorityColor = PRIORITY_COLORS[resolvedPriority];
223
+
224
+ const completedCount = tasks.filter((t) => t.completed).length;
225
+ const totalCount = tasks.length;
226
+ const hasTasks = totalCount > 0;
227
+ const allDone = hasTasks && completedCount === totalCount;
228
+
229
+ // Find the next pending task to surface AI agent info
230
+ const nextPendingTask = tasks.find((t) => !t.completed);
231
+ const agentName = nextPendingTask?.aiAgentName ?? null;
232
+
233
+ const [subtasksExpanded, setSubtasksExpanded] = useState(false);
234
+
235
+ const hasMenu = onViewDetails || onChangePriority || onPutOnHold || onDelete;
236
+ const stopProp = (e: React.MouseEvent): void => {
237
+ e.stopPropagation();
238
+ };
239
+
240
+ // Compact deal reference: "John Smith · Buy a House · $850,000"
241
+ const purposeLabel =
242
+ loanPurposeLabel ?? (loanType ? formatLoanType(loanType) : null);
243
+ const dealRef = [
244
+ customerName,
245
+ purposeLabel,
246
+ amount > 0 ? formatCurrency(amount) : null,
247
+ ]
248
+ .filter(Boolean)
249
+ .join(" · ");
250
+
251
+ // Task header content (no nested ternary — avoids lint error)
252
+ let taskHeader: React.ReactNode;
253
+ if (allDone) {
254
+ taskHeader = (
255
+ <p
256
+ className="flex items-center gap-1.5 text-sm font-semibold"
257
+ style={{ color: "var(--color-success-text)" }}
258
+ >
259
+ <Check className="size-3.5 shrink-0" />
260
+ All tasks complete
261
+ </p>
262
+ );
263
+ } else if (nextTask) {
264
+ taskHeader = (
265
+ <>
266
+ <div className="flex items-center gap-1.5">
267
+ {agentName && (
268
+ <Bot
269
+ className="size-3.5 shrink-0 text-primary"
270
+ aria-hidden="true"
271
+ />
272
+ )}
273
+ <p className="truncate text-sm font-semibold leading-snug">
274
+ {nextTask}
275
+ </p>
276
+ </div>
277
+ {agentName && (
278
+ <p className="mt-0.5 text-xs text-muted-foreground">{agentName}</p>
279
+ )}
280
+ </>
281
+ );
282
+ } else {
283
+ taskHeader = (
284
+ <p className="text-xs text-muted-foreground">
285
+ No tasks defined for this stage
286
+ </p>
287
+ );
288
+ }
289
+
290
+ return (
291
+ <div
292
+ className={cn(
293
+ "flex flex-col gap-2.5 border border-border bg-background p-3 text-foreground shadow-sm",
294
+ onCardClick && "cursor-pointer",
295
+ draggable && "cursor-grab active:cursor-grabbing",
296
+ isSubmitting && "opacity-60",
297
+ className,
298
+ )}
299
+ data-slot="opportunity-card"
300
+ draggable={draggable}
301
+ onDragStart={onDragStart}
302
+ onClick={onCardClick}
303
+ >
304
+ {/* ── Task header + priority dot ── */}
305
+ <div className="flex items-start gap-2">
306
+ <div className="min-w-0 flex-1">{taskHeader}</div>
307
+ <span
308
+ role="img"
309
+ className="mt-0.5 inline-block size-2.5 shrink-0 rounded-full"
310
+ style={{ backgroundColor: priorityColor }}
311
+ aria-label={`Priority: ${resolvedPriority}`}
312
+ />
313
+ </div>
314
+
315
+ {/* ── Progress (only when tasks exist) ── */}
316
+ {hasTasks && (
317
+ <div className="flex items-center gap-2">
318
+ <span className="shrink-0 tabular-nums text-xs text-muted-foreground">
319
+ {completedCount}/{totalCount}
320
+ </span>
321
+ <Progress
322
+ value={(completedCount / totalCount) * 100}
323
+ className={cn(
324
+ "h-1.5 flex-1",
325
+ allDone && "[&_[data-slot=progress-indicator]]:bg-success",
326
+ )}
327
+ />
328
+ </div>
329
+ )}
330
+
331
+ {/* ── Collapsible subtask list ── */}
332
+ {hasTasks && (
333
+ <div
334
+ className="space-y-1 border-t border-border pt-2"
335
+ onClick={stopProp}
336
+ role="presentation"
337
+ onKeyDown={(e) => e.stopPropagation()}
338
+ >
339
+ <button
340
+ type="button"
341
+ onClick={() => setSubtasksExpanded((v) => !v)}
342
+ className="flex w-full items-center justify-between text-xs text-muted-foreground hover:text-foreground"
343
+ >
344
+ <span className="flex items-center gap-1">
345
+ {subtasksExpanded ? (
346
+ <ChevronDown className="size-3" />
347
+ ) : (
348
+ <ChevronRight className="size-3" />
349
+ )}
350
+ Subtasks
351
+ </span>
352
+ <span>
353
+ {completedCount} / {totalCount}
354
+ </span>
355
+ </button>
356
+ {subtasksExpanded && (
357
+ <ul className="space-y-1.5 pt-1">
358
+ {tasks.map((task) => (
359
+ <TaskCheckItem
360
+ key={task.id}
361
+ title={task.title}
362
+ completed={task.completed}
363
+ aiAgentName={task.aiAgentName}
364
+ onToggle={() => onTaskToggle?.(task.id)}
365
+ size="xs"
366
+ />
367
+ ))}
368
+ </ul>
369
+ )}
370
+ </div>
371
+ )}
372
+
373
+ {/* ── Deal reference line ── */}
374
+ <p className="truncate text-xs text-muted-foreground">{dealRef}</p>
375
+
376
+ {/* ── Footer: days badge + 3-dot menu ── */}
377
+ <div className="flex items-center gap-1.5" onClick={stopProp}>
378
+ {daysSinceColumnChanged !== undefined && (
379
+ <Badge variant="secondary" className="px-1.5 text-[10px] font-normal">
380
+ {daysSinceColumnChanged}d
381
+ </Badge>
382
+ )}
383
+ {hasMenu && (
384
+ <div className="ml-auto">
385
+ <DropdownMenu>
386
+ <DropdownMenuTrigger
387
+ className={cn(
388
+ buttonVariants({ variant: "ghost", size: "icon" }),
389
+ "size-7 shrink-0",
390
+ )}
391
+ aria-label="Opportunity actions"
392
+ >
393
+ <MoreVertical className="size-4" />
394
+ </DropdownMenuTrigger>
395
+ <DropdownMenuContent align="end">
396
+ {onViewDetails && (
397
+ <DropdownMenuItem onClick={onViewDetails}>
398
+ View details
399
+ </DropdownMenuItem>
400
+ )}
401
+ {onChangePriority && (
402
+ <DropdownMenuItem onClick={onChangePriority}>
403
+ Change priority
404
+ </DropdownMenuItem>
405
+ )}
406
+ {onPutOnHold && (
407
+ <DropdownMenuItem onClick={onPutOnHold}>
408
+ Put on hold
409
+ </DropdownMenuItem>
410
+ )}
411
+ {onDelete && (
412
+ <>
413
+ <DropdownMenuSeparator />
414
+ <DropdownMenuItem onClick={onDelete} variant="destructive">
415
+ Delete
416
+ </DropdownMenuItem>
417
+ </>
418
+ )}
419
+ </DropdownMenuContent>
420
+ </DropdownMenu>
421
+ </div>
422
+ )}
423
+ </div>
424
+
425
+ {/* ── Action button ── */}
426
+ {allDone && onMoveToNextStage && (
427
+ <div onClick={stopProp}>
428
+ <Separator />
429
+ <div className="pt-2">
430
+ <Button
431
+ variant="outline"
432
+ size="sm"
433
+ className="w-full text-caption"
434
+ onClick={onMoveToNextStage}
435
+ disabled={isSubmitting}
436
+ >
437
+ Move to next stage
438
+ </Button>
439
+ </div>
440
+ </div>
441
+ )}
442
+ {!allDone && nextTask && onMarkAsDone && (
443
+ <div onClick={stopProp}>
444
+ <Separator />
445
+ <div className="pt-2">
446
+ <Button
447
+ variant="default"
448
+ size="sm"
449
+ className="w-full text-caption"
450
+ onClick={onMarkAsDone}
451
+ disabled={isSubmitting}
452
+ >
453
+ Mark as done
454
+ </Button>
455
+ </div>
456
+ </div>
457
+ )}
458
+ </div>
459
+ );
460
+ }
461
+
172
462
  // ---------------------------------------------------------------------------
173
463
  // OpportunityCard
174
464
  // ---------------------------------------------------------------------------
175
465
 
176
466
  export function OpportunityCard({
467
+ id,
177
468
  customerName,
178
469
  customerPhone,
179
470
  customerEmail,
@@ -203,7 +494,41 @@ export function OpportunityCard({
203
494
  draggable = false,
204
495
  onDragStart,
205
496
  className,
497
+ viewMode = "deal",
206
498
  }: OpportunityCardProps) {
499
+ if (viewMode === "task") {
500
+ return (
501
+ <TaskViewCard
502
+ customerName={customerName}
503
+ customerPhone={customerPhone}
504
+ customerEmail={customerEmail}
505
+ additionalContacts={additionalContacts}
506
+ loanType={loanType}
507
+ loanPurposeLabel={loanPurposeLabel}
508
+ amount={amount}
509
+ date={date}
510
+ priority={priority}
511
+ daysSinceColumnChanged={daysSinceColumnChanged}
512
+ warningDays={warningDays}
513
+ priorityDays={priorityDays}
514
+ tasks={tasks}
515
+ nextTask={nextTask}
516
+ onCardClick={onCardClick}
517
+ onViewDetails={onViewDetails}
518
+ onTaskToggle={onTaskToggle}
519
+ onMarkAsDone={onMarkAsDone}
520
+ onMoveToNextStage={onMoveToNextStage}
521
+ onChangePriority={onChangePriority}
522
+ onDelete={onDelete}
523
+ onPutOnHold={onPutOnHold}
524
+ isSubmitting={isSubmitting}
525
+ draggable={draggable}
526
+ onDragStart={onDragStart}
527
+ className={className}
528
+ />
529
+ );
530
+ }
531
+
207
532
  const resolvedPriority = resolvePriority(
208
533
  daysSinceColumnChanged,
209
534
  warningDays,
@@ -218,7 +543,9 @@ export function OpportunityCard({
218
543
  const hasActions = onMarkAsDone || onMoveToNextStage;
219
544
  const hasMenu = onViewDetails || onChangePriority || onPutOnHold || onDelete;
220
545
 
221
- const stopProp = (e: React.MouseEvent) => e.stopPropagation();
546
+ const stopProp = (e: React.MouseEvent): void => {
547
+ e.stopPropagation();
548
+ };
222
549
 
223
550
  return (
224
551
  <div