@contractspec/example.crm-pipeline 3.7.27 → 3.7.29
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 +4 -3
- package/CHANGELOG.md +50 -0
- package/dist/browser/index.js +1 -1
- package/dist/index.js +1 -1
- package/dist/node/index.js +1 -1
- package/package.json +12 -11
- package/src/index.ts +1 -1
package/.turbo/turbo-build.log
CHANGED
|
@@ -2,7 +2,7 @@ $ contractspec-bun-build prebuild
|
|
|
2
2
|
$ bun run build:bundle && bun run build:types
|
|
3
3
|
$ contractspec-bun-build transpile
|
|
4
4
|
[contractspec-bun-build] transpile target=bun root=src entries=45 noBundle=false
|
|
5
|
-
Bundled 45 modules in
|
|
5
|
+
Bundled 45 modules in 110ms
|
|
6
6
|
|
|
7
7
|
./crm-pipeline.feature.js 1.94 KB (entry point)
|
|
8
8
|
seeders/index.js 0.72 KB (entry point)
|
|
@@ -51,7 +51,7 @@ Bundled 45 modules in 64ms
|
|
|
51
51
|
docs/crm-pipeline.docblock.js 4.16 KB (entry point)
|
|
52
52
|
|
|
53
53
|
[contractspec-bun-build] transpile target=node root=src entries=45 noBundle=false
|
|
54
|
-
Bundled 45 modules in
|
|
54
|
+
Bundled 45 modules in 88ms
|
|
55
55
|
|
|
56
56
|
./crm-pipeline.feature.js 1.94 KB (entry point)
|
|
57
57
|
seeders/index.js 0.72 KB (entry point)
|
|
@@ -100,7 +100,7 @@ Bundled 45 modules in 100ms
|
|
|
100
100
|
docs/crm-pipeline.docblock.js 4.14 KB (entry point)
|
|
101
101
|
|
|
102
102
|
[contractspec-bun-build] transpile target=browser root=src entries=45 noBundle=false
|
|
103
|
-
Bundled 45 modules in
|
|
103
|
+
Bundled 45 modules in 92ms
|
|
104
104
|
|
|
105
105
|
./crm-pipeline.feature.js 1.94 KB (entry point)
|
|
106
106
|
seeders/index.js 0.72 KB (entry point)
|
|
@@ -152,3 +152,4 @@ $ contractspec-bun-build types
|
|
|
152
152
|
$ contractspec-bun-build types
|
|
153
153
|
$ contractspec-bun-build types
|
|
154
154
|
$ contractspec-bun-build types
|
|
155
|
+
$ contractspec-bun-build types
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,55 @@
|
|
|
1
1
|
# @contractspec/example.crm-pipeline
|
|
2
2
|
|
|
3
|
+
## 3.7.29
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Move notifications to library-first contracts/runtime surfaces and add AppShell in-app notification affordances.
|
|
8
|
+
- Packages: @contractspec/lib.contracts-spec (minor), @contractspec/lib.notification (minor), @contractspec/module.notifications (patch), @contractspec/lib.design-system (minor), @contractspec/example.crm-pipeline (patch), @contractspec/example.wealth-snapshot (patch), @contractspec/example.saas-boilerplate (patch)
|
|
9
|
+
- Migration: Move new notification integrations away from the module shim.; Provide notification items and callbacks to the design-system shell without coupling it to a delivery runtime.
|
|
10
|
+
- Deprecations: The `@contractspec/module.notifications` package remains import-compatible for this release, but new code should import contracts from `@contractspec/lib.contracts-spec/notifications` and runtime helpers from `@contractspec/lib.notification`.
|
|
11
|
+
- Updated dependencies because of Promote object-reference detail panels to a reusable adaptive sheet/drawer surface.
|
|
12
|
+
- Updated dependencies because of Route design-system mobile menu overlays through the shared AdaptivePanel primitive.
|
|
13
|
+
- Updated dependencies because of chore: auto-bump internal dependents
|
|
14
|
+
- Updated dependencies because of Add a ContractSpec-native production-grade translation runtime and optional i18next adapter.
|
|
15
|
+
- Updated dependencies because of Add preference-aware DataView collection defaults and personalization adapters.
|
|
16
|
+
- Updated dependencies because of Move notifications to library-first contracts/runtime surfaces and add AppShell in-app notification affordances.
|
|
17
|
+
- Updated dependencies because of Add first-class FormSpec phone input support with country detection, split outputs, and flag rendering.
|
|
18
|
+
- Updated dependencies because of Add PWA update management contracts and runtime helpers.
|
|
19
|
+
- Updated dependencies because of Add a shared roles and permissions policy system across contracts, RBAC evaluation, AppShell adaptation, and personalization suppression.
|
|
20
|
+
- @contractspec/lib.design-system@4.4.0
|
|
21
|
+
- @contractspec/lib.example-shared-ui@7.0.6
|
|
22
|
+
- @contractspec/lib.presentation-runtime-core@5.2.1
|
|
23
|
+
- @contractspec/lib.presentation-runtime-react@40.0.1
|
|
24
|
+
- @contractspec/lib.runtime-sandbox@3.0.6
|
|
25
|
+
- @contractspec/lib.ui-kit-web@3.13.2
|
|
26
|
+
- @contractspec/module.audit-trail@3.7.27
|
|
27
|
+
- @contractspec/lib.contracts-spec@6.2.0
|
|
28
|
+
- @contractspec/lib.notification@0.2.0
|
|
29
|
+
- @contractspec/lib.identity-rbac@3.8.0
|
|
30
|
+
|
|
31
|
+
## 3.7.28
|
|
32
|
+
|
|
33
|
+
### Patch Changes
|
|
34
|
+
|
|
35
|
+
- chore: auto-bump internal dependents
|
|
36
|
+
- Updated dependencies because of Add design-system application shell primitives with typed navigation, command search, breadcrumbs, native bottom-tab adaptation, and PageOutline support.
|
|
37
|
+
- Updated dependencies because of chore: auto-bump internal dependents
|
|
38
|
+
- Updated dependencies because of Add contract-driven overflow behavior and typed DataView hints for shared DataView and DataTable surfaces.
|
|
39
|
+
- Updated dependencies because of Add production-ready collection defaults and renderer mode switching for DataView list, grid, and table specs.
|
|
40
|
+
- Updated dependencies because of Add numeric and temporal FormSpec field kinds with shared renderer support for number, percent, currency, and duration inputs.
|
|
41
|
+
- Updated dependencies because of Add an extensible design-system object reference handler for actionable references.
|
|
42
|
+
- Updated dependencies because of Render resolver-backed combobox results as a floating overlay instead of inline form content.
|
|
43
|
+
- @contractspec/lib.design-system@4.3.0
|
|
44
|
+
- @contractspec/lib.example-shared-ui@7.0.5
|
|
45
|
+
- @contractspec/lib.identity-rbac@3.7.26
|
|
46
|
+
- @contractspec/lib.runtime-sandbox@3.0.5
|
|
47
|
+
- @contractspec/module.audit-trail@3.7.26
|
|
48
|
+
- @contractspec/module.notifications@3.8.2
|
|
49
|
+
- @contractspec/lib.contracts-spec@6.1.0
|
|
50
|
+
- @contractspec/lib.presentation-runtime-core@5.2.0
|
|
51
|
+
- @contractspec/lib.ui-kit-web@3.13.1
|
|
52
|
+
|
|
3
53
|
## 3.7.27
|
|
4
54
|
|
|
5
55
|
### Patch Changes
|
package/dist/browser/index.js
CHANGED
|
@@ -69,4 +69,4 @@ import{defineFeature as vX}from"@contractspec/lib.contracts-spec/features";var f
|
|
|
69
69
|
- Use Feature Flags for experimental stages/SLAs; default safe/off.`}];LX(IX);import{defineEntity as wX,defineEntityEnum as DX,field as I,index as $X}from"@contractspec/lib.schema";var fJ=DX({name:"CompanySize",values:["STARTUP","SMALL","MEDIUM","LARGE","ENTERPRISE"],schema:"crm",description:"Size category of a company."}),qX=wX({name:"Company",description:"A company/organization in the CRM.",schema:"crm",map:"company",fields:{id:I.id({description:"Unique company ID"}),name:I.string({description:"Company name"}),domain:I.string({isOptional:!0,description:"Website domain"}),website:I.url({isOptional:!0}),industry:I.string({isOptional:!0}),size:I.enum("CompanySize",{isOptional:!0}),employeeCount:I.int({isOptional:!0}),annualRevenue:I.decimal({isOptional:!0}),organizationId:I.foreignKey(),ownerId:I.foreignKey({description:"Account owner"}),phone:I.string({isOptional:!0}),email:I.email({isOptional:!0}),address:I.string({isOptional:!0}),city:I.string({isOptional:!0}),state:I.string({isOptional:!0}),country:I.string({isOptional:!0}),postalCode:I.string({isOptional:!0}),linkedInUrl:I.url({isOptional:!0}),description:I.string({isOptional:!0}),tags:I.string({isArray:!0}),customFields:I.json({isOptional:!0}),createdAt:I.createdAt(),updatedAt:I.updatedAt(),contacts:I.hasMany("Contact"),deals:I.hasMany("Deal")},indexes:[$X.on(["organizationId","ownerId"]),$X.on(["domain"])],enums:[fJ]});import{defineEntity as hX,defineEntityEnum as bX,field as v,index as kJ}from"@contractspec/lib.schema";var gJ=bX({name:"ContactStatus",values:["LEAD","PROSPECT","CUSTOMER","CHURNED","ARCHIVED"],schema:"crm",description:"Status of a contact in the sales funnel."}),GX=hX({name:"Contact",description:"An individual person in the CRM.",schema:"crm",map:"contact",fields:{id:v.id({description:"Unique contact ID"}),firstName:v.string({description:"First name"}),lastName:v.string({description:"Last name"}),email:v.email({isOptional:!0,isUnique:!0}),phone:v.string({isOptional:!0}),companyId:v.string({isOptional:!0,description:"Associated company"}),jobTitle:v.string({isOptional:!0}),status:v.enum("ContactStatus",{default:"LEAD"}),organizationId:v.foreignKey(),ownerId:v.foreignKey({description:"Sales rep who owns this contact"}),source:v.string({isOptional:!0,description:"Lead source"}),linkedInUrl:v.url({isOptional:!0}),twitterHandle:v.string({isOptional:!0}),address:v.string({isOptional:!0}),city:v.string({isOptional:!0}),state:v.string({isOptional:!0}),country:v.string({isOptional:!0}),postalCode:v.string({isOptional:!0}),notes:v.string({isOptional:!0}),tags:v.string({isArray:!0}),customFields:v.json({isOptional:!0}),lastContactedAt:v.dateTime({isOptional:!0}),nextFollowUpAt:v.dateTime({isOptional:!0}),createdAt:v.createdAt(),updatedAt:v.updatedAt(),company:v.belongsTo("Company",["companyId"],["id"]),deals:v.hasMany("Deal"),tasks:v.hasMany("Task"),activities:v.hasMany("Activity")},indexes:[kJ.on(["organizationId","status"]),kJ.on(["organizationId","ownerId"]),kJ.on(["organizationId","companyId"]),kJ.on(["email"])],enums:[gJ]});import{defineEntity as xJ,defineEntityEnum as TX,field as U,index as YJ}from"@contractspec/lib.schema";var SJ=TX({name:"DealStatus",values:["OPEN","WON","LOST","STALE"],schema:"crm",description:"Status of a deal."}),HX=xJ({name:"Pipeline",description:"A sales pipeline with stages.",schema:"crm",map:"pipeline",fields:{id:U.id(),name:U.string({description:"Pipeline name"}),description:U.string({isOptional:!0}),organizationId:U.foreignKey(),isDefault:U.boolean({default:!1}),createdAt:U.createdAt(),updatedAt:U.updatedAt(),stages:U.hasMany("Stage"),deals:U.hasMany("Deal")}}),QX=xJ({name:"Stage",description:"A stage within a sales pipeline.",schema:"crm",map:"stage",fields:{id:U.id(),name:U.string({description:"Stage name"}),pipelineId:U.foreignKey(),position:U.int({description:"Order in pipeline"}),probability:U.int({default:0,description:"Win probability (0-100)"}),isWonStage:U.boolean({default:!1}),isLostStage:U.boolean({default:!1}),color:U.string({isOptional:!0,description:"Stage color for UI"}),createdAt:U.createdAt(),updatedAt:U.updatedAt(),pipeline:U.belongsTo("Pipeline",["pipelineId"],["id"],{onDelete:"Cascade"}),deals:U.hasMany("Deal")},indexes:[YJ.on(["pipelineId","position"])]}),FX=xJ({name:"Deal",description:"A sales opportunity/deal.",schema:"crm",map:"deal",fields:{id:U.id({description:"Unique deal ID"}),name:U.string({description:"Deal name"}),value:U.decimal({description:"Deal value"}),currency:U.string({default:'"USD"'}),pipelineId:U.foreignKey(),stageId:U.foreignKey(),status:U.enum("DealStatus",{default:"OPEN"}),contactId:U.string({isOptional:!0}),companyId:U.string({isOptional:!0}),organizationId:U.foreignKey(),ownerId:U.foreignKey({description:"Deal owner"}),expectedCloseDate:U.dateTime({isOptional:!0}),closedAt:U.dateTime({isOptional:!0}),lostReason:U.string({isOptional:!0}),wonSource:U.string({isOptional:!0}),notes:U.string({isOptional:!0}),tags:U.string({isArray:!0}),customFields:U.json({isOptional:!0}),stagePosition:U.int({default:0}),createdAt:U.createdAt(),updatedAt:U.updatedAt(),pipeline:U.belongsTo("Pipeline",["pipelineId"],["id"]),stage:U.belongsTo("Stage",["stageId"],["id"]),contact:U.belongsTo("Contact",["contactId"],["id"]),company:U.belongsTo("Company",["companyId"],["id"]),tasks:U.hasMany("Task"),activities:U.hasMany("Activity")},indexes:[YJ.on(["organizationId","status"]),YJ.on(["pipelineId","stageId","stagePosition"]),YJ.on(["ownerId","status"]),YJ.on(["expectedCloseDate"])],enums:[SJ]});import{defineEntity as zX,defineEntityEnum as mJ,field as O,index as n}from"@contractspec/lib.schema";var pJ=mJ({name:"TaskType",values:["CALL","EMAIL","MEETING","TODO","FOLLOW_UP","OTHER"],schema:"crm",description:"Type of CRM task."}),cJ=mJ({name:"TaskPriority",values:["LOW","NORMAL","HIGH","URGENT"],schema:"crm",description:"Priority of a task."}),uJ=mJ({name:"TaskStatus",values:["PENDING","IN_PROGRESS","COMPLETED","CANCELLED"],schema:"crm",description:"Status of a task."}),WX=zX({name:"Task",description:"A task or follow-up activity.",schema:"crm",map:"task",fields:{id:O.id(),title:O.string({description:"Task title"}),description:O.string({isOptional:!0}),type:O.enum("TaskType",{default:"TODO"}),priority:O.enum("TaskPriority",{default:"NORMAL"}),status:O.enum("TaskStatus",{default:"PENDING"}),dueDate:O.dateTime({isOptional:!0}),reminderAt:O.dateTime({isOptional:!0}),contactId:O.string({isOptional:!0}),dealId:O.string({isOptional:!0}),companyId:O.string({isOptional:!0}),organizationId:O.foreignKey(),assignedTo:O.foreignKey({description:"User assigned to this task"}),createdBy:O.foreignKey(),completedAt:O.dateTime({isOptional:!0}),completedBy:O.string({isOptional:!0}),createdAt:O.createdAt(),updatedAt:O.updatedAt(),contact:O.belongsTo("Contact",["contactId"],["id"]),deal:O.belongsTo("Deal",["dealId"],["id"]),company:O.belongsTo("Company",["companyId"],["id"])},indexes:[n.on(["organizationId","assignedTo","status"]),n.on(["dueDate","status"]),n.on(["contactId"]),n.on(["dealId"])],enums:[pJ,cJ,uJ]}),UX=zX({name:"Activity",description:"An activity/interaction logged in the CRM.",schema:"crm",map:"activity",fields:{id:O.id(),type:O.enum("TaskType"),subject:O.string(),description:O.string({isOptional:!0}),contactId:O.string({isOptional:!0}),dealId:O.string({isOptional:!0}),companyId:O.string({isOptional:!0}),organizationId:O.foreignKey(),performedBy:O.foreignKey(),outcome:O.string({isOptional:!0}),occurredAt:O.dateTime(),duration:O.int({isOptional:!0,description:"Duration in minutes"}),createdAt:O.createdAt(),contact:O.belongsTo("Contact",["contactId"],["id"]),deal:O.belongsTo("Deal",["dealId"],["id"]),company:O.belongsTo("Company",["companyId"],["id"])},indexes:[n.on(["contactId","occurredAt"]),n.on(["dealId","occurredAt"])]});var KX={moduleId:"@contractspec/example.crm-pipeline",entities:[qX,GX,FX,HX,QX,WX,UX],enums:[fJ,gJ,SJ,pJ,cJ,uJ]};import{defineEvent as CX}from"@contractspec/lib.contracts-spec";import{defineSchemaModel as EX,ScalarTypeEnum as ZJ}from"@contractspec/lib.schema";var jX=EX({name:"ContactCreatedPayload",description:"Payload when a contact is created",fields:{contactId:{type:ZJ.String_unsecure(),isOptional:!1},email:{type:ZJ.EmailAddress(),isOptional:!0},organizationId:{type:ZJ.String_unsecure(),isOptional:!1},ownerId:{type:ZJ.String_unsecure(),isOptional:!1},createdAt:{type:ZJ.DateTime(),isOptional:!1}}}),RZ=CX({meta:{key:"contact.created",version:"1.0.0",description:"A new contact has been created.",stability:"stable",owners:["@crm-team"],tags:["contact","created"]},payload:jX});import{defineEvent as BJ}from"@contractspec/lib.contracts-spec";import{defineSchemaModel as vJ,ScalarTypeEnum as D}from"@contractspec/lib.schema";var yX=vJ({name:"DealCreatedPayload",description:"Payload when a deal is created",fields:{dealId:{type:D.String_unsecure(),isOptional:!1},name:{type:D.String_unsecure(),isOptional:!1},value:{type:D.Float_unsecure(),isOptional:!1},pipelineId:{type:D.String_unsecure(),isOptional:!1},stageId:{type:D.String_unsecure(),isOptional:!1},ownerId:{type:D.String_unsecure(),isOptional:!1},createdAt:{type:D.DateTime(),isOptional:!1}}}),fX=vJ({name:"DealMovedEventPayload",description:"Payload when a deal is moved to another stage",fields:{dealId:{type:D.String_unsecure(),isOptional:!1},fromStageId:{type:D.String_unsecure(),isOptional:!1},toStageId:{type:D.String_unsecure(),isOptional:!1},movedBy:{type:D.String_unsecure(),isOptional:!1},movedAt:{type:D.DateTime(),isOptional:!1}}}),gX=vJ({name:"DealWonEventPayload",description:"Payload when a deal is won",fields:{dealId:{type:D.String_unsecure(),isOptional:!1},value:{type:D.Float_unsecure(),isOptional:!1},currency:{type:D.String_unsecure(),isOptional:!1},contactId:{type:D.String_unsecure(),isOptional:!0},companyId:{type:D.String_unsecure(),isOptional:!0},ownerId:{type:D.String_unsecure(),isOptional:!1},wonAt:{type:D.DateTime(),isOptional:!1}}}),xX=vJ({name:"DealLostEventPayload",description:"Payload when a deal is lost",fields:{dealId:{type:D.String_unsecure(),isOptional:!1},value:{type:D.Float_unsecure(),isOptional:!1},reason:{type:D.String_unsecure(),isOptional:!1},ownerId:{type:D.String_unsecure(),isOptional:!1},lostAt:{type:D.DateTime(),isOptional:!1}}}),kZ=BJ({meta:{key:"deal.created",version:"1.0.0",description:"A new deal has been created.",stability:"stable",owners:["@crm-team"],tags:["deal","created"]},payload:yX}),BZ=BJ({meta:{key:"deal.moved",version:"1.0.0",description:"A deal has been moved to a different stage.",stability:"stable",owners:["@crm-team"],tags:["deal","moved"]},payload:fX}),vZ=BJ({meta:{key:"deal.won",version:"1.0.0",description:"A deal has been won.",stability:"stable",owners:["@crm-team"],tags:["deal","won"]},payload:gX}),MZ=BJ({meta:{key:"deal.lost",version:"1.0.0",description:"A deal has been lost.",stability:"stable",owners:["@crm-team"],tags:["deal","lost"]},payload:xX});import{defineEvent as SX}from"@contractspec/lib.contracts-spec";import{defineSchemaModel as mX,ScalarTypeEnum as $J}from"@contractspec/lib.schema";var pX=mX({name:"TaskCompletedPayload",description:"Payload when a task is completed",fields:{taskId:{type:$J.String_unsecure(),isOptional:!1},type:{type:$J.String_unsecure(),isOptional:!1},assignedTo:{type:$J.String_unsecure(),isOptional:!1},completedBy:{type:$J.String_unsecure(),isOptional:!1},completedAt:{type:$J.DateTime(),isOptional:!1}}}),DZ=SX({meta:{key:"task.completed",version:"1.0.0",description:"A task has been completed.",stability:"stable",owners:["@crm-team"],tags:["task","lifecycle"]},payload:pX});import{defineExample as cX}from"@contractspec/lib.contracts-spec/examples";var uX=cX({meta:{key:"examples.crm-pipeline",version:"1.0.0",title:"Crm Pipeline",description:"CRM Pipeline - Contacts, Companies, Deals, Tasks",kind:"template",visibility:"experimental",stability:"experimental",owners:["@contractspec-core"],tags:["package","examples","crm-pipeline"]},surfaces:{templates:!0,sandbox:{enabled:!0,modes:["playground","specs"]},studio:{enabled:!1,installable:!1},mcp:{enabled:!1}},entrypoints:{packageName:"@contractspec/example.crm-pipeline"}}),rX=uX;import{web as oX}from"@contractspec/lib.runtime-sandbox";var{generateId:dX}=oX;function s(J){return{id:J.id,projectId:J.projectId,name:J.name,value:J.value,currency:J.currency,pipelineId:J.pipelineId,stageId:J.stageId,status:J.status,contactId:J.contactId??void 0,companyId:J.companyId??void 0,ownerId:J.ownerId,expectedCloseDate:J.expectedCloseDate?new Date(J.expectedCloseDate):void 0,wonSource:J.wonSource??void 0,lostReason:J.lostReason??void 0,notes:J.notes??void 0,createdAt:new Date(J.createdAt),updatedAt:new Date(J.updatedAt)}}var NX={name:"name",value:"value",status:"status",expectedCloseDate:"expectedCloseDate",updatedAt:"updatedAt"};function gZ(J){async function Y(X){let{projectId:H,pipelineId:W,stageId:q,status:Z,ownerId:V,search:$,limit:L=20,offset:_=0,sortBy:K="value",sortDirection:z="desc"}=X,M="WHERE projectId = ?",T=[H];if(W)M+=" AND pipelineId = ?",T.push(W);if(q)M+=" AND stageId = ?",T.push(q);if(Z&&Z!=="all")M+=" AND status = ?",T.push(Z);if(V)M+=" AND ownerId = ?",T.push(V);if($)M+=" AND name LIKE ?",T.push(`%${$}%`);let d=(await J.query(`SELECT COUNT(*) as count FROM crm_deal ${M}`,T)).rows[0]?.count??0,j=(await J.query(`SELECT COALESCE(SUM(value), 0) as total FROM crm_deal ${M}`,T)).rows[0]?.total??0,k=NX[K]??NX.value,JJ=z==="asc"?"ASC":"DESC";return{deals:(await J.query(`SELECT * FROM crm_deal ${M} ORDER BY ${k} ${JJ} LIMIT ? OFFSET ?`,[...T,L,_])).rows.map(s),total:d,totalValue:j}}async function Q(X,H){let W=dX("deal"),q=new Date().toISOString();await J.execute(`INSERT INTO crm_deal (id, projectId, pipelineId, stageId, name, value, currency, status, contactId, companyId, ownerId, expectedCloseDate, createdAt, updatedAt)
|
|
70
70
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,[W,H.projectId,X.pipelineId,X.stageId,X.name,X.value,X.currency??"USD","OPEN",X.contactId??null,X.companyId??null,H.ownerId,X.expectedCloseDate?.toISOString()??null,q,q]);let Z=(await J.query("SELECT * FROM crm_deal WHERE id = ?",[W])).rows;if(!Z[0])throw Error("Failed to create deal");return s(Z[0])}async function G(X){let H=new Date().toISOString();if(!(await J.query("SELECT * FROM crm_deal WHERE id = ?",[X.dealId])).rows[0])throw Error("NOT_FOUND");if(!(await J.query("SELECT * FROM crm_stage WHERE id = ?",[X.stageId])).rows[0])throw Error("INVALID_STAGE");await J.execute("UPDATE crm_deal SET stageId = ?, updatedAt = ? WHERE id = ?",[X.stageId,H,X.dealId]);let Z=(await J.query("SELECT * FROM crm_deal WHERE id = ?",[X.dealId])).rows;return s(Z[0])}async function F(X){let H=new Date().toISOString();if(!(await J.query("SELECT * FROM crm_deal WHERE id = ?",[X.dealId])).rows[0])throw Error("NOT_FOUND");await J.execute("UPDATE crm_deal SET status = 'WON', wonSource = ?, notes = ?, updatedAt = ? WHERE id = ?",[X.wonSource??null,X.notes??null,H,X.dealId]);let q=(await J.query("SELECT * FROM crm_deal WHERE id = ?",[X.dealId])).rows;return s(q[0])}async function P(X){let H=new Date().toISOString();if(!(await J.query("SELECT * FROM crm_deal WHERE id = ?",[X.dealId])).rows[0])throw Error("NOT_FOUND");await J.execute("UPDATE crm_deal SET status = 'LOST', lostReason = ?, notes = ?, updatedAt = ? WHERE id = ?",[X.lostReason,X.notes??null,H,X.dealId]);let q=(await J.query("SELECT * FROM crm_deal WHERE id = ?",[X.dealId])).rows;return s(q[0])}async function B(X){let H=(await J.query("SELECT * FROM crm_deal WHERE projectId = ? AND pipelineId = ? AND status = 'OPEN' ORDER BY value DESC",[X.projectId,X.pipelineId])).rows,W=(await J.query("SELECT * FROM crm_stage WHERE pipelineId = ? ORDER BY position",[X.pipelineId])).rows,q={};for(let Z of W)q[Z.id]=H.filter((V)=>V.stageId===Z.id).map(s);return q}async function R(X){return(await J.query("SELECT * FROM crm_stage WHERE pipelineId = ? ORDER BY position",[X.pipelineId])).rows.map((W)=>({id:W.id,pipelineId:W.pipelineId,name:W.name,position:W.position}))}return{listDeals:Y,createDeal:Q,moveDeal:G,winDeal:F,loseDeal:P,getDealsByStage:B,getPipelineStages:R}}var qJ=[{id:"stage-1",name:"Lead",position:1,pipelineId:"pipeline-1"},{id:"stage-2",name:"Qualified",position:2,pipelineId:"pipeline-1"},{id:"stage-3",name:"Proposal",position:3,pipelineId:"pipeline-1"},{id:"stage-4",name:"Negotiation",position:4,pipelineId:"pipeline-1"},{id:"stage-5",name:"Closed",position:5,pipelineId:"pipeline-1"}],f=[{id:"deal-1",name:"Enterprise License - Acme Corp",value:75000,currency:"USD",pipelineId:"pipeline-1",stageId:"stage-3",status:"OPEN",contactId:"contact-1",companyId:"company-1",ownerId:"user-1",expectedCloseDate:new Date("2024-05-15T00:00:00Z"),createdAt:new Date("2024-02-01T10:00:00Z"),updatedAt:new Date("2024-04-10T14:30:00Z")},{id:"deal-2",name:"Startup Plan - TechStart Inc",value:12000,currency:"USD",pipelineId:"pipeline-1",stageId:"stage-2",status:"OPEN",contactId:"contact-2",companyId:"company-2",ownerId:"user-2",expectedCloseDate:new Date("2024-04-30T00:00:00Z"),createdAt:new Date("2024-03-15T09:00:00Z"),updatedAt:new Date("2024-04-08T11:15:00Z")},{id:"deal-3",name:"Professional Services - Global Ltd",value:45000,currency:"USD",pipelineId:"pipeline-1",stageId:"stage-4",status:"OPEN",contactId:"contact-3",companyId:"company-3",ownerId:"user-1",expectedCloseDate:new Date("2024-04-20T00:00:00Z"),createdAt:new Date("2024-01-20T08:00:00Z"),updatedAt:new Date("2024-04-12T16:45:00Z")},{id:"deal-4",name:"Annual Contract - SmallBiz Co",value:8500,currency:"USD",pipelineId:"pipeline-1",stageId:"stage-1",status:"OPEN",contactId:"contact-4",companyId:"company-4",ownerId:"user-3",createdAt:new Date("2024-04-05T12:00:00Z"),updatedAt:new Date("2024-04-05T12:00:00Z")},{id:"deal-5",name:"Custom Integration - MegaCorp",value:125000,currency:"USD",pipelineId:"pipeline-1",stageId:"stage-5",status:"WON",contactId:"contact-5",companyId:"company-5",ownerId:"user-1",expectedCloseDate:new Date("2024-03-31T00:00:00Z"),createdAt:new Date("2023-11-10T10:00:00Z"),updatedAt:new Date("2024-03-28T09:00:00Z")},{id:"deal-6",name:"Pilot Project - NewCo",value:5000,currency:"USD",pipelineId:"pipeline-1",stageId:"stage-2",status:"LOST",contactId:"contact-6",companyId:"company-6",ownerId:"user-2",createdAt:new Date("2024-01-15T14:00:00Z"),updatedAt:new Date("2024-02-28T10:30:00Z")}],SZ=[{id:"company-1",name:"Acme Corporation",domain:"acme.com",industry:"Technology",size:"1000-5000",website:"https://acme.com",createdAt:new Date("2024-01-01T00:00:00Z")},{id:"company-2",name:"TechStart Inc",domain:"techstart.io",industry:"Software",size:"10-50",website:"https://techstart.io",createdAt:new Date("2024-02-15T00:00:00Z")},{id:"company-3",name:"Global Ltd",domain:"global.com",industry:"Consulting",size:"500-1000",website:"https://global.com",createdAt:new Date("2023-12-01T00:00:00Z")}],mZ=[{id:"contact-1",firstName:"John",lastName:"Smith",email:"john.smith@acme.com",phone:"+1-555-0101",title:"VP of Engineering",companyId:"company-1",createdAt:new Date("2024-01-05T00:00:00Z")},{id:"contact-2",firstName:"Sarah",lastName:"Johnson",email:"sarah@techstart.io",phone:"+1-555-0102",title:"CEO",companyId:"company-2",createdAt:new Date("2024-02-20T00:00:00Z")},{id:"contact-3",firstName:"Michael",lastName:"Brown",email:"michael.brown@global.com",phone:"+1-555-0103",title:"CTO",companyId:"company-3",createdAt:new Date("2023-12-10T00:00:00Z")}];async function MJ(J){let{pipelineId:Y,stageId:Q,status:G,ownerId:F,search:P,limit:B=20,offset:R=0}=J,X=[...f];if(Y)X=X.filter((Z)=>Z.pipelineId===Y);if(Q)X=X.filter((Z)=>Z.stageId===Q);if(G&&G!=="all")X=X.filter((Z)=>Z.status===G);if(F)X=X.filter((Z)=>Z.ownerId===F);if(P){let Z=P.toLowerCase();X=X.filter((V)=>V.name.toLowerCase().includes(Z))}X.sort((Z,V)=>V.value-Z.value);let H=X.length,W=X.reduce((Z,V)=>Z+V.value,0);return{deals:X.slice(R,R+B),total:H,totalValue:W}}async function lX(J,Y){let Q=new Date,G={id:`deal-${Date.now()}`,name:J.name,value:J.value,currency:J.currency??"USD",pipelineId:J.pipelineId,stageId:J.stageId,status:"OPEN",contactId:J.contactId,companyId:J.companyId,ownerId:Y.ownerId,expectedCloseDate:J.expectedCloseDate,createdAt:Q,updatedAt:Q};return f.push(G),G}async function iX(J){let Y=f.findIndex((P)=>P.id===J.dealId);if(Y===-1)throw Error("NOT_FOUND");let Q=f[Y];if(!Q)throw Error("NOT_FOUND");if(!qJ.find((P)=>P.id===J.stageId))throw Error("INVALID_STAGE");let F={...Q,stageId:J.stageId,updatedAt:new Date};return f[Y]=F,F}async function nX(J){let Y=f.findIndex((F)=>F.id===J.dealId);if(Y===-1)throw Error("NOT_FOUND");let Q=f[Y];if(!Q)throw Error("NOT_FOUND");let G={...Q,status:"WON",updatedAt:new Date};return f[Y]=G,G}async function sX(J){let Y=f.findIndex((F)=>F.id===J.dealId);if(Y===-1)throw Error("NOT_FOUND");let Q=f[Y];if(!Q)throw Error("NOT_FOUND");let G={...Q,status:"LOST",updatedAt:new Date};return f[Y]=G,G}async function tX(J){let Y=f.filter((G)=>G.pipelineId===J.pipelineId&&G.status==="OPEN"),Q={};for(let G of qJ)Q[G.id]=Y.filter((F)=>F.stageId===G.id);return Q}async function LJ(J){return qJ.filter((Y)=>Y.pipelineId===J.pipelineId)}import{definePresentation as VX,StabilityEnum as _X}from"@contractspec/lib.contracts-spec";var tZ=VX({meta:{key:"crm.dashboard",version:"1.0.0",title:"CRM Dashboard",description:"Main CRM dashboard with pipeline overview, deal stats, and activities",domain:"crm-pipeline",owners:["@crm-team"],tags:["dashboard","overview"],stability:_X.Experimental,goal:"Provide a high-level overview of CRM performance and active deals.",context:"The landing page for CRM users."},source:{type:"component",framework:"react",componentKey:"CrmDashboard"},targets:["react","markdown"],policy:{flags:["crm.enabled"]}}),aZ=VX({meta:{key:"crm.pipeline.metrics",version:"1.0.0",title:"Pipeline Metrics",description:"Pipeline metrics and forecasting view",domain:"crm-pipeline",owners:["@crm-team"],tags:["pipeline","metrics","forecast"],stability:_X.Experimental,goal:"Track pipeline health and sales forecasts.",context:"Data-intensive widget for sales managers."},source:{type:"component",framework:"react",componentKey:"PipelineMetricsView"},targets:["react","markdown"],policy:{flags:["crm.metrics.enabled"]}});import{definePresentation as IJ,StabilityEnum as wJ}from"@contractspec/lib.contracts-spec";var Y0=IJ({meta:{key:"crm.pipeline.kanban",version:"1.0.0",title:"Pipeline Kanban",description:"Kanban board view of deals organized by stage",domain:"crm-pipeline",owners:["@crm-team"],tags:["pipeline","kanban","deals"],stability:wJ.Experimental,goal:"Visualize the sales pipeline status and deal distribution across stages.",context:"Used in the sales dashboard and management reports."},source:{type:"component",framework:"react",componentKey:"PipelineKanbanView",props:y},targets:["react","markdown"],policy:{flags:["crm.pipeline.enabled"]}}),Z0=IJ({meta:{key:"crm.deal.viewList",version:"1.0.0",title:"Deal List",description:"List view of deals with value, status, and owner info",domain:"crm-pipeline",owners:["@crm-team"],tags:["deal","list"],stability:wJ.Experimental,goal:"Search, filter, and review deal lists.",context:"Standard view for deal management and bulk actions."},source:{type:"component",framework:"react",componentKey:"DealListView",props:y},targets:["react","markdown","application/json"],policy:{flags:["crm.deals.enabled"]}}),$0=IJ({meta:{key:"crm.deal.detail",version:"1.0.0",title:"Deal Details",description:"Detailed view of a deal with activities, contacts, and history",domain:"crm-pipeline",owners:["@crm-team"],tags:["deal","detail"],stability:wJ.Experimental,goal:"Deep dive into deal details and historical activities.",context:"The main workspace for managing a single deal execution."},source:{type:"component",framework:"react",componentKey:"DealDetailView"},targets:["react","markdown"],policy:{flags:["crm.deals.enabled"]}}),q0=IJ({meta:{key:"crm.deal.card",version:"1.0.0",title:"Deal Card",description:"Compact deal card for kanban board display",domain:"crm-pipeline",owners:["@crm-team"],tags:["deal","card","kanban"],stability:wJ.Experimental,goal:"Provide a quick overview of deal status in the pipeline view.",context:"Condensed representation used within the Pipeline Kanban board."},source:{type:"component",framework:"react",componentKey:"DealCard",props:y},targets:["react"],policy:{flags:["crm.deals.enabled"]}});import{jsx as DJ,jsxs as RX}from"react/jsx-runtime";function aX(J,Y){return new Intl.NumberFormat("en-US",{style:"currency",currency:Y,minimumFractionDigits:0,maximumFractionDigits:0}).format(J)}function AX({deal:J,onClick:Y}){let Q=J.expectedCloseDate?Math.ceil((J.expectedCloseDate.getTime()-Date.now())/86400000):null;return RX("div",{onClick:Y,className:"cursor-pointer rounded-lg border border-border bg-card p-3 shadow-sm transition-shadow hover:shadow-md",role:"button",tabIndex:0,onKeyDown:(G)=>{if(G.key==="Enter"||G.key===" ")Y?.()},children:[DJ("h4",{className:"font-medium leading-snug",children:J.name}),DJ("div",{className:"mt-2 font-semibold text-lg text-primary",children:aX(J.value,J.currency)}),RX("div",{className:"mt-3 flex items-center justify-between text-muted-foreground text-xs",children:[Q!==null&&DJ("span",{className:Q<0?"text-red-500":Q<=7?"text-yellow-600 dark:text-yellow-500":"",children:Q<0?`${Math.abs(Q)}d overdue`:Q===0?"Due today":`${Q}d left`}),DJ("span",{className:`rounded px-1.5 py-0.5 font-medium text-xs ${J.status==="WON"?"bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400":J.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"}`,children:J.status})]})]})}import{useState as eX}from"react";import{jsx as u,jsxs as l}from"react/jsx-runtime";function JY(J){if(J>=1e6)return`$${(J/1e6).toFixed(1)}M`;if(J>=1000)return`$${(J/1000).toFixed(0)}K`;return`$${J}`}function hJ({dealsByStage:J,stages:Y,onDealClick:Q,onDealMove:G}){let[F,P]=eX(null),B=[...Y].sort((X,H)=>X.position-H.position),R=(X,H)=>{G?.(X,H),P(null)};return u("div",{className:"flex gap-4 overflow-x-auto pb-4",children:B.map((X)=>{let H=J[X.id]??[],W=H.reduce((q,Z)=>q+Z.value,0);return l("div",{className:"flex w-72 flex-shrink-0 flex-col rounded-lg bg-muted/30",children:[l("div",{className:"flex items-center justify-between border-border border-b px-3 py-2",children:[l("div",{children:[u("h3",{className:"font-medium",children:X.name}),l("p",{className:"text-muted-foreground text-xs",children:[H.length," deals · ",JY(W)]})]}),u("span",{className:"flex h-6 w-6 items-center justify-center rounded-full bg-muted font-medium text-xs",children:H.length})]}),u("div",{className:"flex flex-1 flex-col gap-2 p-2",children:H.length===0?u("div",{className:"flex h-24 items-center justify-center rounded-md border-2 border-muted-foreground/20 border-dashed text-muted-foreground text-xs",children:"No deals"}):H.map((q)=>l("div",{className:"group relative",children:[u(AX,{deal:q,onClick:()=>Q?.(q.id)}),q.status==="OPEN"&&G&&l("div",{className:"absolute top-1 right-1 opacity-0 transition-opacity group-hover:opacity-100",children:[u("button",{type:"button",onClick:(Z)=>{Z.stopPropagation(),P(F===q.id?null:q.id)},className:"flex h-6 w-6 items-center justify-center rounded border border-border bg-background text-xs shadow-sm hover:bg-muted",title:"Quick move",children:"➡️"}),F===q.id&&l("div",{className:"absolute top-7 right-0 z-20 min-w-[140px] rounded-lg border border-border bg-card py-1 shadow-lg",children:[u("p",{className:"px-3 py-1 font-medium text-muted-foreground text-xs",children:"Move to:"}),B.filter((Z)=>Z.id!==q.stageId).map((Z)=>u("button",{type:"button",onClick:(V)=>{V.stopPropagation(),R(q.id,Z.id)},className:"w-full px-3 py-1.5 text-left text-sm hover:bg-muted",children:Z.name},Z.id))]})]})]},q.id))})]},X.id)})})}import{useTemplateRuntime as XY}from"@contractspec/lib.example-shared-ui";import{useCallback as YY,useEffect as ZY,useMemo as $Y,useState as t}from"react";function i(J={}){let{handlers:Y,projectId:Q}=XY(),{crm:G}=Y,[F,P]=t(null),[B,R]=t({}),[X,H]=t([]),[W,q]=t(!0),[Z,V]=t(null),[$,L]=t(0),_=J.pipelineId??"pipeline-1",K=J.pageIndex??$,z=J.pageSize??J.limit??50,[M]=J.sorting??[],T=M?.id==="deal"?"name":M?.id,x=M?M.desc?"desc":"asc":void 0,d=YY(async()=>{q(!0),V(null);try{let[j,k,JJ]=await Promise.all([G.listDeals({projectId:Q,pipelineId:_,stageId:J.stageId,status:J.status==="all"?void 0:J.status,search:J.search,limit:z,offset:K*z,sortBy:T==="name"||T==="value"||T==="status"||T==="expectedCloseDate"||T==="updatedAt"?T:void 0,sortDirection:x}),G.getDealsByStage({projectId:Q,pipelineId:_}),G.getPipelineStages({pipelineId:_})]);P(j),R(k),H(JJ)}catch(j){V(j instanceof Error?j:Error("Unknown error"))}finally{q(!1)}},[G,Q,_,J.stageId,J.status,J.search,K,z,T,x]);ZY(()=>{d()},[d]);let FJ=$Y(()=>{if(!F)return null;let j=F.deals.filter((S)=>S.status==="OPEN"),k=F.deals.filter((S)=>S.status==="WON"),JJ=F.deals.filter((S)=>S.status==="LOST");return{total:F.total,totalValue:F.totalValue,openCount:j.length,openValue:j.reduce((S,jJ)=>S+jJ.value,0),wonCount:k.length,wonValue:k.reduce((S,jJ)=>S+jJ.value,0),lostCount:JJ.length}},[F]);return{data:F,dealsByStage:B,stages:X,loading:W,error:Z,stats:FJ,page:K+1,pageIndex:K,pageSize:z,refetch:d,nextPage:J.pageIndex===void 0?()=>L((j)=>j+1):void 0,prevPage:J.pageIndex===void 0?()=>K>0&&L((j)=>j-1):void 0}}import{useTemplateRuntime as qY}from"@contractspec/lib.example-shared-ui";import{useCallback as bJ,useState as TJ}from"react";function rJ(J={}){let{handlers:Y,projectId:Q}=qY(),{crm:G}=Y,[F,P]=TJ({loading:!1,error:null,data:null}),[B,R]=TJ({loading:!1,error:null,data:null}),[X,H]=TJ({loading:!1,error:null,data:null}),[W,q]=TJ({loading:!1,error:null,data:null}),Z=bJ(async(_)=>{P({loading:!0,error:null,data:null});try{let K=await G.createDeal(_,{projectId:Q,ownerId:"user-1"});return P({loading:!1,error:null,data:K}),J.onSuccess?.(),K}catch(K){let z=K instanceof Error?K:Error("Failed to create deal");return P({loading:!1,error:z,data:null}),J.onError?.(z),null}},[G,Q,J]),V=bJ(async(_)=>{R({loading:!0,error:null,data:null});try{let K=await G.moveDeal(_);return R({loading:!1,error:null,data:K}),J.onSuccess?.(),K}catch(K){let z=K instanceof Error?K:Error("Failed to move deal");return R({loading:!1,error:z,data:null}),J.onError?.(z),null}},[G,J]),$=bJ(async(_)=>{H({loading:!0,error:null,data:null});try{let K=await G.winDeal(_);return H({loading:!1,error:null,data:K}),J.onSuccess?.(),K}catch(K){let z=K instanceof Error?K:Error("Failed to mark deal as won");return H({loading:!1,error:z,data:null}),J.onError?.(z),null}},[G,J]),L=bJ(async(_)=>{q({loading:!0,error:null,data:null});try{let K=await G.loseDeal(_);return q({loading:!1,error:null,data:K}),J.onSuccess?.(),K}catch(K){let z=K instanceof Error?K:Error("Failed to mark deal as lost");return q({loading:!1,error:z,data:null}),J.onError?.(z),null}},[G,J]);return{createDeal:Z,moveDeal:V,winDeal:$,loseDeal:L,createState:F,moveState:B,winState:X,loseState:W,isLoading:F.loading||B.loading||X.loading||W.loading}}import{Button as PX,Input as oJ}from"@contractspec/lib.design-system";import{useState as a}from"react";import{jsx as C,jsxs as p}from"react/jsx-runtime";var GY=["USD","EUR","GBP","CAD"],HY="pipeline-1";function dJ({isOpen:J,onClose:Y,onSubmit:Q,stages:G,isLoading:F=!1}){let[P,B]=a(""),[R,X]=a(""),[H,W]=a("USD"),[q,Z]=a(G[0]?.id??""),[V,$]=a(""),[L,_]=a(null),K=async(z)=>{if(z.preventDefault(),_(null),!P.trim()){_("Deal name is required");return}let M=parseFloat(R);if(isNaN(M)||M<=0){_("Value must be a positive number");return}if(!q){_("Please select a pipeline stage");return}try{await Q({name:P.trim(),value:M,currency:H,pipelineId:HY,stageId:q,expectedCloseDate:V?new Date(V):void 0}),B(""),X(""),W("USD"),Z(G[0]?.id??""),$(""),Y()}catch(T){_(T instanceof Error?T.message:"Failed to create deal")}};if(!J)return null;return p("div",{className:"fixed inset-0 z-50 flex items-center justify-center",children:[C("div",{className:"absolute inset-0 bg-background/80 backdrop-blur-sm",onClick:Y,role:"button",tabIndex:0,onKeyDown:(z)=>{if(z.key==="Enter"||z.key===" ")Y()},"aria-label":"Close modal"}),p("div",{className:"relative z-10 w-full max-w-md rounded-xl border border-border bg-card p-6 shadow-xl",children:[C("h2",{className:"mb-4 font-semibold text-xl",children:"Create New Deal"}),p("form",{onSubmit:K,className:"space-y-4",children:[p("div",{children:[C("label",{htmlFor:"deal-name",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Deal Name *"}),C(oJ,{id:"deal-name",value:P,onChange:(z)=>B(z.target.value),placeholder:"e.g., Enterprise License - Acme Corp",disabled:F})]}),p("div",{className:"flex gap-3",children:[p("div",{className:"flex-1",children:[C("label",{htmlFor:"deal-value",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Value *"}),C(oJ,{id:"deal-value",type:"number",min:"0",step:"0.01",value:R,onChange:(z)=>X(z.target.value),placeholder:"50000",disabled:F})]}),p("div",{className:"w-24",children:[C("label",{htmlFor:"deal-currency",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Currency"}),C("select",{id:"deal-currency",value:H,onChange:(z)=>W(z.target.value),disabled:F,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",children:GY.map((z)=>C("option",{value:z,children:z},z))})]})]}),p("div",{children:[C("label",{htmlFor:"deal-stage",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Pipeline Stage *"}),C("select",{id:"deal-stage",value:q,onChange:(z)=>Z(z.target.value),disabled:F,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",children:G.map((z)=>C("option",{value:z.id,children:z.name},z.id))})]}),p("div",{children:[C("label",{htmlFor:"deal-close-date",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Expected Close Date"}),C(oJ,{id:"deal-close-date",type:"date",value:V,onChange:(z)=>$(z.target.value),disabled:F})]}),L&&C("div",{className:"rounded-md bg-destructive/10 p-3 text-destructive text-sm",children:L}),p("div",{className:"flex justify-end gap-3 pt-2",children:[C(PX,{type:"button",variant:"ghost",onPress:Y,disabled:F,children:"Cancel"}),C(PX,{type:"submit",disabled:F,children:F?"Creating...":"Create Deal"})]})]})]})]})}import{Button as c}from"@contractspec/lib.design-system";import{useState as e}from"react";import{jsx as A,jsxs as h,Fragment as FY}from"react/jsx-runtime";function QY(J,Y){return new Intl.NumberFormat("en-US",{style:"currency",currency:Y,minimumFractionDigits:0,maximumFractionDigits:0}).format(J)}function lJ({isOpen:J,deal:Y,stages:Q,onClose:G,onWin:F,onLose:P,onMove:B,isLoading:R=!1}){let[X,H]=e("menu"),[W,q]=e(""),[Z,V]=e(""),[$,L]=e(""),[_,K]=e(""),[z,M]=e(null),T=()=>{H("menu"),q(""),V(""),L(""),K(""),M(null)},x=()=>{T(),G()},d=async()=>{if(!Y)return;M(null);try{await F({dealId:Y.id,wonSource:W.trim()||void 0,notes:$.trim()||void 0}),x()}catch(k){M(k instanceof Error?k.message:"Failed to mark deal as won")}},FJ=async()=>{if(!Y)return;if(M(null),!Z.trim()){M("Please provide a reason for losing the deal");return}try{await P({dealId:Y.id,lostReason:Z.trim(),notes:$.trim()||void 0}),x()}catch(k){M(k instanceof Error?k.message:"Failed to mark deal as lost")}},j=async()=>{if(!Y)return;if(M(null),!_){M("Please select a stage");return}if(_===Y.stageId){M("Deal is already in this stage");return}try{await B({dealId:Y.id,stageId:_}),x()}catch(k){M(k instanceof Error?k.message:"Failed to move deal")}};if(!J||!Y)return null;return h("div",{className:"fixed inset-0 z-50 flex items-center justify-center",children:[A("div",{className:"absolute inset-0 bg-background/80 backdrop-blur-sm",onClick:x,role:"button",tabIndex:0,onKeyDown:(k)=>{if(k.key==="Enter"||k.key===" ")x()},"aria-label":"Close modal"}),h("div",{className:"relative z-10 w-full max-w-md rounded-xl border border-border bg-card p-6 shadow-xl",children:[h("div",{className:"mb-4 border-border border-b pb-4",children:[A("h2",{className:"font-semibold text-xl",children:Y.name}),A("p",{className:"font-medium text-lg text-primary",children:QY(Y.value,Y.currency)}),A("span",{className:`mt-2 inline-flex rounded-full px-2 py-0.5 font-medium text-xs ${Y.status==="WON"?"bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400":Y.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"}`,children:Y.status})]}),X==="menu"&&h("div",{className:"space-y-3",children:[Y.status==="OPEN"&&h(FY,{children:[h(c,{className:"w-full justify-start",variant:"ghost",onPress:()=>H("win"),children:[A("span",{className:"mr-2",children:"\uD83C\uDFC6"})," Mark as Won"]}),h(c,{className:"w-full justify-start",variant:"ghost",onPress:()=>H("lose"),children:[A("span",{className:"mr-2",children:"❌"})," Mark as Lost"]}),h(c,{className:"w-full justify-start",variant:"ghost",onPress:()=>{K(Y.stageId),H("move")},children:[A("span",{className:"mr-2",children:"➡️"})," Move to Stage"]})]}),Y.status!=="OPEN"&&h("p",{className:"py-4 text-center text-muted-foreground",children:["This deal is already ",Y.status.toLowerCase(),". No actions available."]}),A("div",{className:"border-border border-t pt-3",children:A(c,{className:"w-full",variant:"outline",onPress:x,children:"Close"})})]}),X==="win"&&h("div",{className:"space-y-4",children:[h("div",{children:[A("label",{htmlFor:"won-source",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"How did you win this deal?"}),h("select",{id:"won-source",value:W,onChange:(k)=>q(k.target.value),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",children:[A("option",{value:"",children:"Select a source..."}),A("option",{value:"referral",children:"Referral"}),A("option",{value:"cold_outreach",children:"Cold Outreach"}),A("option",{value:"inbound",children:"Inbound Lead"}),A("option",{value:"upsell",children:"Upsell"}),A("option",{value:"other",children:"Other"})]})]}),h("div",{children:[A("label",{htmlFor:"win-notes",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Notes (optional)"}),A("textarea",{id:"win-notes",value:$,onChange:(k)=>L(k.target.value),placeholder:"Any additional notes about the win...",rows:3,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"})]}),z&&A("div",{className:"rounded-md bg-destructive/10 p-3 text-destructive text-sm",children:z}),h("div",{className:"flex justify-end gap-3 pt-2",children:[A(c,{variant:"ghost",onPress:()=>H("menu"),disabled:R,children:"Back"}),A(c,{onPress:d,disabled:R,children:R?"Processing...":"\uD83C\uDFC6 Confirm Win"})]})]}),X==="lose"&&h("div",{className:"space-y-4",children:[h("div",{children:[A("label",{htmlFor:"lost-reason",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Why was this deal lost? *"}),h("select",{id:"lost-reason",value:Z,onChange:(k)=>V(k.target.value),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",children:[A("option",{value:"",children:"Select a reason..."}),A("option",{value:"price",children:"Price too high"}),A("option",{value:"competitor",children:"Lost to competitor"}),A("option",{value:"no_budget",children:"No budget"}),A("option",{value:"no_decision",children:"No decision made"}),A("option",{value:"timing",children:"Bad timing"}),A("option",{value:"product_fit",children:"Product not a fit"}),A("option",{value:"other",children:"Other"})]})]}),h("div",{children:[A("label",{htmlFor:"lose-notes",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Notes (optional)"}),A("textarea",{id:"lose-notes",value:$,onChange:(k)=>L(k.target.value),placeholder:"Any additional details...",rows:3,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"})]}),z&&A("div",{className:"rounded-md bg-destructive/10 p-3 text-destructive text-sm",children:z}),h("div",{className:"flex justify-end gap-3 pt-2",children:[A(c,{variant:"ghost",onPress:()=>H("menu"),disabled:R,children:"Back"}),A(c,{variant:"destructive",onPress:FJ,disabled:R,children:R?"Processing...":"❌ Confirm Loss"})]})]}),X==="move"&&h("div",{className:"space-y-4",children:[h("div",{children:[A("label",{htmlFor:"move-stage",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Move to Stage"}),A("select",{id:"move-stage",value:_,onChange:(k)=>K(k.target.value),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",children:Q.map((k)=>h("option",{value:k.id,children:[k.name,k.id===Y.stageId?" (current)":""]},k.id))})]}),z&&A("div",{className:"rounded-md bg-destructive/10 p-3 text-destructive text-sm",children:z}),h("div",{className:"flex justify-end gap-3 pt-2",children:[A(c,{variant:"ghost",onPress:()=>H("menu"),disabled:R,children:"Back"}),A(c,{onPress:j,disabled:R,children:R?"Moving...":"➡️ Move Deal"})]})]})]})]})}import{Button as GJ,DataTable as zY,DataTableToolbar as WY,LoaderBlock as UY}from"@contractspec/lib.design-system";import{useContractTable as KY}from"@contractspec/lib.presentation-runtime-react";import{Badge as NY}from"@contractspec/lib.ui-kit-web/ui/badge";import{HStack as HJ,VStack as iJ}from"@contractspec/lib.ui-kit-web/ui/stack";import{Text as g}from"@contractspec/lib.ui-kit-web/ui/text";import*as QJ from"react";import{jsx as b,jsxs as r}from"react/jsx-runtime";function VY(J,Y="USD"){return new Intl.NumberFormat("en-US",{style:"currency",currency:Y,minimumFractionDigits:0,maximumFractionDigits:0}).format(J)}function _Y(J){switch(J){case"WON":return"default";case"LOST":return"destructive";case"STALE":return"outline";default:return"secondary"}}function RY({value:J,onChange:Y}){return r(HJ,{gap:"sm",className:"flex-wrap",children:[b(GJ,{variant:J==="all"?"secondary":"outline",size:"sm",onPress:()=>Y("all"),children:"All Deals"}),b(GJ,{variant:J==="OPEN"?"secondary":"outline",size:"sm",onPress:()=>Y("OPEN"),children:"Open Only"}),b(GJ,{variant:J==="WON"?"secondary":"outline",size:"sm",onPress:()=>Y("WON"),children:"Won Only"}),b(GJ,{variant:J==="LOST"?"secondary":"outline",size:"sm",onPress:()=>Y("LOST"),children:"Lost Only"})]})}function AY({deals:J,totalItems:Y,pageIndex:Q,pageSize:G,sorting:F,search:P,status:B,loading:R,onSortingChange:X,onPaginationChange:H,onSearchChange:W,onStatusChange:q,onDealClick:Z}){let V=KY({data:J,columns:[{id:"deal",header:"Deal",label:"Deal",accessor:($)=>$.name,cell:({item:$})=>r(iJ,{gap:"xs",children:[b(g,{className:"font-medium text-sm",children:$.name}),b(g,{className:"text-muted-foreground text-xs",children:$.companyId??"Unassigned company"})]}),size:240,minSize:180,canSort:!0,canPin:!0,canResize:!0},{id:"value",header:"Value",label:"Value",accessorKey:"value",cell:({item:$})=>VY($.value,$.currency),align:"right",size:140,canSort:!0,canResize:!0},{id:"status",header:"Status",label:"Status",accessorKey:"status",cell:({value:$})=>b(NY,{variant:_Y($),children:String($)}),size:130,canSort:!0,canHide:!0,canPin:!0,canResize:!0},{id:"expectedCloseDate",header:"Expected Close",label:"Expected Close",accessor:($)=>$.expectedCloseDate?.toISOString()??"",cell:({item:$})=>$.expectedCloseDate?.toLocaleDateString()??"Not scheduled",size:170,canSort:!0,canHide:!0,canResize:!0},{id:"updatedAt",header:"Updated",label:"Updated",accessor:($)=>$.updatedAt.toISOString(),cell:({item:$})=>$.updatedAt.toLocaleDateString(),size:140,canSort:!0,canHide:!0,canResize:!0},{id:"actions",header:"Actions",label:"Actions",accessor:($)=>$.id,cell:({item:$})=>b(GJ,{variant:"ghost",size:"sm",onPress:()=>Z?.($.id),children:"Actions"}),size:120,canSort:!1,canHide:!1,canPin:!1,canResize:!1}],executionMode:"server",selectionMode:"multiple",totalItems:Y,state:{sorting:F,pagination:{pageIndex:Q,pageSize:G}},onSortingChange:X,onPaginationChange:H,initialState:{columnVisibility:{updatedAt:!1},columnPinning:{left:["deal","status"],right:[]}},renderExpandedContent:($)=>r(iJ,{gap:"sm",className:"py-2",children:[r(HJ,{justify:"between",children:[b(g,{className:"font-medium text-sm",children:"Owner"}),b(g,{className:"text-muted-foreground text-sm",children:$.ownerId})]}),r(HJ,{justify:"between",children:[b(g,{className:"font-medium text-sm",children:"Contact"}),b(g,{className:"text-muted-foreground text-sm",children:$.contactId??"No linked contact"})]}),$.wonSource?r(HJ,{justify:"between",children:[b(g,{className:"font-medium text-sm",children:"Won Source"}),b(g,{className:"text-muted-foreground text-sm",children:$.wonSource})]}):null,$.lostReason?r(HJ,{justify:"between",children:[b(g,{className:"font-medium text-sm",children:"Lost Reason"}),b(g,{className:"text-muted-foreground text-sm",children:$.lostReason})]}):null,$.notes?r(iJ,{gap:"xs",children:[b(g,{className:"font-medium text-sm",children:"Notes"}),b(g,{className:"text-muted-foreground text-sm",children:$.notes})]}):null]}),getCanExpand:()=>!0});return b(zY,{controller:V,title:"All Deals",description:"Server-mode table using the shared ContractSpec controller.",loading:R,toolbar:b(WY,{controller:V,searchPlaceholder:"Search deals, companies, contacts, or notes",searchValue:P,onSearchChange:W,activeChips:B==="all"?[]:[{key:"status",label:`Status: ${B}`,onRemove:()=>q("all")}],onClearAll:()=>{W(""),q("all")},actionsStart:RY({value:B,onChange:q}),actionsEnd:r(g,{className:"text-muted-foreground text-sm",children:[Y," total deals"]})}),footer:`Page ${V.pageIndex+1} of ${V.pageCount}`,emptyState:b("div",{className:"rounded-md border border-dashed p-8 text-center text-muted-foreground text-sm",children:"No deals found"})})}function OX({onDealClick:J}){let[Y,Q]=QJ.useState([{id:"value",desc:!0}]),[G,F]=QJ.useState({pageIndex:0,pageSize:3}),[P,B]=QJ.useState(""),[R,X]=QJ.useState("all"),{data:H,loading:W}=i({pageIndex:G.pageIndex,pageSize:G.pageSize,search:P,status:R,sorting:Y});if(W&&!H)return b(UY,{label:"Loading deals..."});return b(AY,{deals:H?.deals??[],totalItems:H?.total??0,pageIndex:G.pageIndex,pageSize:G.pageSize,sorting:Y,search:P,status:R,loading:W,onSortingChange:(q)=>{Q(q),F((Z)=>({...Z,pageIndex:0}))},onPaginationChange:F,onSearchChange:(q)=>{B(q),F((Z)=>({...Z,pageIndex:0}))},onStatusChange:(q)=>{X(q),F((Z)=>({...Z,pageIndex:0}))},onDealClick:J})}import{Button as PY,ErrorState as OY,LoaderBlock as kY,StatCard as CJ,StatCardGroup as BY,Tabs as vY,TabsContent as nJ,TabsList as MY,TabsTrigger as sJ}from"@contractspec/lib.design-system";import{useCallback as kX,useState as tJ}from"react";import{jsx as w,jsxs as E}from"react/jsx-runtime";function EJ(J,Y="USD"){return new Intl.NumberFormat("en-US",{style:"currency",currency:Y,minimumFractionDigits:0,maximumFractionDigits:0}).format(J)}function d0(){let[J,Y]=tJ(!1),[Q,G]=tJ(null),[F,P]=tJ(!1),{data:B,dealsByStage:R,stages:X,loading:H,error:W,stats:q,refetch:Z}=i(),V=rJ({onSuccess:()=>{Z()}}),$=kX((_)=>{let K=R?Object.values(R).flat().find((z)=>z.id===_):null;if(K)G(K),P(!0)},[R]),L=kX(async(_,K)=>{await V.moveDeal({dealId:_,stageId:K})},[V]);if(H&&!B)return w(kY,{label:"Loading CRM..."});if(W)return w(OY,{title:"Failed to load CRM",description:W.message,onRetry:Z,retryLabel:"Retry"});return E("div",{className:"space-y-6",children:[E("div",{className:"flex items-center justify-between",children:[w("h2",{className:"font-bold text-2xl",children:"CRM Pipeline"}),E(PY,{onClick:()=>Y(!0),children:[w("span",{className:"mr-2",children:"+"})," Create Deal"]})]}),q&&E(BY,{children:[w(CJ,{label:"Total Pipeline",value:EJ(q.totalValue),hint:`${q.total} deals`}),w(CJ,{label:"Open Deals",value:EJ(q.openValue),hint:`${q.openCount} active`}),w(CJ,{label:"Won",value:EJ(q.wonValue),hint:`${q.wonCount} closed`}),w(CJ,{label:"Lost",value:q.lostCount,hint:"deals lost"})]}),E(vY,{defaultValue:"pipeline",className:"w-full",children:[E(MY,{children:[E(sJ,{value:"pipeline",children:[w("span",{className:"mr-2",children:"\uD83D\uDCCA"}),"Pipeline"]}),E(sJ,{value:"list",children:[w("span",{className:"mr-2",children:"\uD83D\uDCCB"}),"All Deals"]}),E(sJ,{value:"metrics",children:[w("span",{className:"mr-2",children:"\uD83D\uDCC8"}),"Metrics"]})]}),w(nJ,{value:"pipeline",className:"min-h-[400px]",children:w(hJ,{dealsByStage:R,stages:X,onDealClick:$,onDealMove:L})}),w(nJ,{value:"list",className:"min-h-[400px]",children:w(OX,{onDealClick:$})}),w(nJ,{value:"metrics",className:"min-h-[400px]",children:w(LY,{stats:q})})]}),w(dJ,{isOpen:J,onClose:()=>Y(!1),onSubmit:async(_)=>{await V.createDeal(_)},stages:X,isLoading:V.createState.loading}),w(lJ,{isOpen:F,deal:Q,stages:X,onClose:()=>{P(!1),G(null)},onWin:async(_)=>{await V.winDeal(_)},onLose:async(_)=>{await V.loseDeal(_)},onMove:async(_)=>{await V.moveDeal(_),Z()},isLoading:V.isLoading})]})}function LY({stats:J}){if(!J)return null;return w("div",{className:"space-y-6",children:E("div",{className:"rounded-xl border border-border bg-card p-6",children:[w("h3",{className:"mb-4 font-semibold text-lg",children:"Pipeline Overview"}),E("dl",{className:"grid gap-4 sm:grid-cols-3",children:[E("div",{children:[w("dt",{className:"text-muted-foreground text-sm",children:"Win Rate"}),E("dd",{className:"font-semibold text-2xl",children:[J.total>0?(J.wonCount/J.total*100).toFixed(0):0,"%"]})]}),E("div",{children:[w("dt",{className:"text-muted-foreground text-sm",children:"Avg Deal Size"}),w("dd",{className:"font-semibold text-2xl",children:EJ(J.total>0?J.totalValue/J.total:0)})]}),E("div",{children:[w("dt",{className:"text-muted-foreground text-sm",children:"Conversion"}),E("dd",{className:"font-semibold text-2xl",children:[J.wonCount," / ",J.total]})]})]})]})})}var IY={overlayId:"crm-pipeline.demo-user",version:"1.0.0",description:"Demo mode with sample data",appliesTo:{feature:"crm-pipeline",role:"demo"},modifications:[{type:"hideField",field:"importButton",reason:"Not available in demo"},{type:"hideField",field:"exportButton",reason:"Not available in demo"},{type:"addBadge",position:"header",label:"Demo Mode",variant:"warning"}]},wY={overlayId:"crm-pipeline.sales-rep",version:"1.0.0",description:"Sales rep focused view",appliesTo:{feature:"crm-pipeline",role:"sales-rep"},modifications:[{type:"hideField",field:"teamMetrics",reason:"Team metrics for managers only"},{type:"hideField",field:"pipelineSettings",reason:"Admin only"},{type:"renameLabel",field:"deals",newLabel:"My Deals"}]},X3=[IY,wY];function o(J,Y="USD"){return new Intl.NumberFormat("en-US",{style:"currency",currency:Y,minimumFractionDigits:0}).format(J)}var DY={target:"markdown",render:async(J,Y)=>{if(J.source.type!=="component"||J.source.componentKey!=="PipelineKanbanView")throw Error("crmPipelineMarkdownRenderer: not PipelineKanbanView");let Q="pipeline-1",[G,F]=await Promise.all([MJ({pipelineId:Q,limit:50}),LJ({pipelineId:Q})]),P=G.deals,B=F,R={};for(let H of B)R[H.id]=P.filter((W)=>W.stageId===H.id&&W.status==="OPEN");let X=["# CRM Pipeline","",`**Total Value**: ${o(G.totalValue)}`,`**Total Deals**: ${G.total}`,""];for(let H of B.sort((W,q)=>W.position-q.position)){let W=R[H.id]??[],q=W.reduce((Z,V)=>Z+V.value,0);if(X.push(`## ${H.name}`),X.push(`_${W.length} deals · ${o(q)}_`),X.push(""),W.length===0)X.push("_No deals_");else for(let Z of W)X.push(`- **${Z.name}** - ${o(Z.value,Z.currency)}`);X.push("")}return{mimeType:"text/markdown",body:X.join(`
|
|
71
71
|
`)}}},hY={target:"markdown",render:async(J,Y)=>{if(J.source.type!=="component"||J.source.componentKey!=="CrmDashboard")throw Error("crmDashboardMarkdownRenderer: not CrmDashboard");let Q="pipeline-1",[G,F]=await Promise.all([MJ({pipelineId:Q,limit:100}),LJ({pipelineId:Q})]),P=G.deals,B=F,R=P.filter(($)=>$.status==="OPEN"),X=P.filter(($)=>$.status==="WON"),H=P.filter(($)=>$.status==="LOST"),W=R.reduce(($,L)=>$+L.value,0),q=X.reduce(($,L)=>$+L.value,0),Z=["# CRM Dashboard","","> Sales pipeline overview and key metrics","","## Summary","","| Metric | Value |","|--------|-------|",`| Total Deals | ${G.total} |`,`| Pipeline Value | ${o(G.totalValue)} |`,`| Open Deals | ${R.length} (${o(W)}) |`,`| Won Deals | ${X.length} (${o(q)}) |`,`| Lost Deals | ${H.length} |`,"","## Pipeline Stages",""];Z.push("| Stage | Deals | Value |"),Z.push("|-------|-------|-------|");for(let $ of B.sort((L,_)=>L.position-_.position)){let L=R.filter((K)=>K.stageId===$.id),_=L.reduce((K,z)=>K+z.value,0);Z.push(`| ${$.name} | ${L.length} | ${o(_)} |`)}Z.push(""),Z.push("## Recent Deals"),Z.push("");let V=P.slice(0,10);if(V.length===0)Z.push("_No deals yet._");else{Z.push("| Deal | Value | Stage | Status |"),Z.push("|------|-------|-------|--------|");for(let $ of V){let L=B.find((_)=>_.id===$.stageId);Z.push(`| ${$.name} | ${o($.value,$.currency)} | ${L?.name??"-"} | ${$.status} |`)}}return{mimeType:"text/markdown",body:Z.join(`
|
|
72
|
-
`)}}};import{jsx as BX}from"react/jsx-runtime";function bY(){let{dealsByStage:J,stages:Y}=i();return BX(hJ,{dealsByStage:J,stages:Y})}var TY={target:"react",render:async(J,Y)=>{if(J.source.type!=="component")throw Error("Invalid source type");if(J.source.componentKey!=="CrmPipelineView")throw Error(`Unknown component: ${J.source.componentKey}`);return BX(bY,{})}};import{identityRbacSchemaContribution as CY}from"@contractspec/lib.identity-rbac";import{auditTrailSchemaContribution as EY}from"@contractspec/module.audit-trail";import{notificationsSchemaContribution as jY}from"@contractspec/
|
|
72
|
+
`)}}};import{jsx as BX}from"react/jsx-runtime";function bY(){let{dealsByStage:J,stages:Y}=i();return BX(hJ,{dealsByStage:J,stages:Y})}var TY={target:"react",render:async(J,Y)=>{if(J.source.type!=="component")throw Error("Invalid source type");if(J.source.componentKey!=="CrmPipelineView")throw Error(`Unknown component: ${J.source.componentKey}`);return BX(bY,{})}};import{identityRbacSchemaContribution as CY}from"@contractspec/lib.identity-rbac";import{auditTrailSchemaContribution as EY}from"@contractspec/module.audit-trail";import{notificationsSchemaContribution as jY}from"@contractspec/lib.notification";var w3={modules:[CY,EY,jY,KX],provider:"postgresql",outputPath:"./prisma/schema/generated.prisma"};export{rJ as useDealMutations,i as useDealList,w3 as schemaComposition,nX as mockWinDealHandler,iX as mockMoveDealHandler,sX as mockLoseDealHandler,MJ as mockListDealsHandler,LJ as mockGetPipelineStagesHandler,tX as mockGetDealsByStageHandler,lX as mockCreateDealHandler,rX as example,wY as crmSalesRepOverlay,KX as crmPipelineSchemaContribution,TY as crmPipelineReactRenderer,DY as crmPipelineMarkdownRenderer,X3 as crmOverlays,IY as crmDemoOverlay,hY as crmDashboardMarkdownRenderer,gZ as createCrmHandlers,NJ as WinDealInputModel,XX as WinDealContract,pJ as TaskTypeEnum,uJ as TaskStatusEnum,cJ as TaskPriorityEnum,WX as TaskEntity,DZ as TaskCompletedEvent,QX as StageEntity,aZ as PipelineMetricsPresentation,Y0 as PipelineKanbanPresentation,HX as PipelineEntity,UJ as MoveDealInputModel,JX as MoveDealContract,qJ as MOCK_STAGES,f as MOCK_DEALS,mZ as MOCK_CONTACTS,SZ as MOCK_COMPANIES,_J as LoseDealInputModel,YX as LoseDealContract,PJ as ListDealsOutputModel,AJ as ListDealsInputModel,ZX as ListDealsContract,uX as ExamplesCrmPipelineExample,VJ as DealWonPayloadModel,vZ as DealWonEvent,zJ as DealStatusFilterEnum,SJ as DealStatusEnum,KJ as DealMovedPayloadModel,BZ as DealMovedEvent,y as DealModel,RJ as DealLostPayloadModel,MZ as DealLostEvent,Z0 as DealListPresentation,FX as DealEntity,$0 as DealDetailPresentation,kZ as DealCreatedEvent,q0 as DealCardPresentation,lJ as DealActionsModal,fY as CrmPipelineFeature,hJ as CrmPipelineBoard,AX as CrmDealCard,tZ as CrmDashboardPresentation,d0 as CrmDashboard,dJ as CreateDealModal,WJ as CreateDealInputModel,eJ as CreateDealContract,gJ as ContactStatusEnum,GX as ContactEntity,RZ as ContactCreatedEvent,fJ as CompanySizeEnum,qX as CompanyEntity,UX as ActivityEntity};
|
package/dist/index.js
CHANGED
|
@@ -70,4 +70,4 @@ import{defineFeature as vX}from"@contractspec/lib.contracts-spec/features";var f
|
|
|
70
70
|
- Use Feature Flags for experimental stages/SLAs; default safe/off.`}];LX(IX);import{defineEntity as wX,defineEntityEnum as DX,field as I,index as $X}from"@contractspec/lib.schema";var fJ=DX({name:"CompanySize",values:["STARTUP","SMALL","MEDIUM","LARGE","ENTERPRISE"],schema:"crm",description:"Size category of a company."}),qX=wX({name:"Company",description:"A company/organization in the CRM.",schema:"crm",map:"company",fields:{id:I.id({description:"Unique company ID"}),name:I.string({description:"Company name"}),domain:I.string({isOptional:!0,description:"Website domain"}),website:I.url({isOptional:!0}),industry:I.string({isOptional:!0}),size:I.enum("CompanySize",{isOptional:!0}),employeeCount:I.int({isOptional:!0}),annualRevenue:I.decimal({isOptional:!0}),organizationId:I.foreignKey(),ownerId:I.foreignKey({description:"Account owner"}),phone:I.string({isOptional:!0}),email:I.email({isOptional:!0}),address:I.string({isOptional:!0}),city:I.string({isOptional:!0}),state:I.string({isOptional:!0}),country:I.string({isOptional:!0}),postalCode:I.string({isOptional:!0}),linkedInUrl:I.url({isOptional:!0}),description:I.string({isOptional:!0}),tags:I.string({isArray:!0}),customFields:I.json({isOptional:!0}),createdAt:I.createdAt(),updatedAt:I.updatedAt(),contacts:I.hasMany("Contact"),deals:I.hasMany("Deal")},indexes:[$X.on(["organizationId","ownerId"]),$X.on(["domain"])],enums:[fJ]});import{defineEntity as hX,defineEntityEnum as bX,field as v,index as kJ}from"@contractspec/lib.schema";var gJ=bX({name:"ContactStatus",values:["LEAD","PROSPECT","CUSTOMER","CHURNED","ARCHIVED"],schema:"crm",description:"Status of a contact in the sales funnel."}),GX=hX({name:"Contact",description:"An individual person in the CRM.",schema:"crm",map:"contact",fields:{id:v.id({description:"Unique contact ID"}),firstName:v.string({description:"First name"}),lastName:v.string({description:"Last name"}),email:v.email({isOptional:!0,isUnique:!0}),phone:v.string({isOptional:!0}),companyId:v.string({isOptional:!0,description:"Associated company"}),jobTitle:v.string({isOptional:!0}),status:v.enum("ContactStatus",{default:"LEAD"}),organizationId:v.foreignKey(),ownerId:v.foreignKey({description:"Sales rep who owns this contact"}),source:v.string({isOptional:!0,description:"Lead source"}),linkedInUrl:v.url({isOptional:!0}),twitterHandle:v.string({isOptional:!0}),address:v.string({isOptional:!0}),city:v.string({isOptional:!0}),state:v.string({isOptional:!0}),country:v.string({isOptional:!0}),postalCode:v.string({isOptional:!0}),notes:v.string({isOptional:!0}),tags:v.string({isArray:!0}),customFields:v.json({isOptional:!0}),lastContactedAt:v.dateTime({isOptional:!0}),nextFollowUpAt:v.dateTime({isOptional:!0}),createdAt:v.createdAt(),updatedAt:v.updatedAt(),company:v.belongsTo("Company",["companyId"],["id"]),deals:v.hasMany("Deal"),tasks:v.hasMany("Task"),activities:v.hasMany("Activity")},indexes:[kJ.on(["organizationId","status"]),kJ.on(["organizationId","ownerId"]),kJ.on(["organizationId","companyId"]),kJ.on(["email"])],enums:[gJ]});import{defineEntity as xJ,defineEntityEnum as TX,field as U,index as YJ}from"@contractspec/lib.schema";var SJ=TX({name:"DealStatus",values:["OPEN","WON","LOST","STALE"],schema:"crm",description:"Status of a deal."}),HX=xJ({name:"Pipeline",description:"A sales pipeline with stages.",schema:"crm",map:"pipeline",fields:{id:U.id(),name:U.string({description:"Pipeline name"}),description:U.string({isOptional:!0}),organizationId:U.foreignKey(),isDefault:U.boolean({default:!1}),createdAt:U.createdAt(),updatedAt:U.updatedAt(),stages:U.hasMany("Stage"),deals:U.hasMany("Deal")}}),QX=xJ({name:"Stage",description:"A stage within a sales pipeline.",schema:"crm",map:"stage",fields:{id:U.id(),name:U.string({description:"Stage name"}),pipelineId:U.foreignKey(),position:U.int({description:"Order in pipeline"}),probability:U.int({default:0,description:"Win probability (0-100)"}),isWonStage:U.boolean({default:!1}),isLostStage:U.boolean({default:!1}),color:U.string({isOptional:!0,description:"Stage color for UI"}),createdAt:U.createdAt(),updatedAt:U.updatedAt(),pipeline:U.belongsTo("Pipeline",["pipelineId"],["id"],{onDelete:"Cascade"}),deals:U.hasMany("Deal")},indexes:[YJ.on(["pipelineId","position"])]}),FX=xJ({name:"Deal",description:"A sales opportunity/deal.",schema:"crm",map:"deal",fields:{id:U.id({description:"Unique deal ID"}),name:U.string({description:"Deal name"}),value:U.decimal({description:"Deal value"}),currency:U.string({default:'"USD"'}),pipelineId:U.foreignKey(),stageId:U.foreignKey(),status:U.enum("DealStatus",{default:"OPEN"}),contactId:U.string({isOptional:!0}),companyId:U.string({isOptional:!0}),organizationId:U.foreignKey(),ownerId:U.foreignKey({description:"Deal owner"}),expectedCloseDate:U.dateTime({isOptional:!0}),closedAt:U.dateTime({isOptional:!0}),lostReason:U.string({isOptional:!0}),wonSource:U.string({isOptional:!0}),notes:U.string({isOptional:!0}),tags:U.string({isArray:!0}),customFields:U.json({isOptional:!0}),stagePosition:U.int({default:0}),createdAt:U.createdAt(),updatedAt:U.updatedAt(),pipeline:U.belongsTo("Pipeline",["pipelineId"],["id"]),stage:U.belongsTo("Stage",["stageId"],["id"]),contact:U.belongsTo("Contact",["contactId"],["id"]),company:U.belongsTo("Company",["companyId"],["id"]),tasks:U.hasMany("Task"),activities:U.hasMany("Activity")},indexes:[YJ.on(["organizationId","status"]),YJ.on(["pipelineId","stageId","stagePosition"]),YJ.on(["ownerId","status"]),YJ.on(["expectedCloseDate"])],enums:[SJ]});import{defineEntity as zX,defineEntityEnum as mJ,field as O,index as n}from"@contractspec/lib.schema";var pJ=mJ({name:"TaskType",values:["CALL","EMAIL","MEETING","TODO","FOLLOW_UP","OTHER"],schema:"crm",description:"Type of CRM task."}),cJ=mJ({name:"TaskPriority",values:["LOW","NORMAL","HIGH","URGENT"],schema:"crm",description:"Priority of a task."}),uJ=mJ({name:"TaskStatus",values:["PENDING","IN_PROGRESS","COMPLETED","CANCELLED"],schema:"crm",description:"Status of a task."}),WX=zX({name:"Task",description:"A task or follow-up activity.",schema:"crm",map:"task",fields:{id:O.id(),title:O.string({description:"Task title"}),description:O.string({isOptional:!0}),type:O.enum("TaskType",{default:"TODO"}),priority:O.enum("TaskPriority",{default:"NORMAL"}),status:O.enum("TaskStatus",{default:"PENDING"}),dueDate:O.dateTime({isOptional:!0}),reminderAt:O.dateTime({isOptional:!0}),contactId:O.string({isOptional:!0}),dealId:O.string({isOptional:!0}),companyId:O.string({isOptional:!0}),organizationId:O.foreignKey(),assignedTo:O.foreignKey({description:"User assigned to this task"}),createdBy:O.foreignKey(),completedAt:O.dateTime({isOptional:!0}),completedBy:O.string({isOptional:!0}),createdAt:O.createdAt(),updatedAt:O.updatedAt(),contact:O.belongsTo("Contact",["contactId"],["id"]),deal:O.belongsTo("Deal",["dealId"],["id"]),company:O.belongsTo("Company",["companyId"],["id"])},indexes:[n.on(["organizationId","assignedTo","status"]),n.on(["dueDate","status"]),n.on(["contactId"]),n.on(["dealId"])],enums:[pJ,cJ,uJ]}),UX=zX({name:"Activity",description:"An activity/interaction logged in the CRM.",schema:"crm",map:"activity",fields:{id:O.id(),type:O.enum("TaskType"),subject:O.string(),description:O.string({isOptional:!0}),contactId:O.string({isOptional:!0}),dealId:O.string({isOptional:!0}),companyId:O.string({isOptional:!0}),organizationId:O.foreignKey(),performedBy:O.foreignKey(),outcome:O.string({isOptional:!0}),occurredAt:O.dateTime(),duration:O.int({isOptional:!0,description:"Duration in minutes"}),createdAt:O.createdAt(),contact:O.belongsTo("Contact",["contactId"],["id"]),deal:O.belongsTo("Deal",["dealId"],["id"]),company:O.belongsTo("Company",["companyId"],["id"])},indexes:[n.on(["contactId","occurredAt"]),n.on(["dealId","occurredAt"])]});var KX={moduleId:"@contractspec/example.crm-pipeline",entities:[qX,GX,FX,HX,QX,WX,UX],enums:[fJ,gJ,SJ,pJ,cJ,uJ]};import{defineEvent as CX}from"@contractspec/lib.contracts-spec";import{defineSchemaModel as EX,ScalarTypeEnum as ZJ}from"@contractspec/lib.schema";var jX=EX({name:"ContactCreatedPayload",description:"Payload when a contact is created",fields:{contactId:{type:ZJ.String_unsecure(),isOptional:!1},email:{type:ZJ.EmailAddress(),isOptional:!0},organizationId:{type:ZJ.String_unsecure(),isOptional:!1},ownerId:{type:ZJ.String_unsecure(),isOptional:!1},createdAt:{type:ZJ.DateTime(),isOptional:!1}}}),RZ=CX({meta:{key:"contact.created",version:"1.0.0",description:"A new contact has been created.",stability:"stable",owners:["@crm-team"],tags:["contact","created"]},payload:jX});import{defineEvent as BJ}from"@contractspec/lib.contracts-spec";import{defineSchemaModel as vJ,ScalarTypeEnum as D}from"@contractspec/lib.schema";var yX=vJ({name:"DealCreatedPayload",description:"Payload when a deal is created",fields:{dealId:{type:D.String_unsecure(),isOptional:!1},name:{type:D.String_unsecure(),isOptional:!1},value:{type:D.Float_unsecure(),isOptional:!1},pipelineId:{type:D.String_unsecure(),isOptional:!1},stageId:{type:D.String_unsecure(),isOptional:!1},ownerId:{type:D.String_unsecure(),isOptional:!1},createdAt:{type:D.DateTime(),isOptional:!1}}}),fX=vJ({name:"DealMovedEventPayload",description:"Payload when a deal is moved to another stage",fields:{dealId:{type:D.String_unsecure(),isOptional:!1},fromStageId:{type:D.String_unsecure(),isOptional:!1},toStageId:{type:D.String_unsecure(),isOptional:!1},movedBy:{type:D.String_unsecure(),isOptional:!1},movedAt:{type:D.DateTime(),isOptional:!1}}}),gX=vJ({name:"DealWonEventPayload",description:"Payload when a deal is won",fields:{dealId:{type:D.String_unsecure(),isOptional:!1},value:{type:D.Float_unsecure(),isOptional:!1},currency:{type:D.String_unsecure(),isOptional:!1},contactId:{type:D.String_unsecure(),isOptional:!0},companyId:{type:D.String_unsecure(),isOptional:!0},ownerId:{type:D.String_unsecure(),isOptional:!1},wonAt:{type:D.DateTime(),isOptional:!1}}}),xX=vJ({name:"DealLostEventPayload",description:"Payload when a deal is lost",fields:{dealId:{type:D.String_unsecure(),isOptional:!1},value:{type:D.Float_unsecure(),isOptional:!1},reason:{type:D.String_unsecure(),isOptional:!1},ownerId:{type:D.String_unsecure(),isOptional:!1},lostAt:{type:D.DateTime(),isOptional:!1}}}),kZ=BJ({meta:{key:"deal.created",version:"1.0.0",description:"A new deal has been created.",stability:"stable",owners:["@crm-team"],tags:["deal","created"]},payload:yX}),BZ=BJ({meta:{key:"deal.moved",version:"1.0.0",description:"A deal has been moved to a different stage.",stability:"stable",owners:["@crm-team"],tags:["deal","moved"]},payload:fX}),vZ=BJ({meta:{key:"deal.won",version:"1.0.0",description:"A deal has been won.",stability:"stable",owners:["@crm-team"],tags:["deal","won"]},payload:gX}),MZ=BJ({meta:{key:"deal.lost",version:"1.0.0",description:"A deal has been lost.",stability:"stable",owners:["@crm-team"],tags:["deal","lost"]},payload:xX});import{defineEvent as SX}from"@contractspec/lib.contracts-spec";import{defineSchemaModel as mX,ScalarTypeEnum as $J}from"@contractspec/lib.schema";var pX=mX({name:"TaskCompletedPayload",description:"Payload when a task is completed",fields:{taskId:{type:$J.String_unsecure(),isOptional:!1},type:{type:$J.String_unsecure(),isOptional:!1},assignedTo:{type:$J.String_unsecure(),isOptional:!1},completedBy:{type:$J.String_unsecure(),isOptional:!1},completedAt:{type:$J.DateTime(),isOptional:!1}}}),DZ=SX({meta:{key:"task.completed",version:"1.0.0",description:"A task has been completed.",stability:"stable",owners:["@crm-team"],tags:["task","lifecycle"]},payload:pX});import{defineExample as cX}from"@contractspec/lib.contracts-spec/examples";var uX=cX({meta:{key:"examples.crm-pipeline",version:"1.0.0",title:"Crm Pipeline",description:"CRM Pipeline - Contacts, Companies, Deals, Tasks",kind:"template",visibility:"experimental",stability:"experimental",owners:["@contractspec-core"],tags:["package","examples","crm-pipeline"]},surfaces:{templates:!0,sandbox:{enabled:!0,modes:["playground","specs"]},studio:{enabled:!1,installable:!1},mcp:{enabled:!1}},entrypoints:{packageName:"@contractspec/example.crm-pipeline"}}),rX=uX;import{web as oX}from"@contractspec/lib.runtime-sandbox";var{generateId:dX}=oX;function s(J){return{id:J.id,projectId:J.projectId,name:J.name,value:J.value,currency:J.currency,pipelineId:J.pipelineId,stageId:J.stageId,status:J.status,contactId:J.contactId??void 0,companyId:J.companyId??void 0,ownerId:J.ownerId,expectedCloseDate:J.expectedCloseDate?new Date(J.expectedCloseDate):void 0,wonSource:J.wonSource??void 0,lostReason:J.lostReason??void 0,notes:J.notes??void 0,createdAt:new Date(J.createdAt),updatedAt:new Date(J.updatedAt)}}var NX={name:"name",value:"value",status:"status",expectedCloseDate:"expectedCloseDate",updatedAt:"updatedAt"};function gZ(J){async function Y(X){let{projectId:H,pipelineId:W,stageId:q,status:Z,ownerId:V,search:$,limit:L=20,offset:_=0,sortBy:K="value",sortDirection:z="desc"}=X,M="WHERE projectId = ?",T=[H];if(W)M+=" AND pipelineId = ?",T.push(W);if(q)M+=" AND stageId = ?",T.push(q);if(Z&&Z!=="all")M+=" AND status = ?",T.push(Z);if(V)M+=" AND ownerId = ?",T.push(V);if($)M+=" AND name LIKE ?",T.push(`%${$}%`);let d=(await J.query(`SELECT COUNT(*) as count FROM crm_deal ${M}`,T)).rows[0]?.count??0,j=(await J.query(`SELECT COALESCE(SUM(value), 0) as total FROM crm_deal ${M}`,T)).rows[0]?.total??0,k=NX[K]??NX.value,JJ=z==="asc"?"ASC":"DESC";return{deals:(await J.query(`SELECT * FROM crm_deal ${M} ORDER BY ${k} ${JJ} LIMIT ? OFFSET ?`,[...T,L,_])).rows.map(s),total:d,totalValue:j}}async function Q(X,H){let W=dX("deal"),q=new Date().toISOString();await J.execute(`INSERT INTO crm_deal (id, projectId, pipelineId, stageId, name, value, currency, status, contactId, companyId, ownerId, expectedCloseDate, createdAt, updatedAt)
|
|
71
71
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,[W,H.projectId,X.pipelineId,X.stageId,X.name,X.value,X.currency??"USD","OPEN",X.contactId??null,X.companyId??null,H.ownerId,X.expectedCloseDate?.toISOString()??null,q,q]);let Z=(await J.query("SELECT * FROM crm_deal WHERE id = ?",[W])).rows;if(!Z[0])throw Error("Failed to create deal");return s(Z[0])}async function G(X){let H=new Date().toISOString();if(!(await J.query("SELECT * FROM crm_deal WHERE id = ?",[X.dealId])).rows[0])throw Error("NOT_FOUND");if(!(await J.query("SELECT * FROM crm_stage WHERE id = ?",[X.stageId])).rows[0])throw Error("INVALID_STAGE");await J.execute("UPDATE crm_deal SET stageId = ?, updatedAt = ? WHERE id = ?",[X.stageId,H,X.dealId]);let Z=(await J.query("SELECT * FROM crm_deal WHERE id = ?",[X.dealId])).rows;return s(Z[0])}async function F(X){let H=new Date().toISOString();if(!(await J.query("SELECT * FROM crm_deal WHERE id = ?",[X.dealId])).rows[0])throw Error("NOT_FOUND");await J.execute("UPDATE crm_deal SET status = 'WON', wonSource = ?, notes = ?, updatedAt = ? WHERE id = ?",[X.wonSource??null,X.notes??null,H,X.dealId]);let q=(await J.query("SELECT * FROM crm_deal WHERE id = ?",[X.dealId])).rows;return s(q[0])}async function P(X){let H=new Date().toISOString();if(!(await J.query("SELECT * FROM crm_deal WHERE id = ?",[X.dealId])).rows[0])throw Error("NOT_FOUND");await J.execute("UPDATE crm_deal SET status = 'LOST', lostReason = ?, notes = ?, updatedAt = ? WHERE id = ?",[X.lostReason,X.notes??null,H,X.dealId]);let q=(await J.query("SELECT * FROM crm_deal WHERE id = ?",[X.dealId])).rows;return s(q[0])}async function B(X){let H=(await J.query("SELECT * FROM crm_deal WHERE projectId = ? AND pipelineId = ? AND status = 'OPEN' ORDER BY value DESC",[X.projectId,X.pipelineId])).rows,W=(await J.query("SELECT * FROM crm_stage WHERE pipelineId = ? ORDER BY position",[X.pipelineId])).rows,q={};for(let Z of W)q[Z.id]=H.filter((V)=>V.stageId===Z.id).map(s);return q}async function R(X){return(await J.query("SELECT * FROM crm_stage WHERE pipelineId = ? ORDER BY position",[X.pipelineId])).rows.map((W)=>({id:W.id,pipelineId:W.pipelineId,name:W.name,position:W.position}))}return{listDeals:Y,createDeal:Q,moveDeal:G,winDeal:F,loseDeal:P,getDealsByStage:B,getPipelineStages:R}}var qJ=[{id:"stage-1",name:"Lead",position:1,pipelineId:"pipeline-1"},{id:"stage-2",name:"Qualified",position:2,pipelineId:"pipeline-1"},{id:"stage-3",name:"Proposal",position:3,pipelineId:"pipeline-1"},{id:"stage-4",name:"Negotiation",position:4,pipelineId:"pipeline-1"},{id:"stage-5",name:"Closed",position:5,pipelineId:"pipeline-1"}],f=[{id:"deal-1",name:"Enterprise License - Acme Corp",value:75000,currency:"USD",pipelineId:"pipeline-1",stageId:"stage-3",status:"OPEN",contactId:"contact-1",companyId:"company-1",ownerId:"user-1",expectedCloseDate:new Date("2024-05-15T00:00:00Z"),createdAt:new Date("2024-02-01T10:00:00Z"),updatedAt:new Date("2024-04-10T14:30:00Z")},{id:"deal-2",name:"Startup Plan - TechStart Inc",value:12000,currency:"USD",pipelineId:"pipeline-1",stageId:"stage-2",status:"OPEN",contactId:"contact-2",companyId:"company-2",ownerId:"user-2",expectedCloseDate:new Date("2024-04-30T00:00:00Z"),createdAt:new Date("2024-03-15T09:00:00Z"),updatedAt:new Date("2024-04-08T11:15:00Z")},{id:"deal-3",name:"Professional Services - Global Ltd",value:45000,currency:"USD",pipelineId:"pipeline-1",stageId:"stage-4",status:"OPEN",contactId:"contact-3",companyId:"company-3",ownerId:"user-1",expectedCloseDate:new Date("2024-04-20T00:00:00Z"),createdAt:new Date("2024-01-20T08:00:00Z"),updatedAt:new Date("2024-04-12T16:45:00Z")},{id:"deal-4",name:"Annual Contract - SmallBiz Co",value:8500,currency:"USD",pipelineId:"pipeline-1",stageId:"stage-1",status:"OPEN",contactId:"contact-4",companyId:"company-4",ownerId:"user-3",createdAt:new Date("2024-04-05T12:00:00Z"),updatedAt:new Date("2024-04-05T12:00:00Z")},{id:"deal-5",name:"Custom Integration - MegaCorp",value:125000,currency:"USD",pipelineId:"pipeline-1",stageId:"stage-5",status:"WON",contactId:"contact-5",companyId:"company-5",ownerId:"user-1",expectedCloseDate:new Date("2024-03-31T00:00:00Z"),createdAt:new Date("2023-11-10T10:00:00Z"),updatedAt:new Date("2024-03-28T09:00:00Z")},{id:"deal-6",name:"Pilot Project - NewCo",value:5000,currency:"USD",pipelineId:"pipeline-1",stageId:"stage-2",status:"LOST",contactId:"contact-6",companyId:"company-6",ownerId:"user-2",createdAt:new Date("2024-01-15T14:00:00Z"),updatedAt:new Date("2024-02-28T10:30:00Z")}],SZ=[{id:"company-1",name:"Acme Corporation",domain:"acme.com",industry:"Technology",size:"1000-5000",website:"https://acme.com",createdAt:new Date("2024-01-01T00:00:00Z")},{id:"company-2",name:"TechStart Inc",domain:"techstart.io",industry:"Software",size:"10-50",website:"https://techstart.io",createdAt:new Date("2024-02-15T00:00:00Z")},{id:"company-3",name:"Global Ltd",domain:"global.com",industry:"Consulting",size:"500-1000",website:"https://global.com",createdAt:new Date("2023-12-01T00:00:00Z")}],mZ=[{id:"contact-1",firstName:"John",lastName:"Smith",email:"john.smith@acme.com",phone:"+1-555-0101",title:"VP of Engineering",companyId:"company-1",createdAt:new Date("2024-01-05T00:00:00Z")},{id:"contact-2",firstName:"Sarah",lastName:"Johnson",email:"sarah@techstart.io",phone:"+1-555-0102",title:"CEO",companyId:"company-2",createdAt:new Date("2024-02-20T00:00:00Z")},{id:"contact-3",firstName:"Michael",lastName:"Brown",email:"michael.brown@global.com",phone:"+1-555-0103",title:"CTO",companyId:"company-3",createdAt:new Date("2023-12-10T00:00:00Z")}];async function MJ(J){let{pipelineId:Y,stageId:Q,status:G,ownerId:F,search:P,limit:B=20,offset:R=0}=J,X=[...f];if(Y)X=X.filter((Z)=>Z.pipelineId===Y);if(Q)X=X.filter((Z)=>Z.stageId===Q);if(G&&G!=="all")X=X.filter((Z)=>Z.status===G);if(F)X=X.filter((Z)=>Z.ownerId===F);if(P){let Z=P.toLowerCase();X=X.filter((V)=>V.name.toLowerCase().includes(Z))}X.sort((Z,V)=>V.value-Z.value);let H=X.length,W=X.reduce((Z,V)=>Z+V.value,0);return{deals:X.slice(R,R+B),total:H,totalValue:W}}async function lX(J,Y){let Q=new Date,G={id:`deal-${Date.now()}`,name:J.name,value:J.value,currency:J.currency??"USD",pipelineId:J.pipelineId,stageId:J.stageId,status:"OPEN",contactId:J.contactId,companyId:J.companyId,ownerId:Y.ownerId,expectedCloseDate:J.expectedCloseDate,createdAt:Q,updatedAt:Q};return f.push(G),G}async function iX(J){let Y=f.findIndex((P)=>P.id===J.dealId);if(Y===-1)throw Error("NOT_FOUND");let Q=f[Y];if(!Q)throw Error("NOT_FOUND");if(!qJ.find((P)=>P.id===J.stageId))throw Error("INVALID_STAGE");let F={...Q,stageId:J.stageId,updatedAt:new Date};return f[Y]=F,F}async function nX(J){let Y=f.findIndex((F)=>F.id===J.dealId);if(Y===-1)throw Error("NOT_FOUND");let Q=f[Y];if(!Q)throw Error("NOT_FOUND");let G={...Q,status:"WON",updatedAt:new Date};return f[Y]=G,G}async function sX(J){let Y=f.findIndex((F)=>F.id===J.dealId);if(Y===-1)throw Error("NOT_FOUND");let Q=f[Y];if(!Q)throw Error("NOT_FOUND");let G={...Q,status:"LOST",updatedAt:new Date};return f[Y]=G,G}async function tX(J){let Y=f.filter((G)=>G.pipelineId===J.pipelineId&&G.status==="OPEN"),Q={};for(let G of qJ)Q[G.id]=Y.filter((F)=>F.stageId===G.id);return Q}async function LJ(J){return qJ.filter((Y)=>Y.pipelineId===J.pipelineId)}import{definePresentation as VX,StabilityEnum as _X}from"@contractspec/lib.contracts-spec";var tZ=VX({meta:{key:"crm.dashboard",version:"1.0.0",title:"CRM Dashboard",description:"Main CRM dashboard with pipeline overview, deal stats, and activities",domain:"crm-pipeline",owners:["@crm-team"],tags:["dashboard","overview"],stability:_X.Experimental,goal:"Provide a high-level overview of CRM performance and active deals.",context:"The landing page for CRM users."},source:{type:"component",framework:"react",componentKey:"CrmDashboard"},targets:["react","markdown"],policy:{flags:["crm.enabled"]}}),aZ=VX({meta:{key:"crm.pipeline.metrics",version:"1.0.0",title:"Pipeline Metrics",description:"Pipeline metrics and forecasting view",domain:"crm-pipeline",owners:["@crm-team"],tags:["pipeline","metrics","forecast"],stability:_X.Experimental,goal:"Track pipeline health and sales forecasts.",context:"Data-intensive widget for sales managers."},source:{type:"component",framework:"react",componentKey:"PipelineMetricsView"},targets:["react","markdown"],policy:{flags:["crm.metrics.enabled"]}});import{definePresentation as IJ,StabilityEnum as wJ}from"@contractspec/lib.contracts-spec";var Y0=IJ({meta:{key:"crm.pipeline.kanban",version:"1.0.0",title:"Pipeline Kanban",description:"Kanban board view of deals organized by stage",domain:"crm-pipeline",owners:["@crm-team"],tags:["pipeline","kanban","deals"],stability:wJ.Experimental,goal:"Visualize the sales pipeline status and deal distribution across stages.",context:"Used in the sales dashboard and management reports."},source:{type:"component",framework:"react",componentKey:"PipelineKanbanView",props:y},targets:["react","markdown"],policy:{flags:["crm.pipeline.enabled"]}}),Z0=IJ({meta:{key:"crm.deal.viewList",version:"1.0.0",title:"Deal List",description:"List view of deals with value, status, and owner info",domain:"crm-pipeline",owners:["@crm-team"],tags:["deal","list"],stability:wJ.Experimental,goal:"Search, filter, and review deal lists.",context:"Standard view for deal management and bulk actions."},source:{type:"component",framework:"react",componentKey:"DealListView",props:y},targets:["react","markdown","application/json"],policy:{flags:["crm.deals.enabled"]}}),$0=IJ({meta:{key:"crm.deal.detail",version:"1.0.0",title:"Deal Details",description:"Detailed view of a deal with activities, contacts, and history",domain:"crm-pipeline",owners:["@crm-team"],tags:["deal","detail"],stability:wJ.Experimental,goal:"Deep dive into deal details and historical activities.",context:"The main workspace for managing a single deal execution."},source:{type:"component",framework:"react",componentKey:"DealDetailView"},targets:["react","markdown"],policy:{flags:["crm.deals.enabled"]}}),q0=IJ({meta:{key:"crm.deal.card",version:"1.0.0",title:"Deal Card",description:"Compact deal card for kanban board display",domain:"crm-pipeline",owners:["@crm-team"],tags:["deal","card","kanban"],stability:wJ.Experimental,goal:"Provide a quick overview of deal status in the pipeline view.",context:"Condensed representation used within the Pipeline Kanban board."},source:{type:"component",framework:"react",componentKey:"DealCard",props:y},targets:["react"],policy:{flags:["crm.deals.enabled"]}});import{jsx as DJ,jsxs as RX}from"react/jsx-runtime";function aX(J,Y){return new Intl.NumberFormat("en-US",{style:"currency",currency:Y,minimumFractionDigits:0,maximumFractionDigits:0}).format(J)}function AX({deal:J,onClick:Y}){let Q=J.expectedCloseDate?Math.ceil((J.expectedCloseDate.getTime()-Date.now())/86400000):null;return RX("div",{onClick:Y,className:"cursor-pointer rounded-lg border border-border bg-card p-3 shadow-sm transition-shadow hover:shadow-md",role:"button",tabIndex:0,onKeyDown:(G)=>{if(G.key==="Enter"||G.key===" ")Y?.()},children:[DJ("h4",{className:"font-medium leading-snug",children:J.name}),DJ("div",{className:"mt-2 font-semibold text-lg text-primary",children:aX(J.value,J.currency)}),RX("div",{className:"mt-3 flex items-center justify-between text-muted-foreground text-xs",children:[Q!==null&&DJ("span",{className:Q<0?"text-red-500":Q<=7?"text-yellow-600 dark:text-yellow-500":"",children:Q<0?`${Math.abs(Q)}d overdue`:Q===0?"Due today":`${Q}d left`}),DJ("span",{className:`rounded px-1.5 py-0.5 font-medium text-xs ${J.status==="WON"?"bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400":J.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"}`,children:J.status})]})]})}import{useState as eX}from"react";import{jsx as u,jsxs as l}from"react/jsx-runtime";function JY(J){if(J>=1e6)return`$${(J/1e6).toFixed(1)}M`;if(J>=1000)return`$${(J/1000).toFixed(0)}K`;return`$${J}`}function hJ({dealsByStage:J,stages:Y,onDealClick:Q,onDealMove:G}){let[F,P]=eX(null),B=[...Y].sort((X,H)=>X.position-H.position),R=(X,H)=>{G?.(X,H),P(null)};return u("div",{className:"flex gap-4 overflow-x-auto pb-4",children:B.map((X)=>{let H=J[X.id]??[],W=H.reduce((q,Z)=>q+Z.value,0);return l("div",{className:"flex w-72 flex-shrink-0 flex-col rounded-lg bg-muted/30",children:[l("div",{className:"flex items-center justify-between border-border border-b px-3 py-2",children:[l("div",{children:[u("h3",{className:"font-medium",children:X.name}),l("p",{className:"text-muted-foreground text-xs",children:[H.length," deals \xB7 ",JY(W)]})]}),u("span",{className:"flex h-6 w-6 items-center justify-center rounded-full bg-muted font-medium text-xs",children:H.length})]}),u("div",{className:"flex flex-1 flex-col gap-2 p-2",children:H.length===0?u("div",{className:"flex h-24 items-center justify-center rounded-md border-2 border-muted-foreground/20 border-dashed text-muted-foreground text-xs",children:"No deals"}):H.map((q)=>l("div",{className:"group relative",children:[u(AX,{deal:q,onClick:()=>Q?.(q.id)}),q.status==="OPEN"&&G&&l("div",{className:"absolute top-1 right-1 opacity-0 transition-opacity group-hover:opacity-100",children:[u("button",{type:"button",onClick:(Z)=>{Z.stopPropagation(),P(F===q.id?null:q.id)},className:"flex h-6 w-6 items-center justify-center rounded border border-border bg-background text-xs shadow-sm hover:bg-muted",title:"Quick move",children:"\u27A1\uFE0F"}),F===q.id&&l("div",{className:"absolute top-7 right-0 z-20 min-w-[140px] rounded-lg border border-border bg-card py-1 shadow-lg",children:[u("p",{className:"px-3 py-1 font-medium text-muted-foreground text-xs",children:"Move to:"}),B.filter((Z)=>Z.id!==q.stageId).map((Z)=>u("button",{type:"button",onClick:(V)=>{V.stopPropagation(),R(q.id,Z.id)},className:"w-full px-3 py-1.5 text-left text-sm hover:bg-muted",children:Z.name},Z.id))]})]})]},q.id))})]},X.id)})})}import{useTemplateRuntime as XY}from"@contractspec/lib.example-shared-ui";import{useCallback as YY,useEffect as ZY,useMemo as $Y,useState as t}from"react";function i(J={}){let{handlers:Y,projectId:Q}=XY(),{crm:G}=Y,[F,P]=t(null),[B,R]=t({}),[X,H]=t([]),[W,q]=t(!0),[Z,V]=t(null),[$,L]=t(0),_=J.pipelineId??"pipeline-1",K=J.pageIndex??$,z=J.pageSize??J.limit??50,[M]=J.sorting??[],T=M?.id==="deal"?"name":M?.id,x=M?M.desc?"desc":"asc":void 0,d=YY(async()=>{q(!0),V(null);try{let[j,k,JJ]=await Promise.all([G.listDeals({projectId:Q,pipelineId:_,stageId:J.stageId,status:J.status==="all"?void 0:J.status,search:J.search,limit:z,offset:K*z,sortBy:T==="name"||T==="value"||T==="status"||T==="expectedCloseDate"||T==="updatedAt"?T:void 0,sortDirection:x}),G.getDealsByStage({projectId:Q,pipelineId:_}),G.getPipelineStages({pipelineId:_})]);P(j),R(k),H(JJ)}catch(j){V(j instanceof Error?j:Error("Unknown error"))}finally{q(!1)}},[G,Q,_,J.stageId,J.status,J.search,K,z,T,x]);ZY(()=>{d()},[d]);let FJ=$Y(()=>{if(!F)return null;let j=F.deals.filter((S)=>S.status==="OPEN"),k=F.deals.filter((S)=>S.status==="WON"),JJ=F.deals.filter((S)=>S.status==="LOST");return{total:F.total,totalValue:F.totalValue,openCount:j.length,openValue:j.reduce((S,jJ)=>S+jJ.value,0),wonCount:k.length,wonValue:k.reduce((S,jJ)=>S+jJ.value,0),lostCount:JJ.length}},[F]);return{data:F,dealsByStage:B,stages:X,loading:W,error:Z,stats:FJ,page:K+1,pageIndex:K,pageSize:z,refetch:d,nextPage:J.pageIndex===void 0?()=>L((j)=>j+1):void 0,prevPage:J.pageIndex===void 0?()=>K>0&&L((j)=>j-1):void 0}}import{useTemplateRuntime as qY}from"@contractspec/lib.example-shared-ui";import{useCallback as bJ,useState as TJ}from"react";function rJ(J={}){let{handlers:Y,projectId:Q}=qY(),{crm:G}=Y,[F,P]=TJ({loading:!1,error:null,data:null}),[B,R]=TJ({loading:!1,error:null,data:null}),[X,H]=TJ({loading:!1,error:null,data:null}),[W,q]=TJ({loading:!1,error:null,data:null}),Z=bJ(async(_)=>{P({loading:!0,error:null,data:null});try{let K=await G.createDeal(_,{projectId:Q,ownerId:"user-1"});return P({loading:!1,error:null,data:K}),J.onSuccess?.(),K}catch(K){let z=K instanceof Error?K:Error("Failed to create deal");return P({loading:!1,error:z,data:null}),J.onError?.(z),null}},[G,Q,J]),V=bJ(async(_)=>{R({loading:!0,error:null,data:null});try{let K=await G.moveDeal(_);return R({loading:!1,error:null,data:K}),J.onSuccess?.(),K}catch(K){let z=K instanceof Error?K:Error("Failed to move deal");return R({loading:!1,error:z,data:null}),J.onError?.(z),null}},[G,J]),$=bJ(async(_)=>{H({loading:!0,error:null,data:null});try{let K=await G.winDeal(_);return H({loading:!1,error:null,data:K}),J.onSuccess?.(),K}catch(K){let z=K instanceof Error?K:Error("Failed to mark deal as won");return H({loading:!1,error:z,data:null}),J.onError?.(z),null}},[G,J]),L=bJ(async(_)=>{q({loading:!0,error:null,data:null});try{let K=await G.loseDeal(_);return q({loading:!1,error:null,data:K}),J.onSuccess?.(),K}catch(K){let z=K instanceof Error?K:Error("Failed to mark deal as lost");return q({loading:!1,error:z,data:null}),J.onError?.(z),null}},[G,J]);return{createDeal:Z,moveDeal:V,winDeal:$,loseDeal:L,createState:F,moveState:B,winState:X,loseState:W,isLoading:F.loading||B.loading||X.loading||W.loading}}import{Button as PX,Input as oJ}from"@contractspec/lib.design-system";import{useState as a}from"react";import{jsx as C,jsxs as p}from"react/jsx-runtime";var GY=["USD","EUR","GBP","CAD"],HY="pipeline-1";function dJ({isOpen:J,onClose:Y,onSubmit:Q,stages:G,isLoading:F=!1}){let[P,B]=a(""),[R,X]=a(""),[H,W]=a("USD"),[q,Z]=a(G[0]?.id??""),[V,$]=a(""),[L,_]=a(null),K=async(z)=>{if(z.preventDefault(),_(null),!P.trim()){_("Deal name is required");return}let M=parseFloat(R);if(isNaN(M)||M<=0){_("Value must be a positive number");return}if(!q){_("Please select a pipeline stage");return}try{await Q({name:P.trim(),value:M,currency:H,pipelineId:HY,stageId:q,expectedCloseDate:V?new Date(V):void 0}),B(""),X(""),W("USD"),Z(G[0]?.id??""),$(""),Y()}catch(T){_(T instanceof Error?T.message:"Failed to create deal")}};if(!J)return null;return p("div",{className:"fixed inset-0 z-50 flex items-center justify-center",children:[C("div",{className:"absolute inset-0 bg-background/80 backdrop-blur-sm",onClick:Y,role:"button",tabIndex:0,onKeyDown:(z)=>{if(z.key==="Enter"||z.key===" ")Y()},"aria-label":"Close modal"}),p("div",{className:"relative z-10 w-full max-w-md rounded-xl border border-border bg-card p-6 shadow-xl",children:[C("h2",{className:"mb-4 font-semibold text-xl",children:"Create New Deal"}),p("form",{onSubmit:K,className:"space-y-4",children:[p("div",{children:[C("label",{htmlFor:"deal-name",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Deal Name *"}),C(oJ,{id:"deal-name",value:P,onChange:(z)=>B(z.target.value),placeholder:"e.g., Enterprise License - Acme Corp",disabled:F})]}),p("div",{className:"flex gap-3",children:[p("div",{className:"flex-1",children:[C("label",{htmlFor:"deal-value",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Value *"}),C(oJ,{id:"deal-value",type:"number",min:"0",step:"0.01",value:R,onChange:(z)=>X(z.target.value),placeholder:"50000",disabled:F})]}),p("div",{className:"w-24",children:[C("label",{htmlFor:"deal-currency",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Currency"}),C("select",{id:"deal-currency",value:H,onChange:(z)=>W(z.target.value),disabled:F,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",children:GY.map((z)=>C("option",{value:z,children:z},z))})]})]}),p("div",{children:[C("label",{htmlFor:"deal-stage",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Pipeline Stage *"}),C("select",{id:"deal-stage",value:q,onChange:(z)=>Z(z.target.value),disabled:F,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",children:G.map((z)=>C("option",{value:z.id,children:z.name},z.id))})]}),p("div",{children:[C("label",{htmlFor:"deal-close-date",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Expected Close Date"}),C(oJ,{id:"deal-close-date",type:"date",value:V,onChange:(z)=>$(z.target.value),disabled:F})]}),L&&C("div",{className:"rounded-md bg-destructive/10 p-3 text-destructive text-sm",children:L}),p("div",{className:"flex justify-end gap-3 pt-2",children:[C(PX,{type:"button",variant:"ghost",onPress:Y,disabled:F,children:"Cancel"}),C(PX,{type:"submit",disabled:F,children:F?"Creating...":"Create Deal"})]})]})]})]})}import{Button as c}from"@contractspec/lib.design-system";import{useState as e}from"react";import{jsx as A,jsxs as h,Fragment as FY}from"react/jsx-runtime";function QY(J,Y){return new Intl.NumberFormat("en-US",{style:"currency",currency:Y,minimumFractionDigits:0,maximumFractionDigits:0}).format(J)}function lJ({isOpen:J,deal:Y,stages:Q,onClose:G,onWin:F,onLose:P,onMove:B,isLoading:R=!1}){let[X,H]=e("menu"),[W,q]=e(""),[Z,V]=e(""),[$,L]=e(""),[_,K]=e(""),[z,M]=e(null),T=()=>{H("menu"),q(""),V(""),L(""),K(""),M(null)},x=()=>{T(),G()},d=async()=>{if(!Y)return;M(null);try{await F({dealId:Y.id,wonSource:W.trim()||void 0,notes:$.trim()||void 0}),x()}catch(k){M(k instanceof Error?k.message:"Failed to mark deal as won")}},FJ=async()=>{if(!Y)return;if(M(null),!Z.trim()){M("Please provide a reason for losing the deal");return}try{await P({dealId:Y.id,lostReason:Z.trim(),notes:$.trim()||void 0}),x()}catch(k){M(k instanceof Error?k.message:"Failed to mark deal as lost")}},j=async()=>{if(!Y)return;if(M(null),!_){M("Please select a stage");return}if(_===Y.stageId){M("Deal is already in this stage");return}try{await B({dealId:Y.id,stageId:_}),x()}catch(k){M(k instanceof Error?k.message:"Failed to move deal")}};if(!J||!Y)return null;return h("div",{className:"fixed inset-0 z-50 flex items-center justify-center",children:[A("div",{className:"absolute inset-0 bg-background/80 backdrop-blur-sm",onClick:x,role:"button",tabIndex:0,onKeyDown:(k)=>{if(k.key==="Enter"||k.key===" ")x()},"aria-label":"Close modal"}),h("div",{className:"relative z-10 w-full max-w-md rounded-xl border border-border bg-card p-6 shadow-xl",children:[h("div",{className:"mb-4 border-border border-b pb-4",children:[A("h2",{className:"font-semibold text-xl",children:Y.name}),A("p",{className:"font-medium text-lg text-primary",children:QY(Y.value,Y.currency)}),A("span",{className:`mt-2 inline-flex rounded-full px-2 py-0.5 font-medium text-xs ${Y.status==="WON"?"bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400":Y.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"}`,children:Y.status})]}),X==="menu"&&h("div",{className:"space-y-3",children:[Y.status==="OPEN"&&h(FY,{children:[h(c,{className:"w-full justify-start",variant:"ghost",onPress:()=>H("win"),children:[A("span",{className:"mr-2",children:"\uD83C\uDFC6"})," Mark as Won"]}),h(c,{className:"w-full justify-start",variant:"ghost",onPress:()=>H("lose"),children:[A("span",{className:"mr-2",children:"\u274C"})," Mark as Lost"]}),h(c,{className:"w-full justify-start",variant:"ghost",onPress:()=>{K(Y.stageId),H("move")},children:[A("span",{className:"mr-2",children:"\u27A1\uFE0F"})," Move to Stage"]})]}),Y.status!=="OPEN"&&h("p",{className:"py-4 text-center text-muted-foreground",children:["This deal is already ",Y.status.toLowerCase(),". No actions available."]}),A("div",{className:"border-border border-t pt-3",children:A(c,{className:"w-full",variant:"outline",onPress:x,children:"Close"})})]}),X==="win"&&h("div",{className:"space-y-4",children:[h("div",{children:[A("label",{htmlFor:"won-source",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"How did you win this deal?"}),h("select",{id:"won-source",value:W,onChange:(k)=>q(k.target.value),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",children:[A("option",{value:"",children:"Select a source..."}),A("option",{value:"referral",children:"Referral"}),A("option",{value:"cold_outreach",children:"Cold Outreach"}),A("option",{value:"inbound",children:"Inbound Lead"}),A("option",{value:"upsell",children:"Upsell"}),A("option",{value:"other",children:"Other"})]})]}),h("div",{children:[A("label",{htmlFor:"win-notes",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Notes (optional)"}),A("textarea",{id:"win-notes",value:$,onChange:(k)=>L(k.target.value),placeholder:"Any additional notes about the win...",rows:3,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"})]}),z&&A("div",{className:"rounded-md bg-destructive/10 p-3 text-destructive text-sm",children:z}),h("div",{className:"flex justify-end gap-3 pt-2",children:[A(c,{variant:"ghost",onPress:()=>H("menu"),disabled:R,children:"Back"}),A(c,{onPress:d,disabled:R,children:R?"Processing...":"\uD83C\uDFC6 Confirm Win"})]})]}),X==="lose"&&h("div",{className:"space-y-4",children:[h("div",{children:[A("label",{htmlFor:"lost-reason",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Why was this deal lost? *"}),h("select",{id:"lost-reason",value:Z,onChange:(k)=>V(k.target.value),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",children:[A("option",{value:"",children:"Select a reason..."}),A("option",{value:"price",children:"Price too high"}),A("option",{value:"competitor",children:"Lost to competitor"}),A("option",{value:"no_budget",children:"No budget"}),A("option",{value:"no_decision",children:"No decision made"}),A("option",{value:"timing",children:"Bad timing"}),A("option",{value:"product_fit",children:"Product not a fit"}),A("option",{value:"other",children:"Other"})]})]}),h("div",{children:[A("label",{htmlFor:"lose-notes",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Notes (optional)"}),A("textarea",{id:"lose-notes",value:$,onChange:(k)=>L(k.target.value),placeholder:"Any additional details...",rows:3,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"})]}),z&&A("div",{className:"rounded-md bg-destructive/10 p-3 text-destructive text-sm",children:z}),h("div",{className:"flex justify-end gap-3 pt-2",children:[A(c,{variant:"ghost",onPress:()=>H("menu"),disabled:R,children:"Back"}),A(c,{variant:"destructive",onPress:FJ,disabled:R,children:R?"Processing...":"\u274C Confirm Loss"})]})]}),X==="move"&&h("div",{className:"space-y-4",children:[h("div",{children:[A("label",{htmlFor:"move-stage",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Move to Stage"}),A("select",{id:"move-stage",value:_,onChange:(k)=>K(k.target.value),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",children:Q.map((k)=>h("option",{value:k.id,children:[k.name,k.id===Y.stageId?" (current)":""]},k.id))})]}),z&&A("div",{className:"rounded-md bg-destructive/10 p-3 text-destructive text-sm",children:z}),h("div",{className:"flex justify-end gap-3 pt-2",children:[A(c,{variant:"ghost",onPress:()=>H("menu"),disabled:R,children:"Back"}),A(c,{onPress:j,disabled:R,children:R?"Moving...":"\u27A1\uFE0F Move Deal"})]})]})]})]})}import{Button as GJ,DataTable as zY,DataTableToolbar as WY,LoaderBlock as UY}from"@contractspec/lib.design-system";import{useContractTable as KY}from"@contractspec/lib.presentation-runtime-react";import{Badge as NY}from"@contractspec/lib.ui-kit-web/ui/badge";import{HStack as HJ,VStack as iJ}from"@contractspec/lib.ui-kit-web/ui/stack";import{Text as g}from"@contractspec/lib.ui-kit-web/ui/text";import*as QJ from"react";import{jsx as b,jsxs as r}from"react/jsx-runtime";function VY(J,Y="USD"){return new Intl.NumberFormat("en-US",{style:"currency",currency:Y,minimumFractionDigits:0,maximumFractionDigits:0}).format(J)}function _Y(J){switch(J){case"WON":return"default";case"LOST":return"destructive";case"STALE":return"outline";default:return"secondary"}}function RY({value:J,onChange:Y}){return r(HJ,{gap:"sm",className:"flex-wrap",children:[b(GJ,{variant:J==="all"?"secondary":"outline",size:"sm",onPress:()=>Y("all"),children:"All Deals"}),b(GJ,{variant:J==="OPEN"?"secondary":"outline",size:"sm",onPress:()=>Y("OPEN"),children:"Open Only"}),b(GJ,{variant:J==="WON"?"secondary":"outline",size:"sm",onPress:()=>Y("WON"),children:"Won Only"}),b(GJ,{variant:J==="LOST"?"secondary":"outline",size:"sm",onPress:()=>Y("LOST"),children:"Lost Only"})]})}function AY({deals:J,totalItems:Y,pageIndex:Q,pageSize:G,sorting:F,search:P,status:B,loading:R,onSortingChange:X,onPaginationChange:H,onSearchChange:W,onStatusChange:q,onDealClick:Z}){let V=KY({data:J,columns:[{id:"deal",header:"Deal",label:"Deal",accessor:($)=>$.name,cell:({item:$})=>r(iJ,{gap:"xs",children:[b(g,{className:"font-medium text-sm",children:$.name}),b(g,{className:"text-muted-foreground text-xs",children:$.companyId??"Unassigned company"})]}),size:240,minSize:180,canSort:!0,canPin:!0,canResize:!0},{id:"value",header:"Value",label:"Value",accessorKey:"value",cell:({item:$})=>VY($.value,$.currency),align:"right",size:140,canSort:!0,canResize:!0},{id:"status",header:"Status",label:"Status",accessorKey:"status",cell:({value:$})=>b(NY,{variant:_Y($),children:String($)}),size:130,canSort:!0,canHide:!0,canPin:!0,canResize:!0},{id:"expectedCloseDate",header:"Expected Close",label:"Expected Close",accessor:($)=>$.expectedCloseDate?.toISOString()??"",cell:({item:$})=>$.expectedCloseDate?.toLocaleDateString()??"Not scheduled",size:170,canSort:!0,canHide:!0,canResize:!0},{id:"updatedAt",header:"Updated",label:"Updated",accessor:($)=>$.updatedAt.toISOString(),cell:({item:$})=>$.updatedAt.toLocaleDateString(),size:140,canSort:!0,canHide:!0,canResize:!0},{id:"actions",header:"Actions",label:"Actions",accessor:($)=>$.id,cell:({item:$})=>b(GJ,{variant:"ghost",size:"sm",onPress:()=>Z?.($.id),children:"Actions"}),size:120,canSort:!1,canHide:!1,canPin:!1,canResize:!1}],executionMode:"server",selectionMode:"multiple",totalItems:Y,state:{sorting:F,pagination:{pageIndex:Q,pageSize:G}},onSortingChange:X,onPaginationChange:H,initialState:{columnVisibility:{updatedAt:!1},columnPinning:{left:["deal","status"],right:[]}},renderExpandedContent:($)=>r(iJ,{gap:"sm",className:"py-2",children:[r(HJ,{justify:"between",children:[b(g,{className:"font-medium text-sm",children:"Owner"}),b(g,{className:"text-muted-foreground text-sm",children:$.ownerId})]}),r(HJ,{justify:"between",children:[b(g,{className:"font-medium text-sm",children:"Contact"}),b(g,{className:"text-muted-foreground text-sm",children:$.contactId??"No linked contact"})]}),$.wonSource?r(HJ,{justify:"between",children:[b(g,{className:"font-medium text-sm",children:"Won Source"}),b(g,{className:"text-muted-foreground text-sm",children:$.wonSource})]}):null,$.lostReason?r(HJ,{justify:"between",children:[b(g,{className:"font-medium text-sm",children:"Lost Reason"}),b(g,{className:"text-muted-foreground text-sm",children:$.lostReason})]}):null,$.notes?r(iJ,{gap:"xs",children:[b(g,{className:"font-medium text-sm",children:"Notes"}),b(g,{className:"text-muted-foreground text-sm",children:$.notes})]}):null]}),getCanExpand:()=>!0});return b(zY,{controller:V,title:"All Deals",description:"Server-mode table using the shared ContractSpec controller.",loading:R,toolbar:b(WY,{controller:V,searchPlaceholder:"Search deals, companies, contacts, or notes",searchValue:P,onSearchChange:W,activeChips:B==="all"?[]:[{key:"status",label:`Status: ${B}`,onRemove:()=>q("all")}],onClearAll:()=>{W(""),q("all")},actionsStart:RY({value:B,onChange:q}),actionsEnd:r(g,{className:"text-muted-foreground text-sm",children:[Y," total deals"]})}),footer:`Page ${V.pageIndex+1} of ${V.pageCount}`,emptyState:b("div",{className:"rounded-md border border-dashed p-8 text-center text-muted-foreground text-sm",children:"No deals found"})})}function OX({onDealClick:J}){let[Y,Q]=QJ.useState([{id:"value",desc:!0}]),[G,F]=QJ.useState({pageIndex:0,pageSize:3}),[P,B]=QJ.useState(""),[R,X]=QJ.useState("all"),{data:H,loading:W}=i({pageIndex:G.pageIndex,pageSize:G.pageSize,search:P,status:R,sorting:Y});if(W&&!H)return b(UY,{label:"Loading deals..."});return b(AY,{deals:H?.deals??[],totalItems:H?.total??0,pageIndex:G.pageIndex,pageSize:G.pageSize,sorting:Y,search:P,status:R,loading:W,onSortingChange:(q)=>{Q(q),F((Z)=>({...Z,pageIndex:0}))},onPaginationChange:F,onSearchChange:(q)=>{B(q),F((Z)=>({...Z,pageIndex:0}))},onStatusChange:(q)=>{X(q),F((Z)=>({...Z,pageIndex:0}))},onDealClick:J})}import{Button as PY,ErrorState as OY,LoaderBlock as kY,StatCard as CJ,StatCardGroup as BY,Tabs as vY,TabsContent as nJ,TabsList as MY,TabsTrigger as sJ}from"@contractspec/lib.design-system";import{useCallback as kX,useState as tJ}from"react";import{jsx as w,jsxs as E}from"react/jsx-runtime";function EJ(J,Y="USD"){return new Intl.NumberFormat("en-US",{style:"currency",currency:Y,minimumFractionDigits:0,maximumFractionDigits:0}).format(J)}function d0(){let[J,Y]=tJ(!1),[Q,G]=tJ(null),[F,P]=tJ(!1),{data:B,dealsByStage:R,stages:X,loading:H,error:W,stats:q,refetch:Z}=i(),V=rJ({onSuccess:()=>{Z()}}),$=kX((_)=>{let K=R?Object.values(R).flat().find((z)=>z.id===_):null;if(K)G(K),P(!0)},[R]),L=kX(async(_,K)=>{await V.moveDeal({dealId:_,stageId:K})},[V]);if(H&&!B)return w(kY,{label:"Loading CRM..."});if(W)return w(OY,{title:"Failed to load CRM",description:W.message,onRetry:Z,retryLabel:"Retry"});return E("div",{className:"space-y-6",children:[E("div",{className:"flex items-center justify-between",children:[w("h2",{className:"font-bold text-2xl",children:"CRM Pipeline"}),E(PY,{onClick:()=>Y(!0),children:[w("span",{className:"mr-2",children:"+"})," Create Deal"]})]}),q&&E(BY,{children:[w(CJ,{label:"Total Pipeline",value:EJ(q.totalValue),hint:`${q.total} deals`}),w(CJ,{label:"Open Deals",value:EJ(q.openValue),hint:`${q.openCount} active`}),w(CJ,{label:"Won",value:EJ(q.wonValue),hint:`${q.wonCount} closed`}),w(CJ,{label:"Lost",value:q.lostCount,hint:"deals lost"})]}),E(vY,{defaultValue:"pipeline",className:"w-full",children:[E(MY,{children:[E(sJ,{value:"pipeline",children:[w("span",{className:"mr-2",children:"\uD83D\uDCCA"}),"Pipeline"]}),E(sJ,{value:"list",children:[w("span",{className:"mr-2",children:"\uD83D\uDCCB"}),"All Deals"]}),E(sJ,{value:"metrics",children:[w("span",{className:"mr-2",children:"\uD83D\uDCC8"}),"Metrics"]})]}),w(nJ,{value:"pipeline",className:"min-h-[400px]",children:w(hJ,{dealsByStage:R,stages:X,onDealClick:$,onDealMove:L})}),w(nJ,{value:"list",className:"min-h-[400px]",children:w(OX,{onDealClick:$})}),w(nJ,{value:"metrics",className:"min-h-[400px]",children:w(LY,{stats:q})})]}),w(dJ,{isOpen:J,onClose:()=>Y(!1),onSubmit:async(_)=>{await V.createDeal(_)},stages:X,isLoading:V.createState.loading}),w(lJ,{isOpen:F,deal:Q,stages:X,onClose:()=>{P(!1),G(null)},onWin:async(_)=>{await V.winDeal(_)},onLose:async(_)=>{await V.loseDeal(_)},onMove:async(_)=>{await V.moveDeal(_),Z()},isLoading:V.isLoading})]})}function LY({stats:J}){if(!J)return null;return w("div",{className:"space-y-6",children:E("div",{className:"rounded-xl border border-border bg-card p-6",children:[w("h3",{className:"mb-4 font-semibold text-lg",children:"Pipeline Overview"}),E("dl",{className:"grid gap-4 sm:grid-cols-3",children:[E("div",{children:[w("dt",{className:"text-muted-foreground text-sm",children:"Win Rate"}),E("dd",{className:"font-semibold text-2xl",children:[J.total>0?(J.wonCount/J.total*100).toFixed(0):0,"%"]})]}),E("div",{children:[w("dt",{className:"text-muted-foreground text-sm",children:"Avg Deal Size"}),w("dd",{className:"font-semibold text-2xl",children:EJ(J.total>0?J.totalValue/J.total:0)})]}),E("div",{children:[w("dt",{className:"text-muted-foreground text-sm",children:"Conversion"}),E("dd",{className:"font-semibold text-2xl",children:[J.wonCount," / ",J.total]})]})]})]})})}var IY={overlayId:"crm-pipeline.demo-user",version:"1.0.0",description:"Demo mode with sample data",appliesTo:{feature:"crm-pipeline",role:"demo"},modifications:[{type:"hideField",field:"importButton",reason:"Not available in demo"},{type:"hideField",field:"exportButton",reason:"Not available in demo"},{type:"addBadge",position:"header",label:"Demo Mode",variant:"warning"}]},wY={overlayId:"crm-pipeline.sales-rep",version:"1.0.0",description:"Sales rep focused view",appliesTo:{feature:"crm-pipeline",role:"sales-rep"},modifications:[{type:"hideField",field:"teamMetrics",reason:"Team metrics for managers only"},{type:"hideField",field:"pipelineSettings",reason:"Admin only"},{type:"renameLabel",field:"deals",newLabel:"My Deals"}]},X3=[IY,wY];function o(J,Y="USD"){return new Intl.NumberFormat("en-US",{style:"currency",currency:Y,minimumFractionDigits:0}).format(J)}var DY={target:"markdown",render:async(J,Y)=>{if(J.source.type!=="component"||J.source.componentKey!=="PipelineKanbanView")throw Error("crmPipelineMarkdownRenderer: not PipelineKanbanView");let Q="pipeline-1",[G,F]=await Promise.all([MJ({pipelineId:Q,limit:50}),LJ({pipelineId:Q})]),P=G.deals,B=F,R={};for(let H of B)R[H.id]=P.filter((W)=>W.stageId===H.id&&W.status==="OPEN");let X=["# CRM Pipeline","",`**Total Value**: ${o(G.totalValue)}`,`**Total Deals**: ${G.total}`,""];for(let H of B.sort((W,q)=>W.position-q.position)){let W=R[H.id]??[],q=W.reduce((Z,V)=>Z+V.value,0);if(X.push(`## ${H.name}`),X.push(`_${W.length} deals \xB7 ${o(q)}_`),X.push(""),W.length===0)X.push("_No deals_");else for(let Z of W)X.push(`- **${Z.name}** - ${o(Z.value,Z.currency)}`);X.push("")}return{mimeType:"text/markdown",body:X.join(`
|
|
72
72
|
`)}}},hY={target:"markdown",render:async(J,Y)=>{if(J.source.type!=="component"||J.source.componentKey!=="CrmDashboard")throw Error("crmDashboardMarkdownRenderer: not CrmDashboard");let Q="pipeline-1",[G,F]=await Promise.all([MJ({pipelineId:Q,limit:100}),LJ({pipelineId:Q})]),P=G.deals,B=F,R=P.filter(($)=>$.status==="OPEN"),X=P.filter(($)=>$.status==="WON"),H=P.filter(($)=>$.status==="LOST"),W=R.reduce(($,L)=>$+L.value,0),q=X.reduce(($,L)=>$+L.value,0),Z=["# CRM Dashboard","","> Sales pipeline overview and key metrics","","## Summary","","| Metric | Value |","|--------|-------|",`| Total Deals | ${G.total} |`,`| Pipeline Value | ${o(G.totalValue)} |`,`| Open Deals | ${R.length} (${o(W)}) |`,`| Won Deals | ${X.length} (${o(q)}) |`,`| Lost Deals | ${H.length} |`,"","## Pipeline Stages",""];Z.push("| Stage | Deals | Value |"),Z.push("|-------|-------|-------|");for(let $ of B.sort((L,_)=>L.position-_.position)){let L=R.filter((K)=>K.stageId===$.id),_=L.reduce((K,z)=>K+z.value,0);Z.push(`| ${$.name} | ${L.length} | ${o(_)} |`)}Z.push(""),Z.push("## Recent Deals"),Z.push("");let V=P.slice(0,10);if(V.length===0)Z.push("_No deals yet._");else{Z.push("| Deal | Value | Stage | Status |"),Z.push("|------|-------|-------|--------|");for(let $ of V){let L=B.find((_)=>_.id===$.stageId);Z.push(`| ${$.name} | ${o($.value,$.currency)} | ${L?.name??"-"} | ${$.status} |`)}}return{mimeType:"text/markdown",body:Z.join(`
|
|
73
|
-
`)}}};import{jsx as BX}from"react/jsx-runtime";function bY(){let{dealsByStage:J,stages:Y}=i();return BX(hJ,{dealsByStage:J,stages:Y})}var TY={target:"react",render:async(J,Y)=>{if(J.source.type!=="component")throw Error("Invalid source type");if(J.source.componentKey!=="CrmPipelineView")throw Error(`Unknown component: ${J.source.componentKey}`);return BX(bY,{})}};import{identityRbacSchemaContribution as CY}from"@contractspec/lib.identity-rbac";import{auditTrailSchemaContribution as EY}from"@contractspec/module.audit-trail";import{notificationsSchemaContribution as jY}from"@contractspec/
|
|
73
|
+
`)}}};import{jsx as BX}from"react/jsx-runtime";function bY(){let{dealsByStage:J,stages:Y}=i();return BX(hJ,{dealsByStage:J,stages:Y})}var TY={target:"react",render:async(J,Y)=>{if(J.source.type!=="component")throw Error("Invalid source type");if(J.source.componentKey!=="CrmPipelineView")throw Error(`Unknown component: ${J.source.componentKey}`);return BX(bY,{})}};import{identityRbacSchemaContribution as CY}from"@contractspec/lib.identity-rbac";import{auditTrailSchemaContribution as EY}from"@contractspec/module.audit-trail";import{notificationsSchemaContribution as jY}from"@contractspec/lib.notification";var w3={modules:[CY,EY,jY,KX],provider:"postgresql",outputPath:"./prisma/schema/generated.prisma"};export{rJ as useDealMutations,i as useDealList,w3 as schemaComposition,nX as mockWinDealHandler,iX as mockMoveDealHandler,sX as mockLoseDealHandler,MJ as mockListDealsHandler,LJ as mockGetPipelineStagesHandler,tX as mockGetDealsByStageHandler,lX as mockCreateDealHandler,rX as example,wY as crmSalesRepOverlay,KX as crmPipelineSchemaContribution,TY as crmPipelineReactRenderer,DY as crmPipelineMarkdownRenderer,X3 as crmOverlays,IY as crmDemoOverlay,hY as crmDashboardMarkdownRenderer,gZ as createCrmHandlers,NJ as WinDealInputModel,XX as WinDealContract,pJ as TaskTypeEnum,uJ as TaskStatusEnum,cJ as TaskPriorityEnum,WX as TaskEntity,DZ as TaskCompletedEvent,QX as StageEntity,aZ as PipelineMetricsPresentation,Y0 as PipelineKanbanPresentation,HX as PipelineEntity,UJ as MoveDealInputModel,JX as MoveDealContract,qJ as MOCK_STAGES,f as MOCK_DEALS,mZ as MOCK_CONTACTS,SZ as MOCK_COMPANIES,_J as LoseDealInputModel,YX as LoseDealContract,PJ as ListDealsOutputModel,AJ as ListDealsInputModel,ZX as ListDealsContract,uX as ExamplesCrmPipelineExample,VJ as DealWonPayloadModel,vZ as DealWonEvent,zJ as DealStatusFilterEnum,SJ as DealStatusEnum,KJ as DealMovedPayloadModel,BZ as DealMovedEvent,y as DealModel,RJ as DealLostPayloadModel,MZ as DealLostEvent,Z0 as DealListPresentation,FX as DealEntity,$0 as DealDetailPresentation,kZ as DealCreatedEvent,q0 as DealCardPresentation,lJ as DealActionsModal,fY as CrmPipelineFeature,hJ as CrmPipelineBoard,AX as CrmDealCard,tZ as CrmDashboardPresentation,d0 as CrmDashboard,dJ as CreateDealModal,WJ as CreateDealInputModel,eJ as CreateDealContract,gJ as ContactStatusEnum,GX as ContactEntity,RZ as ContactCreatedEvent,fJ as CompanySizeEnum,qX as CompanyEntity,UX as ActivityEntity};
|
package/dist/node/index.js
CHANGED
|
@@ -69,4 +69,4 @@ import{defineFeature as vX}from"@contractspec/lib.contracts-spec/features";var f
|
|
|
69
69
|
- Use Feature Flags for experimental stages/SLAs; default safe/off.`}];LX(IX);import{defineEntity as wX,defineEntityEnum as DX,field as I,index as $X}from"@contractspec/lib.schema";var fJ=DX({name:"CompanySize",values:["STARTUP","SMALL","MEDIUM","LARGE","ENTERPRISE"],schema:"crm",description:"Size category of a company."}),qX=wX({name:"Company",description:"A company/organization in the CRM.",schema:"crm",map:"company",fields:{id:I.id({description:"Unique company ID"}),name:I.string({description:"Company name"}),domain:I.string({isOptional:!0,description:"Website domain"}),website:I.url({isOptional:!0}),industry:I.string({isOptional:!0}),size:I.enum("CompanySize",{isOptional:!0}),employeeCount:I.int({isOptional:!0}),annualRevenue:I.decimal({isOptional:!0}),organizationId:I.foreignKey(),ownerId:I.foreignKey({description:"Account owner"}),phone:I.string({isOptional:!0}),email:I.email({isOptional:!0}),address:I.string({isOptional:!0}),city:I.string({isOptional:!0}),state:I.string({isOptional:!0}),country:I.string({isOptional:!0}),postalCode:I.string({isOptional:!0}),linkedInUrl:I.url({isOptional:!0}),description:I.string({isOptional:!0}),tags:I.string({isArray:!0}),customFields:I.json({isOptional:!0}),createdAt:I.createdAt(),updatedAt:I.updatedAt(),contacts:I.hasMany("Contact"),deals:I.hasMany("Deal")},indexes:[$X.on(["organizationId","ownerId"]),$X.on(["domain"])],enums:[fJ]});import{defineEntity as hX,defineEntityEnum as bX,field as v,index as kJ}from"@contractspec/lib.schema";var gJ=bX({name:"ContactStatus",values:["LEAD","PROSPECT","CUSTOMER","CHURNED","ARCHIVED"],schema:"crm",description:"Status of a contact in the sales funnel."}),GX=hX({name:"Contact",description:"An individual person in the CRM.",schema:"crm",map:"contact",fields:{id:v.id({description:"Unique contact ID"}),firstName:v.string({description:"First name"}),lastName:v.string({description:"Last name"}),email:v.email({isOptional:!0,isUnique:!0}),phone:v.string({isOptional:!0}),companyId:v.string({isOptional:!0,description:"Associated company"}),jobTitle:v.string({isOptional:!0}),status:v.enum("ContactStatus",{default:"LEAD"}),organizationId:v.foreignKey(),ownerId:v.foreignKey({description:"Sales rep who owns this contact"}),source:v.string({isOptional:!0,description:"Lead source"}),linkedInUrl:v.url({isOptional:!0}),twitterHandle:v.string({isOptional:!0}),address:v.string({isOptional:!0}),city:v.string({isOptional:!0}),state:v.string({isOptional:!0}),country:v.string({isOptional:!0}),postalCode:v.string({isOptional:!0}),notes:v.string({isOptional:!0}),tags:v.string({isArray:!0}),customFields:v.json({isOptional:!0}),lastContactedAt:v.dateTime({isOptional:!0}),nextFollowUpAt:v.dateTime({isOptional:!0}),createdAt:v.createdAt(),updatedAt:v.updatedAt(),company:v.belongsTo("Company",["companyId"],["id"]),deals:v.hasMany("Deal"),tasks:v.hasMany("Task"),activities:v.hasMany("Activity")},indexes:[kJ.on(["organizationId","status"]),kJ.on(["organizationId","ownerId"]),kJ.on(["organizationId","companyId"]),kJ.on(["email"])],enums:[gJ]});import{defineEntity as xJ,defineEntityEnum as TX,field as U,index as YJ}from"@contractspec/lib.schema";var SJ=TX({name:"DealStatus",values:["OPEN","WON","LOST","STALE"],schema:"crm",description:"Status of a deal."}),HX=xJ({name:"Pipeline",description:"A sales pipeline with stages.",schema:"crm",map:"pipeline",fields:{id:U.id(),name:U.string({description:"Pipeline name"}),description:U.string({isOptional:!0}),organizationId:U.foreignKey(),isDefault:U.boolean({default:!1}),createdAt:U.createdAt(),updatedAt:U.updatedAt(),stages:U.hasMany("Stage"),deals:U.hasMany("Deal")}}),QX=xJ({name:"Stage",description:"A stage within a sales pipeline.",schema:"crm",map:"stage",fields:{id:U.id(),name:U.string({description:"Stage name"}),pipelineId:U.foreignKey(),position:U.int({description:"Order in pipeline"}),probability:U.int({default:0,description:"Win probability (0-100)"}),isWonStage:U.boolean({default:!1}),isLostStage:U.boolean({default:!1}),color:U.string({isOptional:!0,description:"Stage color for UI"}),createdAt:U.createdAt(),updatedAt:U.updatedAt(),pipeline:U.belongsTo("Pipeline",["pipelineId"],["id"],{onDelete:"Cascade"}),deals:U.hasMany("Deal")},indexes:[YJ.on(["pipelineId","position"])]}),FX=xJ({name:"Deal",description:"A sales opportunity/deal.",schema:"crm",map:"deal",fields:{id:U.id({description:"Unique deal ID"}),name:U.string({description:"Deal name"}),value:U.decimal({description:"Deal value"}),currency:U.string({default:'"USD"'}),pipelineId:U.foreignKey(),stageId:U.foreignKey(),status:U.enum("DealStatus",{default:"OPEN"}),contactId:U.string({isOptional:!0}),companyId:U.string({isOptional:!0}),organizationId:U.foreignKey(),ownerId:U.foreignKey({description:"Deal owner"}),expectedCloseDate:U.dateTime({isOptional:!0}),closedAt:U.dateTime({isOptional:!0}),lostReason:U.string({isOptional:!0}),wonSource:U.string({isOptional:!0}),notes:U.string({isOptional:!0}),tags:U.string({isArray:!0}),customFields:U.json({isOptional:!0}),stagePosition:U.int({default:0}),createdAt:U.createdAt(),updatedAt:U.updatedAt(),pipeline:U.belongsTo("Pipeline",["pipelineId"],["id"]),stage:U.belongsTo("Stage",["stageId"],["id"]),contact:U.belongsTo("Contact",["contactId"],["id"]),company:U.belongsTo("Company",["companyId"],["id"]),tasks:U.hasMany("Task"),activities:U.hasMany("Activity")},indexes:[YJ.on(["organizationId","status"]),YJ.on(["pipelineId","stageId","stagePosition"]),YJ.on(["ownerId","status"]),YJ.on(["expectedCloseDate"])],enums:[SJ]});import{defineEntity as zX,defineEntityEnum as mJ,field as O,index as n}from"@contractspec/lib.schema";var pJ=mJ({name:"TaskType",values:["CALL","EMAIL","MEETING","TODO","FOLLOW_UP","OTHER"],schema:"crm",description:"Type of CRM task."}),cJ=mJ({name:"TaskPriority",values:["LOW","NORMAL","HIGH","URGENT"],schema:"crm",description:"Priority of a task."}),uJ=mJ({name:"TaskStatus",values:["PENDING","IN_PROGRESS","COMPLETED","CANCELLED"],schema:"crm",description:"Status of a task."}),WX=zX({name:"Task",description:"A task or follow-up activity.",schema:"crm",map:"task",fields:{id:O.id(),title:O.string({description:"Task title"}),description:O.string({isOptional:!0}),type:O.enum("TaskType",{default:"TODO"}),priority:O.enum("TaskPriority",{default:"NORMAL"}),status:O.enum("TaskStatus",{default:"PENDING"}),dueDate:O.dateTime({isOptional:!0}),reminderAt:O.dateTime({isOptional:!0}),contactId:O.string({isOptional:!0}),dealId:O.string({isOptional:!0}),companyId:O.string({isOptional:!0}),organizationId:O.foreignKey(),assignedTo:O.foreignKey({description:"User assigned to this task"}),createdBy:O.foreignKey(),completedAt:O.dateTime({isOptional:!0}),completedBy:O.string({isOptional:!0}),createdAt:O.createdAt(),updatedAt:O.updatedAt(),contact:O.belongsTo("Contact",["contactId"],["id"]),deal:O.belongsTo("Deal",["dealId"],["id"]),company:O.belongsTo("Company",["companyId"],["id"])},indexes:[n.on(["organizationId","assignedTo","status"]),n.on(["dueDate","status"]),n.on(["contactId"]),n.on(["dealId"])],enums:[pJ,cJ,uJ]}),UX=zX({name:"Activity",description:"An activity/interaction logged in the CRM.",schema:"crm",map:"activity",fields:{id:O.id(),type:O.enum("TaskType"),subject:O.string(),description:O.string({isOptional:!0}),contactId:O.string({isOptional:!0}),dealId:O.string({isOptional:!0}),companyId:O.string({isOptional:!0}),organizationId:O.foreignKey(),performedBy:O.foreignKey(),outcome:O.string({isOptional:!0}),occurredAt:O.dateTime(),duration:O.int({isOptional:!0,description:"Duration in minutes"}),createdAt:O.createdAt(),contact:O.belongsTo("Contact",["contactId"],["id"]),deal:O.belongsTo("Deal",["dealId"],["id"]),company:O.belongsTo("Company",["companyId"],["id"])},indexes:[n.on(["contactId","occurredAt"]),n.on(["dealId","occurredAt"])]});var KX={moduleId:"@contractspec/example.crm-pipeline",entities:[qX,GX,FX,HX,QX,WX,UX],enums:[fJ,gJ,SJ,pJ,cJ,uJ]};import{defineEvent as CX}from"@contractspec/lib.contracts-spec";import{defineSchemaModel as EX,ScalarTypeEnum as ZJ}from"@contractspec/lib.schema";var jX=EX({name:"ContactCreatedPayload",description:"Payload when a contact is created",fields:{contactId:{type:ZJ.String_unsecure(),isOptional:!1},email:{type:ZJ.EmailAddress(),isOptional:!0},organizationId:{type:ZJ.String_unsecure(),isOptional:!1},ownerId:{type:ZJ.String_unsecure(),isOptional:!1},createdAt:{type:ZJ.DateTime(),isOptional:!1}}}),RZ=CX({meta:{key:"contact.created",version:"1.0.0",description:"A new contact has been created.",stability:"stable",owners:["@crm-team"],tags:["contact","created"]},payload:jX});import{defineEvent as BJ}from"@contractspec/lib.contracts-spec";import{defineSchemaModel as vJ,ScalarTypeEnum as D}from"@contractspec/lib.schema";var yX=vJ({name:"DealCreatedPayload",description:"Payload when a deal is created",fields:{dealId:{type:D.String_unsecure(),isOptional:!1},name:{type:D.String_unsecure(),isOptional:!1},value:{type:D.Float_unsecure(),isOptional:!1},pipelineId:{type:D.String_unsecure(),isOptional:!1},stageId:{type:D.String_unsecure(),isOptional:!1},ownerId:{type:D.String_unsecure(),isOptional:!1},createdAt:{type:D.DateTime(),isOptional:!1}}}),fX=vJ({name:"DealMovedEventPayload",description:"Payload when a deal is moved to another stage",fields:{dealId:{type:D.String_unsecure(),isOptional:!1},fromStageId:{type:D.String_unsecure(),isOptional:!1},toStageId:{type:D.String_unsecure(),isOptional:!1},movedBy:{type:D.String_unsecure(),isOptional:!1},movedAt:{type:D.DateTime(),isOptional:!1}}}),gX=vJ({name:"DealWonEventPayload",description:"Payload when a deal is won",fields:{dealId:{type:D.String_unsecure(),isOptional:!1},value:{type:D.Float_unsecure(),isOptional:!1},currency:{type:D.String_unsecure(),isOptional:!1},contactId:{type:D.String_unsecure(),isOptional:!0},companyId:{type:D.String_unsecure(),isOptional:!0},ownerId:{type:D.String_unsecure(),isOptional:!1},wonAt:{type:D.DateTime(),isOptional:!1}}}),xX=vJ({name:"DealLostEventPayload",description:"Payload when a deal is lost",fields:{dealId:{type:D.String_unsecure(),isOptional:!1},value:{type:D.Float_unsecure(),isOptional:!1},reason:{type:D.String_unsecure(),isOptional:!1},ownerId:{type:D.String_unsecure(),isOptional:!1},lostAt:{type:D.DateTime(),isOptional:!1}}}),kZ=BJ({meta:{key:"deal.created",version:"1.0.0",description:"A new deal has been created.",stability:"stable",owners:["@crm-team"],tags:["deal","created"]},payload:yX}),BZ=BJ({meta:{key:"deal.moved",version:"1.0.0",description:"A deal has been moved to a different stage.",stability:"stable",owners:["@crm-team"],tags:["deal","moved"]},payload:fX}),vZ=BJ({meta:{key:"deal.won",version:"1.0.0",description:"A deal has been won.",stability:"stable",owners:["@crm-team"],tags:["deal","won"]},payload:gX}),MZ=BJ({meta:{key:"deal.lost",version:"1.0.0",description:"A deal has been lost.",stability:"stable",owners:["@crm-team"],tags:["deal","lost"]},payload:xX});import{defineEvent as SX}from"@contractspec/lib.contracts-spec";import{defineSchemaModel as mX,ScalarTypeEnum as $J}from"@contractspec/lib.schema";var pX=mX({name:"TaskCompletedPayload",description:"Payload when a task is completed",fields:{taskId:{type:$J.String_unsecure(),isOptional:!1},type:{type:$J.String_unsecure(),isOptional:!1},assignedTo:{type:$J.String_unsecure(),isOptional:!1},completedBy:{type:$J.String_unsecure(),isOptional:!1},completedAt:{type:$J.DateTime(),isOptional:!1}}}),DZ=SX({meta:{key:"task.completed",version:"1.0.0",description:"A task has been completed.",stability:"stable",owners:["@crm-team"],tags:["task","lifecycle"]},payload:pX});import{defineExample as cX}from"@contractspec/lib.contracts-spec/examples";var uX=cX({meta:{key:"examples.crm-pipeline",version:"1.0.0",title:"Crm Pipeline",description:"CRM Pipeline - Contacts, Companies, Deals, Tasks",kind:"template",visibility:"experimental",stability:"experimental",owners:["@contractspec-core"],tags:["package","examples","crm-pipeline"]},surfaces:{templates:!0,sandbox:{enabled:!0,modes:["playground","specs"]},studio:{enabled:!1,installable:!1},mcp:{enabled:!1}},entrypoints:{packageName:"@contractspec/example.crm-pipeline"}}),rX=uX;import{web as oX}from"@contractspec/lib.runtime-sandbox";var{generateId:dX}=oX;function s(J){return{id:J.id,projectId:J.projectId,name:J.name,value:J.value,currency:J.currency,pipelineId:J.pipelineId,stageId:J.stageId,status:J.status,contactId:J.contactId??void 0,companyId:J.companyId??void 0,ownerId:J.ownerId,expectedCloseDate:J.expectedCloseDate?new Date(J.expectedCloseDate):void 0,wonSource:J.wonSource??void 0,lostReason:J.lostReason??void 0,notes:J.notes??void 0,createdAt:new Date(J.createdAt),updatedAt:new Date(J.updatedAt)}}var NX={name:"name",value:"value",status:"status",expectedCloseDate:"expectedCloseDate",updatedAt:"updatedAt"};function gZ(J){async function Y(X){let{projectId:H,pipelineId:W,stageId:q,status:Z,ownerId:V,search:$,limit:L=20,offset:_=0,sortBy:K="value",sortDirection:z="desc"}=X,M="WHERE projectId = ?",T=[H];if(W)M+=" AND pipelineId = ?",T.push(W);if(q)M+=" AND stageId = ?",T.push(q);if(Z&&Z!=="all")M+=" AND status = ?",T.push(Z);if(V)M+=" AND ownerId = ?",T.push(V);if($)M+=" AND name LIKE ?",T.push(`%${$}%`);let d=(await J.query(`SELECT COUNT(*) as count FROM crm_deal ${M}`,T)).rows[0]?.count??0,j=(await J.query(`SELECT COALESCE(SUM(value), 0) as total FROM crm_deal ${M}`,T)).rows[0]?.total??0,k=NX[K]??NX.value,JJ=z==="asc"?"ASC":"DESC";return{deals:(await J.query(`SELECT * FROM crm_deal ${M} ORDER BY ${k} ${JJ} LIMIT ? OFFSET ?`,[...T,L,_])).rows.map(s),total:d,totalValue:j}}async function Q(X,H){let W=dX("deal"),q=new Date().toISOString();await J.execute(`INSERT INTO crm_deal (id, projectId, pipelineId, stageId, name, value, currency, status, contactId, companyId, ownerId, expectedCloseDate, createdAt, updatedAt)
|
|
70
70
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,[W,H.projectId,X.pipelineId,X.stageId,X.name,X.value,X.currency??"USD","OPEN",X.contactId??null,X.companyId??null,H.ownerId,X.expectedCloseDate?.toISOString()??null,q,q]);let Z=(await J.query("SELECT * FROM crm_deal WHERE id = ?",[W])).rows;if(!Z[0])throw Error("Failed to create deal");return s(Z[0])}async function G(X){let H=new Date().toISOString();if(!(await J.query("SELECT * FROM crm_deal WHERE id = ?",[X.dealId])).rows[0])throw Error("NOT_FOUND");if(!(await J.query("SELECT * FROM crm_stage WHERE id = ?",[X.stageId])).rows[0])throw Error("INVALID_STAGE");await J.execute("UPDATE crm_deal SET stageId = ?, updatedAt = ? WHERE id = ?",[X.stageId,H,X.dealId]);let Z=(await J.query("SELECT * FROM crm_deal WHERE id = ?",[X.dealId])).rows;return s(Z[0])}async function F(X){let H=new Date().toISOString();if(!(await J.query("SELECT * FROM crm_deal WHERE id = ?",[X.dealId])).rows[0])throw Error("NOT_FOUND");await J.execute("UPDATE crm_deal SET status = 'WON', wonSource = ?, notes = ?, updatedAt = ? WHERE id = ?",[X.wonSource??null,X.notes??null,H,X.dealId]);let q=(await J.query("SELECT * FROM crm_deal WHERE id = ?",[X.dealId])).rows;return s(q[0])}async function P(X){let H=new Date().toISOString();if(!(await J.query("SELECT * FROM crm_deal WHERE id = ?",[X.dealId])).rows[0])throw Error("NOT_FOUND");await J.execute("UPDATE crm_deal SET status = 'LOST', lostReason = ?, notes = ?, updatedAt = ? WHERE id = ?",[X.lostReason,X.notes??null,H,X.dealId]);let q=(await J.query("SELECT * FROM crm_deal WHERE id = ?",[X.dealId])).rows;return s(q[0])}async function B(X){let H=(await J.query("SELECT * FROM crm_deal WHERE projectId = ? AND pipelineId = ? AND status = 'OPEN' ORDER BY value DESC",[X.projectId,X.pipelineId])).rows,W=(await J.query("SELECT * FROM crm_stage WHERE pipelineId = ? ORDER BY position",[X.pipelineId])).rows,q={};for(let Z of W)q[Z.id]=H.filter((V)=>V.stageId===Z.id).map(s);return q}async function R(X){return(await J.query("SELECT * FROM crm_stage WHERE pipelineId = ? ORDER BY position",[X.pipelineId])).rows.map((W)=>({id:W.id,pipelineId:W.pipelineId,name:W.name,position:W.position}))}return{listDeals:Y,createDeal:Q,moveDeal:G,winDeal:F,loseDeal:P,getDealsByStage:B,getPipelineStages:R}}var qJ=[{id:"stage-1",name:"Lead",position:1,pipelineId:"pipeline-1"},{id:"stage-2",name:"Qualified",position:2,pipelineId:"pipeline-1"},{id:"stage-3",name:"Proposal",position:3,pipelineId:"pipeline-1"},{id:"stage-4",name:"Negotiation",position:4,pipelineId:"pipeline-1"},{id:"stage-5",name:"Closed",position:5,pipelineId:"pipeline-1"}],f=[{id:"deal-1",name:"Enterprise License - Acme Corp",value:75000,currency:"USD",pipelineId:"pipeline-1",stageId:"stage-3",status:"OPEN",contactId:"contact-1",companyId:"company-1",ownerId:"user-1",expectedCloseDate:new Date("2024-05-15T00:00:00Z"),createdAt:new Date("2024-02-01T10:00:00Z"),updatedAt:new Date("2024-04-10T14:30:00Z")},{id:"deal-2",name:"Startup Plan - TechStart Inc",value:12000,currency:"USD",pipelineId:"pipeline-1",stageId:"stage-2",status:"OPEN",contactId:"contact-2",companyId:"company-2",ownerId:"user-2",expectedCloseDate:new Date("2024-04-30T00:00:00Z"),createdAt:new Date("2024-03-15T09:00:00Z"),updatedAt:new Date("2024-04-08T11:15:00Z")},{id:"deal-3",name:"Professional Services - Global Ltd",value:45000,currency:"USD",pipelineId:"pipeline-1",stageId:"stage-4",status:"OPEN",contactId:"contact-3",companyId:"company-3",ownerId:"user-1",expectedCloseDate:new Date("2024-04-20T00:00:00Z"),createdAt:new Date("2024-01-20T08:00:00Z"),updatedAt:new Date("2024-04-12T16:45:00Z")},{id:"deal-4",name:"Annual Contract - SmallBiz Co",value:8500,currency:"USD",pipelineId:"pipeline-1",stageId:"stage-1",status:"OPEN",contactId:"contact-4",companyId:"company-4",ownerId:"user-3",createdAt:new Date("2024-04-05T12:00:00Z"),updatedAt:new Date("2024-04-05T12:00:00Z")},{id:"deal-5",name:"Custom Integration - MegaCorp",value:125000,currency:"USD",pipelineId:"pipeline-1",stageId:"stage-5",status:"WON",contactId:"contact-5",companyId:"company-5",ownerId:"user-1",expectedCloseDate:new Date("2024-03-31T00:00:00Z"),createdAt:new Date("2023-11-10T10:00:00Z"),updatedAt:new Date("2024-03-28T09:00:00Z")},{id:"deal-6",name:"Pilot Project - NewCo",value:5000,currency:"USD",pipelineId:"pipeline-1",stageId:"stage-2",status:"LOST",contactId:"contact-6",companyId:"company-6",ownerId:"user-2",createdAt:new Date("2024-01-15T14:00:00Z"),updatedAt:new Date("2024-02-28T10:30:00Z")}],SZ=[{id:"company-1",name:"Acme Corporation",domain:"acme.com",industry:"Technology",size:"1000-5000",website:"https://acme.com",createdAt:new Date("2024-01-01T00:00:00Z")},{id:"company-2",name:"TechStart Inc",domain:"techstart.io",industry:"Software",size:"10-50",website:"https://techstart.io",createdAt:new Date("2024-02-15T00:00:00Z")},{id:"company-3",name:"Global Ltd",domain:"global.com",industry:"Consulting",size:"500-1000",website:"https://global.com",createdAt:new Date("2023-12-01T00:00:00Z")}],mZ=[{id:"contact-1",firstName:"John",lastName:"Smith",email:"john.smith@acme.com",phone:"+1-555-0101",title:"VP of Engineering",companyId:"company-1",createdAt:new Date("2024-01-05T00:00:00Z")},{id:"contact-2",firstName:"Sarah",lastName:"Johnson",email:"sarah@techstart.io",phone:"+1-555-0102",title:"CEO",companyId:"company-2",createdAt:new Date("2024-02-20T00:00:00Z")},{id:"contact-3",firstName:"Michael",lastName:"Brown",email:"michael.brown@global.com",phone:"+1-555-0103",title:"CTO",companyId:"company-3",createdAt:new Date("2023-12-10T00:00:00Z")}];async function MJ(J){let{pipelineId:Y,stageId:Q,status:G,ownerId:F,search:P,limit:B=20,offset:R=0}=J,X=[...f];if(Y)X=X.filter((Z)=>Z.pipelineId===Y);if(Q)X=X.filter((Z)=>Z.stageId===Q);if(G&&G!=="all")X=X.filter((Z)=>Z.status===G);if(F)X=X.filter((Z)=>Z.ownerId===F);if(P){let Z=P.toLowerCase();X=X.filter((V)=>V.name.toLowerCase().includes(Z))}X.sort((Z,V)=>V.value-Z.value);let H=X.length,W=X.reduce((Z,V)=>Z+V.value,0);return{deals:X.slice(R,R+B),total:H,totalValue:W}}async function lX(J,Y){let Q=new Date,G={id:`deal-${Date.now()}`,name:J.name,value:J.value,currency:J.currency??"USD",pipelineId:J.pipelineId,stageId:J.stageId,status:"OPEN",contactId:J.contactId,companyId:J.companyId,ownerId:Y.ownerId,expectedCloseDate:J.expectedCloseDate,createdAt:Q,updatedAt:Q};return f.push(G),G}async function iX(J){let Y=f.findIndex((P)=>P.id===J.dealId);if(Y===-1)throw Error("NOT_FOUND");let Q=f[Y];if(!Q)throw Error("NOT_FOUND");if(!qJ.find((P)=>P.id===J.stageId))throw Error("INVALID_STAGE");let F={...Q,stageId:J.stageId,updatedAt:new Date};return f[Y]=F,F}async function nX(J){let Y=f.findIndex((F)=>F.id===J.dealId);if(Y===-1)throw Error("NOT_FOUND");let Q=f[Y];if(!Q)throw Error("NOT_FOUND");let G={...Q,status:"WON",updatedAt:new Date};return f[Y]=G,G}async function sX(J){let Y=f.findIndex((F)=>F.id===J.dealId);if(Y===-1)throw Error("NOT_FOUND");let Q=f[Y];if(!Q)throw Error("NOT_FOUND");let G={...Q,status:"LOST",updatedAt:new Date};return f[Y]=G,G}async function tX(J){let Y=f.filter((G)=>G.pipelineId===J.pipelineId&&G.status==="OPEN"),Q={};for(let G of qJ)Q[G.id]=Y.filter((F)=>F.stageId===G.id);return Q}async function LJ(J){return qJ.filter((Y)=>Y.pipelineId===J.pipelineId)}import{definePresentation as VX,StabilityEnum as _X}from"@contractspec/lib.contracts-spec";var tZ=VX({meta:{key:"crm.dashboard",version:"1.0.0",title:"CRM Dashboard",description:"Main CRM dashboard with pipeline overview, deal stats, and activities",domain:"crm-pipeline",owners:["@crm-team"],tags:["dashboard","overview"],stability:_X.Experimental,goal:"Provide a high-level overview of CRM performance and active deals.",context:"The landing page for CRM users."},source:{type:"component",framework:"react",componentKey:"CrmDashboard"},targets:["react","markdown"],policy:{flags:["crm.enabled"]}}),aZ=VX({meta:{key:"crm.pipeline.metrics",version:"1.0.0",title:"Pipeline Metrics",description:"Pipeline metrics and forecasting view",domain:"crm-pipeline",owners:["@crm-team"],tags:["pipeline","metrics","forecast"],stability:_X.Experimental,goal:"Track pipeline health and sales forecasts.",context:"Data-intensive widget for sales managers."},source:{type:"component",framework:"react",componentKey:"PipelineMetricsView"},targets:["react","markdown"],policy:{flags:["crm.metrics.enabled"]}});import{definePresentation as IJ,StabilityEnum as wJ}from"@contractspec/lib.contracts-spec";var Y0=IJ({meta:{key:"crm.pipeline.kanban",version:"1.0.0",title:"Pipeline Kanban",description:"Kanban board view of deals organized by stage",domain:"crm-pipeline",owners:["@crm-team"],tags:["pipeline","kanban","deals"],stability:wJ.Experimental,goal:"Visualize the sales pipeline status and deal distribution across stages.",context:"Used in the sales dashboard and management reports."},source:{type:"component",framework:"react",componentKey:"PipelineKanbanView",props:y},targets:["react","markdown"],policy:{flags:["crm.pipeline.enabled"]}}),Z0=IJ({meta:{key:"crm.deal.viewList",version:"1.0.0",title:"Deal List",description:"List view of deals with value, status, and owner info",domain:"crm-pipeline",owners:["@crm-team"],tags:["deal","list"],stability:wJ.Experimental,goal:"Search, filter, and review deal lists.",context:"Standard view for deal management and bulk actions."},source:{type:"component",framework:"react",componentKey:"DealListView",props:y},targets:["react","markdown","application/json"],policy:{flags:["crm.deals.enabled"]}}),$0=IJ({meta:{key:"crm.deal.detail",version:"1.0.0",title:"Deal Details",description:"Detailed view of a deal with activities, contacts, and history",domain:"crm-pipeline",owners:["@crm-team"],tags:["deal","detail"],stability:wJ.Experimental,goal:"Deep dive into deal details and historical activities.",context:"The main workspace for managing a single deal execution."},source:{type:"component",framework:"react",componentKey:"DealDetailView"},targets:["react","markdown"],policy:{flags:["crm.deals.enabled"]}}),q0=IJ({meta:{key:"crm.deal.card",version:"1.0.0",title:"Deal Card",description:"Compact deal card for kanban board display",domain:"crm-pipeline",owners:["@crm-team"],tags:["deal","card","kanban"],stability:wJ.Experimental,goal:"Provide a quick overview of deal status in the pipeline view.",context:"Condensed representation used within the Pipeline Kanban board."},source:{type:"component",framework:"react",componentKey:"DealCard",props:y},targets:["react"],policy:{flags:["crm.deals.enabled"]}});import{jsx as DJ,jsxs as RX}from"react/jsx-runtime";function aX(J,Y){return new Intl.NumberFormat("en-US",{style:"currency",currency:Y,minimumFractionDigits:0,maximumFractionDigits:0}).format(J)}function AX({deal:J,onClick:Y}){let Q=J.expectedCloseDate?Math.ceil((J.expectedCloseDate.getTime()-Date.now())/86400000):null;return RX("div",{onClick:Y,className:"cursor-pointer rounded-lg border border-border bg-card p-3 shadow-sm transition-shadow hover:shadow-md",role:"button",tabIndex:0,onKeyDown:(G)=>{if(G.key==="Enter"||G.key===" ")Y?.()},children:[DJ("h4",{className:"font-medium leading-snug",children:J.name}),DJ("div",{className:"mt-2 font-semibold text-lg text-primary",children:aX(J.value,J.currency)}),RX("div",{className:"mt-3 flex items-center justify-between text-muted-foreground text-xs",children:[Q!==null&&DJ("span",{className:Q<0?"text-red-500":Q<=7?"text-yellow-600 dark:text-yellow-500":"",children:Q<0?`${Math.abs(Q)}d overdue`:Q===0?"Due today":`${Q}d left`}),DJ("span",{className:`rounded px-1.5 py-0.5 font-medium text-xs ${J.status==="WON"?"bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400":J.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"}`,children:J.status})]})]})}import{useState as eX}from"react";import{jsx as u,jsxs as l}from"react/jsx-runtime";function JY(J){if(J>=1e6)return`$${(J/1e6).toFixed(1)}M`;if(J>=1000)return`$${(J/1000).toFixed(0)}K`;return`$${J}`}function hJ({dealsByStage:J,stages:Y,onDealClick:Q,onDealMove:G}){let[F,P]=eX(null),B=[...Y].sort((X,H)=>X.position-H.position),R=(X,H)=>{G?.(X,H),P(null)};return u("div",{className:"flex gap-4 overflow-x-auto pb-4",children:B.map((X)=>{let H=J[X.id]??[],W=H.reduce((q,Z)=>q+Z.value,0);return l("div",{className:"flex w-72 flex-shrink-0 flex-col rounded-lg bg-muted/30",children:[l("div",{className:"flex items-center justify-between border-border border-b px-3 py-2",children:[l("div",{children:[u("h3",{className:"font-medium",children:X.name}),l("p",{className:"text-muted-foreground text-xs",children:[H.length," deals · ",JY(W)]})]}),u("span",{className:"flex h-6 w-6 items-center justify-center rounded-full bg-muted font-medium text-xs",children:H.length})]}),u("div",{className:"flex flex-1 flex-col gap-2 p-2",children:H.length===0?u("div",{className:"flex h-24 items-center justify-center rounded-md border-2 border-muted-foreground/20 border-dashed text-muted-foreground text-xs",children:"No deals"}):H.map((q)=>l("div",{className:"group relative",children:[u(AX,{deal:q,onClick:()=>Q?.(q.id)}),q.status==="OPEN"&&G&&l("div",{className:"absolute top-1 right-1 opacity-0 transition-opacity group-hover:opacity-100",children:[u("button",{type:"button",onClick:(Z)=>{Z.stopPropagation(),P(F===q.id?null:q.id)},className:"flex h-6 w-6 items-center justify-center rounded border border-border bg-background text-xs shadow-sm hover:bg-muted",title:"Quick move",children:"➡️"}),F===q.id&&l("div",{className:"absolute top-7 right-0 z-20 min-w-[140px] rounded-lg border border-border bg-card py-1 shadow-lg",children:[u("p",{className:"px-3 py-1 font-medium text-muted-foreground text-xs",children:"Move to:"}),B.filter((Z)=>Z.id!==q.stageId).map((Z)=>u("button",{type:"button",onClick:(V)=>{V.stopPropagation(),R(q.id,Z.id)},className:"w-full px-3 py-1.5 text-left text-sm hover:bg-muted",children:Z.name},Z.id))]})]})]},q.id))})]},X.id)})})}import{useTemplateRuntime as XY}from"@contractspec/lib.example-shared-ui";import{useCallback as YY,useEffect as ZY,useMemo as $Y,useState as t}from"react";function i(J={}){let{handlers:Y,projectId:Q}=XY(),{crm:G}=Y,[F,P]=t(null),[B,R]=t({}),[X,H]=t([]),[W,q]=t(!0),[Z,V]=t(null),[$,L]=t(0),_=J.pipelineId??"pipeline-1",K=J.pageIndex??$,z=J.pageSize??J.limit??50,[M]=J.sorting??[],T=M?.id==="deal"?"name":M?.id,x=M?M.desc?"desc":"asc":void 0,d=YY(async()=>{q(!0),V(null);try{let[j,k,JJ]=await Promise.all([G.listDeals({projectId:Q,pipelineId:_,stageId:J.stageId,status:J.status==="all"?void 0:J.status,search:J.search,limit:z,offset:K*z,sortBy:T==="name"||T==="value"||T==="status"||T==="expectedCloseDate"||T==="updatedAt"?T:void 0,sortDirection:x}),G.getDealsByStage({projectId:Q,pipelineId:_}),G.getPipelineStages({pipelineId:_})]);P(j),R(k),H(JJ)}catch(j){V(j instanceof Error?j:Error("Unknown error"))}finally{q(!1)}},[G,Q,_,J.stageId,J.status,J.search,K,z,T,x]);ZY(()=>{d()},[d]);let FJ=$Y(()=>{if(!F)return null;let j=F.deals.filter((S)=>S.status==="OPEN"),k=F.deals.filter((S)=>S.status==="WON"),JJ=F.deals.filter((S)=>S.status==="LOST");return{total:F.total,totalValue:F.totalValue,openCount:j.length,openValue:j.reduce((S,jJ)=>S+jJ.value,0),wonCount:k.length,wonValue:k.reduce((S,jJ)=>S+jJ.value,0),lostCount:JJ.length}},[F]);return{data:F,dealsByStage:B,stages:X,loading:W,error:Z,stats:FJ,page:K+1,pageIndex:K,pageSize:z,refetch:d,nextPage:J.pageIndex===void 0?()=>L((j)=>j+1):void 0,prevPage:J.pageIndex===void 0?()=>K>0&&L((j)=>j-1):void 0}}import{useTemplateRuntime as qY}from"@contractspec/lib.example-shared-ui";import{useCallback as bJ,useState as TJ}from"react";function rJ(J={}){let{handlers:Y,projectId:Q}=qY(),{crm:G}=Y,[F,P]=TJ({loading:!1,error:null,data:null}),[B,R]=TJ({loading:!1,error:null,data:null}),[X,H]=TJ({loading:!1,error:null,data:null}),[W,q]=TJ({loading:!1,error:null,data:null}),Z=bJ(async(_)=>{P({loading:!0,error:null,data:null});try{let K=await G.createDeal(_,{projectId:Q,ownerId:"user-1"});return P({loading:!1,error:null,data:K}),J.onSuccess?.(),K}catch(K){let z=K instanceof Error?K:Error("Failed to create deal");return P({loading:!1,error:z,data:null}),J.onError?.(z),null}},[G,Q,J]),V=bJ(async(_)=>{R({loading:!0,error:null,data:null});try{let K=await G.moveDeal(_);return R({loading:!1,error:null,data:K}),J.onSuccess?.(),K}catch(K){let z=K instanceof Error?K:Error("Failed to move deal");return R({loading:!1,error:z,data:null}),J.onError?.(z),null}},[G,J]),$=bJ(async(_)=>{H({loading:!0,error:null,data:null});try{let K=await G.winDeal(_);return H({loading:!1,error:null,data:K}),J.onSuccess?.(),K}catch(K){let z=K instanceof Error?K:Error("Failed to mark deal as won");return H({loading:!1,error:z,data:null}),J.onError?.(z),null}},[G,J]),L=bJ(async(_)=>{q({loading:!0,error:null,data:null});try{let K=await G.loseDeal(_);return q({loading:!1,error:null,data:K}),J.onSuccess?.(),K}catch(K){let z=K instanceof Error?K:Error("Failed to mark deal as lost");return q({loading:!1,error:z,data:null}),J.onError?.(z),null}},[G,J]);return{createDeal:Z,moveDeal:V,winDeal:$,loseDeal:L,createState:F,moveState:B,winState:X,loseState:W,isLoading:F.loading||B.loading||X.loading||W.loading}}import{Button as PX,Input as oJ}from"@contractspec/lib.design-system";import{useState as a}from"react";import{jsx as C,jsxs as p}from"react/jsx-runtime";var GY=["USD","EUR","GBP","CAD"],HY="pipeline-1";function dJ({isOpen:J,onClose:Y,onSubmit:Q,stages:G,isLoading:F=!1}){let[P,B]=a(""),[R,X]=a(""),[H,W]=a("USD"),[q,Z]=a(G[0]?.id??""),[V,$]=a(""),[L,_]=a(null),K=async(z)=>{if(z.preventDefault(),_(null),!P.trim()){_("Deal name is required");return}let M=parseFloat(R);if(isNaN(M)||M<=0){_("Value must be a positive number");return}if(!q){_("Please select a pipeline stage");return}try{await Q({name:P.trim(),value:M,currency:H,pipelineId:HY,stageId:q,expectedCloseDate:V?new Date(V):void 0}),B(""),X(""),W("USD"),Z(G[0]?.id??""),$(""),Y()}catch(T){_(T instanceof Error?T.message:"Failed to create deal")}};if(!J)return null;return p("div",{className:"fixed inset-0 z-50 flex items-center justify-center",children:[C("div",{className:"absolute inset-0 bg-background/80 backdrop-blur-sm",onClick:Y,role:"button",tabIndex:0,onKeyDown:(z)=>{if(z.key==="Enter"||z.key===" ")Y()},"aria-label":"Close modal"}),p("div",{className:"relative z-10 w-full max-w-md rounded-xl border border-border bg-card p-6 shadow-xl",children:[C("h2",{className:"mb-4 font-semibold text-xl",children:"Create New Deal"}),p("form",{onSubmit:K,className:"space-y-4",children:[p("div",{children:[C("label",{htmlFor:"deal-name",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Deal Name *"}),C(oJ,{id:"deal-name",value:P,onChange:(z)=>B(z.target.value),placeholder:"e.g., Enterprise License - Acme Corp",disabled:F})]}),p("div",{className:"flex gap-3",children:[p("div",{className:"flex-1",children:[C("label",{htmlFor:"deal-value",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Value *"}),C(oJ,{id:"deal-value",type:"number",min:"0",step:"0.01",value:R,onChange:(z)=>X(z.target.value),placeholder:"50000",disabled:F})]}),p("div",{className:"w-24",children:[C("label",{htmlFor:"deal-currency",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Currency"}),C("select",{id:"deal-currency",value:H,onChange:(z)=>W(z.target.value),disabled:F,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",children:GY.map((z)=>C("option",{value:z,children:z},z))})]})]}),p("div",{children:[C("label",{htmlFor:"deal-stage",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Pipeline Stage *"}),C("select",{id:"deal-stage",value:q,onChange:(z)=>Z(z.target.value),disabled:F,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",children:G.map((z)=>C("option",{value:z.id,children:z.name},z.id))})]}),p("div",{children:[C("label",{htmlFor:"deal-close-date",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Expected Close Date"}),C(oJ,{id:"deal-close-date",type:"date",value:V,onChange:(z)=>$(z.target.value),disabled:F})]}),L&&C("div",{className:"rounded-md bg-destructive/10 p-3 text-destructive text-sm",children:L}),p("div",{className:"flex justify-end gap-3 pt-2",children:[C(PX,{type:"button",variant:"ghost",onPress:Y,disabled:F,children:"Cancel"}),C(PX,{type:"submit",disabled:F,children:F?"Creating...":"Create Deal"})]})]})]})]})}import{Button as c}from"@contractspec/lib.design-system";import{useState as e}from"react";import{jsx as A,jsxs as h,Fragment as FY}from"react/jsx-runtime";function QY(J,Y){return new Intl.NumberFormat("en-US",{style:"currency",currency:Y,minimumFractionDigits:0,maximumFractionDigits:0}).format(J)}function lJ({isOpen:J,deal:Y,stages:Q,onClose:G,onWin:F,onLose:P,onMove:B,isLoading:R=!1}){let[X,H]=e("menu"),[W,q]=e(""),[Z,V]=e(""),[$,L]=e(""),[_,K]=e(""),[z,M]=e(null),T=()=>{H("menu"),q(""),V(""),L(""),K(""),M(null)},x=()=>{T(),G()},d=async()=>{if(!Y)return;M(null);try{await F({dealId:Y.id,wonSource:W.trim()||void 0,notes:$.trim()||void 0}),x()}catch(k){M(k instanceof Error?k.message:"Failed to mark deal as won")}},FJ=async()=>{if(!Y)return;if(M(null),!Z.trim()){M("Please provide a reason for losing the deal");return}try{await P({dealId:Y.id,lostReason:Z.trim(),notes:$.trim()||void 0}),x()}catch(k){M(k instanceof Error?k.message:"Failed to mark deal as lost")}},j=async()=>{if(!Y)return;if(M(null),!_){M("Please select a stage");return}if(_===Y.stageId){M("Deal is already in this stage");return}try{await B({dealId:Y.id,stageId:_}),x()}catch(k){M(k instanceof Error?k.message:"Failed to move deal")}};if(!J||!Y)return null;return h("div",{className:"fixed inset-0 z-50 flex items-center justify-center",children:[A("div",{className:"absolute inset-0 bg-background/80 backdrop-blur-sm",onClick:x,role:"button",tabIndex:0,onKeyDown:(k)=>{if(k.key==="Enter"||k.key===" ")x()},"aria-label":"Close modal"}),h("div",{className:"relative z-10 w-full max-w-md rounded-xl border border-border bg-card p-6 shadow-xl",children:[h("div",{className:"mb-4 border-border border-b pb-4",children:[A("h2",{className:"font-semibold text-xl",children:Y.name}),A("p",{className:"font-medium text-lg text-primary",children:QY(Y.value,Y.currency)}),A("span",{className:`mt-2 inline-flex rounded-full px-2 py-0.5 font-medium text-xs ${Y.status==="WON"?"bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400":Y.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"}`,children:Y.status})]}),X==="menu"&&h("div",{className:"space-y-3",children:[Y.status==="OPEN"&&h(FY,{children:[h(c,{className:"w-full justify-start",variant:"ghost",onPress:()=>H("win"),children:[A("span",{className:"mr-2",children:"\uD83C\uDFC6"})," Mark as Won"]}),h(c,{className:"w-full justify-start",variant:"ghost",onPress:()=>H("lose"),children:[A("span",{className:"mr-2",children:"❌"})," Mark as Lost"]}),h(c,{className:"w-full justify-start",variant:"ghost",onPress:()=>{K(Y.stageId),H("move")},children:[A("span",{className:"mr-2",children:"➡️"})," Move to Stage"]})]}),Y.status!=="OPEN"&&h("p",{className:"py-4 text-center text-muted-foreground",children:["This deal is already ",Y.status.toLowerCase(),". No actions available."]}),A("div",{className:"border-border border-t pt-3",children:A(c,{className:"w-full",variant:"outline",onPress:x,children:"Close"})})]}),X==="win"&&h("div",{className:"space-y-4",children:[h("div",{children:[A("label",{htmlFor:"won-source",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"How did you win this deal?"}),h("select",{id:"won-source",value:W,onChange:(k)=>q(k.target.value),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",children:[A("option",{value:"",children:"Select a source..."}),A("option",{value:"referral",children:"Referral"}),A("option",{value:"cold_outreach",children:"Cold Outreach"}),A("option",{value:"inbound",children:"Inbound Lead"}),A("option",{value:"upsell",children:"Upsell"}),A("option",{value:"other",children:"Other"})]})]}),h("div",{children:[A("label",{htmlFor:"win-notes",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Notes (optional)"}),A("textarea",{id:"win-notes",value:$,onChange:(k)=>L(k.target.value),placeholder:"Any additional notes about the win...",rows:3,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"})]}),z&&A("div",{className:"rounded-md bg-destructive/10 p-3 text-destructive text-sm",children:z}),h("div",{className:"flex justify-end gap-3 pt-2",children:[A(c,{variant:"ghost",onPress:()=>H("menu"),disabled:R,children:"Back"}),A(c,{onPress:d,disabled:R,children:R?"Processing...":"\uD83C\uDFC6 Confirm Win"})]})]}),X==="lose"&&h("div",{className:"space-y-4",children:[h("div",{children:[A("label",{htmlFor:"lost-reason",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Why was this deal lost? *"}),h("select",{id:"lost-reason",value:Z,onChange:(k)=>V(k.target.value),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",children:[A("option",{value:"",children:"Select a reason..."}),A("option",{value:"price",children:"Price too high"}),A("option",{value:"competitor",children:"Lost to competitor"}),A("option",{value:"no_budget",children:"No budget"}),A("option",{value:"no_decision",children:"No decision made"}),A("option",{value:"timing",children:"Bad timing"}),A("option",{value:"product_fit",children:"Product not a fit"}),A("option",{value:"other",children:"Other"})]})]}),h("div",{children:[A("label",{htmlFor:"lose-notes",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Notes (optional)"}),A("textarea",{id:"lose-notes",value:$,onChange:(k)=>L(k.target.value),placeholder:"Any additional details...",rows:3,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"})]}),z&&A("div",{className:"rounded-md bg-destructive/10 p-3 text-destructive text-sm",children:z}),h("div",{className:"flex justify-end gap-3 pt-2",children:[A(c,{variant:"ghost",onPress:()=>H("menu"),disabled:R,children:"Back"}),A(c,{variant:"destructive",onPress:FJ,disabled:R,children:R?"Processing...":"❌ Confirm Loss"})]})]}),X==="move"&&h("div",{className:"space-y-4",children:[h("div",{children:[A("label",{htmlFor:"move-stage",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Move to Stage"}),A("select",{id:"move-stage",value:_,onChange:(k)=>K(k.target.value),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",children:Q.map((k)=>h("option",{value:k.id,children:[k.name,k.id===Y.stageId?" (current)":""]},k.id))})]}),z&&A("div",{className:"rounded-md bg-destructive/10 p-3 text-destructive text-sm",children:z}),h("div",{className:"flex justify-end gap-3 pt-2",children:[A(c,{variant:"ghost",onPress:()=>H("menu"),disabled:R,children:"Back"}),A(c,{onPress:j,disabled:R,children:R?"Moving...":"➡️ Move Deal"})]})]})]})]})}import{Button as GJ,DataTable as zY,DataTableToolbar as WY,LoaderBlock as UY}from"@contractspec/lib.design-system";import{useContractTable as KY}from"@contractspec/lib.presentation-runtime-react";import{Badge as NY}from"@contractspec/lib.ui-kit-web/ui/badge";import{HStack as HJ,VStack as iJ}from"@contractspec/lib.ui-kit-web/ui/stack";import{Text as g}from"@contractspec/lib.ui-kit-web/ui/text";import*as QJ from"react";import{jsx as b,jsxs as r}from"react/jsx-runtime";function VY(J,Y="USD"){return new Intl.NumberFormat("en-US",{style:"currency",currency:Y,minimumFractionDigits:0,maximumFractionDigits:0}).format(J)}function _Y(J){switch(J){case"WON":return"default";case"LOST":return"destructive";case"STALE":return"outline";default:return"secondary"}}function RY({value:J,onChange:Y}){return r(HJ,{gap:"sm",className:"flex-wrap",children:[b(GJ,{variant:J==="all"?"secondary":"outline",size:"sm",onPress:()=>Y("all"),children:"All Deals"}),b(GJ,{variant:J==="OPEN"?"secondary":"outline",size:"sm",onPress:()=>Y("OPEN"),children:"Open Only"}),b(GJ,{variant:J==="WON"?"secondary":"outline",size:"sm",onPress:()=>Y("WON"),children:"Won Only"}),b(GJ,{variant:J==="LOST"?"secondary":"outline",size:"sm",onPress:()=>Y("LOST"),children:"Lost Only"})]})}function AY({deals:J,totalItems:Y,pageIndex:Q,pageSize:G,sorting:F,search:P,status:B,loading:R,onSortingChange:X,onPaginationChange:H,onSearchChange:W,onStatusChange:q,onDealClick:Z}){let V=KY({data:J,columns:[{id:"deal",header:"Deal",label:"Deal",accessor:($)=>$.name,cell:({item:$})=>r(iJ,{gap:"xs",children:[b(g,{className:"font-medium text-sm",children:$.name}),b(g,{className:"text-muted-foreground text-xs",children:$.companyId??"Unassigned company"})]}),size:240,minSize:180,canSort:!0,canPin:!0,canResize:!0},{id:"value",header:"Value",label:"Value",accessorKey:"value",cell:({item:$})=>VY($.value,$.currency),align:"right",size:140,canSort:!0,canResize:!0},{id:"status",header:"Status",label:"Status",accessorKey:"status",cell:({value:$})=>b(NY,{variant:_Y($),children:String($)}),size:130,canSort:!0,canHide:!0,canPin:!0,canResize:!0},{id:"expectedCloseDate",header:"Expected Close",label:"Expected Close",accessor:($)=>$.expectedCloseDate?.toISOString()??"",cell:({item:$})=>$.expectedCloseDate?.toLocaleDateString()??"Not scheduled",size:170,canSort:!0,canHide:!0,canResize:!0},{id:"updatedAt",header:"Updated",label:"Updated",accessor:($)=>$.updatedAt.toISOString(),cell:({item:$})=>$.updatedAt.toLocaleDateString(),size:140,canSort:!0,canHide:!0,canResize:!0},{id:"actions",header:"Actions",label:"Actions",accessor:($)=>$.id,cell:({item:$})=>b(GJ,{variant:"ghost",size:"sm",onPress:()=>Z?.($.id),children:"Actions"}),size:120,canSort:!1,canHide:!1,canPin:!1,canResize:!1}],executionMode:"server",selectionMode:"multiple",totalItems:Y,state:{sorting:F,pagination:{pageIndex:Q,pageSize:G}},onSortingChange:X,onPaginationChange:H,initialState:{columnVisibility:{updatedAt:!1},columnPinning:{left:["deal","status"],right:[]}},renderExpandedContent:($)=>r(iJ,{gap:"sm",className:"py-2",children:[r(HJ,{justify:"between",children:[b(g,{className:"font-medium text-sm",children:"Owner"}),b(g,{className:"text-muted-foreground text-sm",children:$.ownerId})]}),r(HJ,{justify:"between",children:[b(g,{className:"font-medium text-sm",children:"Contact"}),b(g,{className:"text-muted-foreground text-sm",children:$.contactId??"No linked contact"})]}),$.wonSource?r(HJ,{justify:"between",children:[b(g,{className:"font-medium text-sm",children:"Won Source"}),b(g,{className:"text-muted-foreground text-sm",children:$.wonSource})]}):null,$.lostReason?r(HJ,{justify:"between",children:[b(g,{className:"font-medium text-sm",children:"Lost Reason"}),b(g,{className:"text-muted-foreground text-sm",children:$.lostReason})]}):null,$.notes?r(iJ,{gap:"xs",children:[b(g,{className:"font-medium text-sm",children:"Notes"}),b(g,{className:"text-muted-foreground text-sm",children:$.notes})]}):null]}),getCanExpand:()=>!0});return b(zY,{controller:V,title:"All Deals",description:"Server-mode table using the shared ContractSpec controller.",loading:R,toolbar:b(WY,{controller:V,searchPlaceholder:"Search deals, companies, contacts, or notes",searchValue:P,onSearchChange:W,activeChips:B==="all"?[]:[{key:"status",label:`Status: ${B}`,onRemove:()=>q("all")}],onClearAll:()=>{W(""),q("all")},actionsStart:RY({value:B,onChange:q}),actionsEnd:r(g,{className:"text-muted-foreground text-sm",children:[Y," total deals"]})}),footer:`Page ${V.pageIndex+1} of ${V.pageCount}`,emptyState:b("div",{className:"rounded-md border border-dashed p-8 text-center text-muted-foreground text-sm",children:"No deals found"})})}function OX({onDealClick:J}){let[Y,Q]=QJ.useState([{id:"value",desc:!0}]),[G,F]=QJ.useState({pageIndex:0,pageSize:3}),[P,B]=QJ.useState(""),[R,X]=QJ.useState("all"),{data:H,loading:W}=i({pageIndex:G.pageIndex,pageSize:G.pageSize,search:P,status:R,sorting:Y});if(W&&!H)return b(UY,{label:"Loading deals..."});return b(AY,{deals:H?.deals??[],totalItems:H?.total??0,pageIndex:G.pageIndex,pageSize:G.pageSize,sorting:Y,search:P,status:R,loading:W,onSortingChange:(q)=>{Q(q),F((Z)=>({...Z,pageIndex:0}))},onPaginationChange:F,onSearchChange:(q)=>{B(q),F((Z)=>({...Z,pageIndex:0}))},onStatusChange:(q)=>{X(q),F((Z)=>({...Z,pageIndex:0}))},onDealClick:J})}import{Button as PY,ErrorState as OY,LoaderBlock as kY,StatCard as CJ,StatCardGroup as BY,Tabs as vY,TabsContent as nJ,TabsList as MY,TabsTrigger as sJ}from"@contractspec/lib.design-system";import{useCallback as kX,useState as tJ}from"react";import{jsx as w,jsxs as E}from"react/jsx-runtime";function EJ(J,Y="USD"){return new Intl.NumberFormat("en-US",{style:"currency",currency:Y,minimumFractionDigits:0,maximumFractionDigits:0}).format(J)}function d0(){let[J,Y]=tJ(!1),[Q,G]=tJ(null),[F,P]=tJ(!1),{data:B,dealsByStage:R,stages:X,loading:H,error:W,stats:q,refetch:Z}=i(),V=rJ({onSuccess:()=>{Z()}}),$=kX((_)=>{let K=R?Object.values(R).flat().find((z)=>z.id===_):null;if(K)G(K),P(!0)},[R]),L=kX(async(_,K)=>{await V.moveDeal({dealId:_,stageId:K})},[V]);if(H&&!B)return w(kY,{label:"Loading CRM..."});if(W)return w(OY,{title:"Failed to load CRM",description:W.message,onRetry:Z,retryLabel:"Retry"});return E("div",{className:"space-y-6",children:[E("div",{className:"flex items-center justify-between",children:[w("h2",{className:"font-bold text-2xl",children:"CRM Pipeline"}),E(PY,{onClick:()=>Y(!0),children:[w("span",{className:"mr-2",children:"+"})," Create Deal"]})]}),q&&E(BY,{children:[w(CJ,{label:"Total Pipeline",value:EJ(q.totalValue),hint:`${q.total} deals`}),w(CJ,{label:"Open Deals",value:EJ(q.openValue),hint:`${q.openCount} active`}),w(CJ,{label:"Won",value:EJ(q.wonValue),hint:`${q.wonCount} closed`}),w(CJ,{label:"Lost",value:q.lostCount,hint:"deals lost"})]}),E(vY,{defaultValue:"pipeline",className:"w-full",children:[E(MY,{children:[E(sJ,{value:"pipeline",children:[w("span",{className:"mr-2",children:"\uD83D\uDCCA"}),"Pipeline"]}),E(sJ,{value:"list",children:[w("span",{className:"mr-2",children:"\uD83D\uDCCB"}),"All Deals"]}),E(sJ,{value:"metrics",children:[w("span",{className:"mr-2",children:"\uD83D\uDCC8"}),"Metrics"]})]}),w(nJ,{value:"pipeline",className:"min-h-[400px]",children:w(hJ,{dealsByStage:R,stages:X,onDealClick:$,onDealMove:L})}),w(nJ,{value:"list",className:"min-h-[400px]",children:w(OX,{onDealClick:$})}),w(nJ,{value:"metrics",className:"min-h-[400px]",children:w(LY,{stats:q})})]}),w(dJ,{isOpen:J,onClose:()=>Y(!1),onSubmit:async(_)=>{await V.createDeal(_)},stages:X,isLoading:V.createState.loading}),w(lJ,{isOpen:F,deal:Q,stages:X,onClose:()=>{P(!1),G(null)},onWin:async(_)=>{await V.winDeal(_)},onLose:async(_)=>{await V.loseDeal(_)},onMove:async(_)=>{await V.moveDeal(_),Z()},isLoading:V.isLoading})]})}function LY({stats:J}){if(!J)return null;return w("div",{className:"space-y-6",children:E("div",{className:"rounded-xl border border-border bg-card p-6",children:[w("h3",{className:"mb-4 font-semibold text-lg",children:"Pipeline Overview"}),E("dl",{className:"grid gap-4 sm:grid-cols-3",children:[E("div",{children:[w("dt",{className:"text-muted-foreground text-sm",children:"Win Rate"}),E("dd",{className:"font-semibold text-2xl",children:[J.total>0?(J.wonCount/J.total*100).toFixed(0):0,"%"]})]}),E("div",{children:[w("dt",{className:"text-muted-foreground text-sm",children:"Avg Deal Size"}),w("dd",{className:"font-semibold text-2xl",children:EJ(J.total>0?J.totalValue/J.total:0)})]}),E("div",{children:[w("dt",{className:"text-muted-foreground text-sm",children:"Conversion"}),E("dd",{className:"font-semibold text-2xl",children:[J.wonCount," / ",J.total]})]})]})]})})}var IY={overlayId:"crm-pipeline.demo-user",version:"1.0.0",description:"Demo mode with sample data",appliesTo:{feature:"crm-pipeline",role:"demo"},modifications:[{type:"hideField",field:"importButton",reason:"Not available in demo"},{type:"hideField",field:"exportButton",reason:"Not available in demo"},{type:"addBadge",position:"header",label:"Demo Mode",variant:"warning"}]},wY={overlayId:"crm-pipeline.sales-rep",version:"1.0.0",description:"Sales rep focused view",appliesTo:{feature:"crm-pipeline",role:"sales-rep"},modifications:[{type:"hideField",field:"teamMetrics",reason:"Team metrics for managers only"},{type:"hideField",field:"pipelineSettings",reason:"Admin only"},{type:"renameLabel",field:"deals",newLabel:"My Deals"}]},X3=[IY,wY];function o(J,Y="USD"){return new Intl.NumberFormat("en-US",{style:"currency",currency:Y,minimumFractionDigits:0}).format(J)}var DY={target:"markdown",render:async(J,Y)=>{if(J.source.type!=="component"||J.source.componentKey!=="PipelineKanbanView")throw Error("crmPipelineMarkdownRenderer: not PipelineKanbanView");let Q="pipeline-1",[G,F]=await Promise.all([MJ({pipelineId:Q,limit:50}),LJ({pipelineId:Q})]),P=G.deals,B=F,R={};for(let H of B)R[H.id]=P.filter((W)=>W.stageId===H.id&&W.status==="OPEN");let X=["# CRM Pipeline","",`**Total Value**: ${o(G.totalValue)}`,`**Total Deals**: ${G.total}`,""];for(let H of B.sort((W,q)=>W.position-q.position)){let W=R[H.id]??[],q=W.reduce((Z,V)=>Z+V.value,0);if(X.push(`## ${H.name}`),X.push(`_${W.length} deals · ${o(q)}_`),X.push(""),W.length===0)X.push("_No deals_");else for(let Z of W)X.push(`- **${Z.name}** - ${o(Z.value,Z.currency)}`);X.push("")}return{mimeType:"text/markdown",body:X.join(`
|
|
71
71
|
`)}}},hY={target:"markdown",render:async(J,Y)=>{if(J.source.type!=="component"||J.source.componentKey!=="CrmDashboard")throw Error("crmDashboardMarkdownRenderer: not CrmDashboard");let Q="pipeline-1",[G,F]=await Promise.all([MJ({pipelineId:Q,limit:100}),LJ({pipelineId:Q})]),P=G.deals,B=F,R=P.filter(($)=>$.status==="OPEN"),X=P.filter(($)=>$.status==="WON"),H=P.filter(($)=>$.status==="LOST"),W=R.reduce(($,L)=>$+L.value,0),q=X.reduce(($,L)=>$+L.value,0),Z=["# CRM Dashboard","","> Sales pipeline overview and key metrics","","## Summary","","| Metric | Value |","|--------|-------|",`| Total Deals | ${G.total} |`,`| Pipeline Value | ${o(G.totalValue)} |`,`| Open Deals | ${R.length} (${o(W)}) |`,`| Won Deals | ${X.length} (${o(q)}) |`,`| Lost Deals | ${H.length} |`,"","## Pipeline Stages",""];Z.push("| Stage | Deals | Value |"),Z.push("|-------|-------|-------|");for(let $ of B.sort((L,_)=>L.position-_.position)){let L=R.filter((K)=>K.stageId===$.id),_=L.reduce((K,z)=>K+z.value,0);Z.push(`| ${$.name} | ${L.length} | ${o(_)} |`)}Z.push(""),Z.push("## Recent Deals"),Z.push("");let V=P.slice(0,10);if(V.length===0)Z.push("_No deals yet._");else{Z.push("| Deal | Value | Stage | Status |"),Z.push("|------|-------|-------|--------|");for(let $ of V){let L=B.find((_)=>_.id===$.stageId);Z.push(`| ${$.name} | ${o($.value,$.currency)} | ${L?.name??"-"} | ${$.status} |`)}}return{mimeType:"text/markdown",body:Z.join(`
|
|
72
|
-
`)}}};import{jsx as BX}from"react/jsx-runtime";function bY(){let{dealsByStage:J,stages:Y}=i();return BX(hJ,{dealsByStage:J,stages:Y})}var TY={target:"react",render:async(J,Y)=>{if(J.source.type!=="component")throw Error("Invalid source type");if(J.source.componentKey!=="CrmPipelineView")throw Error(`Unknown component: ${J.source.componentKey}`);return BX(bY,{})}};import{identityRbacSchemaContribution as CY}from"@contractspec/lib.identity-rbac";import{auditTrailSchemaContribution as EY}from"@contractspec/module.audit-trail";import{notificationsSchemaContribution as jY}from"@contractspec/
|
|
72
|
+
`)}}};import{jsx as BX}from"react/jsx-runtime";function bY(){let{dealsByStage:J,stages:Y}=i();return BX(hJ,{dealsByStage:J,stages:Y})}var TY={target:"react",render:async(J,Y)=>{if(J.source.type!=="component")throw Error("Invalid source type");if(J.source.componentKey!=="CrmPipelineView")throw Error(`Unknown component: ${J.source.componentKey}`);return BX(bY,{})}};import{identityRbacSchemaContribution as CY}from"@contractspec/lib.identity-rbac";import{auditTrailSchemaContribution as EY}from"@contractspec/module.audit-trail";import{notificationsSchemaContribution as jY}from"@contractspec/lib.notification";var w3={modules:[CY,EY,jY,KX],provider:"postgresql",outputPath:"./prisma/schema/generated.prisma"};export{rJ as useDealMutations,i as useDealList,w3 as schemaComposition,nX as mockWinDealHandler,iX as mockMoveDealHandler,sX as mockLoseDealHandler,MJ as mockListDealsHandler,LJ as mockGetPipelineStagesHandler,tX as mockGetDealsByStageHandler,lX as mockCreateDealHandler,rX as example,wY as crmSalesRepOverlay,KX as crmPipelineSchemaContribution,TY as crmPipelineReactRenderer,DY as crmPipelineMarkdownRenderer,X3 as crmOverlays,IY as crmDemoOverlay,hY as crmDashboardMarkdownRenderer,gZ as createCrmHandlers,NJ as WinDealInputModel,XX as WinDealContract,pJ as TaskTypeEnum,uJ as TaskStatusEnum,cJ as TaskPriorityEnum,WX as TaskEntity,DZ as TaskCompletedEvent,QX as StageEntity,aZ as PipelineMetricsPresentation,Y0 as PipelineKanbanPresentation,HX as PipelineEntity,UJ as MoveDealInputModel,JX as MoveDealContract,qJ as MOCK_STAGES,f as MOCK_DEALS,mZ as MOCK_CONTACTS,SZ as MOCK_COMPANIES,_J as LoseDealInputModel,YX as LoseDealContract,PJ as ListDealsOutputModel,AJ as ListDealsInputModel,ZX as ListDealsContract,uX as ExamplesCrmPipelineExample,VJ as DealWonPayloadModel,vZ as DealWonEvent,zJ as DealStatusFilterEnum,SJ as DealStatusEnum,KJ as DealMovedPayloadModel,BZ as DealMovedEvent,y as DealModel,RJ as DealLostPayloadModel,MZ as DealLostEvent,Z0 as DealListPresentation,FX as DealEntity,$0 as DealDetailPresentation,kZ as DealCreatedEvent,q0 as DealCardPresentation,lJ as DealActionsModal,fY as CrmPipelineFeature,hJ as CrmPipelineBoard,AX as CrmDealCard,tZ as CrmDashboardPresentation,d0 as CrmDashboard,dJ as CreateDealModal,WJ as CreateDealInputModel,eJ as CreateDealContract,gJ as ContactStatusEnum,GX as ContactEntity,RZ as ContactCreatedEvent,fJ as CompanySizeEnum,qX as CompanyEntity,UX as ActivityEntity};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contractspec/example.crm-pipeline",
|
|
3
|
-
"version": "3.7.
|
|
3
|
+
"version": "3.7.29",
|
|
4
4
|
"description": "CRM Pipeline - Contacts, Companies, Deals, Tasks",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -338,18 +338,19 @@
|
|
|
338
338
|
"typecheck": "tsc --noEmit"
|
|
339
339
|
},
|
|
340
340
|
"dependencies": {
|
|
341
|
-
"@contractspec/lib.contracts-spec": "6.
|
|
342
|
-
"@contractspec/lib.design-system": "4.
|
|
343
|
-
"@contractspec/lib.example-shared-ui": "7.0.
|
|
344
|
-
"@contractspec/lib.identity-rbac": "3.
|
|
345
|
-
"@contractspec/lib.
|
|
341
|
+
"@contractspec/lib.contracts-spec": "6.2.0",
|
|
342
|
+
"@contractspec/lib.design-system": "4.4.0",
|
|
343
|
+
"@contractspec/lib.example-shared-ui": "7.0.6",
|
|
344
|
+
"@contractspec/lib.identity-rbac": "3.8.0",
|
|
345
|
+
"@contractspec/lib.notification": "0.2.0",
|
|
346
|
+
"@contractspec/lib.presentation-runtime-core": "5.2.1",
|
|
347
|
+
"@contractspec/lib.presentation-runtime-react": "40.0.1",
|
|
348
|
+
"@contractspec/lib.runtime-sandbox": "3.0.6",
|
|
346
349
|
"@contractspec/lib.schema": "3.7.14",
|
|
347
|
-
"@contractspec/lib.ui-kit-web": "3.13.
|
|
348
|
-
"@contractspec/module.audit-trail": "3.7.
|
|
349
|
-
"@contractspec/module.notifications": "3.8.1",
|
|
350
|
+
"@contractspec/lib.ui-kit-web": "3.13.2",
|
|
351
|
+
"@contractspec/module.audit-trail": "3.7.27",
|
|
350
352
|
"react": "19.2.0",
|
|
351
|
-
"react-dom": "19.2.0"
|
|
352
|
-
"@contractspec/lib.presentation-runtime-core": "5.1.1"
|
|
353
|
+
"react-dom": "19.2.0"
|
|
353
354
|
},
|
|
354
355
|
"devDependencies": {
|
|
355
356
|
"@contractspec/tool.typescript": "3.7.13",
|
package/src/index.ts
CHANGED
|
@@ -14,7 +14,7 @@ import './docs';
|
|
|
14
14
|
// Schema composition configuration
|
|
15
15
|
import { identityRbacSchemaContribution } from '@contractspec/lib.identity-rbac';
|
|
16
16
|
import { auditTrailSchemaContribution } from '@contractspec/module.audit-trail';
|
|
17
|
-
import { notificationsSchemaContribution } from '@contractspec/
|
|
17
|
+
import { notificationsSchemaContribution } from '@contractspec/lib.notification';
|
|
18
18
|
import { crmPipelineSchemaContribution } from './entities';
|
|
19
19
|
|
|
20
20
|
/**
|