@contractspec/example.crm-pipeline 3.7.5 → 3.7.7
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/.turbo/turbo-build.log +8 -8
- package/AGENTS.md +51 -33
- package/CHANGELOG.md +16 -0
- package/README.md +66 -148
- package/dist/browser/events/contact.event.js +1 -1
- package/dist/browser/events/deal.event.js +1 -1
- package/dist/browser/events/index.js +3 -3
- package/dist/browser/events/task.event.js +1 -1
- package/dist/browser/index.js +293 -293
- package/dist/browser/ui/CrmDashboard.js +221 -221
- package/dist/browser/ui/CrmDealCard.js +5 -5
- package/dist/browser/ui/CrmPipelineBoard.js +13 -13
- package/dist/browser/ui/hooks/index.js +2 -2
- package/dist/browser/ui/hooks/useDealList.js +1 -1
- package/dist/browser/ui/hooks/useDealMutations.js +1 -1
- package/dist/browser/ui/index.js +290 -290
- package/dist/browser/ui/modals/CreateDealModal.js +12 -12
- package/dist/browser/ui/modals/DealActionsModal.js +21 -21
- package/dist/browser/ui/modals/index.js +33 -33
- package/dist/browser/ui/renderers/index.js +116 -116
- package/dist/browser/ui/renderers/pipeline.renderer.js +97 -97
- package/dist/deal/index.d.ts +2 -2
- package/dist/events/contact.event.js +1 -1
- package/dist/events/deal.event.js +1 -1
- package/dist/events/index.js +3 -3
- package/dist/events/task.event.js +1 -1
- package/dist/handlers/index.d.ts +2 -2
- package/dist/index.d.ts +3 -3
- package/dist/index.js +293 -293
- package/dist/node/events/contact.event.js +1 -1
- package/dist/node/events/deal.event.js +1 -1
- package/dist/node/events/index.js +3 -3
- package/dist/node/events/task.event.js +1 -1
- package/dist/node/index.js +293 -293
- package/dist/node/ui/CrmDashboard.js +221 -221
- package/dist/node/ui/CrmDealCard.js +5 -5
- package/dist/node/ui/CrmPipelineBoard.js +13 -13
- package/dist/node/ui/hooks/index.js +2 -2
- package/dist/node/ui/hooks/useDealList.js +1 -1
- package/dist/node/ui/hooks/useDealMutations.js +1 -1
- package/dist/node/ui/index.js +290 -290
- package/dist/node/ui/modals/CreateDealModal.js +12 -12
- package/dist/node/ui/modals/DealActionsModal.js +21 -21
- package/dist/node/ui/modals/index.js +33 -33
- package/dist/node/ui/renderers/index.js +116 -116
- package/dist/node/ui/renderers/pipeline.renderer.js +97 -97
- package/dist/operations/index.d.ts +1 -1
- package/dist/ui/CrmDashboard.js +221 -221
- package/dist/ui/CrmDealCard.js +5 -5
- package/dist/ui/CrmPipelineBoard.js +13 -13
- package/dist/ui/hooks/index.d.ts +2 -2
- package/dist/ui/hooks/index.js +2 -2
- package/dist/ui/hooks/useDealList.js +1 -1
- package/dist/ui/hooks/useDealMutations.d.ts +9 -0
- package/dist/ui/hooks/useDealMutations.js +1 -1
- package/dist/ui/index.d.ts +3 -3
- package/dist/ui/index.js +290 -290
- package/dist/ui/modals/CreateDealModal.js +12 -12
- package/dist/ui/modals/DealActionsModal.js +21 -21
- package/dist/ui/modals/index.js +33 -33
- package/dist/ui/renderers/index.d.ts +1 -1
- package/dist/ui/renderers/index.js +116 -116
- package/dist/ui/renderers/pipeline.renderer.d.ts +1 -1
- package/dist/ui/renderers/pipeline.renderer.js +97 -97
- package/package.json +14 -14
- package/src/crm-pipeline.feature.ts +86 -86
- package/src/deal/deal.enum.ts +8 -8
- package/src/deal/deal.operation.ts +255 -255
- package/src/deal/deal.schema.ts +92 -92
- package/src/deal/deal.test-spec.ts +48 -48
- package/src/deal/index.ts +17 -19
- package/src/docs/crm-pipeline.docblock.ts +43 -43
- package/src/entities/company.entity.ts +52 -52
- package/src/entities/contact.entity.ts +67 -67
- package/src/entities/deal.entity.ts +134 -134
- package/src/entities/index.ts +27 -27
- package/src/entities/task.entity.ts +105 -105
- package/src/events/contact.event.ts +22 -22
- package/src/events/deal.event.ts +77 -77
- package/src/events/task.event.ts +19 -19
- package/src/example.ts +32 -32
- package/src/handlers/crm.handlers.ts +358 -357
- package/src/handlers/deal.handlers.ts +179 -179
- package/src/handlers/index.ts +18 -19
- package/src/handlers/mock-data.ts +167 -167
- package/src/index.ts +11 -11
- package/src/operations/index.ts +16 -16
- package/src/presentations/dashboard.presentation.ts +45 -45
- package/src/presentations/pipeline.presentation.ts +90 -90
- package/src/seeders/index.ts +26 -26
- package/src/shared/overlay-types.ts +23 -23
- package/src/ui/CrmDashboard.tsx +256 -256
- package/src/ui/CrmDealCard.tsx +64 -64
- package/src/ui/CrmPipelineBoard.tsx +105 -105
- package/src/ui/hooks/index.ts +3 -3
- package/src/ui/hooks/useDealList.ts +85 -85
- package/src/ui/hooks/useDealMutations.ts +151 -150
- package/src/ui/index.ts +5 -10
- package/src/ui/modals/CreateDealModal.tsx +217 -217
- package/src/ui/modals/DealActionsModal.tsx +390 -390
- package/src/ui/overlays/demo-overlays.ts +43 -43
- package/src/ui/renderers/index.ts +4 -3
- package/src/ui/renderers/pipeline.markdown.ts +165 -165
- package/src/ui/renderers/pipeline.renderer.tsx +17 -16
- package/tsconfig.json +7 -8
- package/tsdown.config.js +7 -3
package/dist/browser/ui/index.js
CHANGED
|
@@ -440,19 +440,177 @@ async function mockGetDealsByStageHandler(input) {
|
|
|
440
440
|
async function mockGetPipelineStagesHandler(input) {
|
|
441
441
|
return MOCK_STAGES.filter((s) => s.pipelineId === input.pipelineId);
|
|
442
442
|
}
|
|
443
|
+
// src/ui/CrmDealCard.tsx
|
|
444
|
+
import { jsxDEV } from "react/jsx-dev-runtime";
|
|
445
|
+
"use client";
|
|
446
|
+
function formatCurrency(value, currency) {
|
|
447
|
+
return new Intl.NumberFormat("en-US", {
|
|
448
|
+
style: "currency",
|
|
449
|
+
currency,
|
|
450
|
+
minimumFractionDigits: 0,
|
|
451
|
+
maximumFractionDigits: 0
|
|
452
|
+
}).format(value);
|
|
453
|
+
}
|
|
454
|
+
function CrmDealCard({ deal, onClick }) {
|
|
455
|
+
const daysUntilClose = deal.expectedCloseDate ? Math.ceil((deal.expectedCloseDate.getTime() - Date.now()) / (1000 * 60 * 60 * 24)) : null;
|
|
456
|
+
return /* @__PURE__ */ jsxDEV("div", {
|
|
457
|
+
onClick,
|
|
458
|
+
className: "cursor-pointer rounded-lg border border-border bg-card p-3 shadow-sm transition-shadow hover:shadow-md",
|
|
459
|
+
role: "button",
|
|
460
|
+
tabIndex: 0,
|
|
461
|
+
onKeyDown: (e) => {
|
|
462
|
+
if (e.key === "Enter" || e.key === " ")
|
|
463
|
+
onClick?.();
|
|
464
|
+
},
|
|
465
|
+
children: [
|
|
466
|
+
/* @__PURE__ */ jsxDEV("h4", {
|
|
467
|
+
className: "font-medium leading-snug",
|
|
468
|
+
children: deal.name
|
|
469
|
+
}, undefined, false, undefined, this),
|
|
470
|
+
/* @__PURE__ */ jsxDEV("div", {
|
|
471
|
+
className: "mt-2 font-semibold text-lg text-primary",
|
|
472
|
+
children: formatCurrency(deal.value, deal.currency)
|
|
473
|
+
}, undefined, false, undefined, this),
|
|
474
|
+
/* @__PURE__ */ jsxDEV("div", {
|
|
475
|
+
className: "mt-3 flex items-center justify-between text-muted-foreground text-xs",
|
|
476
|
+
children: [
|
|
477
|
+
daysUntilClose !== null && /* @__PURE__ */ jsxDEV("span", {
|
|
478
|
+
className: daysUntilClose < 0 ? "text-red-500" : daysUntilClose <= 7 ? "text-yellow-600 dark:text-yellow-500" : "",
|
|
479
|
+
children: daysUntilClose < 0 ? `${Math.abs(daysUntilClose)}d overdue` : daysUntilClose === 0 ? "Due today" : `${daysUntilClose}d left`
|
|
480
|
+
}, undefined, false, undefined, this),
|
|
481
|
+
/* @__PURE__ */ jsxDEV("span", {
|
|
482
|
+
className: `rounded px-1.5 py-0.5 font-medium text-xs ${deal.status === "WON" ? "bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400" : deal.status === "LOST" ? "bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400" : "bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400"}`,
|
|
483
|
+
children: deal.status
|
|
484
|
+
}, undefined, false, undefined, this)
|
|
485
|
+
]
|
|
486
|
+
}, undefined, true, undefined, this)
|
|
487
|
+
]
|
|
488
|
+
}, undefined, true, undefined, this);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// src/ui/CrmPipelineBoard.tsx
|
|
492
|
+
import { useState } from "react";
|
|
493
|
+
import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
|
|
494
|
+
"use client";
|
|
495
|
+
function formatCurrency2(value) {
|
|
496
|
+
if (value >= 1e6)
|
|
497
|
+
return `$${(value / 1e6).toFixed(1)}M`;
|
|
498
|
+
if (value >= 1000)
|
|
499
|
+
return `$${(value / 1000).toFixed(0)}K`;
|
|
500
|
+
return `$${value}`;
|
|
501
|
+
}
|
|
502
|
+
function CrmPipelineBoard({
|
|
503
|
+
dealsByStage,
|
|
504
|
+
stages,
|
|
505
|
+
onDealClick,
|
|
506
|
+
onDealMove
|
|
507
|
+
}) {
|
|
508
|
+
const [quickMoveOpen, setQuickMoveOpen] = useState(null);
|
|
509
|
+
const sortedStages = [...stages].sort((a, b) => a.position - b.position);
|
|
510
|
+
const handleQuickMove = (dealId, toStageId) => {
|
|
511
|
+
onDealMove?.(dealId, toStageId);
|
|
512
|
+
setQuickMoveOpen(null);
|
|
513
|
+
};
|
|
514
|
+
return /* @__PURE__ */ jsxDEV2("div", {
|
|
515
|
+
className: "flex gap-4 overflow-x-auto pb-4",
|
|
516
|
+
children: sortedStages.map((stage) => {
|
|
517
|
+
const deals = dealsByStage[stage.id] ?? [];
|
|
518
|
+
const stageValue = deals.reduce((sum, d) => sum + d.value, 0);
|
|
519
|
+
return /* @__PURE__ */ jsxDEV2("div", {
|
|
520
|
+
className: "flex w-72 flex-shrink-0 flex-col rounded-lg bg-muted/30",
|
|
521
|
+
children: [
|
|
522
|
+
/* @__PURE__ */ jsxDEV2("div", {
|
|
523
|
+
className: "flex items-center justify-between border-border border-b px-3 py-2",
|
|
524
|
+
children: [
|
|
525
|
+
/* @__PURE__ */ jsxDEV2("div", {
|
|
526
|
+
children: [
|
|
527
|
+
/* @__PURE__ */ jsxDEV2("h3", {
|
|
528
|
+
className: "font-medium",
|
|
529
|
+
children: stage.name
|
|
530
|
+
}, undefined, false, undefined, this),
|
|
531
|
+
/* @__PURE__ */ jsxDEV2("p", {
|
|
532
|
+
className: "text-muted-foreground text-xs",
|
|
533
|
+
children: [
|
|
534
|
+
deals.length,
|
|
535
|
+
" deals · ",
|
|
536
|
+
formatCurrency2(stageValue)
|
|
537
|
+
]
|
|
538
|
+
}, undefined, true, undefined, this)
|
|
539
|
+
]
|
|
540
|
+
}, undefined, true, undefined, this),
|
|
541
|
+
/* @__PURE__ */ jsxDEV2("span", {
|
|
542
|
+
className: "flex h-6 w-6 items-center justify-center rounded-full bg-muted font-medium text-xs",
|
|
543
|
+
children: deals.length
|
|
544
|
+
}, undefined, false, undefined, this)
|
|
545
|
+
]
|
|
546
|
+
}, undefined, true, undefined, this),
|
|
547
|
+
/* @__PURE__ */ jsxDEV2("div", {
|
|
548
|
+
className: "flex flex-1 flex-col gap-2 p-2",
|
|
549
|
+
children: deals.length === 0 ? /* @__PURE__ */ jsxDEV2("div", {
|
|
550
|
+
className: "flex h-24 items-center justify-center rounded-md border-2 border-muted-foreground/20 border-dashed text-muted-foreground text-xs",
|
|
551
|
+
children: "No deals"
|
|
552
|
+
}, undefined, false, undefined, this) : deals.map((deal) => /* @__PURE__ */ jsxDEV2("div", {
|
|
553
|
+
className: "group relative",
|
|
554
|
+
children: [
|
|
555
|
+
/* @__PURE__ */ jsxDEV2(CrmDealCard, {
|
|
556
|
+
deal,
|
|
557
|
+
onClick: () => onDealClick?.(deal.id)
|
|
558
|
+
}, undefined, false, undefined, this),
|
|
559
|
+
deal.status === "OPEN" && onDealMove && /* @__PURE__ */ jsxDEV2("div", {
|
|
560
|
+
className: "absolute top-1 right-1 opacity-0 transition-opacity group-hover:opacity-100",
|
|
561
|
+
children: [
|
|
562
|
+
/* @__PURE__ */ jsxDEV2("button", {
|
|
563
|
+
type: "button",
|
|
564
|
+
onClick: (e) => {
|
|
565
|
+
e.stopPropagation();
|
|
566
|
+
setQuickMoveOpen(quickMoveOpen === deal.id ? null : deal.id);
|
|
567
|
+
},
|
|
568
|
+
className: "flex h-6 w-6 items-center justify-center rounded border border-border bg-background text-xs shadow-sm hover:bg-muted",
|
|
569
|
+
title: "Quick move",
|
|
570
|
+
children: "➡️"
|
|
571
|
+
}, undefined, false, undefined, this),
|
|
572
|
+
quickMoveOpen === deal.id && /* @__PURE__ */ jsxDEV2("div", {
|
|
573
|
+
className: "absolute top-7 right-0 z-20 min-w-[140px] rounded-lg border border-border bg-card py-1 shadow-lg",
|
|
574
|
+
children: [
|
|
575
|
+
/* @__PURE__ */ jsxDEV2("p", {
|
|
576
|
+
className: "px-3 py-1 font-medium text-muted-foreground text-xs",
|
|
577
|
+
children: "Move to:"
|
|
578
|
+
}, undefined, false, undefined, this),
|
|
579
|
+
sortedStages.filter((s) => s.id !== deal.stageId).map((s) => /* @__PURE__ */ jsxDEV2("button", {
|
|
580
|
+
type: "button",
|
|
581
|
+
onClick: (e) => {
|
|
582
|
+
e.stopPropagation();
|
|
583
|
+
handleQuickMove(deal.id, s.id);
|
|
584
|
+
},
|
|
585
|
+
className: "w-full px-3 py-1.5 text-left text-sm hover:bg-muted",
|
|
586
|
+
children: s.name
|
|
587
|
+
}, s.id, false, undefined, this))
|
|
588
|
+
]
|
|
589
|
+
}, undefined, true, undefined, this)
|
|
590
|
+
]
|
|
591
|
+
}, undefined, true, undefined, this)
|
|
592
|
+
]
|
|
593
|
+
}, deal.id, true, undefined, this))
|
|
594
|
+
}, undefined, false, undefined, this)
|
|
595
|
+
]
|
|
596
|
+
}, stage.id, true, undefined, this);
|
|
597
|
+
})
|
|
598
|
+
}, undefined, false, undefined, this);
|
|
599
|
+
}
|
|
600
|
+
|
|
443
601
|
// src/ui/hooks/useDealList.ts
|
|
444
|
-
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
445
602
|
import { useTemplateRuntime } from "@contractspec/lib.example-shared-ui";
|
|
603
|
+
import { useCallback, useEffect, useMemo, useState as useState2 } from "react";
|
|
446
604
|
"use client";
|
|
447
605
|
function useDealList(options = {}) {
|
|
448
606
|
const { handlers, projectId } = useTemplateRuntime();
|
|
449
607
|
const { crm: crm2 } = handlers;
|
|
450
|
-
const [data, setData] =
|
|
451
|
-
const [dealsByStage, setDealsByStage] =
|
|
452
|
-
const [stages, setStages] =
|
|
453
|
-
const [loading, setLoading] =
|
|
454
|
-
const [error, setError] =
|
|
455
|
-
const [page, setPage] =
|
|
608
|
+
const [data, setData] = useState2(null);
|
|
609
|
+
const [dealsByStage, setDealsByStage] = useState2({});
|
|
610
|
+
const [stages, setStages] = useState2([]);
|
|
611
|
+
const [loading, setLoading] = useState2(true);
|
|
612
|
+
const [error, setError] = useState2(null);
|
|
613
|
+
const [page, setPage] = useState2(1);
|
|
456
614
|
const pipelineId = options.pipelineId ?? "pipeline-1";
|
|
457
615
|
const fetchData = useCallback(async () => {
|
|
458
616
|
setLoading(true);
|
|
@@ -523,27 +681,27 @@ function useDealList(options = {}) {
|
|
|
523
681
|
}
|
|
524
682
|
|
|
525
683
|
// src/ui/hooks/useDealMutations.ts
|
|
526
|
-
import { useCallback as useCallback2, useState as useState2 } from "react";
|
|
527
684
|
import { useTemplateRuntime as useTemplateRuntime2 } from "@contractspec/lib.example-shared-ui";
|
|
685
|
+
import { useCallback as useCallback2, useState as useState3 } from "react";
|
|
528
686
|
function useDealMutations(options = {}) {
|
|
529
687
|
const { handlers, projectId } = useTemplateRuntime2();
|
|
530
688
|
const { crm: crm2 } = handlers;
|
|
531
|
-
const [createState, setCreateState] =
|
|
689
|
+
const [createState, setCreateState] = useState3({
|
|
532
690
|
loading: false,
|
|
533
691
|
error: null,
|
|
534
692
|
data: null
|
|
535
693
|
});
|
|
536
|
-
const [moveState, setMoveState] =
|
|
694
|
+
const [moveState, setMoveState] = useState3({
|
|
537
695
|
loading: false,
|
|
538
696
|
error: null,
|
|
539
697
|
data: null
|
|
540
698
|
});
|
|
541
|
-
const [winState, setWinState] =
|
|
699
|
+
const [winState, setWinState] = useState3({
|
|
542
700
|
loading: false,
|
|
543
701
|
error: null,
|
|
544
702
|
data: null
|
|
545
703
|
});
|
|
546
|
-
const [loseState, setLoseState] =
|
|
704
|
+
const [loseState, setLoseState] = useState3({
|
|
547
705
|
loading: false,
|
|
548
706
|
error: null,
|
|
549
707
|
data: null
|
|
@@ -620,167 +778,9 @@ function useDealMutations(options = {}) {
|
|
|
620
778
|
};
|
|
621
779
|
}
|
|
622
780
|
|
|
623
|
-
// src/ui/CrmDealCard.tsx
|
|
624
|
-
import { jsxDEV } from "react/jsx-dev-runtime";
|
|
625
|
-
"use client";
|
|
626
|
-
function formatCurrency(value, currency) {
|
|
627
|
-
return new Intl.NumberFormat("en-US", {
|
|
628
|
-
style: "currency",
|
|
629
|
-
currency,
|
|
630
|
-
minimumFractionDigits: 0,
|
|
631
|
-
maximumFractionDigits: 0
|
|
632
|
-
}).format(value);
|
|
633
|
-
}
|
|
634
|
-
function CrmDealCard({ deal, onClick }) {
|
|
635
|
-
const daysUntilClose = deal.expectedCloseDate ? Math.ceil((deal.expectedCloseDate.getTime() - Date.now()) / (1000 * 60 * 60 * 24)) : null;
|
|
636
|
-
return /* @__PURE__ */ jsxDEV("div", {
|
|
637
|
-
onClick,
|
|
638
|
-
className: "border-border bg-card cursor-pointer rounded-lg border p-3 shadow-sm transition-shadow hover:shadow-md",
|
|
639
|
-
role: "button",
|
|
640
|
-
tabIndex: 0,
|
|
641
|
-
onKeyDown: (e) => {
|
|
642
|
-
if (e.key === "Enter" || e.key === " ")
|
|
643
|
-
onClick?.();
|
|
644
|
-
},
|
|
645
|
-
children: [
|
|
646
|
-
/* @__PURE__ */ jsxDEV("h4", {
|
|
647
|
-
className: "leading-snug font-medium",
|
|
648
|
-
children: deal.name
|
|
649
|
-
}, undefined, false, undefined, this),
|
|
650
|
-
/* @__PURE__ */ jsxDEV("div", {
|
|
651
|
-
className: "text-primary mt-2 text-lg font-semibold",
|
|
652
|
-
children: formatCurrency(deal.value, deal.currency)
|
|
653
|
-
}, undefined, false, undefined, this),
|
|
654
|
-
/* @__PURE__ */ jsxDEV("div", {
|
|
655
|
-
className: "text-muted-foreground mt-3 flex items-center justify-between text-xs",
|
|
656
|
-
children: [
|
|
657
|
-
daysUntilClose !== null && /* @__PURE__ */ jsxDEV("span", {
|
|
658
|
-
className: daysUntilClose < 0 ? "text-red-500" : daysUntilClose <= 7 ? "text-yellow-600 dark:text-yellow-500" : "",
|
|
659
|
-
children: daysUntilClose < 0 ? `${Math.abs(daysUntilClose)}d overdue` : daysUntilClose === 0 ? "Due today" : `${daysUntilClose}d left`
|
|
660
|
-
}, undefined, false, undefined, this),
|
|
661
|
-
/* @__PURE__ */ jsxDEV("span", {
|
|
662
|
-
className: `rounded px-1.5 py-0.5 text-xs font-medium ${deal.status === "WON" ? "bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400" : deal.status === "LOST" ? "bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400" : "bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400"}`,
|
|
663
|
-
children: deal.status
|
|
664
|
-
}, undefined, false, undefined, this)
|
|
665
|
-
]
|
|
666
|
-
}, undefined, true, undefined, this)
|
|
667
|
-
]
|
|
668
|
-
}, undefined, true, undefined, this);
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
// src/ui/CrmPipelineBoard.tsx
|
|
672
|
-
import { useState as useState3 } from "react";
|
|
673
|
-
import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
|
|
674
|
-
"use client";
|
|
675
|
-
function formatCurrency2(value) {
|
|
676
|
-
if (value >= 1e6)
|
|
677
|
-
return `$${(value / 1e6).toFixed(1)}M`;
|
|
678
|
-
if (value >= 1000)
|
|
679
|
-
return `$${(value / 1000).toFixed(0)}K`;
|
|
680
|
-
return `$${value}`;
|
|
681
|
-
}
|
|
682
|
-
function CrmPipelineBoard({
|
|
683
|
-
dealsByStage,
|
|
684
|
-
stages,
|
|
685
|
-
onDealClick,
|
|
686
|
-
onDealMove
|
|
687
|
-
}) {
|
|
688
|
-
const [quickMoveOpen, setQuickMoveOpen] = useState3(null);
|
|
689
|
-
const sortedStages = [...stages].sort((a, b) => a.position - b.position);
|
|
690
|
-
const handleQuickMove = (dealId, toStageId) => {
|
|
691
|
-
onDealMove?.(dealId, toStageId);
|
|
692
|
-
setQuickMoveOpen(null);
|
|
693
|
-
};
|
|
694
|
-
return /* @__PURE__ */ jsxDEV2("div", {
|
|
695
|
-
className: "flex gap-4 overflow-x-auto pb-4",
|
|
696
|
-
children: sortedStages.map((stage) => {
|
|
697
|
-
const deals = dealsByStage[stage.id] ?? [];
|
|
698
|
-
const stageValue = deals.reduce((sum, d) => sum + d.value, 0);
|
|
699
|
-
return /* @__PURE__ */ jsxDEV2("div", {
|
|
700
|
-
className: "bg-muted/30 flex w-72 flex-shrink-0 flex-col rounded-lg",
|
|
701
|
-
children: [
|
|
702
|
-
/* @__PURE__ */ jsxDEV2("div", {
|
|
703
|
-
className: "border-border flex items-center justify-between border-b px-3 py-2",
|
|
704
|
-
children: [
|
|
705
|
-
/* @__PURE__ */ jsxDEV2("div", {
|
|
706
|
-
children: [
|
|
707
|
-
/* @__PURE__ */ jsxDEV2("h3", {
|
|
708
|
-
className: "font-medium",
|
|
709
|
-
children: stage.name
|
|
710
|
-
}, undefined, false, undefined, this),
|
|
711
|
-
/* @__PURE__ */ jsxDEV2("p", {
|
|
712
|
-
className: "text-muted-foreground text-xs",
|
|
713
|
-
children: [
|
|
714
|
-
deals.length,
|
|
715
|
-
" deals · ",
|
|
716
|
-
formatCurrency2(stageValue)
|
|
717
|
-
]
|
|
718
|
-
}, undefined, true, undefined, this)
|
|
719
|
-
]
|
|
720
|
-
}, undefined, true, undefined, this),
|
|
721
|
-
/* @__PURE__ */ jsxDEV2("span", {
|
|
722
|
-
className: "bg-muted flex h-6 w-6 items-center justify-center rounded-full text-xs font-medium",
|
|
723
|
-
children: deals.length
|
|
724
|
-
}, undefined, false, undefined, this)
|
|
725
|
-
]
|
|
726
|
-
}, undefined, true, undefined, this),
|
|
727
|
-
/* @__PURE__ */ jsxDEV2("div", {
|
|
728
|
-
className: "flex flex-1 flex-col gap-2 p-2",
|
|
729
|
-
children: deals.length === 0 ? /* @__PURE__ */ jsxDEV2("div", {
|
|
730
|
-
className: "border-muted-foreground/20 text-muted-foreground flex h-24 items-center justify-center rounded-md border-2 border-dashed text-xs",
|
|
731
|
-
children: "No deals"
|
|
732
|
-
}, undefined, false, undefined, this) : deals.map((deal) => /* @__PURE__ */ jsxDEV2("div", {
|
|
733
|
-
className: "group relative",
|
|
734
|
-
children: [
|
|
735
|
-
/* @__PURE__ */ jsxDEV2(CrmDealCard, {
|
|
736
|
-
deal,
|
|
737
|
-
onClick: () => onDealClick?.(deal.id)
|
|
738
|
-
}, undefined, false, undefined, this),
|
|
739
|
-
deal.status === "OPEN" && onDealMove && /* @__PURE__ */ jsxDEV2("div", {
|
|
740
|
-
className: "absolute top-1 right-1 opacity-0 transition-opacity group-hover:opacity-100",
|
|
741
|
-
children: [
|
|
742
|
-
/* @__PURE__ */ jsxDEV2("button", {
|
|
743
|
-
type: "button",
|
|
744
|
-
onClick: (e) => {
|
|
745
|
-
e.stopPropagation();
|
|
746
|
-
setQuickMoveOpen(quickMoveOpen === deal.id ? null : deal.id);
|
|
747
|
-
},
|
|
748
|
-
className: "bg-background border-border hover:bg-muted flex h-6 w-6 items-center justify-center rounded border text-xs shadow-sm",
|
|
749
|
-
title: "Quick move",
|
|
750
|
-
children: "➡️"
|
|
751
|
-
}, undefined, false, undefined, this),
|
|
752
|
-
quickMoveOpen === deal.id && /* @__PURE__ */ jsxDEV2("div", {
|
|
753
|
-
className: "bg-card border-border absolute top-7 right-0 z-20 min-w-[140px] rounded-lg border py-1 shadow-lg",
|
|
754
|
-
children: [
|
|
755
|
-
/* @__PURE__ */ jsxDEV2("p", {
|
|
756
|
-
className: "text-muted-foreground px-3 py-1 text-xs font-medium",
|
|
757
|
-
children: "Move to:"
|
|
758
|
-
}, undefined, false, undefined, this),
|
|
759
|
-
sortedStages.filter((s) => s.id !== deal.stageId).map((s) => /* @__PURE__ */ jsxDEV2("button", {
|
|
760
|
-
type: "button",
|
|
761
|
-
onClick: (e) => {
|
|
762
|
-
e.stopPropagation();
|
|
763
|
-
handleQuickMove(deal.id, s.id);
|
|
764
|
-
},
|
|
765
|
-
className: "hover:bg-muted w-full px-3 py-1.5 text-left text-sm",
|
|
766
|
-
children: s.name
|
|
767
|
-
}, s.id, false, undefined, this))
|
|
768
|
-
]
|
|
769
|
-
}, undefined, true, undefined, this)
|
|
770
|
-
]
|
|
771
|
-
}, undefined, true, undefined, this)
|
|
772
|
-
]
|
|
773
|
-
}, deal.id, true, undefined, this))
|
|
774
|
-
}, undefined, false, undefined, this)
|
|
775
|
-
]
|
|
776
|
-
}, stage.id, true, undefined, this);
|
|
777
|
-
})
|
|
778
|
-
}, undefined, false, undefined, this);
|
|
779
|
-
}
|
|
780
|
-
|
|
781
781
|
// src/ui/modals/CreateDealModal.tsx
|
|
782
|
-
import { useState as useState4 } from "react";
|
|
783
782
|
import { Button, Input } from "@contractspec/lib.design-system";
|
|
783
|
+
import { useState as useState4 } from "react";
|
|
784
784
|
import { jsxDEV as jsxDEV3 } from "react/jsx-dev-runtime";
|
|
785
785
|
"use client";
|
|
786
786
|
var CURRENCIES = ["USD", "EUR", "GBP", "CAD"];
|
|
@@ -839,7 +839,7 @@ function CreateDealModal({
|
|
|
839
839
|
className: "fixed inset-0 z-50 flex items-center justify-center",
|
|
840
840
|
children: [
|
|
841
841
|
/* @__PURE__ */ jsxDEV3("div", {
|
|
842
|
-
className: "bg-background/80
|
|
842
|
+
className: "absolute inset-0 bg-background/80 backdrop-blur-sm",
|
|
843
843
|
onClick: onClose,
|
|
844
844
|
role: "button",
|
|
845
845
|
tabIndex: 0,
|
|
@@ -850,10 +850,10 @@ function CreateDealModal({
|
|
|
850
850
|
"aria-label": "Close modal"
|
|
851
851
|
}, undefined, false, undefined, this),
|
|
852
852
|
/* @__PURE__ */ jsxDEV3("div", {
|
|
853
|
-
className: "
|
|
853
|
+
className: "relative z-10 w-full max-w-md rounded-xl border border-border bg-card p-6 shadow-xl",
|
|
854
854
|
children: [
|
|
855
855
|
/* @__PURE__ */ jsxDEV3("h2", {
|
|
856
|
-
className: "mb-4 text-xl
|
|
856
|
+
className: "mb-4 font-semibold text-xl",
|
|
857
857
|
children: "Create New Deal"
|
|
858
858
|
}, undefined, false, undefined, this),
|
|
859
859
|
/* @__PURE__ */ jsxDEV3("form", {
|
|
@@ -864,7 +864,7 @@ function CreateDealModal({
|
|
|
864
864
|
children: [
|
|
865
865
|
/* @__PURE__ */ jsxDEV3("label", {
|
|
866
866
|
htmlFor: "deal-name",
|
|
867
|
-
className: "
|
|
867
|
+
className: "mb-1 block font-medium text-muted-foreground text-sm",
|
|
868
868
|
children: "Deal Name *"
|
|
869
869
|
}, undefined, false, undefined, this),
|
|
870
870
|
/* @__PURE__ */ jsxDEV3(Input, {
|
|
@@ -884,7 +884,7 @@ function CreateDealModal({
|
|
|
884
884
|
children: [
|
|
885
885
|
/* @__PURE__ */ jsxDEV3("label", {
|
|
886
886
|
htmlFor: "deal-value",
|
|
887
|
-
className: "
|
|
887
|
+
className: "mb-1 block font-medium text-muted-foreground text-sm",
|
|
888
888
|
children: "Value *"
|
|
889
889
|
}, undefined, false, undefined, this),
|
|
890
890
|
/* @__PURE__ */ jsxDEV3(Input, {
|
|
@@ -904,7 +904,7 @@ function CreateDealModal({
|
|
|
904
904
|
children: [
|
|
905
905
|
/* @__PURE__ */ jsxDEV3("label", {
|
|
906
906
|
htmlFor: "deal-currency",
|
|
907
|
-
className: "
|
|
907
|
+
className: "mb-1 block font-medium text-muted-foreground text-sm",
|
|
908
908
|
children: "Currency"
|
|
909
909
|
}, undefined, false, undefined, this),
|
|
910
910
|
/* @__PURE__ */ jsxDEV3("select", {
|
|
@@ -912,7 +912,7 @@ function CreateDealModal({
|
|
|
912
912
|
value: currency,
|
|
913
913
|
onChange: (e) => setCurrency(e.target.value),
|
|
914
914
|
disabled: isLoading,
|
|
915
|
-
className: "
|
|
915
|
+
className: "h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring disabled:opacity-50",
|
|
916
916
|
children: CURRENCIES.map((c) => /* @__PURE__ */ jsxDEV3("option", {
|
|
917
917
|
value: c,
|
|
918
918
|
children: c
|
|
@@ -926,7 +926,7 @@ function CreateDealModal({
|
|
|
926
926
|
children: [
|
|
927
927
|
/* @__PURE__ */ jsxDEV3("label", {
|
|
928
928
|
htmlFor: "deal-stage",
|
|
929
|
-
className: "
|
|
929
|
+
className: "mb-1 block font-medium text-muted-foreground text-sm",
|
|
930
930
|
children: "Pipeline Stage *"
|
|
931
931
|
}, undefined, false, undefined, this),
|
|
932
932
|
/* @__PURE__ */ jsxDEV3("select", {
|
|
@@ -934,7 +934,7 @@ function CreateDealModal({
|
|
|
934
934
|
value: stageId,
|
|
935
935
|
onChange: (e) => setStageId(e.target.value),
|
|
936
936
|
disabled: isLoading,
|
|
937
|
-
className: "
|
|
937
|
+
className: "h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring disabled:opacity-50",
|
|
938
938
|
children: stages.map((stage) => /* @__PURE__ */ jsxDEV3("option", {
|
|
939
939
|
value: stage.id,
|
|
940
940
|
children: stage.name
|
|
@@ -946,7 +946,7 @@ function CreateDealModal({
|
|
|
946
946
|
children: [
|
|
947
947
|
/* @__PURE__ */ jsxDEV3("label", {
|
|
948
948
|
htmlFor: "deal-close-date",
|
|
949
|
-
className: "
|
|
949
|
+
className: "mb-1 block font-medium text-muted-foreground text-sm",
|
|
950
950
|
children: "Expected Close Date"
|
|
951
951
|
}, undefined, false, undefined, this),
|
|
952
952
|
/* @__PURE__ */ jsxDEV3(Input, {
|
|
@@ -959,7 +959,7 @@ function CreateDealModal({
|
|
|
959
959
|
]
|
|
960
960
|
}, undefined, true, undefined, this),
|
|
961
961
|
error && /* @__PURE__ */ jsxDEV3("div", {
|
|
962
|
-
className: "bg-destructive/10
|
|
962
|
+
className: "rounded-md bg-destructive/10 p-3 text-destructive text-sm",
|
|
963
963
|
children: error
|
|
964
964
|
}, undefined, false, undefined, this),
|
|
965
965
|
/* @__PURE__ */ jsxDEV3("div", {
|
|
@@ -988,8 +988,8 @@ function CreateDealModal({
|
|
|
988
988
|
}
|
|
989
989
|
|
|
990
990
|
// src/ui/modals/DealActionsModal.tsx
|
|
991
|
-
import { useState as useState5 } from "react";
|
|
992
991
|
import { Button as Button2 } from "@contractspec/lib.design-system";
|
|
992
|
+
import { useState as useState5 } from "react";
|
|
993
993
|
import { jsxDEV as jsxDEV4, Fragment } from "react/jsx-dev-runtime";
|
|
994
994
|
"use client";
|
|
995
995
|
function formatCurrency3(value, currency) {
|
|
@@ -1090,7 +1090,7 @@ function DealActionsModal({
|
|
|
1090
1090
|
className: "fixed inset-0 z-50 flex items-center justify-center",
|
|
1091
1091
|
children: [
|
|
1092
1092
|
/* @__PURE__ */ jsxDEV4("div", {
|
|
1093
|
-
className: "bg-background/80
|
|
1093
|
+
className: "absolute inset-0 bg-background/80 backdrop-blur-sm",
|
|
1094
1094
|
onClick: handleClose,
|
|
1095
1095
|
role: "button",
|
|
1096
1096
|
tabIndex: 0,
|
|
@@ -1101,21 +1101,21 @@ function DealActionsModal({
|
|
|
1101
1101
|
"aria-label": "Close modal"
|
|
1102
1102
|
}, undefined, false, undefined, this),
|
|
1103
1103
|
/* @__PURE__ */ jsxDEV4("div", {
|
|
1104
|
-
className: "
|
|
1104
|
+
className: "relative z-10 w-full max-w-md rounded-xl border border-border bg-card p-6 shadow-xl",
|
|
1105
1105
|
children: [
|
|
1106
1106
|
/* @__PURE__ */ jsxDEV4("div", {
|
|
1107
|
-
className: "
|
|
1107
|
+
className: "mb-4 border-border border-b pb-4",
|
|
1108
1108
|
children: [
|
|
1109
1109
|
/* @__PURE__ */ jsxDEV4("h2", {
|
|
1110
|
-
className: "text-xl
|
|
1110
|
+
className: "font-semibold text-xl",
|
|
1111
1111
|
children: deal.name
|
|
1112
1112
|
}, undefined, false, undefined, this),
|
|
1113
1113
|
/* @__PURE__ */ jsxDEV4("p", {
|
|
1114
|
-
className: "
|
|
1114
|
+
className: "font-medium text-lg text-primary",
|
|
1115
1115
|
children: formatCurrency3(deal.value, deal.currency)
|
|
1116
1116
|
}, undefined, false, undefined, this),
|
|
1117
1117
|
/* @__PURE__ */ jsxDEV4("span", {
|
|
1118
|
-
className: `mt-2 inline-flex rounded-full px-2 py-0.5 text-xs
|
|
1118
|
+
className: `mt-2 inline-flex rounded-full px-2 py-0.5 font-medium text-xs ${deal.status === "WON" ? "bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400" : deal.status === "LOST" ? "bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400" : "bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400"}`,
|
|
1119
1119
|
children: deal.status
|
|
1120
1120
|
}, undefined, false, undefined, this)
|
|
1121
1121
|
]
|
|
@@ -1167,7 +1167,7 @@ function DealActionsModal({
|
|
|
1167
1167
|
]
|
|
1168
1168
|
}, undefined, true, undefined, this),
|
|
1169
1169
|
deal.status !== "OPEN" && /* @__PURE__ */ jsxDEV4("p", {
|
|
1170
|
-
className: "
|
|
1170
|
+
className: "py-4 text-center text-muted-foreground",
|
|
1171
1171
|
children: [
|
|
1172
1172
|
"This deal is already ",
|
|
1173
1173
|
deal.status.toLowerCase(),
|
|
@@ -1192,14 +1192,14 @@ function DealActionsModal({
|
|
|
1192
1192
|
children: [
|
|
1193
1193
|
/* @__PURE__ */ jsxDEV4("label", {
|
|
1194
1194
|
htmlFor: "won-source",
|
|
1195
|
-
className: "
|
|
1195
|
+
className: "mb-1 block font-medium text-muted-foreground text-sm",
|
|
1196
1196
|
children: "How did you win this deal?"
|
|
1197
1197
|
}, undefined, false, undefined, this),
|
|
1198
1198
|
/* @__PURE__ */ jsxDEV4("select", {
|
|
1199
1199
|
id: "won-source",
|
|
1200
1200
|
value: wonSource,
|
|
1201
1201
|
onChange: (e) => setWonSource(e.target.value),
|
|
1202
|
-
className: "
|
|
1202
|
+
className: "h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring",
|
|
1203
1203
|
children: [
|
|
1204
1204
|
/* @__PURE__ */ jsxDEV4("option", {
|
|
1205
1205
|
value: "",
|
|
@@ -1233,7 +1233,7 @@ function DealActionsModal({
|
|
|
1233
1233
|
children: [
|
|
1234
1234
|
/* @__PURE__ */ jsxDEV4("label", {
|
|
1235
1235
|
htmlFor: "win-notes",
|
|
1236
|
-
className: "
|
|
1236
|
+
className: "mb-1 block font-medium text-muted-foreground text-sm",
|
|
1237
1237
|
children: "Notes (optional)"
|
|
1238
1238
|
}, undefined, false, undefined, this),
|
|
1239
1239
|
/* @__PURE__ */ jsxDEV4("textarea", {
|
|
@@ -1242,12 +1242,12 @@ function DealActionsModal({
|
|
|
1242
1242
|
onChange: (e) => setNotes(e.target.value),
|
|
1243
1243
|
placeholder: "Any additional notes about the win...",
|
|
1244
1244
|
rows: 3,
|
|
1245
|
-
className: "
|
|
1245
|
+
className: "w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring"
|
|
1246
1246
|
}, undefined, false, undefined, this)
|
|
1247
1247
|
]
|
|
1248
1248
|
}, undefined, true, undefined, this),
|
|
1249
1249
|
error && /* @__PURE__ */ jsxDEV4("div", {
|
|
1250
|
-
className: "bg-destructive/10
|
|
1250
|
+
className: "rounded-md bg-destructive/10 p-3 text-destructive text-sm",
|
|
1251
1251
|
children: error
|
|
1252
1252
|
}, undefined, false, undefined, this),
|
|
1253
1253
|
/* @__PURE__ */ jsxDEV4("div", {
|
|
@@ -1275,14 +1275,14 @@ function DealActionsModal({
|
|
|
1275
1275
|
children: [
|
|
1276
1276
|
/* @__PURE__ */ jsxDEV4("label", {
|
|
1277
1277
|
htmlFor: "lost-reason",
|
|
1278
|
-
className: "
|
|
1278
|
+
className: "mb-1 block font-medium text-muted-foreground text-sm",
|
|
1279
1279
|
children: "Why was this deal lost? *"
|
|
1280
1280
|
}, undefined, false, undefined, this),
|
|
1281
1281
|
/* @__PURE__ */ jsxDEV4("select", {
|
|
1282
1282
|
id: "lost-reason",
|
|
1283
1283
|
value: lostReason,
|
|
1284
1284
|
onChange: (e) => setLostReason(e.target.value),
|
|
1285
|
-
className: "
|
|
1285
|
+
className: "h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring",
|
|
1286
1286
|
children: [
|
|
1287
1287
|
/* @__PURE__ */ jsxDEV4("option", {
|
|
1288
1288
|
value: "",
|
|
@@ -1324,7 +1324,7 @@ function DealActionsModal({
|
|
|
1324
1324
|
children: [
|
|
1325
1325
|
/* @__PURE__ */ jsxDEV4("label", {
|
|
1326
1326
|
htmlFor: "lose-notes",
|
|
1327
|
-
className: "
|
|
1327
|
+
className: "mb-1 block font-medium text-muted-foreground text-sm",
|
|
1328
1328
|
children: "Notes (optional)"
|
|
1329
1329
|
}, undefined, false, undefined, this),
|
|
1330
1330
|
/* @__PURE__ */ jsxDEV4("textarea", {
|
|
@@ -1333,12 +1333,12 @@ function DealActionsModal({
|
|
|
1333
1333
|
onChange: (e) => setNotes(e.target.value),
|
|
1334
1334
|
placeholder: "Any additional details...",
|
|
1335
1335
|
rows: 3,
|
|
1336
|
-
className: "
|
|
1336
|
+
className: "w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring"
|
|
1337
1337
|
}, undefined, false, undefined, this)
|
|
1338
1338
|
]
|
|
1339
1339
|
}, undefined, true, undefined, this),
|
|
1340
1340
|
error && /* @__PURE__ */ jsxDEV4("div", {
|
|
1341
|
-
className: "bg-destructive/10
|
|
1341
|
+
className: "rounded-md bg-destructive/10 p-3 text-destructive text-sm",
|
|
1342
1342
|
children: error
|
|
1343
1343
|
}, undefined, false, undefined, this),
|
|
1344
1344
|
/* @__PURE__ */ jsxDEV4("div", {
|
|
@@ -1367,14 +1367,14 @@ function DealActionsModal({
|
|
|
1367
1367
|
children: [
|
|
1368
1368
|
/* @__PURE__ */ jsxDEV4("label", {
|
|
1369
1369
|
htmlFor: "move-stage",
|
|
1370
|
-
className: "
|
|
1370
|
+
className: "mb-1 block font-medium text-muted-foreground text-sm",
|
|
1371
1371
|
children: "Move to Stage"
|
|
1372
1372
|
}, undefined, false, undefined, this),
|
|
1373
1373
|
/* @__PURE__ */ jsxDEV4("select", {
|
|
1374
1374
|
id: "move-stage",
|
|
1375
1375
|
value: selectedStageId,
|
|
1376
1376
|
onChange: (e) => setSelectedStageId(e.target.value),
|
|
1377
|
-
className: "
|
|
1377
|
+
className: "h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-ring",
|
|
1378
1378
|
children: stages.map((stage) => /* @__PURE__ */ jsxDEV4("option", {
|
|
1379
1379
|
value: stage.id,
|
|
1380
1380
|
children: [
|
|
@@ -1386,7 +1386,7 @@ function DealActionsModal({
|
|
|
1386
1386
|
]
|
|
1387
1387
|
}, undefined, true, undefined, this),
|
|
1388
1388
|
error && /* @__PURE__ */ jsxDEV4("div", {
|
|
1389
|
-
className: "bg-destructive/10
|
|
1389
|
+
className: "rounded-md bg-destructive/10 p-3 text-destructive text-sm",
|
|
1390
1390
|
children: error
|
|
1391
1391
|
}, undefined, false, undefined, this),
|
|
1392
1392
|
/* @__PURE__ */ jsxDEV4("div", {
|
|
@@ -1414,7 +1414,6 @@ function DealActionsModal({
|
|
|
1414
1414
|
}
|
|
1415
1415
|
|
|
1416
1416
|
// src/ui/CrmDashboard.tsx
|
|
1417
|
-
import { useCallback as useCallback3, useState as useState6 } from "react";
|
|
1418
1417
|
import {
|
|
1419
1418
|
Button as Button3,
|
|
1420
1419
|
ErrorState,
|
|
@@ -1428,6 +1427,7 @@ import {
|
|
|
1428
1427
|
TabsList,
|
|
1429
1428
|
TabsTrigger
|
|
1430
1429
|
} from "@contractspec/lib.ui-kit-web/ui/tabs";
|
|
1430
|
+
import { useCallback as useCallback3, useState as useState6 } from "react";
|
|
1431
1431
|
import { jsxDEV as jsxDEV5 } from "react/jsx-dev-runtime";
|
|
1432
1432
|
"use client";
|
|
1433
1433
|
function formatCurrency4(value, currency = "USD") {
|
|
@@ -1478,7 +1478,7 @@ function CrmDashboard() {
|
|
|
1478
1478
|
className: "flex items-center justify-between",
|
|
1479
1479
|
children: [
|
|
1480
1480
|
/* @__PURE__ */ jsxDEV5("h2", {
|
|
1481
|
-
className: "text-2xl
|
|
1481
|
+
className: "font-bold text-2xl",
|
|
1482
1482
|
children: "CRM Pipeline"
|
|
1483
1483
|
}, undefined, false, undefined, this),
|
|
1484
1484
|
/* @__PURE__ */ jsxDEV5(Button3, {
|
|
@@ -1617,44 +1617,44 @@ function CrmDashboard() {
|
|
|
1617
1617
|
function DealListTab({ data, onDealClick }) {
|
|
1618
1618
|
if (!data?.deals.length) {
|
|
1619
1619
|
return /* @__PURE__ */ jsxDEV5("div", {
|
|
1620
|
-
className: "
|
|
1620
|
+
className: "flex h-64 items-center justify-center text-muted-foreground",
|
|
1621
1621
|
children: "No deals found"
|
|
1622
1622
|
}, undefined, false, undefined, this);
|
|
1623
1623
|
}
|
|
1624
1624
|
return /* @__PURE__ */ jsxDEV5("div", {
|
|
1625
|
-
className: "
|
|
1625
|
+
className: "rounded-lg border border-border",
|
|
1626
1626
|
children: /* @__PURE__ */ jsxDEV5("table", {
|
|
1627
1627
|
className: "w-full",
|
|
1628
1628
|
children: [
|
|
1629
1629
|
/* @__PURE__ */ jsxDEV5("thead", {
|
|
1630
|
-
className: "border-border bg-muted/30
|
|
1630
|
+
className: "border-border border-b bg-muted/30",
|
|
1631
1631
|
children: /* @__PURE__ */ jsxDEV5("tr", {
|
|
1632
1632
|
children: [
|
|
1633
1633
|
/* @__PURE__ */ jsxDEV5("th", {
|
|
1634
|
-
className: "px-4 py-3 text-left text-sm
|
|
1634
|
+
className: "px-4 py-3 text-left font-medium text-sm",
|
|
1635
1635
|
children: "Deal"
|
|
1636
1636
|
}, undefined, false, undefined, this),
|
|
1637
1637
|
/* @__PURE__ */ jsxDEV5("th", {
|
|
1638
|
-
className: "px-4 py-3 text-left text-sm
|
|
1638
|
+
className: "px-4 py-3 text-left font-medium text-sm",
|
|
1639
1639
|
children: "Value"
|
|
1640
1640
|
}, undefined, false, undefined, this),
|
|
1641
1641
|
/* @__PURE__ */ jsxDEV5("th", {
|
|
1642
|
-
className: "px-4 py-3 text-left text-sm
|
|
1642
|
+
className: "px-4 py-3 text-left font-medium text-sm",
|
|
1643
1643
|
children: "Status"
|
|
1644
1644
|
}, undefined, false, undefined, this),
|
|
1645
1645
|
/* @__PURE__ */ jsxDEV5("th", {
|
|
1646
|
-
className: "px-4 py-3 text-left text-sm
|
|
1646
|
+
className: "px-4 py-3 text-left font-medium text-sm",
|
|
1647
1647
|
children: "Expected Close"
|
|
1648
1648
|
}, undefined, false, undefined, this),
|
|
1649
1649
|
/* @__PURE__ */ jsxDEV5("th", {
|
|
1650
|
-
className: "px-4 py-3 text-left text-sm
|
|
1650
|
+
className: "px-4 py-3 text-left font-medium text-sm",
|
|
1651
1651
|
children: "Actions"
|
|
1652
1652
|
}, undefined, false, undefined, this)
|
|
1653
1653
|
]
|
|
1654
1654
|
}, undefined, true, undefined, this)
|
|
1655
1655
|
}, undefined, false, undefined, this),
|
|
1656
1656
|
/* @__PURE__ */ jsxDEV5("tbody", {
|
|
1657
|
-
className: "divide-
|
|
1657
|
+
className: "divide-y divide-border",
|
|
1658
1658
|
children: data.deals.map((deal) => /* @__PURE__ */ jsxDEV5("tr", {
|
|
1659
1659
|
className: "hover:bg-muted/50",
|
|
1660
1660
|
children: [
|
|
@@ -1672,12 +1672,12 @@ function DealListTab({ data, onDealClick }) {
|
|
|
1672
1672
|
/* @__PURE__ */ jsxDEV5("td", {
|
|
1673
1673
|
className: "px-4 py-3",
|
|
1674
1674
|
children: /* @__PURE__ */ jsxDEV5("span", {
|
|
1675
|
-
className: `inline-flex rounded-full px-2 py-0.5 text-xs
|
|
1675
|
+
className: `inline-flex rounded-full px-2 py-0.5 font-medium text-xs ${deal.status === "WON" ? "bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400" : deal.status === "LOST" ? "bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400" : "bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400"}`,
|
|
1676
1676
|
children: deal.status
|
|
1677
1677
|
}, undefined, false, undefined, this)
|
|
1678
1678
|
}, undefined, false, undefined, this),
|
|
1679
1679
|
/* @__PURE__ */ jsxDEV5("td", {
|
|
1680
|
-
className: "
|
|
1680
|
+
className: "px-4 py-3 text-muted-foreground",
|
|
1681
1681
|
children: deal.expectedCloseDate?.toLocaleDateString() ?? "-"
|
|
1682
1682
|
}, undefined, false, undefined, this),
|
|
1683
1683
|
/* @__PURE__ */ jsxDEV5("td", {
|
|
@@ -1704,10 +1704,10 @@ function MetricsTab({
|
|
|
1704
1704
|
return /* @__PURE__ */ jsxDEV5("div", {
|
|
1705
1705
|
className: "space-y-6",
|
|
1706
1706
|
children: /* @__PURE__ */ jsxDEV5("div", {
|
|
1707
|
-
className: "border-border bg-card
|
|
1707
|
+
className: "rounded-xl border border-border bg-card p-6",
|
|
1708
1708
|
children: [
|
|
1709
1709
|
/* @__PURE__ */ jsxDEV5("h3", {
|
|
1710
|
-
className: "mb-4 text-lg
|
|
1710
|
+
className: "mb-4 font-semibold text-lg",
|
|
1711
1711
|
children: "Pipeline Overview"
|
|
1712
1712
|
}, undefined, false, undefined, this),
|
|
1713
1713
|
/* @__PURE__ */ jsxDEV5("dl", {
|
|
@@ -1720,7 +1720,7 @@ function MetricsTab({
|
|
|
1720
1720
|
children: "Win Rate"
|
|
1721
1721
|
}, undefined, false, undefined, this),
|
|
1722
1722
|
/* @__PURE__ */ jsxDEV5("dd", {
|
|
1723
|
-
className: "text-2xl
|
|
1723
|
+
className: "font-semibold text-2xl",
|
|
1724
1724
|
children: [
|
|
1725
1725
|
stats.total > 0 ? (stats.wonCount / stats.total * 100).toFixed(0) : 0,
|
|
1726
1726
|
"%"
|
|
@@ -1735,7 +1735,7 @@ function MetricsTab({
|
|
|
1735
1735
|
children: "Avg Deal Size"
|
|
1736
1736
|
}, undefined, false, undefined, this),
|
|
1737
1737
|
/* @__PURE__ */ jsxDEV5("dd", {
|
|
1738
|
-
className: "text-2xl
|
|
1738
|
+
className: "font-semibold text-2xl",
|
|
1739
1739
|
children: formatCurrency4(stats.total > 0 ? stats.totalValue / stats.total : 0)
|
|
1740
1740
|
}, undefined, false, undefined, this)
|
|
1741
1741
|
]
|
|
@@ -1747,7 +1747,7 @@ function MetricsTab({
|
|
|
1747
1747
|
children: "Conversion"
|
|
1748
1748
|
}, undefined, false, undefined, this),
|
|
1749
1749
|
/* @__PURE__ */ jsxDEV5("dd", {
|
|
1750
|
-
className: "text-2xl
|
|
1750
|
+
className: "font-semibold text-2xl",
|
|
1751
1751
|
children: [
|
|
1752
1752
|
stats.wonCount,
|
|
1753
1753
|
" / ",
|
|
@@ -1762,31 +1762,59 @@ function MetricsTab({
|
|
|
1762
1762
|
}, undefined, true, undefined, this)
|
|
1763
1763
|
}, undefined, false, undefined, this);
|
|
1764
1764
|
}
|
|
1765
|
+
|
|
1765
1766
|
// src/ui/hooks/index.ts
|
|
1766
1767
|
"use client";
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
},
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1768
|
+
// src/ui/overlays/demo-overlays.ts
|
|
1769
|
+
var crmDemoOverlay = {
|
|
1770
|
+
overlayId: "crm-pipeline.demo-user",
|
|
1771
|
+
version: "1.0.0",
|
|
1772
|
+
description: "Demo mode with sample data",
|
|
1773
|
+
appliesTo: {
|
|
1774
|
+
feature: "crm-pipeline",
|
|
1775
|
+
role: "demo"
|
|
1776
|
+
},
|
|
1777
|
+
modifications: [
|
|
1778
|
+
{
|
|
1779
|
+
type: "hideField",
|
|
1780
|
+
field: "importButton",
|
|
1781
|
+
reason: "Not available in demo"
|
|
1782
|
+
},
|
|
1783
|
+
{
|
|
1784
|
+
type: "hideField",
|
|
1785
|
+
field: "exportButton",
|
|
1786
|
+
reason: "Not available in demo"
|
|
1787
|
+
},
|
|
1788
|
+
{
|
|
1789
|
+
type: "addBadge",
|
|
1790
|
+
position: "header",
|
|
1791
|
+
label: "Demo Mode",
|
|
1792
|
+
variant: "warning"
|
|
1785
1793
|
}
|
|
1786
|
-
|
|
1787
|
-
}
|
|
1794
|
+
]
|
|
1788
1795
|
};
|
|
1789
|
-
|
|
1796
|
+
var crmSalesRepOverlay = {
|
|
1797
|
+
overlayId: "crm-pipeline.sales-rep",
|
|
1798
|
+
version: "1.0.0",
|
|
1799
|
+
description: "Sales rep focused view",
|
|
1800
|
+
appliesTo: {
|
|
1801
|
+
feature: "crm-pipeline",
|
|
1802
|
+
role: "sales-rep"
|
|
1803
|
+
},
|
|
1804
|
+
modifications: [
|
|
1805
|
+
{
|
|
1806
|
+
type: "hideField",
|
|
1807
|
+
field: "teamMetrics",
|
|
1808
|
+
reason: "Team metrics for managers only"
|
|
1809
|
+
},
|
|
1810
|
+
{ type: "hideField", field: "pipelineSettings", reason: "Admin only" },
|
|
1811
|
+
{ type: "renameLabel", field: "deals", newLabel: "My Deals" }
|
|
1812
|
+
]
|
|
1813
|
+
};
|
|
1814
|
+
var crmOverlays = [
|
|
1815
|
+
crmDemoOverlay,
|
|
1816
|
+
crmSalesRepOverlay
|
|
1817
|
+
];
|
|
1790
1818
|
// src/ui/renderers/pipeline.markdown.ts
|
|
1791
1819
|
function formatCurrency5(value, currency = "USD") {
|
|
1792
1820
|
return new Intl.NumberFormat("en-US", {
|
|
@@ -1905,56 +1933,28 @@ var crmDashboardMarkdownRenderer = {
|
|
|
1905
1933
|
};
|
|
1906
1934
|
}
|
|
1907
1935
|
};
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
},
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
{
|
|
1924
|
-
type: "hideField",
|
|
1925
|
-
field: "exportButton",
|
|
1926
|
-
reason: "Not available in demo"
|
|
1927
|
-
},
|
|
1928
|
-
{
|
|
1929
|
-
type: "addBadge",
|
|
1930
|
-
position: "header",
|
|
1931
|
-
label: "Demo Mode",
|
|
1932
|
-
variant: "warning"
|
|
1936
|
+
|
|
1937
|
+
// src/ui/renderers/pipeline.renderer.tsx
|
|
1938
|
+
import { jsxDEV as jsxDEV6 } from "react/jsx-dev-runtime";
|
|
1939
|
+
function CrmPipelineBoardWrapper() {
|
|
1940
|
+
const { dealsByStage, stages } = useDealList();
|
|
1941
|
+
return /* @__PURE__ */ jsxDEV6(CrmPipelineBoard, {
|
|
1942
|
+
dealsByStage,
|
|
1943
|
+
stages
|
|
1944
|
+
}, undefined, false, undefined, this);
|
|
1945
|
+
}
|
|
1946
|
+
var crmPipelineReactRenderer = {
|
|
1947
|
+
target: "react",
|
|
1948
|
+
render: async (desc, _ctx) => {
|
|
1949
|
+
if (desc.source.type !== "component") {
|
|
1950
|
+
throw new Error("Invalid source type");
|
|
1933
1951
|
}
|
|
1934
|
-
|
|
1935
|
-
};
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
description: "Sales rep focused view",
|
|
1940
|
-
appliesTo: {
|
|
1941
|
-
feature: "crm-pipeline",
|
|
1942
|
-
role: "sales-rep"
|
|
1943
|
-
},
|
|
1944
|
-
modifications: [
|
|
1945
|
-
{
|
|
1946
|
-
type: "hideField",
|
|
1947
|
-
field: "teamMetrics",
|
|
1948
|
-
reason: "Team metrics for managers only"
|
|
1949
|
-
},
|
|
1950
|
-
{ type: "hideField", field: "pipelineSettings", reason: "Admin only" },
|
|
1951
|
-
{ type: "renameLabel", field: "deals", newLabel: "My Deals" }
|
|
1952
|
-
]
|
|
1952
|
+
if (desc.source.componentKey !== "CrmPipelineView") {
|
|
1953
|
+
throw new Error(`Unknown component: ${desc.source.componentKey}`);
|
|
1954
|
+
}
|
|
1955
|
+
return /* @__PURE__ */ jsxDEV6(CrmPipelineBoardWrapper, {}, undefined, false, undefined, this);
|
|
1956
|
+
}
|
|
1953
1957
|
};
|
|
1954
|
-
var crmOverlays = [
|
|
1955
|
-
crmDemoOverlay,
|
|
1956
|
-
crmSalesRepOverlay
|
|
1957
|
-
];
|
|
1958
1958
|
export {
|
|
1959
1959
|
useDealMutations,
|
|
1960
1960
|
useDealList,
|