@contractspec/example.crm-pipeline 3.7.18 → 3.7.20

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.
@@ -1,4 +1,4 @@
1
- import{web as OJ}from"@contractspec/lib.runtime-sandbox";var{generateId:fJ}=OJ;function c(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 NJ={name:"name",value:"value",status:"status",expectedCloseDate:"expectedCloseDate",updatedAt:"updatedAt"};function GX(J){async function Y(X){let{projectId:H,pipelineId:Z,stageId:G,status:$,ownerId:R,search:V,limit:f=20,offset:U=0,sortBy:W="value",sortDirection:z="desc"}=X,O="WHERE projectId = ?",T=[H];if(Z)O+=" AND pipelineId = ?",T.push(Z);if(G)O+=" AND stageId = ?",T.push(G);if($&&$!=="all")O+=" AND status = ?",T.push($);if(R)O+=" AND ownerId = ?",T.push(R);if(V)O+=" AND name LIKE ?",T.push(`%${V}%`);let j=(await J.query(`SELECT COUNT(*) as count FROM crm_deal ${O}`,T)).rows[0]?.count??0,M=(await J.query(`SELECT COALESCE(SUM(value), 0) as total FROM crm_deal ${O}`,T)).rows[0]?.total??0,_=NJ[W]??NJ.value,r=z==="asc"?"ASC":"DESC";return{deals:(await J.query(`SELECT * FROM crm_deal ${O} ORDER BY ${_} ${r} LIMIT ? OFFSET ?`,[...T,f,U])).rows.map(c),total:j,totalValue:M}}async function Q(X,H){let Z=fJ("deal"),G=new Date().toISOString();await J.execute(`INSERT INTO crm_deal (id, projectId, pipelineId, stageId, name, value, currency, status, contactId, companyId, ownerId, expectedCloseDate, createdAt, updatedAt)
2
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,[Z,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,G,G]);let $=(await J.query("SELECT * FROM crm_deal WHERE id = ?",[Z])).rows;if(!$[0])throw Error("Failed to create deal");return c($[0])}async function q(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 $=(await J.query("SELECT * FROM crm_deal WHERE id = ?",[X.dealId])).rows;return c($[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 G=(await J.query("SELECT * FROM crm_deal WHERE id = ?",[X.dealId])).rows;return c(G[0])}async function K(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 G=(await J.query("SELECT * FROM crm_deal WHERE id = ?",[X.dealId])).rows;return c(G[0])}async function A(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,Z=(await J.query("SELECT * FROM crm_stage WHERE pipelineId = ? ORDER BY position",[X.pipelineId])).rows,G={};for(let $ of Z)G[$.id]=H.filter((R)=>R.stageId===$.id).map(c);return G}async function N(X){return(await J.query("SELECT * FROM crm_stage WHERE pipelineId = ? ORDER BY position",[X.pipelineId])).rows.map((Z)=>({id:Z.id,pipelineId:Z.pipelineId,name:Z.name,position:Z.position}))}return{listDeals:Y,createDeal:Q,moveDeal:q,winDeal:F,loseDeal:K,getDealsByStage:A,getPipelineStages:N}}var l=[{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"}],L=[{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")}],zX=[{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")}],FX=[{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 o(J){let{pipelineId:Y,stageId:Q,status:q,ownerId:F,search:K,limit:A=20,offset:N=0}=J,X=[...L];if(Y)X=X.filter(($)=>$.pipelineId===Y);if(Q)X=X.filter(($)=>$.stageId===Q);if(q&&q!=="all")X=X.filter(($)=>$.status===q);if(F)X=X.filter(($)=>$.ownerId===F);if(K){let $=K.toLowerCase();X=X.filter((R)=>R.name.toLowerCase().includes($))}X.sort(($,R)=>R.value-$.value);let H=X.length,Z=X.reduce(($,R)=>$+R.value,0);return{deals:X.slice(N,N+A),total:H,totalValue:Z}}async function kJ(J,Y){let Q=new Date,q={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 L.push(q),q}async function EJ(J){let Y=L.findIndex((K)=>K.id===J.dealId);if(Y===-1)throw Error("NOT_FOUND");let Q=L[Y];if(!Q)throw Error("NOT_FOUND");if(!l.find((K)=>K.id===J.stageId))throw Error("INVALID_STAGE");let F={...Q,stageId:J.stageId,updatedAt:new Date};return L[Y]=F,F}async function TJ(J){let Y=L.findIndex((F)=>F.id===J.dealId);if(Y===-1)throw Error("NOT_FOUND");let Q=L[Y];if(!Q)throw Error("NOT_FOUND");let q={...Q,status:"WON",updatedAt:new Date};return L[Y]=q,q}async function BJ(J){let Y=L.findIndex((F)=>F.id===J.dealId);if(Y===-1)throw Error("NOT_FOUND");let Q=L[Y];if(!Q)throw Error("NOT_FOUND");let q={...Q,status:"LOST",updatedAt:new Date};return L[Y]=q,q}async function vJ(J){let Y=L.filter((q)=>q.pipelineId===J.pipelineId&&q.status==="OPEN"),Q={};for(let q of l)Q[q.id]=Y.filter((F)=>F.stageId===q.id);return Q}async function a(J){return l.filter((Y)=>Y.pipelineId===J.pipelineId)}import{jsx as t,jsxs as PJ}from"react/jsx-runtime";function hJ(J,Y){return new Intl.NumberFormat("en-US",{style:"currency",currency:Y,minimumFractionDigits:0,maximumFractionDigits:0}).format(J)}function KJ({deal:J,onClick:Y}){let Q=J.expectedCloseDate?Math.ceil((J.expectedCloseDate.getTime()-Date.now())/86400000):null;return PJ("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:(q)=>{if(q.key==="Enter"||q.key===" ")Y?.()},children:[t("h4",{className:"font-medium leading-snug",children:J.name}),t("div",{className:"mt-2 font-semibold text-lg text-primary",children:hJ(J.value,J.currency)}),PJ("div",{className:"mt-3 flex items-center justify-between text-muted-foreground text-xs",children:[Q!==null&&t("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`}),t("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 MJ}from"react";import{jsx as C,jsxs as g}from"react/jsx-runtime";function yJ(J){if(J>=1e6)return`$${(J/1e6).toFixed(1)}M`;if(J>=1000)return`$${(J/1000).toFixed(0)}K`;return`$${J}`}function s({dealsByStage:J,stages:Y,onDealClick:Q,onDealMove:q}){let[F,K]=MJ(null),A=[...Y].sort((X,H)=>X.position-H.position),N=(X,H)=>{q?.(X,H),K(null)};return C("div",{className:"flex gap-4 overflow-x-auto pb-4",children:A.map((X)=>{let H=J[X.id]??[],Z=H.reduce((G,$)=>G+$.value,0);return g("div",{className:"flex w-72 flex-shrink-0 flex-col rounded-lg bg-muted/30",children:[g("div",{className:"flex items-center justify-between border-border border-b px-3 py-2",children:[g("div",{children:[C("h3",{className:"font-medium",children:X.name}),g("p",{className:"text-muted-foreground text-xs",children:[H.length," deals · ",yJ(Z)]})]}),C("span",{className:"flex h-6 w-6 items-center justify-center rounded-full bg-muted font-medium text-xs",children:H.length})]}),C("div",{className:"flex flex-1 flex-col gap-2 p-2",children:H.length===0?C("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((G)=>g("div",{className:"group relative",children:[C(KJ,{deal:G,onClick:()=>Q?.(G.id)}),G.status==="OPEN"&&q&&g("div",{className:"absolute top-1 right-1 opacity-0 transition-opacity group-hover:opacity-100",children:[C("button",{type:"button",onClick:($)=>{$.stopPropagation(),K(F===G.id?null:G.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===G.id&&g("div",{className:"absolute top-7 right-0 z-20 min-w-[140px] rounded-lg border border-border bg-card py-1 shadow-lg",children:[C("p",{className:"px-3 py-1 font-medium text-muted-foreground text-xs",children:"Move to:"}),A.filter(($)=>$.id!==G.stageId).map(($)=>C("button",{type:"button",onClick:(R)=>{R.stopPropagation(),N(G.id,$.id)},className:"w-full px-3 py-1.5 text-left text-sm hover:bg-muted",children:$.name},$.id))]})]})]},G.id))})]},X.id)})})}import{useTemplateRuntime as LJ}from"@contractspec/lib.example-shared-ui";import{useCallback as IJ,useEffect as bJ,useMemo as wJ,useState as p}from"react";function x(J={}){let{handlers:Y,projectId:Q}=LJ(),{crm:q}=Y,[F,K]=p(null),[A,N]=p({}),[X,H]=p([]),[Z,G]=p(!0),[$,R]=p(null),[V,f]=p(0),U=J.pipelineId??"pipeline-1",W=J.pageIndex??V,z=J.pageSize??J.limit??50,[O]=J.sorting??[],T=O?.id,I=O?O.desc?"desc":"asc":void 0,j=IJ(async()=>{G(!0),R(null);try{let[M,_,r]=await Promise.all([q.listDeals({projectId:Q,pipelineId:U,stageId:J.stageId,status:J.status==="all"?void 0:J.status,search:J.search,limit:z,offset:W*z,sortBy:T==="name"||T==="value"||T==="status"||T==="expectedCloseDate"||T==="updatedAt"?T:void 0,sortDirection:I}),q.getDealsByStage({projectId:Q,pipelineId:U}),q.getPipelineStages({pipelineId:U})]);K(M),N(_),H(r)}catch(M){R(M instanceof Error?M:Error("Unknown error"))}finally{G(!1)}},[q,Q,U,J.stageId,J.status,J.search,W,z,T,I]);bJ(()=>{j()},[j]);let n=wJ(()=>{if(!F)return null;let M=F.deals.filter((b)=>b.status==="OPEN"),_=F.deals.filter((b)=>b.status==="WON"),r=F.deals.filter((b)=>b.status==="LOST");return{total:F.total,totalValue:F.totalValue,openCount:M.length,openValue:M.reduce((b,ZJ)=>b+ZJ.value,0),wonCount:_.length,wonValue:_.reduce((b,ZJ)=>b+ZJ.value,0),lostCount:r.length}},[F]);return{data:F,dealsByStage:A,stages:X,loading:Z,error:$,stats:n,page:W+1,pageIndex:W,pageSize:z,refetch:j,nextPage:J.pageIndex===void 0?()=>f((M)=>M+1):void 0,prevPage:J.pageIndex===void 0?()=>W>0&&f((M)=>M-1):void 0}}import{useTemplateRuntime as DJ}from"@contractspec/lib.example-shared-ui";import{useCallback as e,useState as JJ}from"react";function $J(J={}){let{handlers:Y,projectId:Q}=DJ(),{crm:q}=Y,[F,K]=JJ({loading:!1,error:null,data:null}),[A,N]=JJ({loading:!1,error:null,data:null}),[X,H]=JJ({loading:!1,error:null,data:null}),[Z,G]=JJ({loading:!1,error:null,data:null}),$=e(async(U)=>{K({loading:!0,error:null,data:null});try{let W=await q.createDeal(U,{projectId:Q,ownerId:"user-1"});return K({loading:!1,error:null,data:W}),J.onSuccess?.(),W}catch(W){let z=W instanceof Error?W:Error("Failed to create deal");return K({loading:!1,error:z,data:null}),J.onError?.(z),null}},[q,Q,J]),R=e(async(U)=>{N({loading:!0,error:null,data:null});try{let W=await q.moveDeal(U);return N({loading:!1,error:null,data:W}),J.onSuccess?.(),W}catch(W){let z=W instanceof Error?W:Error("Failed to move deal");return N({loading:!1,error:z,data:null}),J.onError?.(z),null}},[q,J]),V=e(async(U)=>{H({loading:!0,error:null,data:null});try{let W=await q.winDeal(U);return H({loading:!1,error:null,data:W}),J.onSuccess?.(),W}catch(W){let z=W instanceof Error?W:Error("Failed to mark deal as won");return H({loading:!1,error:z,data:null}),J.onError?.(z),null}},[q,J]),f=e(async(U)=>{G({loading:!0,error:null,data:null});try{let W=await q.loseDeal(U);return G({loading:!1,error:null,data:W}),J.onSuccess?.(),W}catch(W){let z=W instanceof Error?W:Error("Failed to mark deal as lost");return G({loading:!1,error:z,data:null}),J.onError?.(z),null}},[q,J]);return{createDeal:$,moveDeal:R,winDeal:V,loseDeal:f,createState:F,moveState:A,winState:X,loseState:Z,isLoading:F.loading||A.loading||X.loading||Z.loading}}import{Button as RJ,Input as qJ}from"@contractspec/lib.design-system";import{useState as u}from"react";import{jsx as v,jsxs as w}from"react/jsx-runtime";var mJ=["USD","EUR","GBP","CAD"],CJ="pipeline-1";function HJ({isOpen:J,onClose:Y,onSubmit:Q,stages:q,isLoading:F=!1}){let[K,A]=u(""),[N,X]=u(""),[H,Z]=u("USD"),[G,$]=u(q[0]?.id??""),[R,V]=u(""),[f,U]=u(null),W=async(z)=>{if(z.preventDefault(),U(null),!K.trim()){U("Deal name is required");return}let O=parseFloat(N);if(isNaN(O)||O<=0){U("Value must be a positive number");return}if(!G){U("Please select a pipeline stage");return}try{await Q({name:K.trim(),value:O,currency:H,pipelineId:CJ,stageId:G,expectedCloseDate:R?new Date(R):void 0}),A(""),X(""),Z("USD"),$(q[0]?.id??""),V(""),Y()}catch(T){U(T instanceof Error?T.message:"Failed to create deal")}};if(!J)return null;return w("div",{className:"fixed inset-0 z-50 flex items-center justify-center",children:[v("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"}),w("div",{className:"relative z-10 w-full max-w-md rounded-xl border border-border bg-card p-6 shadow-xl",children:[v("h2",{className:"mb-4 font-semibold text-xl",children:"Create New Deal"}),w("form",{onSubmit:W,className:"space-y-4",children:[w("div",{children:[v("label",{htmlFor:"deal-name",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Deal Name *"}),v(qJ,{id:"deal-name",value:K,onChange:(z)=>A(z.target.value),placeholder:"e.g., Enterprise License - Acme Corp",disabled:F})]}),w("div",{className:"flex gap-3",children:[w("div",{className:"flex-1",children:[v("label",{htmlFor:"deal-value",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Value *"}),v(qJ,{id:"deal-value",type:"number",min:"0",step:"0.01",value:N,onChange:(z)=>X(z.target.value),placeholder:"50000",disabled:F})]}),w("div",{className:"w-24",children:[v("label",{htmlFor:"deal-currency",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Currency"}),v("select",{id:"deal-currency",value:H,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:mJ.map((z)=>v("option",{value:z,children:z},z))})]})]}),w("div",{children:[v("label",{htmlFor:"deal-stage",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Pipeline Stage *"}),v("select",{id:"deal-stage",value:G,onChange:(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:q.map((z)=>v("option",{value:z.id,children:z.name},z.id))})]}),w("div",{children:[v("label",{htmlFor:"deal-close-date",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Expected Close Date"}),v(qJ,{id:"deal-close-date",type:"date",value:R,onChange:(z)=>V(z.target.value),disabled:F})]}),f&&v("div",{className:"rounded-md bg-destructive/10 p-3 text-destructive text-sm",children:f}),w("div",{className:"flex justify-end gap-3 pt-2",children:[v(RJ,{type:"button",variant:"ghost",onPress:Y,disabled:F,children:"Cancel"}),v(RJ,{type:"submit",disabled:F,children:F?"Creating...":"Create Deal"})]})]})]})]})}import{Button as D}from"@contractspec/lib.design-system";import{useState as i}from"react";import{jsx as P,jsxs as E,Fragment as jJ}from"react/jsx-runtime";function SJ(J,Y){return new Intl.NumberFormat("en-US",{style:"currency",currency:Y,minimumFractionDigits:0,maximumFractionDigits:0}).format(J)}function GJ({isOpen:J,deal:Y,stages:Q,onClose:q,onWin:F,onLose:K,onMove:A,isLoading:N=!1}){let[X,H]=i("menu"),[Z,G]=i(""),[$,R]=i(""),[V,f]=i(""),[U,W]=i(""),[z,O]=i(null),T=()=>{H("menu"),G(""),R(""),f(""),W(""),O(null)},I=()=>{T(),q()},j=async()=>{if(!Y)return;O(null);try{await F({dealId:Y.id,wonSource:Z.trim()||void 0,notes:V.trim()||void 0}),I()}catch(_){O(_ instanceof Error?_.message:"Failed to mark deal as won")}},n=async()=>{if(!Y)return;if(O(null),!$.trim()){O("Please provide a reason for losing the deal");return}try{await K({dealId:Y.id,lostReason:$.trim(),notes:V.trim()||void 0}),I()}catch(_){O(_ instanceof Error?_.message:"Failed to mark deal as lost")}},M=async()=>{if(!Y)return;if(O(null),!U){O("Please select a stage");return}if(U===Y.stageId){O("Deal is already in this stage");return}try{await A({dealId:Y.id,stageId:U}),I()}catch(_){O(_ instanceof Error?_.message:"Failed to move deal")}};if(!J||!Y)return null;return E("div",{className:"fixed inset-0 z-50 flex items-center justify-center",children:[P("div",{className:"absolute inset-0 bg-background/80 backdrop-blur-sm",onClick:I,role:"button",tabIndex:0,onKeyDown:(_)=>{if(_.key==="Enter"||_.key===" ")I()},"aria-label":"Close modal"}),E("div",{className:"relative z-10 w-full max-w-md rounded-xl border border-border bg-card p-6 shadow-xl",children:[E("div",{className:"mb-4 border-border border-b pb-4",children:[P("h2",{className:"font-semibold text-xl",children:Y.name}),P("p",{className:"font-medium text-lg text-primary",children:SJ(Y.value,Y.currency)}),P("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"&&E("div",{className:"space-y-3",children:[Y.status==="OPEN"&&E(jJ,{children:[E(D,{className:"w-full justify-start",variant:"ghost",onPress:()=>H("win"),children:[P("span",{className:"mr-2",children:"\uD83C\uDFC6"})," Mark as Won"]}),E(D,{className:"w-full justify-start",variant:"ghost",onPress:()=>H("lose"),children:[P("span",{className:"mr-2",children:"❌"})," Mark as Lost"]}),E(D,{className:"w-full justify-start",variant:"ghost",onPress:()=>{W(Y.stageId),H("move")},children:[P("span",{className:"mr-2",children:"➡️"})," Move to Stage"]})]}),Y.status!=="OPEN"&&E("p",{className:"py-4 text-center text-muted-foreground",children:["This deal is already ",Y.status.toLowerCase(),". No actions available."]}),P("div",{className:"border-border border-t pt-3",children:P(D,{className:"w-full",variant:"outline",onPress:I,children:"Close"})})]}),X==="win"&&E("div",{className:"space-y-4",children:[E("div",{children:[P("label",{htmlFor:"won-source",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"How did you win this deal?"}),E("select",{id:"won-source",value:Z,onChange:(_)=>G(_.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:[P("option",{value:"",children:"Select a source..."}),P("option",{value:"referral",children:"Referral"}),P("option",{value:"cold_outreach",children:"Cold Outreach"}),P("option",{value:"inbound",children:"Inbound Lead"}),P("option",{value:"upsell",children:"Upsell"}),P("option",{value:"other",children:"Other"})]})]}),E("div",{children:[P("label",{htmlFor:"win-notes",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Notes (optional)"}),P("textarea",{id:"win-notes",value:V,onChange:(_)=>f(_.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&&P("div",{className:"rounded-md bg-destructive/10 p-3 text-destructive text-sm",children:z}),E("div",{className:"flex justify-end gap-3 pt-2",children:[P(D,{variant:"ghost",onPress:()=>H("menu"),disabled:N,children:"Back"}),P(D,{onPress:j,disabled:N,children:N?"Processing...":"\uD83C\uDFC6 Confirm Win"})]})]}),X==="lose"&&E("div",{className:"space-y-4",children:[E("div",{children:[P("label",{htmlFor:"lost-reason",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Why was this deal lost? *"}),E("select",{id:"lost-reason",value:$,onChange:(_)=>R(_.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:[P("option",{value:"",children:"Select a reason..."}),P("option",{value:"price",children:"Price too high"}),P("option",{value:"competitor",children:"Lost to competitor"}),P("option",{value:"no_budget",children:"No budget"}),P("option",{value:"no_decision",children:"No decision made"}),P("option",{value:"timing",children:"Bad timing"}),P("option",{value:"product_fit",children:"Product not a fit"}),P("option",{value:"other",children:"Other"})]})]}),E("div",{children:[P("label",{htmlFor:"lose-notes",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Notes (optional)"}),P("textarea",{id:"lose-notes",value:V,onChange:(_)=>f(_.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&&P("div",{className:"rounded-md bg-destructive/10 p-3 text-destructive text-sm",children:z}),E("div",{className:"flex justify-end gap-3 pt-2",children:[P(D,{variant:"ghost",onPress:()=>H("menu"),disabled:N,children:"Back"}),P(D,{variant:"destructive",onPress:n,disabled:N,children:N?"Processing...":"❌ Confirm Loss"})]})]}),X==="move"&&E("div",{className:"space-y-4",children:[E("div",{children:[P("label",{htmlFor:"move-stage",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Move to Stage"}),P("select",{id:"move-stage",value:U,onChange:(_)=>W(_.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((_)=>E("option",{value:_.id,children:[_.name,_.id===Y.stageId?" (current)":""]},_.id))})]}),z&&P("div",{className:"rounded-md bg-destructive/10 p-3 text-destructive text-sm",children:z}),E("div",{className:"flex justify-end gap-3 pt-2",children:[P(D,{variant:"ghost",onPress:()=>H("menu"),disabled:N,children:"Back"}),P(D,{onPress:M,disabled:N,children:N?"Moving...":"➡️ Move Deal"})]})]})]})]})}import{Button as gJ,DataTable as xJ,LoaderBlock as cJ}from"@contractspec/lib.design-system";import{useContractTable as pJ}from"@contractspec/lib.presentation-runtime-react";import{Badge as uJ}from"@contractspec/lib.ui-kit-web/ui/badge";import{HStack as d,VStack as QJ}from"@contractspec/lib.ui-kit-web/ui/stack";import{Text as y}from"@contractspec/lib.ui-kit-web/ui/text";import*as zJ from"react";import{jsx as B,jsxs as m}from"react/jsx-runtime";function iJ(J,Y="USD"){return new Intl.NumberFormat("en-US",{style:"currency",currency:Y,minimumFractionDigits:0,maximumFractionDigits:0}).format(J)}function rJ(J){switch(J){case"WON":return"default";case"LOST":return"destructive";case"STALE":return"outline";default:return"secondary"}}function lJ({deals:J,totalItems:Y,pageIndex:Q,pageSize:q,sorting:F,loading:K,onSortingChange:A,onPaginationChange:N,onDealClick:X}){let H=pJ({data:J,columns:[{id:"deal",header:"Deal",label:"Deal",accessor:(Z)=>Z.name,cell:({item:Z})=>m(QJ,{gap:"xs",children:[B(y,{className:"font-medium text-sm",children:Z.name}),B(y,{className:"text-muted-foreground text-xs",children:Z.companyId??"Unassigned company"})]}),size:240,minSize:180,canSort:!0,canPin:!0,canResize:!0},{id:"value",header:"Value",label:"Value",accessorKey:"value",cell:({item:Z})=>iJ(Z.value,Z.currency),align:"right",size:140,canSort:!0,canResize:!0},{id:"status",header:"Status",label:"Status",accessorKey:"status",cell:({value:Z})=>B(uJ,{variant:rJ(Z),children:String(Z)}),size:130,canSort:!0,canHide:!0,canPin:!0,canResize:!0},{id:"expectedCloseDate",header:"Expected Close",label:"Expected Close",accessor:(Z)=>Z.expectedCloseDate?.toISOString()??"",cell:({item:Z})=>Z.expectedCloseDate?.toLocaleDateString()??"Not scheduled",size:170,canSort:!0,canHide:!0,canResize:!0},{id:"updatedAt",header:"Updated",label:"Updated",accessor:(Z)=>Z.updatedAt.toISOString(),cell:({item:Z})=>Z.updatedAt.toLocaleDateString(),size:140,canSort:!0,canHide:!0,canResize:!0},{id:"actions",header:"Actions",label:"Actions",accessor:(Z)=>Z.id,cell:({item:Z})=>B(gJ,{variant:"ghost",size:"sm",onPress:()=>X?.(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:q}},onSortingChange:A,onPaginationChange:N,initialState:{columnVisibility:{updatedAt:!1},columnPinning:{left:["deal","status"],right:[]}},renderExpandedContent:(Z)=>m(QJ,{gap:"sm",className:"py-2",children:[m(d,{justify:"between",children:[B(y,{className:"font-medium text-sm",children:"Owner"}),B(y,{className:"text-muted-foreground text-sm",children:Z.ownerId})]}),m(d,{justify:"between",children:[B(y,{className:"font-medium text-sm",children:"Contact"}),B(y,{className:"text-muted-foreground text-sm",children:Z.contactId??"No linked contact"})]}),Z.wonSource?m(d,{justify:"between",children:[B(y,{className:"font-medium text-sm",children:"Won Source"}),B(y,{className:"text-muted-foreground text-sm",children:Z.wonSource})]}):null,Z.lostReason?m(d,{justify:"between",children:[B(y,{className:"font-medium text-sm",children:"Lost Reason"}),B(y,{className:"text-muted-foreground text-sm",children:Z.lostReason})]}):null,Z.notes?m(QJ,{gap:"xs",children:[B(y,{className:"font-medium text-sm",children:"Notes"}),B(y,{className:"text-muted-foreground text-sm",children:Z.notes})]}):null]}),getCanExpand:()=>!0});return B(xJ,{controller:H,title:"All Deals",description:"Server-mode table using the shared ContractSpec controller.",loading:K,toolbar:m(d,{gap:"sm",className:"flex-wrap",children:[m(y,{className:"text-muted-foreground text-sm",children:["Selected ",H.selectedRowIds.length]}),m(y,{className:"text-muted-foreground text-sm",children:[Y," total deals"]})]}),footer:`Page ${H.pageIndex+1} of ${H.pageCount}`,emptyState:B("div",{className:"rounded-md border border-dashed p-8 text-center text-muted-foreground text-sm",children:"No deals found"})})}function VJ({onDealClick:J}){let[Y,Q]=zJ.useState([{id:"value",desc:!0}]),[q,F]=zJ.useState({pageIndex:0,pageSize:3}),{data:K,loading:A}=x({pageIndex:q.pageIndex,pageSize:q.pageSize,sorting:Y});if(A&&!K)return B(cJ,{label:"Loading deals..."});return B(lJ,{deals:K?.deals??[],totalItems:K?.total??0,pageIndex:q.pageIndex,pageSize:q.pageSize,sorting:Y,loading:A,onSortingChange:(N)=>{Q(N),F((X)=>({...X,pageIndex:0}))},onPaginationChange:F,onDealClick:J})}import{Button as dJ,ErrorState as nJ,LoaderBlock as oJ,StatCard as XJ,StatCardGroup as aJ}from"@contractspec/lib.design-system";import{Tabs as tJ,TabsContent as FJ,TabsList as sJ,TabsTrigger as WJ}from"@contractspec/lib.ui-kit-web/ui/tabs";import{useCallback as _J,useState as UJ}from"react";import{jsx as k,jsxs as h}from"react/jsx-runtime";function YJ(J,Y="USD"){return new Intl.NumberFormat("en-US",{style:"currency",currency:Y,minimumFractionDigits:0,maximumFractionDigits:0}).format(J)}function XY(){let[J,Y]=UJ(!1),[Q,q]=UJ(null),[F,K]=UJ(!1),{data:A,dealsByStage:N,stages:X,loading:H,error:Z,stats:G,refetch:$}=x(),R=$J({onSuccess:()=>{$()}}),V=_J((U)=>{let W=N?Object.values(N).flat().find((z)=>z.id===U):null;if(W)q(W),K(!0)},[N]),f=_J(async(U,W)=>{await R.moveDeal({dealId:U,stageId:W})},[R]);if(H&&!A)return k(oJ,{label:"Loading CRM..."});if(Z)return k(nJ,{title:"Failed to load CRM",description:Z.message,onRetry:$,retryLabel:"Retry"});return h("div",{className:"space-y-6",children:[h("div",{className:"flex items-center justify-between",children:[k("h2",{className:"font-bold text-2xl",children:"CRM Pipeline"}),h(dJ,{onClick:()=>Y(!0),children:[k("span",{className:"mr-2",children:"+"})," Create Deal"]})]}),G&&h(aJ,{children:[k(XJ,{label:"Total Pipeline",value:YJ(G.totalValue),hint:`${G.total} deals`}),k(XJ,{label:"Open Deals",value:YJ(G.openValue),hint:`${G.openCount} active`}),k(XJ,{label:"Won",value:YJ(G.wonValue),hint:`${G.wonCount} closed`}),k(XJ,{label:"Lost",value:G.lostCount,hint:"deals lost"})]}),h(tJ,{defaultValue:"pipeline",className:"w-full",children:[h(sJ,{children:[h(WJ,{value:"pipeline",children:[k("span",{className:"mr-2",children:"\uD83D\uDCCA"}),"Pipeline"]}),h(WJ,{value:"list",children:[k("span",{className:"mr-2",children:"\uD83D\uDCCB"}),"All Deals"]}),h(WJ,{value:"metrics",children:[k("span",{className:"mr-2",children:"\uD83D\uDCC8"}),"Metrics"]})]}),k(FJ,{value:"pipeline",className:"min-h-[400px]",children:k(s,{dealsByStage:N,stages:X,onDealClick:V,onDealMove:f})}),k(FJ,{value:"list",className:"min-h-[400px]",children:k(VJ,{onDealClick:V})}),k(FJ,{value:"metrics",className:"min-h-[400px]",children:k(eJ,{stats:G})})]}),k(HJ,{isOpen:J,onClose:()=>Y(!1),onSubmit:async(U)=>{await R.createDeal(U)},stages:X,isLoading:R.createState.loading}),k(GJ,{isOpen:F,deal:Q,stages:X,onClose:()=>{K(!1),q(null)},onWin:async(U)=>{await R.winDeal(U)},onLose:async(U)=>{await R.loseDeal(U)},onMove:async(U)=>{await R.moveDeal(U),$()},isLoading:R.isLoading})]})}function eJ({stats:J}){if(!J)return null;return k("div",{className:"space-y-6",children:h("div",{className:"rounded-xl border border-border bg-card p-6",children:[k("h3",{className:"mb-4 font-semibold text-lg",children:"Pipeline Overview"}),h("dl",{className:"grid gap-4 sm:grid-cols-3",children:[h("div",{children:[k("dt",{className:"text-muted-foreground text-sm",children:"Win Rate"}),h("dd",{className:"font-semibold text-2xl",children:[J.total>0?(J.wonCount/J.total*100).toFixed(0):0,"%"]})]}),h("div",{children:[k("dt",{className:"text-muted-foreground text-sm",children:"Avg Deal Size"}),k("dd",{className:"font-semibold text-2xl",children:YJ(J.total>0?J.totalValue/J.total:0)})]}),h("div",{children:[k("dt",{className:"text-muted-foreground text-sm",children:"Conversion"}),h("dd",{className:"font-semibold text-2xl",children:[J.wonCount," / ",J.total]})]})]})]})})}var JX={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"}]},XX={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"}]},FY=[JX,XX];function S(J,Y="USD"){return new Intl.NumberFormat("en-US",{style:"currency",currency:Y,minimumFractionDigits:0}).format(J)}var YX={target:"markdown",render:async(J,Y)=>{if(J.source.type!=="component"||J.source.componentKey!=="PipelineKanbanView")throw Error("crmPipelineMarkdownRenderer: not PipelineKanbanView");let Q="pipeline-1",[q,F]=await Promise.all([o({pipelineId:Q,limit:50}),a({pipelineId:Q})]),K=q.deals,A=F,N={};for(let H of A)N[H.id]=K.filter((Z)=>Z.stageId===H.id&&Z.status==="OPEN");let X=["# CRM Pipeline","",`**Total Value**: ${S(q.totalValue)}`,`**Total Deals**: ${q.total}`,""];for(let H of A.sort((Z,G)=>Z.position-G.position)){let Z=N[H.id]??[],G=Z.reduce(($,R)=>$+R.value,0);if(X.push(`## ${H.name}`),X.push(`_${Z.length} deals · ${S(G)}_`),X.push(""),Z.length===0)X.push("_No deals_");else for(let $ of Z)X.push(`- **${$.name}** - ${S($.value,$.currency)}`);X.push("")}return{mimeType:"text/markdown",body:X.join(`
3
- `)}}},ZX={target:"markdown",render:async(J,Y)=>{if(J.source.type!=="component"||J.source.componentKey!=="CrmDashboard")throw Error("crmDashboardMarkdownRenderer: not CrmDashboard");let Q="pipeline-1",[q,F]=await Promise.all([o({pipelineId:Q,limit:100}),a({pipelineId:Q})]),K=q.deals,A=F,N=K.filter((V)=>V.status==="OPEN"),X=K.filter((V)=>V.status==="WON"),H=K.filter((V)=>V.status==="LOST"),Z=N.reduce((V,f)=>V+f.value,0),G=X.reduce((V,f)=>V+f.value,0),$=["# CRM Dashboard","","> Sales pipeline overview and key metrics","","## Summary","","| Metric | Value |","|--------|-------|",`| Total Deals | ${q.total} |`,`| Pipeline Value | ${S(q.totalValue)} |`,`| Open Deals | ${N.length} (${S(Z)}) |`,`| Won Deals | ${X.length} (${S(G)}) |`,`| Lost Deals | ${H.length} |`,"","## Pipeline Stages",""];$.push("| Stage | Deals | Value |"),$.push("|-------|-------|-------|");for(let V of A.sort((f,U)=>f.position-U.position)){let f=N.filter((W)=>W.stageId===V.id),U=f.reduce((W,z)=>W+z.value,0);$.push(`| ${V.name} | ${f.length} | ${S(U)} |`)}$.push(""),$.push("## Recent Deals"),$.push("");let R=K.slice(0,10);if(R.length===0)$.push("_No deals yet._");else{$.push("| Deal | Value | Stage | Status |"),$.push("|------|-------|-------|--------|");for(let V of R){let f=A.find((U)=>U.id===V.stageId);$.push(`| ${V.name} | ${S(V.value,V.currency)} | ${f?.name??"-"} | ${V.status} |`)}}return{mimeType:"text/markdown",body:$.join(`
4
- `)}}};import{jsx as AJ}from"react/jsx-runtime";function $X(){let{dealsByStage:J,stages:Y}=x();return AJ(s,{dealsByStage:J,stages:Y})}var qX={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 AJ($X,{})}};export{$J as useDealMutations,x as useDealList,XX as crmSalesRepOverlay,qX as crmPipelineReactRenderer,YX as crmPipelineMarkdownRenderer,FY as crmOverlays,JX as crmDemoOverlay,ZX as crmDashboardMarkdownRenderer,GJ as DealActionsModal,s as CrmPipelineBoard,KJ as CrmDealCard,XY as CrmDashboard,HJ as CreateDealModal};
1
+ import{web as fJ}from"@contractspec/lib.runtime-sandbox";var{generateId:kJ}=fJ;function c(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 PJ={name:"name",value:"value",status:"status",expectedCloseDate:"expectedCloseDate",updatedAt:"updatedAt"};function zX(J){async function Y(X){let{projectId:G,pipelineId:W,stageId:q,status:Z,ownerId:N,search:$,limit:f=20,offset:P=0,sortBy:U="value",sortDirection:F="desc"}=X,O="WHERE projectId = ?",B=[G];if(W)O+=" AND pipelineId = ?",B.push(W);if(q)O+=" AND stageId = ?",B.push(q);if(Z&&Z!=="all")O+=" AND status = ?",B.push(Z);if(N)O+=" AND ownerId = ?",B.push(N);if($)O+=" AND name LIKE ?",B.push(`%${$}%`);let j=(await J.query(`SELECT COUNT(*) as count FROM crm_deal ${O}`,B)).rows[0]?.count??0,y=(await J.query(`SELECT COALESCE(SUM(value), 0) as total FROM crm_deal ${O}`,B)).rows[0]?.total??0,_=PJ[U]??PJ.value,r=F==="asc"?"ASC":"DESC";return{deals:(await J.query(`SELECT * FROM crm_deal ${O} ORDER BY ${_} ${r} LIMIT ? OFFSET ?`,[...B,f,P])).rows.map(c),total:j,totalValue:y}}async function Q(X,G){let W=kJ("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)
2
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,[W,G.projectId,X.pipelineId,X.stageId,X.name,X.value,X.currency??"USD","OPEN",X.contactId??null,X.companyId??null,G.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 c(Z[0])}async function H(X){let G=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,G,X.dealId]);let Z=(await J.query("SELECT * FROM crm_deal WHERE id = ?",[X.dealId])).rows;return c(Z[0])}async function z(X){let G=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,G,X.dealId]);let q=(await J.query("SELECT * FROM crm_deal WHERE id = ?",[X.dealId])).rows;return c(q[0])}async function V(X){let G=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,G,X.dealId]);let q=(await J.query("SELECT * FROM crm_deal WHERE id = ?",[X.dealId])).rows;return c(q[0])}async function A(X){let G=(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]=G.filter((N)=>N.stageId===Z.id).map(c);return q}async function K(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:H,winDeal:z,loseDeal:V,getDealsByStage:A,getPipelineStages:K}}var d=[{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"}],L=[{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")}],WX=[{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")}],UX=[{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 t(J){let{pipelineId:Y,stageId:Q,status:H,ownerId:z,search:V,limit:A=20,offset:K=0}=J,X=[...L];if(Y)X=X.filter((Z)=>Z.pipelineId===Y);if(Q)X=X.filter((Z)=>Z.stageId===Q);if(H&&H!=="all")X=X.filter((Z)=>Z.status===H);if(z)X=X.filter((Z)=>Z.ownerId===z);if(V){let Z=V.toLowerCase();X=X.filter((N)=>N.name.toLowerCase().includes(Z))}X.sort((Z,N)=>N.value-Z.value);let G=X.length,W=X.reduce((Z,N)=>Z+N.value,0);return{deals:X.slice(K,K+A),total:G,totalValue:W}}async function EJ(J,Y){let Q=new Date,H={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 L.push(H),H}async function TJ(J){let Y=L.findIndex((V)=>V.id===J.dealId);if(Y===-1)throw Error("NOT_FOUND");let Q=L[Y];if(!Q)throw Error("NOT_FOUND");if(!d.find((V)=>V.id===J.stageId))throw Error("INVALID_STAGE");let z={...Q,stageId:J.stageId,updatedAt:new Date};return L[Y]=z,z}async function BJ(J){let Y=L.findIndex((z)=>z.id===J.dealId);if(Y===-1)throw Error("NOT_FOUND");let Q=L[Y];if(!Q)throw Error("NOT_FOUND");let H={...Q,status:"WON",updatedAt:new Date};return L[Y]=H,H}async function MJ(J){let Y=L.findIndex((z)=>z.id===J.dealId);if(Y===-1)throw Error("NOT_FOUND");let Q=L[Y];if(!Q)throw Error("NOT_FOUND");let H={...Q,status:"LOST",updatedAt:new Date};return L[Y]=H,H}async function vJ(J){let Y=L.filter((H)=>H.pipelineId===J.pipelineId&&H.status==="OPEN"),Q={};for(let H of d)Q[H.id]=Y.filter((z)=>z.stageId===H.id);return Q}async function s(J){return d.filter((Y)=>Y.pipelineId===J.pipelineId)}import{jsx as e,jsxs as KJ}from"react/jsx-runtime";function yJ(J,Y){return new Intl.NumberFormat("en-US",{style:"currency",currency:Y,minimumFractionDigits:0,maximumFractionDigits:0}).format(J)}function RJ({deal:J,onClick:Y}){let Q=J.expectedCloseDate?Math.ceil((J.expectedCloseDate.getTime()-Date.now())/86400000):null;return KJ("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:(H)=>{if(H.key==="Enter"||H.key===" ")Y?.()},children:[e("h4",{className:"font-medium leading-snug",children:J.name}),e("div",{className:"mt-2 font-semibold text-lg text-primary",children:yJ(J.value,J.currency)}),KJ("div",{className:"mt-3 flex items-center justify-between text-muted-foreground text-xs",children:[Q!==null&&e("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`}),e("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 LJ}from"react";import{jsx as m,jsxs as x}from"react/jsx-runtime";function hJ(J){if(J>=1e6)return`$${(J/1e6).toFixed(1)}M`;if(J>=1000)return`$${(J/1000).toFixed(0)}K`;return`$${J}`}function JJ({dealsByStage:J,stages:Y,onDealClick:Q,onDealMove:H}){let[z,V]=LJ(null),A=[...Y].sort((X,G)=>X.position-G.position),K=(X,G)=>{H?.(X,G),V(null)};return m("div",{className:"flex gap-4 overflow-x-auto pb-4",children:A.map((X)=>{let G=J[X.id]??[],W=G.reduce((q,Z)=>q+Z.value,0);return x("div",{className:"flex w-72 flex-shrink-0 flex-col rounded-lg bg-muted/30",children:[x("div",{className:"flex items-center justify-between border-border border-b px-3 py-2",children:[x("div",{children:[m("h3",{className:"font-medium",children:X.name}),x("p",{className:"text-muted-foreground text-xs",children:[G.length," deals · ",hJ(W)]})]}),m("span",{className:"flex h-6 w-6 items-center justify-center rounded-full bg-muted font-medium text-xs",children:G.length})]}),m("div",{className:"flex flex-1 flex-col gap-2 p-2",children:G.length===0?m("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"}):G.map((q)=>x("div",{className:"group relative",children:[m(RJ,{deal:q,onClick:()=>Q?.(q.id)}),q.status==="OPEN"&&H&&x("div",{className:"absolute top-1 right-1 opacity-0 transition-opacity group-hover:opacity-100",children:[m("button",{type:"button",onClick:(Z)=>{Z.stopPropagation(),V(z===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:"➡️"}),z===q.id&&x("div",{className:"absolute top-7 right-0 z-20 min-w-[140px] rounded-lg border border-border bg-card py-1 shadow-lg",children:[m("p",{className:"px-3 py-1 font-medium text-muted-foreground text-xs",children:"Move to:"}),A.filter((Z)=>Z.id!==q.stageId).map((Z)=>m("button",{type:"button",onClick:(N)=>{N.stopPropagation(),K(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 IJ}from"@contractspec/lib.example-shared-ui";import{useCallback as bJ,useEffect as wJ,useMemo as DJ,useState as p}from"react";function g(J={}){let{handlers:Y,projectId:Q}=IJ(),{crm:H}=Y,[z,V]=p(null),[A,K]=p({}),[X,G]=p([]),[W,q]=p(!0),[Z,N]=p(null),[$,f]=p(0),P=J.pipelineId??"pipeline-1",U=J.pageIndex??$,F=J.pageSize??J.limit??50,[O]=J.sorting??[],B=O?.id==="deal"?"name":O?.id,I=O?O.desc?"desc":"asc":void 0,j=bJ(async()=>{q(!0),N(null);try{let[y,_,r]=await Promise.all([H.listDeals({projectId:Q,pipelineId:P,stageId:J.stageId,status:J.status==="all"?void 0:J.status,search:J.search,limit:F,offset:U*F,sortBy:B==="name"||B==="value"||B==="status"||B==="expectedCloseDate"||B==="updatedAt"?B:void 0,sortDirection:I}),H.getDealsByStage({projectId:Q,pipelineId:P}),H.getPipelineStages({pipelineId:P})]);V(y),K(_),G(r)}catch(y){N(y instanceof Error?y:Error("Unknown error"))}finally{q(!1)}},[H,Q,P,J.stageId,J.status,J.search,U,F,B,I]);wJ(()=>{j()},[j]);let a=DJ(()=>{if(!z)return null;let y=z.deals.filter((b)=>b.status==="OPEN"),_=z.deals.filter((b)=>b.status==="WON"),r=z.deals.filter((b)=>b.status==="LOST");return{total:z.total,totalValue:z.totalValue,openCount:y.length,openValue:y.reduce((b,qJ)=>b+qJ.value,0),wonCount:_.length,wonValue:_.reduce((b,qJ)=>b+qJ.value,0),lostCount:r.length}},[z]);return{data:z,dealsByStage:A,stages:X,loading:W,error:Z,stats:a,page:U+1,pageIndex:U,pageSize:F,refetch:j,nextPage:J.pageIndex===void 0?()=>f((y)=>y+1):void 0,prevPage:J.pageIndex===void 0?()=>U>0&&f((y)=>y-1):void 0}}import{useTemplateRuntime as mJ}from"@contractspec/lib.example-shared-ui";import{useCallback as XJ,useState as YJ}from"react";function HJ(J={}){let{handlers:Y,projectId:Q}=mJ(),{crm:H}=Y,[z,V]=YJ({loading:!1,error:null,data:null}),[A,K]=YJ({loading:!1,error:null,data:null}),[X,G]=YJ({loading:!1,error:null,data:null}),[W,q]=YJ({loading:!1,error:null,data:null}),Z=XJ(async(P)=>{V({loading:!0,error:null,data:null});try{let U=await H.createDeal(P,{projectId:Q,ownerId:"user-1"});return V({loading:!1,error:null,data:U}),J.onSuccess?.(),U}catch(U){let F=U instanceof Error?U:Error("Failed to create deal");return V({loading:!1,error:F,data:null}),J.onError?.(F),null}},[H,Q,J]),N=XJ(async(P)=>{K({loading:!0,error:null,data:null});try{let U=await H.moveDeal(P);return K({loading:!1,error:null,data:U}),J.onSuccess?.(),U}catch(U){let F=U instanceof Error?U:Error("Failed to move deal");return K({loading:!1,error:F,data:null}),J.onError?.(F),null}},[H,J]),$=XJ(async(P)=>{G({loading:!0,error:null,data:null});try{let U=await H.winDeal(P);return G({loading:!1,error:null,data:U}),J.onSuccess?.(),U}catch(U){let F=U instanceof Error?U:Error("Failed to mark deal as won");return G({loading:!1,error:F,data:null}),J.onError?.(F),null}},[H,J]),f=XJ(async(P)=>{q({loading:!0,error:null,data:null});try{let U=await H.loseDeal(P);return q({loading:!1,error:null,data:U}),J.onSuccess?.(),U}catch(U){let F=U instanceof Error?U:Error("Failed to mark deal as lost");return q({loading:!1,error:F,data:null}),J.onError?.(F),null}},[H,J]);return{createDeal:Z,moveDeal:N,winDeal:$,loseDeal:f,createState:z,moveState:A,winState:X,loseState:W,isLoading:z.loading||A.loading||X.loading||W.loading}}import{Button as VJ,Input as GJ}from"@contractspec/lib.design-system";import{useState as u}from"react";import{jsx as M,jsxs as w}from"react/jsx-runtime";var CJ=["USD","EUR","GBP","CAD"],SJ="pipeline-1";function QJ({isOpen:J,onClose:Y,onSubmit:Q,stages:H,isLoading:z=!1}){let[V,A]=u(""),[K,X]=u(""),[G,W]=u("USD"),[q,Z]=u(H[0]?.id??""),[N,$]=u(""),[f,P]=u(null),U=async(F)=>{if(F.preventDefault(),P(null),!V.trim()){P("Deal name is required");return}let O=parseFloat(K);if(isNaN(O)||O<=0){P("Value must be a positive number");return}if(!q){P("Please select a pipeline stage");return}try{await Q({name:V.trim(),value:O,currency:G,pipelineId:SJ,stageId:q,expectedCloseDate:N?new Date(N):void 0}),A(""),X(""),W("USD"),Z(H[0]?.id??""),$(""),Y()}catch(B){P(B instanceof Error?B.message:"Failed to create deal")}};if(!J)return null;return w("div",{className:"fixed inset-0 z-50 flex items-center justify-center",children:[M("div",{className:"absolute inset-0 bg-background/80 backdrop-blur-sm",onClick:Y,role:"button",tabIndex:0,onKeyDown:(F)=>{if(F.key==="Enter"||F.key===" ")Y()},"aria-label":"Close modal"}),w("div",{className:"relative z-10 w-full max-w-md rounded-xl border border-border bg-card p-6 shadow-xl",children:[M("h2",{className:"mb-4 font-semibold text-xl",children:"Create New Deal"}),w("form",{onSubmit:U,className:"space-y-4",children:[w("div",{children:[M("label",{htmlFor:"deal-name",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Deal Name *"}),M(GJ,{id:"deal-name",value:V,onChange:(F)=>A(F.target.value),placeholder:"e.g., Enterprise License - Acme Corp",disabled:z})]}),w("div",{className:"flex gap-3",children:[w("div",{className:"flex-1",children:[M("label",{htmlFor:"deal-value",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Value *"}),M(GJ,{id:"deal-value",type:"number",min:"0",step:"0.01",value:K,onChange:(F)=>X(F.target.value),placeholder:"50000",disabled:z})]}),w("div",{className:"w-24",children:[M("label",{htmlFor:"deal-currency",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Currency"}),M("select",{id:"deal-currency",value:G,onChange:(F)=>W(F.target.value),disabled:z,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:CJ.map((F)=>M("option",{value:F,children:F},F))})]})]}),w("div",{children:[M("label",{htmlFor:"deal-stage",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Pipeline Stage *"}),M("select",{id:"deal-stage",value:q,onChange:(F)=>Z(F.target.value),disabled:z,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:H.map((F)=>M("option",{value:F.id,children:F.name},F.id))})]}),w("div",{children:[M("label",{htmlFor:"deal-close-date",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Expected Close Date"}),M(GJ,{id:"deal-close-date",type:"date",value:N,onChange:(F)=>$(F.target.value),disabled:z})]}),f&&M("div",{className:"rounded-md bg-destructive/10 p-3 text-destructive text-sm",children:f}),w("div",{className:"flex justify-end gap-3 pt-2",children:[M(VJ,{type:"button",variant:"ghost",onPress:Y,disabled:z,children:"Cancel"}),M(VJ,{type:"submit",disabled:z,children:z?"Creating...":"Create Deal"})]})]})]})]})}import{Button as D}from"@contractspec/lib.design-system";import{useState as i}from"react";import{jsx as R,jsxs as E,Fragment as xJ}from"react/jsx-runtime";function jJ(J,Y){return new Intl.NumberFormat("en-US",{style:"currency",currency:Y,minimumFractionDigits:0,maximumFractionDigits:0}).format(J)}function zJ({isOpen:J,deal:Y,stages:Q,onClose:H,onWin:z,onLose:V,onMove:A,isLoading:K=!1}){let[X,G]=i("menu"),[W,q]=i(""),[Z,N]=i(""),[$,f]=i(""),[P,U]=i(""),[F,O]=i(null),B=()=>{G("menu"),q(""),N(""),f(""),U(""),O(null)},I=()=>{B(),H()},j=async()=>{if(!Y)return;O(null);try{await z({dealId:Y.id,wonSource:W.trim()||void 0,notes:$.trim()||void 0}),I()}catch(_){O(_ instanceof Error?_.message:"Failed to mark deal as won")}},a=async()=>{if(!Y)return;if(O(null),!Z.trim()){O("Please provide a reason for losing the deal");return}try{await V({dealId:Y.id,lostReason:Z.trim(),notes:$.trim()||void 0}),I()}catch(_){O(_ instanceof Error?_.message:"Failed to mark deal as lost")}},y=async()=>{if(!Y)return;if(O(null),!P){O("Please select a stage");return}if(P===Y.stageId){O("Deal is already in this stage");return}try{await A({dealId:Y.id,stageId:P}),I()}catch(_){O(_ instanceof Error?_.message:"Failed to move deal")}};if(!J||!Y)return null;return E("div",{className:"fixed inset-0 z-50 flex items-center justify-center",children:[R("div",{className:"absolute inset-0 bg-background/80 backdrop-blur-sm",onClick:I,role:"button",tabIndex:0,onKeyDown:(_)=>{if(_.key==="Enter"||_.key===" ")I()},"aria-label":"Close modal"}),E("div",{className:"relative z-10 w-full max-w-md rounded-xl border border-border bg-card p-6 shadow-xl",children:[E("div",{className:"mb-4 border-border border-b pb-4",children:[R("h2",{className:"font-semibold text-xl",children:Y.name}),R("p",{className:"font-medium text-lg text-primary",children:jJ(Y.value,Y.currency)}),R("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"&&E("div",{className:"space-y-3",children:[Y.status==="OPEN"&&E(xJ,{children:[E(D,{className:"w-full justify-start",variant:"ghost",onPress:()=>G("win"),children:[R("span",{className:"mr-2",children:"\uD83C\uDFC6"})," Mark as Won"]}),E(D,{className:"w-full justify-start",variant:"ghost",onPress:()=>G("lose"),children:[R("span",{className:"mr-2",children:"❌"})," Mark as Lost"]}),E(D,{className:"w-full justify-start",variant:"ghost",onPress:()=>{U(Y.stageId),G("move")},children:[R("span",{className:"mr-2",children:"➡️"})," Move to Stage"]})]}),Y.status!=="OPEN"&&E("p",{className:"py-4 text-center text-muted-foreground",children:["This deal is already ",Y.status.toLowerCase(),". No actions available."]}),R("div",{className:"border-border border-t pt-3",children:R(D,{className:"w-full",variant:"outline",onPress:I,children:"Close"})})]}),X==="win"&&E("div",{className:"space-y-4",children:[E("div",{children:[R("label",{htmlFor:"won-source",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"How did you win this deal?"}),E("select",{id:"won-source",value:W,onChange:(_)=>q(_.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:[R("option",{value:"",children:"Select a source..."}),R("option",{value:"referral",children:"Referral"}),R("option",{value:"cold_outreach",children:"Cold Outreach"}),R("option",{value:"inbound",children:"Inbound Lead"}),R("option",{value:"upsell",children:"Upsell"}),R("option",{value:"other",children:"Other"})]})]}),E("div",{children:[R("label",{htmlFor:"win-notes",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Notes (optional)"}),R("textarea",{id:"win-notes",value:$,onChange:(_)=>f(_.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"})]}),F&&R("div",{className:"rounded-md bg-destructive/10 p-3 text-destructive text-sm",children:F}),E("div",{className:"flex justify-end gap-3 pt-2",children:[R(D,{variant:"ghost",onPress:()=>G("menu"),disabled:K,children:"Back"}),R(D,{onPress:j,disabled:K,children:K?"Processing...":"\uD83C\uDFC6 Confirm Win"})]})]}),X==="lose"&&E("div",{className:"space-y-4",children:[E("div",{children:[R("label",{htmlFor:"lost-reason",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Why was this deal lost? *"}),E("select",{id:"lost-reason",value:Z,onChange:(_)=>N(_.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:[R("option",{value:"",children:"Select a reason..."}),R("option",{value:"price",children:"Price too high"}),R("option",{value:"competitor",children:"Lost to competitor"}),R("option",{value:"no_budget",children:"No budget"}),R("option",{value:"no_decision",children:"No decision made"}),R("option",{value:"timing",children:"Bad timing"}),R("option",{value:"product_fit",children:"Product not a fit"}),R("option",{value:"other",children:"Other"})]})]}),E("div",{children:[R("label",{htmlFor:"lose-notes",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Notes (optional)"}),R("textarea",{id:"lose-notes",value:$,onChange:(_)=>f(_.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"})]}),F&&R("div",{className:"rounded-md bg-destructive/10 p-3 text-destructive text-sm",children:F}),E("div",{className:"flex justify-end gap-3 pt-2",children:[R(D,{variant:"ghost",onPress:()=>G("menu"),disabled:K,children:"Back"}),R(D,{variant:"destructive",onPress:a,disabled:K,children:K?"Processing...":"❌ Confirm Loss"})]})]}),X==="move"&&E("div",{className:"space-y-4",children:[E("div",{children:[R("label",{htmlFor:"move-stage",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Move to Stage"}),R("select",{id:"move-stage",value:P,onChange:(_)=>U(_.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((_)=>E("option",{value:_.id,children:[_.name,_.id===Y.stageId?" (current)":""]},_.id))})]}),F&&R("div",{className:"rounded-md bg-destructive/10 p-3 text-destructive text-sm",children:F}),E("div",{className:"flex justify-end gap-3 pt-2",children:[R(D,{variant:"ghost",onPress:()=>G("menu"),disabled:K,children:"Back"}),R(D,{onPress:y,disabled:K,children:K?"Moving...":"➡️ Move Deal"})]})]})]})]})}import{Button as l,DataTable as gJ,DataTableToolbar as cJ,LoaderBlock as pJ}from"@contractspec/lib.design-system";import{useContractTable as uJ}from"@contractspec/lib.presentation-runtime-react";import{Badge as iJ}from"@contractspec/lib.ui-kit-web/ui/badge";import{HStack as n,VStack as FJ}from"@contractspec/lib.ui-kit-web/ui/stack";import{Text as h}from"@contractspec/lib.ui-kit-web/ui/text";import*as o from"react";import{jsx as T,jsxs as C}from"react/jsx-runtime";function rJ(J,Y="USD"){return new Intl.NumberFormat("en-US",{style:"currency",currency:Y,minimumFractionDigits:0,maximumFractionDigits:0}).format(J)}function dJ(J){switch(J){case"WON":return"default";case"LOST":return"destructive";case"STALE":return"outline";default:return"secondary"}}function lJ({value:J,onChange:Y}){return C(n,{gap:"sm",className:"flex-wrap",children:[T(l,{variant:J==="all"?"secondary":"outline",size:"sm",onPress:()=>Y("all"),children:"All Deals"}),T(l,{variant:J==="OPEN"?"secondary":"outline",size:"sm",onPress:()=>Y("OPEN"),children:"Open Only"}),T(l,{variant:J==="WON"?"secondary":"outline",size:"sm",onPress:()=>Y("WON"),children:"Won Only"}),T(l,{variant:J==="LOST"?"secondary":"outline",size:"sm",onPress:()=>Y("LOST"),children:"Lost Only"})]})}function nJ({deals:J,totalItems:Y,pageIndex:Q,pageSize:H,sorting:z,search:V,status:A,loading:K,onSortingChange:X,onPaginationChange:G,onSearchChange:W,onStatusChange:q,onDealClick:Z}){let N=uJ({data:J,columns:[{id:"deal",header:"Deal",label:"Deal",accessor:($)=>$.name,cell:({item:$})=>C(FJ,{gap:"xs",children:[T(h,{className:"font-medium text-sm",children:$.name}),T(h,{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:$})=>rJ($.value,$.currency),align:"right",size:140,canSort:!0,canResize:!0},{id:"status",header:"Status",label:"Status",accessorKey:"status",cell:({value:$})=>T(iJ,{variant:dJ($),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:$})=>T(l,{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:z,pagination:{pageIndex:Q,pageSize:H}},onSortingChange:X,onPaginationChange:G,initialState:{columnVisibility:{updatedAt:!1},columnPinning:{left:["deal","status"],right:[]}},renderExpandedContent:($)=>C(FJ,{gap:"sm",className:"py-2",children:[C(n,{justify:"between",children:[T(h,{className:"font-medium text-sm",children:"Owner"}),T(h,{className:"text-muted-foreground text-sm",children:$.ownerId})]}),C(n,{justify:"between",children:[T(h,{className:"font-medium text-sm",children:"Contact"}),T(h,{className:"text-muted-foreground text-sm",children:$.contactId??"No linked contact"})]}),$.wonSource?C(n,{justify:"between",children:[T(h,{className:"font-medium text-sm",children:"Won Source"}),T(h,{className:"text-muted-foreground text-sm",children:$.wonSource})]}):null,$.lostReason?C(n,{justify:"between",children:[T(h,{className:"font-medium text-sm",children:"Lost Reason"}),T(h,{className:"text-muted-foreground text-sm",children:$.lostReason})]}):null,$.notes?C(FJ,{gap:"xs",children:[T(h,{className:"font-medium text-sm",children:"Notes"}),T(h,{className:"text-muted-foreground text-sm",children:$.notes})]}):null]}),getCanExpand:()=>!0});return T(gJ,{controller:N,title:"All Deals",description:"Server-mode table using the shared ContractSpec controller.",loading:K,toolbar:T(cJ,{controller:N,searchPlaceholder:"Search deals, companies, contacts, or notes",searchValue:V,onSearchChange:W,activeChips:A==="all"?[]:[{key:"status",label:`Status: ${A}`,onRemove:()=>q("all")}],onClearAll:()=>{W(""),q("all")},actionsStart:lJ({value:A,onChange:q}),actionsEnd:C(h,{className:"text-muted-foreground text-sm",children:[Y," total deals"]})}),footer:`Page ${N.pageIndex+1} of ${N.pageCount}`,emptyState:T("div",{className:"rounded-md border border-dashed p-8 text-center text-muted-foreground text-sm",children:"No deals found"})})}function _J({onDealClick:J}){let[Y,Q]=o.useState([{id:"value",desc:!0}]),[H,z]=o.useState({pageIndex:0,pageSize:3}),[V,A]=o.useState(""),[K,X]=o.useState("all"),{data:G,loading:W}=g({pageIndex:H.pageIndex,pageSize:H.pageSize,search:V,status:K,sorting:Y});if(W&&!G)return T(pJ,{label:"Loading deals..."});return T(nJ,{deals:G?.deals??[],totalItems:G?.total??0,pageIndex:H.pageIndex,pageSize:H.pageSize,sorting:Y,search:V,status:K,loading:W,onSortingChange:(q)=>{Q(q),z((Z)=>({...Z,pageIndex:0}))},onPaginationChange:z,onSearchChange:(q)=>{A(q),z((Z)=>({...Z,pageIndex:0}))},onStatusChange:(q)=>{X(q),z((Z)=>({...Z,pageIndex:0}))},onDealClick:J})}import{Button as oJ,ErrorState as aJ,LoaderBlock as tJ,StatCard as ZJ,StatCardGroup as sJ}from"@contractspec/lib.design-system";import{Tabs as eJ,TabsContent as WJ,TabsList as JX,TabsTrigger as UJ}from"@contractspec/lib.ui-kit-web/ui/tabs";import{useCallback as AJ,useState as NJ}from"react";import{jsx as k,jsxs as v}from"react/jsx-runtime";function $J(J,Y="USD"){return new Intl.NumberFormat("en-US",{style:"currency",currency:Y,minimumFractionDigits:0,maximumFractionDigits:0}).format(J)}function ZY(){let[J,Y]=NJ(!1),[Q,H]=NJ(null),[z,V]=NJ(!1),{data:A,dealsByStage:K,stages:X,loading:G,error:W,stats:q,refetch:Z}=g(),N=HJ({onSuccess:()=>{Z()}}),$=AJ((P)=>{let U=K?Object.values(K).flat().find((F)=>F.id===P):null;if(U)H(U),V(!0)},[K]),f=AJ(async(P,U)=>{await N.moveDeal({dealId:P,stageId:U})},[N]);if(G&&!A)return k(tJ,{label:"Loading CRM..."});if(W)return k(aJ,{title:"Failed to load CRM",description:W.message,onRetry:Z,retryLabel:"Retry"});return v("div",{className:"space-y-6",children:[v("div",{className:"flex items-center justify-between",children:[k("h2",{className:"font-bold text-2xl",children:"CRM Pipeline"}),v(oJ,{onClick:()=>Y(!0),children:[k("span",{className:"mr-2",children:"+"})," Create Deal"]})]}),q&&v(sJ,{children:[k(ZJ,{label:"Total Pipeline",value:$J(q.totalValue),hint:`${q.total} deals`}),k(ZJ,{label:"Open Deals",value:$J(q.openValue),hint:`${q.openCount} active`}),k(ZJ,{label:"Won",value:$J(q.wonValue),hint:`${q.wonCount} closed`}),k(ZJ,{label:"Lost",value:q.lostCount,hint:"deals lost"})]}),v(eJ,{defaultValue:"pipeline",className:"w-full",children:[v(JX,{children:[v(UJ,{value:"pipeline",children:[k("span",{className:"mr-2",children:"\uD83D\uDCCA"}),"Pipeline"]}),v(UJ,{value:"list",children:[k("span",{className:"mr-2",children:"\uD83D\uDCCB"}),"All Deals"]}),v(UJ,{value:"metrics",children:[k("span",{className:"mr-2",children:"\uD83D\uDCC8"}),"Metrics"]})]}),k(WJ,{value:"pipeline",className:"min-h-[400px]",children:k(JJ,{dealsByStage:K,stages:X,onDealClick:$,onDealMove:f})}),k(WJ,{value:"list",className:"min-h-[400px]",children:k(_J,{onDealClick:$})}),k(WJ,{value:"metrics",className:"min-h-[400px]",children:k(XX,{stats:q})})]}),k(QJ,{isOpen:J,onClose:()=>Y(!1),onSubmit:async(P)=>{await N.createDeal(P)},stages:X,isLoading:N.createState.loading}),k(zJ,{isOpen:z,deal:Q,stages:X,onClose:()=>{V(!1),H(null)},onWin:async(P)=>{await N.winDeal(P)},onLose:async(P)=>{await N.loseDeal(P)},onMove:async(P)=>{await N.moveDeal(P),Z()},isLoading:N.isLoading})]})}function XX({stats:J}){if(!J)return null;return k("div",{className:"space-y-6",children:v("div",{className:"rounded-xl border border-border bg-card p-6",children:[k("h3",{className:"mb-4 font-semibold text-lg",children:"Pipeline Overview"}),v("dl",{className:"grid gap-4 sm:grid-cols-3",children:[v("div",{children:[k("dt",{className:"text-muted-foreground text-sm",children:"Win Rate"}),v("dd",{className:"font-semibold text-2xl",children:[J.total>0?(J.wonCount/J.total*100).toFixed(0):0,"%"]})]}),v("div",{children:[k("dt",{className:"text-muted-foreground text-sm",children:"Avg Deal Size"}),k("dd",{className:"font-semibold text-2xl",children:$J(J.total>0?J.totalValue/J.total:0)})]}),v("div",{children:[k("dt",{className:"text-muted-foreground text-sm",children:"Conversion"}),v("dd",{className:"font-semibold text-2xl",children:[J.wonCount," / ",J.total]})]})]})]})})}var YX={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"}]},ZX={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"}]},UY=[YX,ZX];function S(J,Y="USD"){return new Intl.NumberFormat("en-US",{style:"currency",currency:Y,minimumFractionDigits:0}).format(J)}var $X={target:"markdown",render:async(J,Y)=>{if(J.source.type!=="component"||J.source.componentKey!=="PipelineKanbanView")throw Error("crmPipelineMarkdownRenderer: not PipelineKanbanView");let Q="pipeline-1",[H,z]=await Promise.all([t({pipelineId:Q,limit:50}),s({pipelineId:Q})]),V=H.deals,A=z,K={};for(let G of A)K[G.id]=V.filter((W)=>W.stageId===G.id&&W.status==="OPEN");let X=["# CRM Pipeline","",`**Total Value**: ${S(H.totalValue)}`,`**Total Deals**: ${H.total}`,""];for(let G of A.sort((W,q)=>W.position-q.position)){let W=K[G.id]??[],q=W.reduce((Z,N)=>Z+N.value,0);if(X.push(`## ${G.name}`),X.push(`_${W.length} deals · ${S(q)}_`),X.push(""),W.length===0)X.push("_No deals_");else for(let Z of W)X.push(`- **${Z.name}** - ${S(Z.value,Z.currency)}`);X.push("")}return{mimeType:"text/markdown",body:X.join(`
3
+ `)}}},qX={target:"markdown",render:async(J,Y)=>{if(J.source.type!=="component"||J.source.componentKey!=="CrmDashboard")throw Error("crmDashboardMarkdownRenderer: not CrmDashboard");let Q="pipeline-1",[H,z]=await Promise.all([t({pipelineId:Q,limit:100}),s({pipelineId:Q})]),V=H.deals,A=z,K=V.filter(($)=>$.status==="OPEN"),X=V.filter(($)=>$.status==="WON"),G=V.filter(($)=>$.status==="LOST"),W=K.reduce(($,f)=>$+f.value,0),q=X.reduce(($,f)=>$+f.value,0),Z=["# CRM Dashboard","","> Sales pipeline overview and key metrics","","## Summary","","| Metric | Value |","|--------|-------|",`| Total Deals | ${H.total} |`,`| Pipeline Value | ${S(H.totalValue)} |`,`| Open Deals | ${K.length} (${S(W)}) |`,`| Won Deals | ${X.length} (${S(q)}) |`,`| Lost Deals | ${G.length} |`,"","## Pipeline Stages",""];Z.push("| Stage | Deals | Value |"),Z.push("|-------|-------|-------|");for(let $ of A.sort((f,P)=>f.position-P.position)){let f=K.filter((U)=>U.stageId===$.id),P=f.reduce((U,F)=>U+F.value,0);Z.push(`| ${$.name} | ${f.length} | ${S(P)} |`)}Z.push(""),Z.push("## Recent Deals"),Z.push("");let N=V.slice(0,10);if(N.length===0)Z.push("_No deals yet._");else{Z.push("| Deal | Value | Stage | Status |"),Z.push("|------|-------|-------|--------|");for(let $ of N){let f=A.find((P)=>P.id===$.stageId);Z.push(`| ${$.name} | ${S($.value,$.currency)} | ${f?.name??"-"} | ${$.status} |`)}}return{mimeType:"text/markdown",body:Z.join(`
4
+ `)}}};import{jsx as OJ}from"react/jsx-runtime";function HX(){let{dealsByStage:J,stages:Y}=g();return OJ(JJ,{dealsByStage:J,stages:Y})}var GX={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 OJ(HX,{})}};export{HJ as useDealMutations,g as useDealList,ZX as crmSalesRepOverlay,GX as crmPipelineReactRenderer,$X as crmPipelineMarkdownRenderer,UY as crmOverlays,YX as crmDemoOverlay,qX as crmDashboardMarkdownRenderer,zJ as DealActionsModal,JJ as CrmPipelineBoard,RJ as CrmDealCard,ZY as CrmDashboard,QJ as CreateDealModal};
@@ -1,4 +1,4 @@
1
- import{web as i}from"@contractspec/lib.runtime-sandbox";var{generateId:o}=i;function M(H){return{id:H.id,projectId:H.projectId,name:H.name,value:H.value,currency:H.currency,pipelineId:H.pipelineId,stageId:H.stageId,status:H.status,contactId:H.contactId??void 0,companyId:H.companyId??void 0,ownerId:H.ownerId,expectedCloseDate:H.expectedCloseDate?new Date(H.expectedCloseDate):void 0,wonSource:H.wonSource??void 0,lostReason:H.lostReason??void 0,notes:H.notes??void 0,createdAt:new Date(H.createdAt),updatedAt:new Date(H.updatedAt)}}var w={name:"name",value:"value",status:"status",expectedCloseDate:"expectedCloseDate",updatedAt:"updatedAt"};function GH(H){async function Y(W){let{projectId:Z,pipelineId:q,stageId:$,status:F,ownerId:P,search:G,limit:V=20,offset:K=0,sortBy:_="value",sortDirection:B="desc"}=W,A="WHERE projectId = ?",Q=[Z];if(q)A+=" AND pipelineId = ?",Q.push(q);if($)A+=" AND stageId = ?",Q.push($);if(F&&F!=="all")A+=" AND status = ?",Q.push(F);if(P)A+=" AND ownerId = ?",Q.push(P);if(G)A+=" AND name LIKE ?",Q.push(`%${G}%`);let f=(await H.query(`SELECT COUNT(*) as count FROM crm_deal ${A}`,Q)).rows[0]?.count??0,T=(await H.query(`SELECT COALESCE(SUM(value), 0) as total FROM crm_deal ${A}`,Q)).rows[0]?.total??0,h=w[_]??w.value,y=B==="asc"?"ASC":"DESC";return{deals:(await H.query(`SELECT * FROM crm_deal ${A} ORDER BY ${h} ${y} LIMIT ? OFFSET ?`,[...Q,V,K])).rows.map(M),total:f,totalValue:T}}async function X(W,Z){let q=o("deal"),$=new Date().toISOString();await H.execute(`INSERT INTO crm_deal (id, projectId, pipelineId, stageId, name, value, currency, status, contactId, companyId, ownerId, expectedCloseDate, createdAt, updatedAt)
2
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,[q,Z.projectId,W.pipelineId,W.stageId,W.name,W.value,W.currency??"USD","OPEN",W.contactId??null,W.companyId??null,Z.ownerId,W.expectedCloseDate?.toISOString()??null,$,$]);let F=(await H.query("SELECT * FROM crm_deal WHERE id = ?",[q])).rows;if(!F[0])throw Error("Failed to create deal");return M(F[0])}async function J(W){let Z=new Date().toISOString();if(!(await H.query("SELECT * FROM crm_deal WHERE id = ?",[W.dealId])).rows[0])throw Error("NOT_FOUND");if(!(await H.query("SELECT * FROM crm_stage WHERE id = ?",[W.stageId])).rows[0])throw Error("INVALID_STAGE");await H.execute("UPDATE crm_deal SET stageId = ?, updatedAt = ? WHERE id = ?",[W.stageId,Z,W.dealId]);let F=(await H.query("SELECT * FROM crm_deal WHERE id = ?",[W.dealId])).rows;return M(F[0])}async function z(W){let Z=new Date().toISOString();if(!(await H.query("SELECT * FROM crm_deal WHERE id = ?",[W.dealId])).rows[0])throw Error("NOT_FOUND");await H.execute("UPDATE crm_deal SET status = 'WON', wonSource = ?, notes = ?, updatedAt = ? WHERE id = ?",[W.wonSource??null,W.notes??null,Z,W.dealId]);let $=(await H.query("SELECT * FROM crm_deal WHERE id = ?",[W.dealId])).rows;return M($[0])}async function N(W){let Z=new Date().toISOString();if(!(await H.query("SELECT * FROM crm_deal WHERE id = ?",[W.dealId])).rows[0])throw Error("NOT_FOUND");await H.execute("UPDATE crm_deal SET status = 'LOST', lostReason = ?, notes = ?, updatedAt = ? WHERE id = ?",[W.lostReason,W.notes??null,Z,W.dealId]);let $=(await H.query("SELECT * FROM crm_deal WHERE id = ?",[W.dealId])).rows;return M($[0])}async function U(W){let Z=(await H.query("SELECT * FROM crm_deal WHERE projectId = ? AND pipelineId = ? AND status = 'OPEN' ORDER BY value DESC",[W.projectId,W.pipelineId])).rows,q=(await H.query("SELECT * FROM crm_stage WHERE pipelineId = ? ORDER BY position",[W.pipelineId])).rows,$={};for(let F of q)$[F.id]=Z.filter((P)=>P.stageId===F.id).map(M);return $}async function R(W){return(await H.query("SELECT * FROM crm_stage WHERE pipelineId = ? ORDER BY position",[W.pipelineId])).rows.map((q)=>({id:q.id,pipelineId:q.pipelineId,name:q.name,position:q.position}))}return{listDeals:Y,createDeal:X,moveDeal:J,winDeal:z,loseDeal:N,getDealsByStage:U,getPipelineStages:R}}var I=[{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"}],k=[{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")}],PH=[{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")}],QH=[{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 x(H){let{pipelineId:Y,stageId:X,status:J,ownerId:z,search:N,limit:U=20,offset:R=0}=H,W=[...k];if(Y)W=W.filter((F)=>F.pipelineId===Y);if(X)W=W.filter((F)=>F.stageId===X);if(J&&J!=="all")W=W.filter((F)=>F.status===J);if(z)W=W.filter((F)=>F.ownerId===z);if(N){let F=N.toLowerCase();W=W.filter((P)=>P.name.toLowerCase().includes(F))}W.sort((F,P)=>P.value-F.value);let Z=W.length,q=W.reduce((F,P)=>F+P.value,0);return{deals:W.slice(R,R+U),total:Z,totalValue:q}}async function n(H,Y){let X=new Date,J={id:`deal-${Date.now()}`,name:H.name,value:H.value,currency:H.currency??"USD",pipelineId:H.pipelineId,stageId:H.stageId,status:"OPEN",contactId:H.contactId,companyId:H.companyId,ownerId:Y.ownerId,expectedCloseDate:H.expectedCloseDate,createdAt:X,updatedAt:X};return k.push(J),J}async function l(H){let Y=k.findIndex((N)=>N.id===H.dealId);if(Y===-1)throw Error("NOT_FOUND");let X=k[Y];if(!X)throw Error("NOT_FOUND");if(!I.find((N)=>N.id===H.stageId))throw Error("INVALID_STAGE");let z={...X,stageId:H.stageId,updatedAt:new Date};return k[Y]=z,z}async function d(H){let Y=k.findIndex((z)=>z.id===H.dealId);if(Y===-1)throw Error("NOT_FOUND");let X=k[Y];if(!X)throw Error("NOT_FOUND");let J={...X,status:"WON",updatedAt:new Date};return k[Y]=J,J}async function s(H){let Y=k.findIndex((z)=>z.id===H.dealId);if(Y===-1)throw Error("NOT_FOUND");let X=k[Y];if(!X)throw Error("NOT_FOUND");let J={...X,status:"LOST",updatedAt:new Date};return k[Y]=J,J}async function t(H){let Y=k.filter((J)=>J.pipelineId===H.pipelineId&&J.status==="OPEN"),X={};for(let J of I)X[J.id]=Y.filter((z)=>z.stageId===J.id);return X}async function m(H){return I.filter((Y)=>Y.pipelineId===H.pipelineId)}import{jsx as S,jsxs as c}from"react/jsx-runtime";function a(H,Y){return new Intl.NumberFormat("en-US",{style:"currency",currency:Y,minimumFractionDigits:0,maximumFractionDigits:0}).format(H)}function g({deal:H,onClick:Y}){let X=H.expectedCloseDate?Math.ceil((H.expectedCloseDate.getTime()-Date.now())/86400000):null;return c("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:(J)=>{if(J.key==="Enter"||J.key===" ")Y?.()},children:[S("h4",{className:"font-medium leading-snug",children:H.name}),S("div",{className:"mt-2 font-semibold text-lg text-primary",children:a(H.value,H.currency)}),c("div",{className:"mt-3 flex items-center justify-between text-muted-foreground text-xs",children:[X!==null&&S("span",{className:X<0?"text-red-500":X<=7?"text-yellow-600 dark:text-yellow-500":"",children:X<0?`${Math.abs(X)}d overdue`:X===0?"Due today":`${X}d left`}),S("span",{className:`rounded px-1.5 py-0.5 font-medium text-xs ${H.status==="WON"?"bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400":H.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:H.status})]})]})}import{useState as e}from"react";import{jsx as E,jsxs as v}from"react/jsx-runtime";function HH(H){if(H>=1e6)return`$${(H/1e6).toFixed(1)}M`;if(H>=1000)return`$${(H/1000).toFixed(0)}K`;return`$${H}`}function p({dealsByStage:H,stages:Y,onDealClick:X,onDealMove:J}){let[z,N]=e(null),U=[...Y].sort((W,Z)=>W.position-Z.position),R=(W,Z)=>{J?.(W,Z),N(null)};return E("div",{className:"flex gap-4 overflow-x-auto pb-4",children:U.map((W)=>{let Z=H[W.id]??[],q=Z.reduce(($,F)=>$+F.value,0);return v("div",{className:"flex w-72 flex-shrink-0 flex-col rounded-lg bg-muted/30",children:[v("div",{className:"flex items-center justify-between border-border border-b px-3 py-2",children:[v("div",{children:[E("h3",{className:"font-medium",children:W.name}),v("p",{className:"text-muted-foreground text-xs",children:[Z.length," deals · ",HH(q)]})]}),E("span",{className:"flex h-6 w-6 items-center justify-center rounded-full bg-muted font-medium text-xs",children:Z.length})]}),E("div",{className:"flex flex-1 flex-col gap-2 p-2",children:Z.length===0?E("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"}):Z.map(($)=>v("div",{className:"group relative",children:[E(g,{deal:$,onClick:()=>X?.($.id)}),$.status==="OPEN"&&J&&v("div",{className:"absolute top-1 right-1 opacity-0 transition-opacity group-hover:opacity-100",children:[E("button",{type:"button",onClick:(F)=>{F.stopPropagation(),N(z===$.id?null:$.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:"➡️"}),z===$.id&&v("div",{className:"absolute top-7 right-0 z-20 min-w-[140px] rounded-lg border border-border bg-card py-1 shadow-lg",children:[E("p",{className:"px-3 py-1 font-medium text-muted-foreground text-xs",children:"Move to:"}),U.filter((F)=>F.id!==$.stageId).map((F)=>E("button",{type:"button",onClick:(P)=>{P.stopPropagation(),R($.id,F.id)},className:"w-full px-3 py-1.5 text-left text-sm hover:bg-muted",children:F.name},F.id))]})]})]},$.id))})]},W.id)})})}import{useTemplateRuntime as WH}from"@contractspec/lib.example-shared-ui";import{useCallback as FH,useEffect as JH,useMemo as XH,useState as j}from"react";function u(H={}){let{handlers:Y,projectId:X}=WH(),{crm:J}=Y,[z,N]=j(null),[U,R]=j({}),[W,Z]=j([]),[q,$]=j(!0),[F,P]=j(null),[G,V]=j(0),K=H.pipelineId??"pipeline-1",_=H.pageIndex??G,B=H.pageSize??H.limit??50,[A]=H.sorting??[],Q=A?.id,b=A?A.desc?"desc":"asc":void 0,f=FH(async()=>{$(!0),P(null);try{let[T,h,y]=await Promise.all([J.listDeals({projectId:X,pipelineId:K,stageId:H.stageId,status:H.status==="all"?void 0:H.status,search:H.search,limit:B,offset:_*B,sortBy:Q==="name"||Q==="value"||Q==="status"||Q==="expectedCloseDate"||Q==="updatedAt"?Q:void 0,sortDirection:b}),J.getDealsByStage({projectId:X,pipelineId:K}),J.getPipelineStages({pipelineId:K})]);N(T),R(h),Z(y)}catch(T){P(T instanceof Error?T:Error("Unknown error"))}finally{$(!1)}},[J,X,K,H.stageId,H.status,H.search,_,B,Q,b]);JH(()=>{f()},[f]);let D=XH(()=>{if(!z)return null;let T=z.deals.filter((L)=>L.status==="OPEN"),h=z.deals.filter((L)=>L.status==="WON"),y=z.deals.filter((L)=>L.status==="LOST");return{total:z.total,totalValue:z.totalValue,openCount:T.length,openValue:T.reduce((L,C)=>L+C.value,0),wonCount:h.length,wonValue:h.reduce((L,C)=>L+C.value,0),lostCount:y.length}},[z]);return{data:z,dealsByStage:U,stages:W,loading:q,error:F,stats:D,page:_+1,pageIndex:_,pageSize:B,refetch:f,nextPage:H.pageIndex===void 0?()=>V((T)=>T+1):void 0,prevPage:H.pageIndex===void 0?()=>_>0&&V((T)=>T-1):void 0}}function O(H,Y="USD"){return new Intl.NumberFormat("en-US",{style:"currency",currency:Y,minimumFractionDigits:0}).format(H)}var YH={target:"markdown",render:async(H,Y)=>{if(H.source.type!=="component"||H.source.componentKey!=="PipelineKanbanView")throw Error("crmPipelineMarkdownRenderer: not PipelineKanbanView");let X="pipeline-1",[J,z]=await Promise.all([x({pipelineId:X,limit:50}),m({pipelineId:X})]),N=J.deals,U=z,R={};for(let Z of U)R[Z.id]=N.filter((q)=>q.stageId===Z.id&&q.status==="OPEN");let W=["# CRM Pipeline","",`**Total Value**: ${O(J.totalValue)}`,`**Total Deals**: ${J.total}`,""];for(let Z of U.sort((q,$)=>q.position-$.position)){let q=R[Z.id]??[],$=q.reduce((F,P)=>F+P.value,0);if(W.push(`## ${Z.name}`),W.push(`_${q.length} deals · ${O($)}_`),W.push(""),q.length===0)W.push("_No deals_");else for(let F of q)W.push(`- **${F.name}** - ${O(F.value,F.currency)}`);W.push("")}return{mimeType:"text/markdown",body:W.join(`
3
- `)}}},ZH={target:"markdown",render:async(H,Y)=>{if(H.source.type!=="component"||H.source.componentKey!=="CrmDashboard")throw Error("crmDashboardMarkdownRenderer: not CrmDashboard");let X="pipeline-1",[J,z]=await Promise.all([x({pipelineId:X,limit:100}),m({pipelineId:X})]),N=J.deals,U=z,R=N.filter((G)=>G.status==="OPEN"),W=N.filter((G)=>G.status==="WON"),Z=N.filter((G)=>G.status==="LOST"),q=R.reduce((G,V)=>G+V.value,0),$=W.reduce((G,V)=>G+V.value,0),F=["# CRM Dashboard","","> Sales pipeline overview and key metrics","","## Summary","","| Metric | Value |","|--------|-------|",`| Total Deals | ${J.total} |`,`| Pipeline Value | ${O(J.totalValue)} |`,`| Open Deals | ${R.length} (${O(q)}) |`,`| Won Deals | ${W.length} (${O($)}) |`,`| Lost Deals | ${Z.length} |`,"","## Pipeline Stages",""];F.push("| Stage | Deals | Value |"),F.push("|-------|-------|-------|");for(let G of U.sort((V,K)=>V.position-K.position)){let V=R.filter((_)=>_.stageId===G.id),K=V.reduce((_,B)=>_+B.value,0);F.push(`| ${G.name} | ${V.length} | ${O(K)} |`)}F.push(""),F.push("## Recent Deals"),F.push("");let P=N.slice(0,10);if(P.length===0)F.push("_No deals yet._");else{F.push("| Deal | Value | Stage | Status |"),F.push("|------|-------|-------|--------|");for(let G of P){let V=U.find((K)=>K.id===G.stageId);F.push(`| ${G.name} | ${O(G.value,G.currency)} | ${V?.name??"-"} | ${G.status} |`)}}return{mimeType:"text/markdown",body:F.join(`
1
+ import{web as i}from"@contractspec/lib.runtime-sandbox";var{generateId:o}=i;function M(H){return{id:H.id,projectId:H.projectId,name:H.name,value:H.value,currency:H.currency,pipelineId:H.pipelineId,stageId:H.stageId,status:H.status,contactId:H.contactId??void 0,companyId:H.companyId??void 0,ownerId:H.ownerId,expectedCloseDate:H.expectedCloseDate?new Date(H.expectedCloseDate):void 0,wonSource:H.wonSource??void 0,lostReason:H.lostReason??void 0,notes:H.notes??void 0,createdAt:new Date(H.createdAt),updatedAt:new Date(H.updatedAt)}}var w={name:"name",value:"value",status:"status",expectedCloseDate:"expectedCloseDate",updatedAt:"updatedAt"};function GH(H){async function Y(W){let{projectId:Z,pipelineId:q,stageId:$,status:F,ownerId:P,search:G,limit:V=20,offset:A=0,sortBy:_="value",sortDirection:B="desc"}=W,T="WHERE projectId = ?",Q=[Z];if(q)T+=" AND pipelineId = ?",Q.push(q);if($)T+=" AND stageId = ?",Q.push($);if(F&&F!=="all")T+=" AND status = ?",Q.push(F);if(P)T+=" AND ownerId = ?",Q.push(P);if(G)T+=" AND name LIKE ?",Q.push(`%${G}%`);let f=(await H.query(`SELECT COUNT(*) as count FROM crm_deal ${T}`,Q)).rows[0]?.count??0,k=(await H.query(`SELECT COALESCE(SUM(value), 0) as total FROM crm_deal ${T}`,Q)).rows[0]?.total??0,h=w[_]??w.value,y=B==="asc"?"ASC":"DESC";return{deals:(await H.query(`SELECT * FROM crm_deal ${T} ORDER BY ${h} ${y} LIMIT ? OFFSET ?`,[...Q,V,A])).rows.map(M),total:f,totalValue:k}}async function X(W,Z){let q=o("deal"),$=new Date().toISOString();await H.execute(`INSERT INTO crm_deal (id, projectId, pipelineId, stageId, name, value, currency, status, contactId, companyId, ownerId, expectedCloseDate, createdAt, updatedAt)
2
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,[q,Z.projectId,W.pipelineId,W.stageId,W.name,W.value,W.currency??"USD","OPEN",W.contactId??null,W.companyId??null,Z.ownerId,W.expectedCloseDate?.toISOString()??null,$,$]);let F=(await H.query("SELECT * FROM crm_deal WHERE id = ?",[q])).rows;if(!F[0])throw Error("Failed to create deal");return M(F[0])}async function J(W){let Z=new Date().toISOString();if(!(await H.query("SELECT * FROM crm_deal WHERE id = ?",[W.dealId])).rows[0])throw Error("NOT_FOUND");if(!(await H.query("SELECT * FROM crm_stage WHERE id = ?",[W.stageId])).rows[0])throw Error("INVALID_STAGE");await H.execute("UPDATE crm_deal SET stageId = ?, updatedAt = ? WHERE id = ?",[W.stageId,Z,W.dealId]);let F=(await H.query("SELECT * FROM crm_deal WHERE id = ?",[W.dealId])).rows;return M(F[0])}async function z(W){let Z=new Date().toISOString();if(!(await H.query("SELECT * FROM crm_deal WHERE id = ?",[W.dealId])).rows[0])throw Error("NOT_FOUND");await H.execute("UPDATE crm_deal SET status = 'WON', wonSource = ?, notes = ?, updatedAt = ? WHERE id = ?",[W.wonSource??null,W.notes??null,Z,W.dealId]);let $=(await H.query("SELECT * FROM crm_deal WHERE id = ?",[W.dealId])).rows;return M($[0])}async function N(W){let Z=new Date().toISOString();if(!(await H.query("SELECT * FROM crm_deal WHERE id = ?",[W.dealId])).rows[0])throw Error("NOT_FOUND");await H.execute("UPDATE crm_deal SET status = 'LOST', lostReason = ?, notes = ?, updatedAt = ? WHERE id = ?",[W.lostReason,W.notes??null,Z,W.dealId]);let $=(await H.query("SELECT * FROM crm_deal WHERE id = ?",[W.dealId])).rows;return M($[0])}async function U(W){let Z=(await H.query("SELECT * FROM crm_deal WHERE projectId = ? AND pipelineId = ? AND status = 'OPEN' ORDER BY value DESC",[W.projectId,W.pipelineId])).rows,q=(await H.query("SELECT * FROM crm_stage WHERE pipelineId = ? ORDER BY position",[W.pipelineId])).rows,$={};for(let F of q)$[F.id]=Z.filter((P)=>P.stageId===F.id).map(M);return $}async function R(W){return(await H.query("SELECT * FROM crm_stage WHERE pipelineId = ? ORDER BY position",[W.pipelineId])).rows.map((q)=>({id:q.id,pipelineId:q.pipelineId,name:q.name,position:q.position}))}return{listDeals:Y,createDeal:X,moveDeal:J,winDeal:z,loseDeal:N,getDealsByStage:U,getPipelineStages:R}}var I=[{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"}],K=[{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")}],PH=[{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")}],QH=[{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 x(H){let{pipelineId:Y,stageId:X,status:J,ownerId:z,search:N,limit:U=20,offset:R=0}=H,W=[...K];if(Y)W=W.filter((F)=>F.pipelineId===Y);if(X)W=W.filter((F)=>F.stageId===X);if(J&&J!=="all")W=W.filter((F)=>F.status===J);if(z)W=W.filter((F)=>F.ownerId===z);if(N){let F=N.toLowerCase();W=W.filter((P)=>P.name.toLowerCase().includes(F))}W.sort((F,P)=>P.value-F.value);let Z=W.length,q=W.reduce((F,P)=>F+P.value,0);return{deals:W.slice(R,R+U),total:Z,totalValue:q}}async function n(H,Y){let X=new Date,J={id:`deal-${Date.now()}`,name:H.name,value:H.value,currency:H.currency??"USD",pipelineId:H.pipelineId,stageId:H.stageId,status:"OPEN",contactId:H.contactId,companyId:H.companyId,ownerId:Y.ownerId,expectedCloseDate:H.expectedCloseDate,createdAt:X,updatedAt:X};return K.push(J),J}async function l(H){let Y=K.findIndex((N)=>N.id===H.dealId);if(Y===-1)throw Error("NOT_FOUND");let X=K[Y];if(!X)throw Error("NOT_FOUND");if(!I.find((N)=>N.id===H.stageId))throw Error("INVALID_STAGE");let z={...X,stageId:H.stageId,updatedAt:new Date};return K[Y]=z,z}async function d(H){let Y=K.findIndex((z)=>z.id===H.dealId);if(Y===-1)throw Error("NOT_FOUND");let X=K[Y];if(!X)throw Error("NOT_FOUND");let J={...X,status:"WON",updatedAt:new Date};return K[Y]=J,J}async function s(H){let Y=K.findIndex((z)=>z.id===H.dealId);if(Y===-1)throw Error("NOT_FOUND");let X=K[Y];if(!X)throw Error("NOT_FOUND");let J={...X,status:"LOST",updatedAt:new Date};return K[Y]=J,J}async function t(H){let Y=K.filter((J)=>J.pipelineId===H.pipelineId&&J.status==="OPEN"),X={};for(let J of I)X[J.id]=Y.filter((z)=>z.stageId===J.id);return X}async function m(H){return I.filter((Y)=>Y.pipelineId===H.pipelineId)}import{jsx as S,jsxs as c}from"react/jsx-runtime";function a(H,Y){return new Intl.NumberFormat("en-US",{style:"currency",currency:Y,minimumFractionDigits:0,maximumFractionDigits:0}).format(H)}function g({deal:H,onClick:Y}){let X=H.expectedCloseDate?Math.ceil((H.expectedCloseDate.getTime()-Date.now())/86400000):null;return c("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:(J)=>{if(J.key==="Enter"||J.key===" ")Y?.()},children:[S("h4",{className:"font-medium leading-snug",children:H.name}),S("div",{className:"mt-2 font-semibold text-lg text-primary",children:a(H.value,H.currency)}),c("div",{className:"mt-3 flex items-center justify-between text-muted-foreground text-xs",children:[X!==null&&S("span",{className:X<0?"text-red-500":X<=7?"text-yellow-600 dark:text-yellow-500":"",children:X<0?`${Math.abs(X)}d overdue`:X===0?"Due today":`${X}d left`}),S("span",{className:`rounded px-1.5 py-0.5 font-medium text-xs ${H.status==="WON"?"bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400":H.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:H.status})]})]})}import{useState as e}from"react";import{jsx as E,jsxs as v}from"react/jsx-runtime";function HH(H){if(H>=1e6)return`$${(H/1e6).toFixed(1)}M`;if(H>=1000)return`$${(H/1000).toFixed(0)}K`;return`$${H}`}function p({dealsByStage:H,stages:Y,onDealClick:X,onDealMove:J}){let[z,N]=e(null),U=[...Y].sort((W,Z)=>W.position-Z.position),R=(W,Z)=>{J?.(W,Z),N(null)};return E("div",{className:"flex gap-4 overflow-x-auto pb-4",children:U.map((W)=>{let Z=H[W.id]??[],q=Z.reduce(($,F)=>$+F.value,0);return v("div",{className:"flex w-72 flex-shrink-0 flex-col rounded-lg bg-muted/30",children:[v("div",{className:"flex items-center justify-between border-border border-b px-3 py-2",children:[v("div",{children:[E("h3",{className:"font-medium",children:W.name}),v("p",{className:"text-muted-foreground text-xs",children:[Z.length," deals · ",HH(q)]})]}),E("span",{className:"flex h-6 w-6 items-center justify-center rounded-full bg-muted font-medium text-xs",children:Z.length})]}),E("div",{className:"flex flex-1 flex-col gap-2 p-2",children:Z.length===0?E("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"}):Z.map(($)=>v("div",{className:"group relative",children:[E(g,{deal:$,onClick:()=>X?.($.id)}),$.status==="OPEN"&&J&&v("div",{className:"absolute top-1 right-1 opacity-0 transition-opacity group-hover:opacity-100",children:[E("button",{type:"button",onClick:(F)=>{F.stopPropagation(),N(z===$.id?null:$.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:"➡️"}),z===$.id&&v("div",{className:"absolute top-7 right-0 z-20 min-w-[140px] rounded-lg border border-border bg-card py-1 shadow-lg",children:[E("p",{className:"px-3 py-1 font-medium text-muted-foreground text-xs",children:"Move to:"}),U.filter((F)=>F.id!==$.stageId).map((F)=>E("button",{type:"button",onClick:(P)=>{P.stopPropagation(),R($.id,F.id)},className:"w-full px-3 py-1.5 text-left text-sm hover:bg-muted",children:F.name},F.id))]})]})]},$.id))})]},W.id)})})}import{useTemplateRuntime as WH}from"@contractspec/lib.example-shared-ui";import{useCallback as FH,useEffect as JH,useMemo as XH,useState as j}from"react";function u(H={}){let{handlers:Y,projectId:X}=WH(),{crm:J}=Y,[z,N]=j(null),[U,R]=j({}),[W,Z]=j([]),[q,$]=j(!0),[F,P]=j(null),[G,V]=j(0),A=H.pipelineId??"pipeline-1",_=H.pageIndex??G,B=H.pageSize??H.limit??50,[T]=H.sorting??[],Q=T?.id==="deal"?"name":T?.id,b=T?T.desc?"desc":"asc":void 0,f=FH(async()=>{$(!0),P(null);try{let[k,h,y]=await Promise.all([J.listDeals({projectId:X,pipelineId:A,stageId:H.stageId,status:H.status==="all"?void 0:H.status,search:H.search,limit:B,offset:_*B,sortBy:Q==="name"||Q==="value"||Q==="status"||Q==="expectedCloseDate"||Q==="updatedAt"?Q:void 0,sortDirection:b}),J.getDealsByStage({projectId:X,pipelineId:A}),J.getPipelineStages({pipelineId:A})]);N(k),R(h),Z(y)}catch(k){P(k instanceof Error?k:Error("Unknown error"))}finally{$(!1)}},[J,X,A,H.stageId,H.status,H.search,_,B,Q,b]);JH(()=>{f()},[f]);let D=XH(()=>{if(!z)return null;let k=z.deals.filter((L)=>L.status==="OPEN"),h=z.deals.filter((L)=>L.status==="WON"),y=z.deals.filter((L)=>L.status==="LOST");return{total:z.total,totalValue:z.totalValue,openCount:k.length,openValue:k.reduce((L,C)=>L+C.value,0),wonCount:h.length,wonValue:h.reduce((L,C)=>L+C.value,0),lostCount:y.length}},[z]);return{data:z,dealsByStage:U,stages:W,loading:q,error:F,stats:D,page:_+1,pageIndex:_,pageSize:B,refetch:f,nextPage:H.pageIndex===void 0?()=>V((k)=>k+1):void 0,prevPage:H.pageIndex===void 0?()=>_>0&&V((k)=>k-1):void 0}}function O(H,Y="USD"){return new Intl.NumberFormat("en-US",{style:"currency",currency:Y,minimumFractionDigits:0}).format(H)}var YH={target:"markdown",render:async(H,Y)=>{if(H.source.type!=="component"||H.source.componentKey!=="PipelineKanbanView")throw Error("crmPipelineMarkdownRenderer: not PipelineKanbanView");let X="pipeline-1",[J,z]=await Promise.all([x({pipelineId:X,limit:50}),m({pipelineId:X})]),N=J.deals,U=z,R={};for(let Z of U)R[Z.id]=N.filter((q)=>q.stageId===Z.id&&q.status==="OPEN");let W=["# CRM Pipeline","",`**Total Value**: ${O(J.totalValue)}`,`**Total Deals**: ${J.total}`,""];for(let Z of U.sort((q,$)=>q.position-$.position)){let q=R[Z.id]??[],$=q.reduce((F,P)=>F+P.value,0);if(W.push(`## ${Z.name}`),W.push(`_${q.length} deals · ${O($)}_`),W.push(""),q.length===0)W.push("_No deals_");else for(let F of q)W.push(`- **${F.name}** - ${O(F.value,F.currency)}`);W.push("")}return{mimeType:"text/markdown",body:W.join(`
3
+ `)}}},ZH={target:"markdown",render:async(H,Y)=>{if(H.source.type!=="component"||H.source.componentKey!=="CrmDashboard")throw Error("crmDashboardMarkdownRenderer: not CrmDashboard");let X="pipeline-1",[J,z]=await Promise.all([x({pipelineId:X,limit:100}),m({pipelineId:X})]),N=J.deals,U=z,R=N.filter((G)=>G.status==="OPEN"),W=N.filter((G)=>G.status==="WON"),Z=N.filter((G)=>G.status==="LOST"),q=R.reduce((G,V)=>G+V.value,0),$=W.reduce((G,V)=>G+V.value,0),F=["# CRM Dashboard","","> Sales pipeline overview and key metrics","","## Summary","","| Metric | Value |","|--------|-------|",`| Total Deals | ${J.total} |`,`| Pipeline Value | ${O(J.totalValue)} |`,`| Open Deals | ${R.length} (${O(q)}) |`,`| Won Deals | ${W.length} (${O($)}) |`,`| Lost Deals | ${Z.length} |`,"","## Pipeline Stages",""];F.push("| Stage | Deals | Value |"),F.push("|-------|-------|-------|");for(let G of U.sort((V,A)=>V.position-A.position)){let V=R.filter((_)=>_.stageId===G.id),A=V.reduce((_,B)=>_+B.value,0);F.push(`| ${G.name} | ${V.length} | ${O(A)} |`)}F.push(""),F.push("## Recent Deals"),F.push("");let P=N.slice(0,10);if(P.length===0)F.push("_No deals yet._");else{F.push("| Deal | Value | Stage | Status |"),F.push("|------|-------|-------|--------|");for(let G of P){let V=U.find((A)=>A.id===G.stageId);F.push(`| ${G.name} | ${O(G.value,G.currency)} | ${V?.name??"-"} | ${G.status} |`)}}return{mimeType:"text/markdown",body:F.join(`
4
4
  `)}}};import{jsx as r}from"react/jsx-runtime";function $H(){let{dealsByStage:H,stages:Y}=u();return r(p,{dealsByStage:H,stages:Y})}var qH={target:"react",render:async(H,Y)=>{if(H.source.type!=="component")throw Error("Invalid source type");if(H.source.componentKey!=="CrmPipelineView")throw Error(`Unknown component: ${H.source.componentKey}`);return r($H,{})}};export{qH as crmPipelineReactRenderer,YH as crmPipelineMarkdownRenderer,ZH as crmDashboardMarkdownRenderer};
@@ -1 +1 @@
1
- import{jsx as P,jsxs as I}from"react/jsx-runtime";function j(A,J){return new Intl.NumberFormat("en-US",{style:"currency",currency:J,minimumFractionDigits:0,maximumFractionDigits:0}).format(A)}function D({deal:A,onClick:J}){let G=A.expectedCloseDate?Math.ceil((A.expectedCloseDate.getTime()-Date.now())/86400000):null;return I("div",{onClick:J,className:"cursor-pointer rounded-lg border border-border bg-card p-3 shadow-sm transition-shadow hover:shadow-md",role:"button",tabIndex:0,onKeyDown:(_)=>{if(_.key==="Enter"||_.key===" ")J?.()},children:[P("h4",{className:"font-medium leading-snug",children:A.name}),P("div",{className:"mt-2 font-semibold text-lg text-primary",children:j(A.value,A.currency)}),I("div",{className:"mt-3 flex items-center justify-between text-muted-foreground text-xs",children:[G!==null&&P("span",{className:G<0?"text-red-500":G<=7?"text-yellow-600 dark:text-yellow-500":"",children:G<0?`${Math.abs(G)}d overdue`:G===0?"Due today":`${G}d left`}),P("span",{className:`rounded px-1.5 py-0.5 font-medium text-xs ${A.status==="WON"?"bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400":A.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:A.status})]})]})}import{useState as x}from"react";import{jsx as W,jsxs as V}from"react/jsx-runtime";function g(A){if(A>=1e6)return`$${(A/1e6).toFixed(1)}M`;if(A>=1000)return`$${(A/1000).toFixed(0)}K`;return`$${A}`}function S({dealsByStage:A,stages:J,onDealClick:G,onDealMove:_}){let[K,z]=x(null),E=[...J].sort(($,X)=>$.position-X.position),b=($,X)=>{_?.($,X),z(null)};return W("div",{className:"flex gap-4 overflow-x-auto pb-4",children:E.map(($)=>{let X=A[$.id]??[],w=X.reduce((F,Y)=>F+Y.value,0);return V("div",{className:"flex w-72 flex-shrink-0 flex-col rounded-lg bg-muted/30",children:[V("div",{className:"flex items-center justify-between border-border border-b px-3 py-2",children:[V("div",{children:[W("h3",{className:"font-medium",children:$.name}),V("p",{className:"text-muted-foreground text-xs",children:[X.length," deals · ",g(w)]})]}),W("span",{className:"flex h-6 w-6 items-center justify-center rounded-full bg-muted font-medium text-xs",children:X.length})]}),W("div",{className:"flex flex-1 flex-col gap-2 p-2",children:X.length===0?W("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"}):X.map((F)=>V("div",{className:"group relative",children:[W(D,{deal:F,onClick:()=>G?.(F.id)}),F.status==="OPEN"&&_&&V("div",{className:"absolute top-1 right-1 opacity-0 transition-opacity group-hover:opacity-100",children:[W("button",{type:"button",onClick:(Y)=>{Y.stopPropagation(),z(K===F.id?null:F.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:"➡️"}),K===F.id&&V("div",{className:"absolute top-7 right-0 z-20 min-w-[140px] rounded-lg border border-border bg-card py-1 shadow-lg",children:[W("p",{className:"px-3 py-1 font-medium text-muted-foreground text-xs",children:"Move to:"}),E.filter((Y)=>Y.id!==F.stageId).map((Y)=>W("button",{type:"button",onClick:(L)=>{L.stopPropagation(),b(F.id,Y.id)},className:"w-full px-3 py-1.5 text-left text-sm hover:bg-muted",children:Y.name},Y.id))]})]})]},F.id))})]},$.id)})})}import{useTemplateRuntime as c}from"@contractspec/lib.example-shared-ui";import{useCallback as u,useEffect as p,useMemo as r,useState as q}from"react";function v(A={}){let{handlers:J,projectId:G}=c(),{crm:_}=J,[K,z]=q(null),[E,b]=q({}),[$,X]=q([]),[w,F]=q(!0),[Y,L]=q(null),[C,B]=q(0),O=A.pipelineId??"pipeline-1",T=A.pageIndex??C,Q=A.pageSize??A.limit??50,[U]=A.sorting??[],N=U?.id,k=U?U.desc?"desc":"asc":void 0,f=u(async()=>{F(!0),L(null);try{let[Z,R,h]=await Promise.all([_.listDeals({projectId:G,pipelineId:O,stageId:A.stageId,status:A.status==="all"?void 0:A.status,search:A.search,limit:Q,offset:T*Q,sortBy:N==="name"||N==="value"||N==="status"||N==="expectedCloseDate"||N==="updatedAt"?N:void 0,sortDirection:k}),_.getDealsByStage({projectId:G,pipelineId:O}),_.getPipelineStages({pipelineId:O})]);z(Z),b(R),X(h)}catch(Z){L(Z instanceof Error?Z:Error("Unknown error"))}finally{F(!1)}},[_,G,O,A.stageId,A.status,A.search,T,Q,N,k]);p(()=>{f()},[f]);let m=r(()=>{if(!K)return null;let Z=K.deals.filter((H)=>H.status==="OPEN"),R=K.deals.filter((H)=>H.status==="WON"),h=K.deals.filter((H)=>H.status==="LOST");return{total:K.total,totalValue:K.totalValue,openCount:Z.length,openValue:Z.reduce((H,M)=>H+M.value,0),wonCount:R.length,wonValue:R.reduce((H,M)=>H+M.value,0),lostCount:h.length}},[K]);return{data:K,dealsByStage:E,stages:$,loading:w,error:Y,stats:m,page:T+1,pageIndex:T,pageSize:Q,refetch:f,nextPage:A.pageIndex===void 0?()=>B((Z)=>Z+1):void 0,prevPage:A.pageIndex===void 0?()=>T>0&&B((Z)=>Z-1):void 0}}import{jsx as y}from"react/jsx-runtime";function i(){let{dealsByStage:A,stages:J}=v();return y(S,{dealsByStage:A,stages:J})}var JA={target:"react",render:async(A,J)=>{if(A.source.type!=="component")throw Error("Invalid source type");if(A.source.componentKey!=="CrmPipelineView")throw Error(`Unknown component: ${A.source.componentKey}`);return y(i,{})}};export{JA as crmPipelineReactRenderer};
1
+ import{jsx as b,jsxs as I}from"react/jsx-runtime";function j(A,J){return new Intl.NumberFormat("en-US",{style:"currency",currency:J,minimumFractionDigits:0,maximumFractionDigits:0}).format(A)}function D({deal:A,onClick:J}){let G=A.expectedCloseDate?Math.ceil((A.expectedCloseDate.getTime()-Date.now())/86400000):null;return I("div",{onClick:J,className:"cursor-pointer rounded-lg border border-border bg-card p-3 shadow-sm transition-shadow hover:shadow-md",role:"button",tabIndex:0,onKeyDown:(_)=>{if(_.key==="Enter"||_.key===" ")J?.()},children:[b("h4",{className:"font-medium leading-snug",children:A.name}),b("div",{className:"mt-2 font-semibold text-lg text-primary",children:j(A.value,A.currency)}),I("div",{className:"mt-3 flex items-center justify-between text-muted-foreground text-xs",children:[G!==null&&b("span",{className:G<0?"text-red-500":G<=7?"text-yellow-600 dark:text-yellow-500":"",children:G<0?`${Math.abs(G)}d overdue`:G===0?"Due today":`${G}d left`}),b("span",{className:`rounded px-1.5 py-0.5 font-medium text-xs ${A.status==="WON"?"bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400":A.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:A.status})]})]})}import{useState as x}from"react";import{jsx as W,jsxs as V}from"react/jsx-runtime";function g(A){if(A>=1e6)return`$${(A/1e6).toFixed(1)}M`;if(A>=1000)return`$${(A/1000).toFixed(0)}K`;return`$${A}`}function S({dealsByStage:A,stages:J,onDealClick:G,onDealMove:_}){let[K,z]=x(null),E=[...J].sort(($,X)=>$.position-X.position),w=($,X)=>{_?.($,X),z(null)};return W("div",{className:"flex gap-4 overflow-x-auto pb-4",children:E.map(($)=>{let X=A[$.id]??[],U=X.reduce((F,Y)=>F+Y.value,0);return V("div",{className:"flex w-72 flex-shrink-0 flex-col rounded-lg bg-muted/30",children:[V("div",{className:"flex items-center justify-between border-border border-b px-3 py-2",children:[V("div",{children:[W("h3",{className:"font-medium",children:$.name}),V("p",{className:"text-muted-foreground text-xs",children:[X.length," deals · ",g(U)]})]}),W("span",{className:"flex h-6 w-6 items-center justify-center rounded-full bg-muted font-medium text-xs",children:X.length})]}),W("div",{className:"flex flex-1 flex-col gap-2 p-2",children:X.length===0?W("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"}):X.map((F)=>V("div",{className:"group relative",children:[W(D,{deal:F,onClick:()=>G?.(F.id)}),F.status==="OPEN"&&_&&V("div",{className:"absolute top-1 right-1 opacity-0 transition-opacity group-hover:opacity-100",children:[W("button",{type:"button",onClick:(Y)=>{Y.stopPropagation(),z(K===F.id?null:F.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:"➡️"}),K===F.id&&V("div",{className:"absolute top-7 right-0 z-20 min-w-[140px] rounded-lg border border-border bg-card py-1 shadow-lg",children:[W("p",{className:"px-3 py-1 font-medium text-muted-foreground text-xs",children:"Move to:"}),E.filter((Y)=>Y.id!==F.stageId).map((Y)=>W("button",{type:"button",onClick:(L)=>{L.stopPropagation(),w(F.id,Y.id)},className:"w-full px-3 py-1.5 text-left text-sm hover:bg-muted",children:Y.name},Y.id))]})]})]},F.id))})]},$.id)})})}import{useTemplateRuntime as c}from"@contractspec/lib.example-shared-ui";import{useCallback as u,useEffect as p,useMemo as r,useState as q}from"react";function v(A={}){let{handlers:J,projectId:G}=c(),{crm:_}=J,[K,z]=q(null),[E,w]=q({}),[$,X]=q([]),[U,F]=q(!0),[Y,L]=q(null),[C,B]=q(0),O=A.pipelineId??"pipeline-1",T=A.pageIndex??C,Q=A.pageSize??A.limit??50,[R]=A.sorting??[],N=R?.id==="deal"?"name":R?.id,k=R?R.desc?"desc":"asc":void 0,f=u(async()=>{F(!0),L(null);try{let[Z,P,h]=await Promise.all([_.listDeals({projectId:G,pipelineId:O,stageId:A.stageId,status:A.status==="all"?void 0:A.status,search:A.search,limit:Q,offset:T*Q,sortBy:N==="name"||N==="value"||N==="status"||N==="expectedCloseDate"||N==="updatedAt"?N:void 0,sortDirection:k}),_.getDealsByStage({projectId:G,pipelineId:O}),_.getPipelineStages({pipelineId:O})]);z(Z),w(P),X(h)}catch(Z){L(Z instanceof Error?Z:Error("Unknown error"))}finally{F(!1)}},[_,G,O,A.stageId,A.status,A.search,T,Q,N,k]);p(()=>{f()},[f]);let m=r(()=>{if(!K)return null;let Z=K.deals.filter((H)=>H.status==="OPEN"),P=K.deals.filter((H)=>H.status==="WON"),h=K.deals.filter((H)=>H.status==="LOST");return{total:K.total,totalValue:K.totalValue,openCount:Z.length,openValue:Z.reduce((H,M)=>H+M.value,0),wonCount:P.length,wonValue:P.reduce((H,M)=>H+M.value,0),lostCount:h.length}},[K]);return{data:K,dealsByStage:E,stages:$,loading:U,error:Y,stats:m,page:T+1,pageIndex:T,pageSize:Q,refetch:f,nextPage:A.pageIndex===void 0?()=>B((Z)=>Z+1):void 0,prevPage:A.pageIndex===void 0?()=>T>0&&B((Z)=>Z-1):void 0}}import{jsx as y}from"react/jsx-runtime";function i(){let{dealsByStage:A,stages:J}=v();return y(S,{dealsByStage:A,stages:J})}var JA={target:"react",render:async(A,J)=>{if(A.source.type!=="component")throw Error("Invalid source type");if(A.source.componentKey!=="CrmPipelineView")throw Error(`Unknown component: ${A.source.componentKey}`);return y(i,{})}};export{JA as crmPipelineReactRenderer};
@@ -1 +1 @@
1
- import{useTemplateRuntime as j}from"@contractspec/lib.example-shared-ui";import{useCallback as m,useEffect as u,useMemo as c,useState as $}from"react";function D(A={}){let{handlers:Q,projectId:W}=j(),{crm:K}=Q,[J,X]=$(null),[Z,M]=$({}),[w,_]=$([]),[q,C]=$(!0),[T,y]=$(null),[I,B]=$(0),V=A.pipelineId??"pipeline-1",E=A.pageIndex??I,H=A.pageSize??A.limit??50,[v]=A.sorting??[],Y=v?.id,k=v?v.desc?"desc":"asc":void 0,z=m(async()=>{C(!0),y(null);try{let[N,f,L]=await Promise.all([K.listDeals({projectId:W,pipelineId:V,stageId:A.stageId,status:A.status==="all"?void 0:A.status,search:A.search,limit:H,offset:E*H,sortBy:Y==="name"||Y==="value"||Y==="status"||Y==="expectedCloseDate"||Y==="updatedAt"?Y:void 0,sortDirection:k}),K.getDealsByStage({projectId:W,pipelineId:V}),K.getPipelineStages({pipelineId:V})]);X(N),M(f),_(L)}catch(N){y(N instanceof Error?N:Error("Unknown error"))}finally{C(!1)}},[K,W,V,A.stageId,A.status,A.search,E,H,Y,k]);u(()=>{z()},[z]);let S=c(()=>{if(!J)return null;let N=J.deals.filter((U)=>U.status==="OPEN"),f=J.deals.filter((U)=>U.status==="WON"),L=J.deals.filter((U)=>U.status==="LOST");return{total:J.total,totalValue:J.totalValue,openCount:N.length,openValue:N.reduce((U,b)=>U+b.value,0),wonCount:f.length,wonValue:f.reduce((U,b)=>U+b.value,0),lostCount:L.length}},[J]);return{data:J,dealsByStage:Z,stages:w,loading:q,error:T,stats:S,page:E+1,pageIndex:E,pageSize:H,refetch:z,nextPage:A.pageIndex===void 0?()=>B((N)=>N+1):void 0,prevPage:A.pageIndex===void 0?()=>E>0&&B((N)=>N-1):void 0}}import{Button as x,DataTable as g,LoaderBlock as p}from"@contractspec/lib.design-system";import{useContractTable as d}from"@contractspec/lib.presentation-runtime-react";import{Badge as r}from"@contractspec/lib.ui-kit-web/ui/badge";import{HStack as R,VStack as P}from"@contractspec/lib.ui-kit-web/ui/stack";import{Text as G}from"@contractspec/lib.ui-kit-web/ui/text";import*as h from"react";import{jsx as F,jsxs as O}from"react/jsx-runtime";function l(A,Q="USD"){return new Intl.NumberFormat("en-US",{style:"currency",currency:Q,minimumFractionDigits:0,maximumFractionDigits:0}).format(A)}function i(A){switch(A){case"WON":return"default";case"LOST":return"destructive";case"STALE":return"outline";default:return"secondary"}}function n({deals:A,totalItems:Q,pageIndex:W,pageSize:K,sorting:J,loading:X,onSortingChange:Z,onPaginationChange:M,onDealClick:w}){let _=d({data:A,columns:[{id:"deal",header:"Deal",label:"Deal",accessor:(q)=>q.name,cell:({item:q})=>O(P,{gap:"xs",children:[F(G,{className:"font-medium text-sm",children:q.name}),F(G,{className:"text-muted-foreground text-xs",children:q.companyId??"Unassigned company"})]}),size:240,minSize:180,canSort:!0,canPin:!0,canResize:!0},{id:"value",header:"Value",label:"Value",accessorKey:"value",cell:({item:q})=>l(q.value,q.currency),align:"right",size:140,canSort:!0,canResize:!0},{id:"status",header:"Status",label:"Status",accessorKey:"status",cell:({value:q})=>F(r,{variant:i(q),children:String(q)}),size:130,canSort:!0,canHide:!0,canPin:!0,canResize:!0},{id:"expectedCloseDate",header:"Expected Close",label:"Expected Close",accessor:(q)=>q.expectedCloseDate?.toISOString()??"",cell:({item:q})=>q.expectedCloseDate?.toLocaleDateString()??"Not scheduled",size:170,canSort:!0,canHide:!0,canResize:!0},{id:"updatedAt",header:"Updated",label:"Updated",accessor:(q)=>q.updatedAt.toISOString(),cell:({item:q})=>q.updatedAt.toLocaleDateString(),size:140,canSort:!0,canHide:!0,canResize:!0},{id:"actions",header:"Actions",label:"Actions",accessor:(q)=>q.id,cell:({item:q})=>F(x,{variant:"ghost",size:"sm",onPress:()=>w?.(q.id),children:"Actions"}),size:120,canSort:!1,canHide:!1,canPin:!1,canResize:!1}],executionMode:"server",selectionMode:"multiple",totalItems:Q,state:{sorting:J,pagination:{pageIndex:W,pageSize:K}},onSortingChange:Z,onPaginationChange:M,initialState:{columnVisibility:{updatedAt:!1},columnPinning:{left:["deal","status"],right:[]}},renderExpandedContent:(q)=>O(P,{gap:"sm",className:"py-2",children:[O(R,{justify:"between",children:[F(G,{className:"font-medium text-sm",children:"Owner"}),F(G,{className:"text-muted-foreground text-sm",children:q.ownerId})]}),O(R,{justify:"between",children:[F(G,{className:"font-medium text-sm",children:"Contact"}),F(G,{className:"text-muted-foreground text-sm",children:q.contactId??"No linked contact"})]}),q.wonSource?O(R,{justify:"between",children:[F(G,{className:"font-medium text-sm",children:"Won Source"}),F(G,{className:"text-muted-foreground text-sm",children:q.wonSource})]}):null,q.lostReason?O(R,{justify:"between",children:[F(G,{className:"font-medium text-sm",children:"Lost Reason"}),F(G,{className:"text-muted-foreground text-sm",children:q.lostReason})]}):null,q.notes?O(P,{gap:"xs",children:[F(G,{className:"font-medium text-sm",children:"Notes"}),F(G,{className:"text-muted-foreground text-sm",children:q.notes})]}):null]}),getCanExpand:()=>!0});return F(g,{controller:_,title:"All Deals",description:"Server-mode table using the shared ContractSpec controller.",loading:X,toolbar:O(R,{gap:"sm",className:"flex-wrap",children:[O(G,{className:"text-muted-foreground text-sm",children:["Selected ",_.selectedRowIds.length]}),O(G,{className:"text-muted-foreground text-sm",children:[Q," total deals"]})]}),footer:`Page ${_.pageIndex+1} of ${_.pageCount}`,emptyState:F("div",{className:"rounded-md border border-dashed p-8 text-center text-muted-foreground text-sm",children:"No deals found"})})}function Jq({onDealClick:A}){let[Q,W]=h.useState([{id:"value",desc:!0}]),[K,J]=h.useState({pageIndex:0,pageSize:3}),{data:X,loading:Z}=D({pageIndex:K.pageIndex,pageSize:K.pageSize,sorting:Q});if(Z&&!X)return F(p,{label:"Loading deals..."});return F(n,{deals:X?.deals??[],totalItems:X?.total??0,pageIndex:K.pageIndex,pageSize:K.pageSize,sorting:Q,loading:Z,onSortingChange:(M)=>{W(M),J((w)=>({...w,pageIndex:0}))},onPaginationChange:J,onDealClick:A})}export{Jq as DealListTab,n as DealListDataTable};
1
+ import{useTemplateRuntime as S}from"@contractspec/lib.example-shared-ui";import{useCallback as x,useEffect as c,useMemo as p,useState as V}from"react";function j(F={}){let{handlers:K,projectId:Z}=S(),{crm:Q}=K,[J,E]=V(null),[_,R]=V({}),[H,$]=V([]),[A,O]=V(!0),[W,M]=V(null),[q,C]=V(0),b=F.pipelineId??"pipeline-1",f=F.pageIndex??q,y=F.pageSize??F.limit??50,[k]=F.sorting??[],w=k?.id==="deal"?"name":k?.id,m=k?k.desc?"desc":"asc":void 0,v=x(async()=>{O(!0),M(null);try{let[U,B,D]=await Promise.all([Q.listDeals({projectId:Z,pipelineId:b,stageId:F.stageId,status:F.status==="all"?void 0:F.status,search:F.search,limit:y,offset:f*y,sortBy:w==="name"||w==="value"||w==="status"||w==="expectedCloseDate"||w==="updatedAt"?w:void 0,sortDirection:m}),Q.getDealsByStage({projectId:Z,pipelineId:b}),Q.getPipelineStages({pipelineId:b})]);E(U),R(B),$(D)}catch(U){M(U instanceof Error?U:Error("Unknown error"))}finally{O(!1)}},[Q,Z,b,F.stageId,F.status,F.search,f,y,w,m]);c(()=>{v()},[v]);let h=p(()=>{if(!J)return null;let U=J.deals.filter((X)=>X.status==="OPEN"),B=J.deals.filter((X)=>X.status==="WON"),D=J.deals.filter((X)=>X.status==="LOST");return{total:J.total,totalValue:J.totalValue,openCount:U.length,openValue:U.reduce((X,T)=>X+T.value,0),wonCount:B.length,wonValue:B.reduce((X,T)=>X+T.value,0),lostCount:D.length}},[J]);return{data:J,dealsByStage:_,stages:H,loading:A,error:W,stats:h,page:f+1,pageIndex:f,pageSize:y,refetch:v,nextPage:F.pageIndex===void 0?()=>C((U)=>U+1):void 0,prevPage:F.pageIndex===void 0?()=>f>0&&C((U)=>U-1):void 0}}import{Button as z,DataTable as d,DataTableToolbar as g,LoaderBlock as u}from"@contractspec/lib.design-system";import{useContractTable as r}from"@contractspec/lib.presentation-runtime-react";import{Badge as l}from"@contractspec/lib.ui-kit-web/ui/badge";import{HStack as L,VStack as I}from"@contractspec/lib.ui-kit-web/ui/stack";import{Text as N}from"@contractspec/lib.ui-kit-web/ui/text";import*as P from"react";import{jsx as G,jsxs as Y}from"react/jsx-runtime";function i(F,K="USD"){return new Intl.NumberFormat("en-US",{style:"currency",currency:K,minimumFractionDigits:0,maximumFractionDigits:0}).format(F)}function n(F){switch(F){case"WON":return"default";case"LOST":return"destructive";case"STALE":return"outline";default:return"secondary"}}function o({value:F,onChange:K}){return Y(L,{gap:"sm",className:"flex-wrap",children:[G(z,{variant:F==="all"?"secondary":"outline",size:"sm",onPress:()=>K("all"),children:"All Deals"}),G(z,{variant:F==="OPEN"?"secondary":"outline",size:"sm",onPress:()=>K("OPEN"),children:"Open Only"}),G(z,{variant:F==="WON"?"secondary":"outline",size:"sm",onPress:()=>K("WON"),children:"Won Only"}),G(z,{variant:F==="LOST"?"secondary":"outline",size:"sm",onPress:()=>K("LOST"),children:"Lost Only"})]})}function s({deals:F,totalItems:K,pageIndex:Z,pageSize:Q,sorting:J,search:E,status:_,loading:R,onSortingChange:H,onPaginationChange:$,onSearchChange:A,onStatusChange:O,onDealClick:W}){let M=r({data:F,columns:[{id:"deal",header:"Deal",label:"Deal",accessor:(q)=>q.name,cell:({item:q})=>Y(I,{gap:"xs",children:[G(N,{className:"font-medium text-sm",children:q.name}),G(N,{className:"text-muted-foreground text-xs",children:q.companyId??"Unassigned company"})]}),size:240,minSize:180,canSort:!0,canPin:!0,canResize:!0},{id:"value",header:"Value",label:"Value",accessorKey:"value",cell:({item:q})=>i(q.value,q.currency),align:"right",size:140,canSort:!0,canResize:!0},{id:"status",header:"Status",label:"Status",accessorKey:"status",cell:({value:q})=>G(l,{variant:n(q),children:String(q)}),size:130,canSort:!0,canHide:!0,canPin:!0,canResize:!0},{id:"expectedCloseDate",header:"Expected Close",label:"Expected Close",accessor:(q)=>q.expectedCloseDate?.toISOString()??"",cell:({item:q})=>q.expectedCloseDate?.toLocaleDateString()??"Not scheduled",size:170,canSort:!0,canHide:!0,canResize:!0},{id:"updatedAt",header:"Updated",label:"Updated",accessor:(q)=>q.updatedAt.toISOString(),cell:({item:q})=>q.updatedAt.toLocaleDateString(),size:140,canSort:!0,canHide:!0,canResize:!0},{id:"actions",header:"Actions",label:"Actions",accessor:(q)=>q.id,cell:({item:q})=>G(z,{variant:"ghost",size:"sm",onPress:()=>W?.(q.id),children:"Actions"}),size:120,canSort:!1,canHide:!1,canPin:!1,canResize:!1}],executionMode:"server",selectionMode:"multiple",totalItems:K,state:{sorting:J,pagination:{pageIndex:Z,pageSize:Q}},onSortingChange:H,onPaginationChange:$,initialState:{columnVisibility:{updatedAt:!1},columnPinning:{left:["deal","status"],right:[]}},renderExpandedContent:(q)=>Y(I,{gap:"sm",className:"py-2",children:[Y(L,{justify:"between",children:[G(N,{className:"font-medium text-sm",children:"Owner"}),G(N,{className:"text-muted-foreground text-sm",children:q.ownerId})]}),Y(L,{justify:"between",children:[G(N,{className:"font-medium text-sm",children:"Contact"}),G(N,{className:"text-muted-foreground text-sm",children:q.contactId??"No linked contact"})]}),q.wonSource?Y(L,{justify:"between",children:[G(N,{className:"font-medium text-sm",children:"Won Source"}),G(N,{className:"text-muted-foreground text-sm",children:q.wonSource})]}):null,q.lostReason?Y(L,{justify:"between",children:[G(N,{className:"font-medium text-sm",children:"Lost Reason"}),G(N,{className:"text-muted-foreground text-sm",children:q.lostReason})]}):null,q.notes?Y(I,{gap:"xs",children:[G(N,{className:"font-medium text-sm",children:"Notes"}),G(N,{className:"text-muted-foreground text-sm",children:q.notes})]}):null]}),getCanExpand:()=>!0});return G(d,{controller:M,title:"All Deals",description:"Server-mode table using the shared ContractSpec controller.",loading:R,toolbar:G(g,{controller:M,searchPlaceholder:"Search deals, companies, contacts, or notes",searchValue:E,onSearchChange:A,activeChips:_==="all"?[]:[{key:"status",label:`Status: ${_}`,onRemove:()=>O("all")}],onClearAll:()=>{A(""),O("all")},actionsStart:o({value:_,onChange:O}),actionsEnd:Y(N,{className:"text-muted-foreground text-sm",children:[K," total deals"]})}),footer:`Page ${M.pageIndex+1} of ${M.pageCount}`,emptyState:G("div",{className:"rounded-md border border-dashed p-8 text-center text-muted-foreground text-sm",children:"No deals found"})})}function Oq({onDealClick:F}){let[K,Z]=P.useState([{id:"value",desc:!0}]),[Q,J]=P.useState({pageIndex:0,pageSize:3}),[E,_]=P.useState(""),[R,H]=P.useState("all"),{data:$,loading:A}=j({pageIndex:Q.pageIndex,pageSize:Q.pageSize,search:E,status:R,sorting:K});if(A&&!$)return G(u,{label:"Loading deals..."});return G(s,{deals:$?.deals??[],totalItems:$?.total??0,pageIndex:Q.pageIndex,pageSize:Q.pageSize,sorting:K,search:E,status:R,loading:A,onSortingChange:(O)=>{Z(O),J((W)=>({...W,pageIndex:0}))},onPaginationChange:J,onSearchChange:(O)=>{_(O),J((W)=>({...W,pageIndex:0}))},onStatusChange:(O)=>{H(O),J((W)=>({...W,pageIndex:0}))},onDealClick:F})}export{Oq as DealListTab,s as DealListDataTable};
@@ -1,2 +1,2 @@
1
1
  // @bun
2
- import{jsx as u,jsxs as YJ}from"react/jsx-runtime";function AJ(J,K){return new Intl.NumberFormat("en-US",{style:"currency",currency:K,minimumFractionDigits:0,maximumFractionDigits:0}).format(J)}function ZJ({deal:J,onClick:K}){let N=J.expectedCloseDate?Math.ceil((J.expectedCloseDate.getTime()-Date.now())/86400000):null;return YJ("div",{onClick:K,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===" ")K?.()},children:[u("h4",{className:"font-medium leading-snug",children:J.name}),u("div",{className:"mt-2 font-semibold text-lg text-primary",children:AJ(J.value,J.currency)}),YJ("div",{className:"mt-3 flex items-center justify-between text-muted-foreground text-xs",children:[N!==null&&u("span",{className:N<0?"text-red-500":N<=7?"text-yellow-600 dark:text-yellow-500":"",children:N<0?`${Math.abs(N)}d overdue`:N===0?"Due today":`${N}d left`}),u("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 HJ}from"react";import{jsx as C,jsxs as m}from"react/jsx-runtime";function NJ(J){if(J>=1e6)return`$${(J/1e6).toFixed(1)}M`;if(J>=1000)return`$${(J/1000).toFixed(0)}K`;return`$${J}`}function $J({dealsByStage:J,stages:K,onDealClick:N,onDealMove:G}){let[z,P]=HJ(null),O=[...K].sort((H,Q)=>H.position-Q.position),A=(H,Q)=>{G?.(H,Q),P(null)};return C("div",{className:"flex gap-4 overflow-x-auto pb-4",children:O.map((H)=>{let Q=J[H.id]??[],Y=Q.reduce((_,W)=>_+W.value,0);return m("div",{className:"flex w-72 flex-shrink-0 flex-col rounded-lg bg-muted/30",children:[m("div",{className:"flex items-center justify-between border-border border-b px-3 py-2",children:[m("div",{children:[C("h3",{className:"font-medium",children:H.name}),m("p",{className:"text-muted-foreground text-xs",children:[Q.length," deals \xB7 ",NJ(Y)]})]}),C("span",{className:"flex h-6 w-6 items-center justify-center rounded-full bg-muted font-medium text-xs",children:Q.length})]}),C("div",{className:"flex flex-1 flex-col gap-2 p-2",children:Q.length===0?C("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"}):Q.map((_)=>m("div",{className:"group relative",children:[C(ZJ,{deal:_,onClick:()=>N?.(_.id)}),_.status==="OPEN"&&G&&m("div",{className:"absolute top-1 right-1 opacity-0 transition-opacity group-hover:opacity-100",children:[C("button",{type:"button",onClick:(W)=>{W.stopPropagation(),P(z===_.id?null:_.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"}),z===_.id&&m("div",{className:"absolute top-7 right-0 z-20 min-w-[140px] rounded-lg border border-border bg-card py-1 shadow-lg",children:[C("p",{className:"px-3 py-1 font-medium text-muted-foreground text-xs",children:"Move to:"}),O.filter((W)=>W.id!==_.stageId).map((W)=>C("button",{type:"button",onClick:(V)=>{V.stopPropagation(),A(_.id,W.id)},className:"w-full px-3 py-1.5 text-left text-sm hover:bg-muted",children:W.name},W.id))]})]})]},_.id))})]},H.id)})})}import{useTemplateRuntime as UJ}from"@contractspec/lib.example-shared-ui";import{useCallback as WJ,useEffect as PJ,useMemo as wJ,useState as S}from"react";function x(J={}){let{handlers:K,projectId:N}=UJ(),{crm:G}=K,[z,P]=S(null),[O,A]=S({}),[H,Q]=S([]),[Y,_]=S(!0),[W,V]=S(null),[T,b]=S(0),q=J.pipelineId??"pipeline-1",Z=J.pageIndex??T,X=J.pageSize??J.limit??50,[R]=J.sorting??[],k=R?.id,L=R?R.desc?"desc":"asc":void 0,c=WJ(async()=>{_(!0),V(null);try{let[M,F,o]=await Promise.all([G.listDeals({projectId:N,pipelineId:q,stageId:J.stageId,status:J.status==="all"?void 0:J.status,search:J.search,limit:X,offset:Z*X,sortBy:k==="name"||k==="value"||k==="status"||k==="expectedCloseDate"||k==="updatedAt"?k:void 0,sortDirection:L}),G.getDealsByStage({projectId:N,pipelineId:q}),G.getPipelineStages({pipelineId:q})]);P(M),A(F),Q(o)}catch(M){V(M instanceof Error?M:Error("Unknown error"))}finally{_(!1)}},[G,N,q,J.stageId,J.status,J.search,Z,X,k,L]);PJ(()=>{c()},[c]);let n=wJ(()=>{if(!z)return null;let M=z.deals.filter((D)=>D.status==="OPEN"),F=z.deals.filter((D)=>D.status==="WON"),o=z.deals.filter((D)=>D.status==="LOST");return{total:z.total,totalValue:z.totalValue,openCount:M.length,openValue:M.reduce((D,a)=>D+a.value,0),wonCount:F.length,wonValue:F.reduce((D,a)=>D+a.value,0),lostCount:o.length}},[z]);return{data:z,dealsByStage:O,stages:H,loading:Y,error:W,stats:n,page:Z+1,pageIndex:Z,pageSize:X,refetch:c,nextPage:J.pageIndex===void 0?()=>b((M)=>M+1):void 0,prevPage:J.pageIndex===void 0?()=>Z>0&&b((M)=>M-1):void 0}}import{useTemplateRuntime as VJ}from"@contractspec/lib.example-shared-ui";import{useCallback as i,useState as d}from"react";function GJ(J={}){let{handlers:K,projectId:N}=VJ(),{crm:G}=K,[z,P]=d({loading:!1,error:null,data:null}),[O,A]=d({loading:!1,error:null,data:null}),[H,Q]=d({loading:!1,error:null,data:null}),[Y,_]=d({loading:!1,error:null,data:null}),W=i(async(q)=>{P({loading:!0,error:null,data:null});try{let Z=await G.createDeal(q,{projectId:N,ownerId:"user-1"});return P({loading:!1,error:null,data:Z}),J.onSuccess?.(),Z}catch(Z){let X=Z instanceof Error?Z:Error("Failed to create deal");return P({loading:!1,error:X,data:null}),J.onError?.(X),null}},[G,N,J]),V=i(async(q)=>{A({loading:!0,error:null,data:null});try{let Z=await G.moveDeal(q);return A({loading:!1,error:null,data:Z}),J.onSuccess?.(),Z}catch(Z){let X=Z instanceof Error?Z:Error("Failed to move deal");return A({loading:!1,error:X,data:null}),J.onError?.(X),null}},[G,J]),T=i(async(q)=>{Q({loading:!0,error:null,data:null});try{let Z=await G.winDeal(q);return Q({loading:!1,error:null,data:Z}),J.onSuccess?.(),Z}catch(Z){let X=Z instanceof Error?Z:Error("Failed to mark deal as won");return Q({loading:!1,error:X,data:null}),J.onError?.(X),null}},[G,J]),b=i(async(q)=>{_({loading:!0,error:null,data:null});try{let Z=await G.loseDeal(q);return _({loading:!1,error:null,data:Z}),J.onSuccess?.(),Z}catch(Z){let X=Z instanceof Error?Z:Error("Failed to mark deal as lost");return _({loading:!1,error:X,data:null}),J.onError?.(X),null}},[G,J]);return{createDeal:W,moveDeal:V,winDeal:T,loseDeal:b,createState:z,moveState:O,winState:H,loseState:Y,isLoading:z.loading||O.loading||H.loading||Y.loading}}import{Button as _J,Input as t}from"@contractspec/lib.design-system";import{useState as p}from"react";import{jsx as E,jsxs as v}from"react/jsx-runtime";var OJ=["USD","EUR","GBP","CAD"],RJ="pipeline-1";function qJ({isOpen:J,onClose:K,onSubmit:N,stages:G,isLoading:z=!1}){let[P,O]=p(""),[A,H]=p(""),[Q,Y]=p("USD"),[_,W]=p(G[0]?.id??""),[V,T]=p(""),[b,q]=p(null),Z=async(X)=>{if(X.preventDefault(),q(null),!P.trim()){q("Deal name is required");return}let R=parseFloat(A);if(isNaN(R)||R<=0){q("Value must be a positive number");return}if(!_){q("Please select a pipeline stage");return}try{await N({name:P.trim(),value:R,currency:Q,pipelineId:RJ,stageId:_,expectedCloseDate:V?new Date(V):void 0}),O(""),H(""),Y("USD"),W(G[0]?.id??""),T(""),K()}catch(k){q(k instanceof Error?k.message:"Failed to create deal")}};if(!J)return null;return v("div",{className:"fixed inset-0 z-50 flex items-center justify-center",children:[E("div",{className:"absolute inset-0 bg-background/80 backdrop-blur-sm",onClick:K,role:"button",tabIndex:0,onKeyDown:(X)=>{if(X.key==="Enter"||X.key===" ")K()},"aria-label":"Close modal"}),v("div",{className:"relative z-10 w-full max-w-md rounded-xl border border-border bg-card p-6 shadow-xl",children:[E("h2",{className:"mb-4 font-semibold text-xl",children:"Create New Deal"}),v("form",{onSubmit:Z,className:"space-y-4",children:[v("div",{children:[E("label",{htmlFor:"deal-name",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Deal Name *"}),E(t,{id:"deal-name",value:P,onChange:(X)=>O(X.target.value),placeholder:"e.g., Enterprise License - Acme Corp",disabled:z})]}),v("div",{className:"flex gap-3",children:[v("div",{className:"flex-1",children:[E("label",{htmlFor:"deal-value",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Value *"}),E(t,{id:"deal-value",type:"number",min:"0",step:"0.01",value:A,onChange:(X)=>H(X.target.value),placeholder:"50000",disabled:z})]}),v("div",{className:"w-24",children:[E("label",{htmlFor:"deal-currency",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Currency"}),E("select",{id:"deal-currency",value:Q,onChange:(X)=>Y(X.target.value),disabled:z,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:OJ.map((X)=>E("option",{value:X,children:X},X))})]})]}),v("div",{children:[E("label",{htmlFor:"deal-stage",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Pipeline Stage *"}),E("select",{id:"deal-stage",value:_,onChange:(X)=>W(X.target.value),disabled:z,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((X)=>E("option",{value:X.id,children:X.name},X.id))})]}),v("div",{children:[E("label",{htmlFor:"deal-close-date",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Expected Close Date"}),E(t,{id:"deal-close-date",type:"date",value:V,onChange:(X)=>T(X.target.value),disabled:z})]}),b&&E("div",{className:"rounded-md bg-destructive/10 p-3 text-destructive text-sm",children:b}),v("div",{className:"flex justify-end gap-3 pt-2",children:[E(_J,{type:"button",variant:"ghost",onPress:K,disabled:z,children:"Cancel"}),E(_J,{type:"submit",disabled:z,children:z?"Creating...":"Create Deal"})]})]})]})]})}import{Button as y}from"@contractspec/lib.design-system";import{useState as g}from"react";import{jsx as $,jsxs as w,Fragment as EJ}from"react/jsx-runtime";function fJ(J,K){return new Intl.NumberFormat("en-US",{style:"currency",currency:K,minimumFractionDigits:0,maximumFractionDigits:0}).format(J)}function QJ({isOpen:J,deal:K,stages:N,onClose:G,onWin:z,onLose:P,onMove:O,isLoading:A=!1}){let[H,Q]=g("menu"),[Y,_]=g(""),[W,V]=g(""),[T,b]=g(""),[q,Z]=g(""),[X,R]=g(null),k=()=>{Q("menu"),_(""),V(""),b(""),Z(""),R(null)},L=()=>{k(),G()},c=async()=>{if(!K)return;R(null);try{await z({dealId:K.id,wonSource:Y.trim()||void 0,notes:T.trim()||void 0}),L()}catch(F){R(F instanceof Error?F.message:"Failed to mark deal as won")}},n=async()=>{if(!K)return;if(R(null),!W.trim()){R("Please provide a reason for losing the deal");return}try{await P({dealId:K.id,lostReason:W.trim(),notes:T.trim()||void 0}),L()}catch(F){R(F instanceof Error?F.message:"Failed to mark deal as lost")}},M=async()=>{if(!K)return;if(R(null),!q){R("Please select a stage");return}if(q===K.stageId){R("Deal is already in this stage");return}try{await O({dealId:K.id,stageId:q}),L()}catch(F){R(F instanceof Error?F.message:"Failed to move deal")}};if(!J||!K)return null;return w("div",{className:"fixed inset-0 z-50 flex items-center justify-center",children:[$("div",{className:"absolute inset-0 bg-background/80 backdrop-blur-sm",onClick:L,role:"button",tabIndex:0,onKeyDown:(F)=>{if(F.key==="Enter"||F.key===" ")L()},"aria-label":"Close modal"}),w("div",{className:"relative z-10 w-full max-w-md rounded-xl border border-border bg-card p-6 shadow-xl",children:[w("div",{className:"mb-4 border-border border-b pb-4",children:[$("h2",{className:"font-semibold text-xl",children:K.name}),$("p",{className:"font-medium text-lg text-primary",children:fJ(K.value,K.currency)}),$("span",{className:`mt-2 inline-flex rounded-full px-2 py-0.5 font-medium text-xs ${K.status==="WON"?"bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400":K.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:K.status})]}),H==="menu"&&w("div",{className:"space-y-3",children:[K.status==="OPEN"&&w(EJ,{children:[w(y,{className:"w-full justify-start",variant:"ghost",onPress:()=>Q("win"),children:[$("span",{className:"mr-2",children:"\uD83C\uDFC6"})," Mark as Won"]}),w(y,{className:"w-full justify-start",variant:"ghost",onPress:()=>Q("lose"),children:[$("span",{className:"mr-2",children:"\u274C"})," Mark as Lost"]}),w(y,{className:"w-full justify-start",variant:"ghost",onPress:()=>{Z(K.stageId),Q("move")},children:[$("span",{className:"mr-2",children:"\u27A1\uFE0F"})," Move to Stage"]})]}),K.status!=="OPEN"&&w("p",{className:"py-4 text-center text-muted-foreground",children:["This deal is already ",K.status.toLowerCase(),". No actions available."]}),$("div",{className:"border-border border-t pt-3",children:$(y,{className:"w-full",variant:"outline",onPress:L,children:"Close"})})]}),H==="win"&&w("div",{className:"space-y-4",children:[w("div",{children:[$("label",{htmlFor:"won-source",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"How did you win this deal?"}),w("select",{id:"won-source",value:Y,onChange:(F)=>_(F.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:[$("option",{value:"",children:"Select a source..."}),$("option",{value:"referral",children:"Referral"}),$("option",{value:"cold_outreach",children:"Cold Outreach"}),$("option",{value:"inbound",children:"Inbound Lead"}),$("option",{value:"upsell",children:"Upsell"}),$("option",{value:"other",children:"Other"})]})]}),w("div",{children:[$("label",{htmlFor:"win-notes",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Notes (optional)"}),$("textarea",{id:"win-notes",value:T,onChange:(F)=>b(F.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"})]}),X&&$("div",{className:"rounded-md bg-destructive/10 p-3 text-destructive text-sm",children:X}),w("div",{className:"flex justify-end gap-3 pt-2",children:[$(y,{variant:"ghost",onPress:()=>Q("menu"),disabled:A,children:"Back"}),$(y,{onPress:c,disabled:A,children:A?"Processing...":"\uD83C\uDFC6 Confirm Win"})]})]}),H==="lose"&&w("div",{className:"space-y-4",children:[w("div",{children:[$("label",{htmlFor:"lost-reason",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Why was this deal lost? *"}),w("select",{id:"lost-reason",value:W,onChange:(F)=>V(F.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:[$("option",{value:"",children:"Select a reason..."}),$("option",{value:"price",children:"Price too high"}),$("option",{value:"competitor",children:"Lost to competitor"}),$("option",{value:"no_budget",children:"No budget"}),$("option",{value:"no_decision",children:"No decision made"}),$("option",{value:"timing",children:"Bad timing"}),$("option",{value:"product_fit",children:"Product not a fit"}),$("option",{value:"other",children:"Other"})]})]}),w("div",{children:[$("label",{htmlFor:"lose-notes",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Notes (optional)"}),$("textarea",{id:"lose-notes",value:T,onChange:(F)=>b(F.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"})]}),X&&$("div",{className:"rounded-md bg-destructive/10 p-3 text-destructive text-sm",children:X}),w("div",{className:"flex justify-end gap-3 pt-2",children:[$(y,{variant:"ghost",onPress:()=>Q("menu"),disabled:A,children:"Back"}),$(y,{variant:"destructive",onPress:n,disabled:A,children:A?"Processing...":"\u274C Confirm Loss"})]})]}),H==="move"&&w("div",{className:"space-y-4",children:[w("div",{children:[$("label",{htmlFor:"move-stage",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Move to Stage"}),$("select",{id:"move-stage",value:q,onChange:(F)=>Z(F.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:N.map((F)=>w("option",{value:F.id,children:[F.name,F.id===K.stageId?" (current)":""]},F.id))})]}),X&&$("div",{className:"rounded-md bg-destructive/10 p-3 text-destructive text-sm",children:X}),w("div",{className:"flex justify-end gap-3 pt-2",children:[$(y,{variant:"ghost",onPress:()=>Q("menu"),disabled:A,children:"Back"}),$(y,{onPress:M,disabled:A,children:A?"Moving...":"\u27A1\uFE0F Move Deal"})]})]})]})]})}import{Button as hJ,DataTable as TJ,LoaderBlock as BJ}from"@contractspec/lib.design-system";import{useContractTable as bJ}from"@contractspec/lib.presentation-runtime-react";import{Badge as MJ}from"@contractspec/lib.ui-kit-web/ui/badge";import{HStack as j,VStack as s}from"@contractspec/lib.ui-kit-web/ui/stack";import{Text as B}from"@contractspec/lib.ui-kit-web/ui/text";import*as e from"react";import{jsx as f,jsxs as I}from"react/jsx-runtime";function kJ(J,K="USD"){return new Intl.NumberFormat("en-US",{style:"currency",currency:K,minimumFractionDigits:0,maximumFractionDigits:0}).format(J)}function vJ(J){switch(J){case"WON":return"default";case"LOST":return"destructive";case"STALE":return"outline";default:return"secondary"}}function yJ({deals:J,totalItems:K,pageIndex:N,pageSize:G,sorting:z,loading:P,onSortingChange:O,onPaginationChange:A,onDealClick:H}){let Q=bJ({data:J,columns:[{id:"deal",header:"Deal",label:"Deal",accessor:(Y)=>Y.name,cell:({item:Y})=>I(s,{gap:"xs",children:[f(B,{className:"font-medium text-sm",children:Y.name}),f(B,{className:"text-muted-foreground text-xs",children:Y.companyId??"Unassigned company"})]}),size:240,minSize:180,canSort:!0,canPin:!0,canResize:!0},{id:"value",header:"Value",label:"Value",accessorKey:"value",cell:({item:Y})=>kJ(Y.value,Y.currency),align:"right",size:140,canSort:!0,canResize:!0},{id:"status",header:"Status",label:"Status",accessorKey:"status",cell:({value:Y})=>f(MJ,{variant:vJ(Y),children:String(Y)}),size:130,canSort:!0,canHide:!0,canPin:!0,canResize:!0},{id:"expectedCloseDate",header:"Expected Close",label:"Expected Close",accessor:(Y)=>Y.expectedCloseDate?.toISOString()??"",cell:({item:Y})=>Y.expectedCloseDate?.toLocaleDateString()??"Not scheduled",size:170,canSort:!0,canHide:!0,canResize:!0},{id:"updatedAt",header:"Updated",label:"Updated",accessor:(Y)=>Y.updatedAt.toISOString(),cell:({item:Y})=>Y.updatedAt.toLocaleDateString(),size:140,canSort:!0,canHide:!0,canResize:!0},{id:"actions",header:"Actions",label:"Actions",accessor:(Y)=>Y.id,cell:({item:Y})=>f(hJ,{variant:"ghost",size:"sm",onPress:()=>H?.(Y.id),children:"Actions"}),size:120,canSort:!1,canHide:!1,canPin:!1,canResize:!1}],executionMode:"server",selectionMode:"multiple",totalItems:K,state:{sorting:z,pagination:{pageIndex:N,pageSize:G}},onSortingChange:O,onPaginationChange:A,initialState:{columnVisibility:{updatedAt:!1},columnPinning:{left:["deal","status"],right:[]}},renderExpandedContent:(Y)=>I(s,{gap:"sm",className:"py-2",children:[I(j,{justify:"between",children:[f(B,{className:"font-medium text-sm",children:"Owner"}),f(B,{className:"text-muted-foreground text-sm",children:Y.ownerId})]}),I(j,{justify:"between",children:[f(B,{className:"font-medium text-sm",children:"Contact"}),f(B,{className:"text-muted-foreground text-sm",children:Y.contactId??"No linked contact"})]}),Y.wonSource?I(j,{justify:"between",children:[f(B,{className:"font-medium text-sm",children:"Won Source"}),f(B,{className:"text-muted-foreground text-sm",children:Y.wonSource})]}):null,Y.lostReason?I(j,{justify:"between",children:[f(B,{className:"font-medium text-sm",children:"Lost Reason"}),f(B,{className:"text-muted-foreground text-sm",children:Y.lostReason})]}):null,Y.notes?I(s,{gap:"xs",children:[f(B,{className:"font-medium text-sm",children:"Notes"}),f(B,{className:"text-muted-foreground text-sm",children:Y.notes})]}):null]}),getCanExpand:()=>!0});return f(TJ,{controller:Q,title:"All Deals",description:"Server-mode table using the shared ContractSpec controller.",loading:P,toolbar:I(j,{gap:"sm",className:"flex-wrap",children:[I(B,{className:"text-muted-foreground text-sm",children:["Selected ",Q.selectedRowIds.length]}),I(B,{className:"text-muted-foreground text-sm",children:[K," total deals"]})]}),footer:`Page ${Q.pageIndex+1} of ${Q.pageCount}`,emptyState:f("div",{className:"rounded-md border border-dashed p-8 text-center text-muted-foreground text-sm",children:"No deals found"})})}function zJ({onDealClick:J}){let[K,N]=e.useState([{id:"value",desc:!0}]),[G,z]=e.useState({pageIndex:0,pageSize:3}),{data:P,loading:O}=x({pageIndex:G.pageIndex,pageSize:G.pageSize,sorting:K});if(O&&!P)return f(BJ,{label:"Loading deals..."});return f(yJ,{deals:P?.deals??[],totalItems:P?.total??0,pageIndex:G.pageIndex,pageSize:G.pageSize,sorting:K,loading:O,onSortingChange:(A)=>{N(A),z((H)=>({...H,pageIndex:0}))},onPaginationChange:z,onDealClick:J})}import{Button as IJ,ErrorState as LJ,LoaderBlock as DJ,StatCard as r,StatCardGroup as CJ}from"@contractspec/lib.design-system";import{Tabs as mJ,TabsContent as JJ,TabsList as SJ,TabsTrigger as KJ}from"@contractspec/lib.ui-kit-web/ui/tabs";import{useCallback as FJ,useState as XJ}from"react";import{jsx as U,jsxs as h}from"react/jsx-runtime";function l(J,K="USD"){return new Intl.NumberFormat("en-US",{style:"currency",currency:K,minimumFractionDigits:0,maximumFractionDigits:0}).format(J)}function fK(){let[J,K]=XJ(!1),[N,G]=XJ(null),[z,P]=XJ(!1),{data:O,dealsByStage:A,stages:H,loading:Q,error:Y,stats:_,refetch:W}=x(),V=GJ({onSuccess:()=>{W()}}),T=FJ((q)=>{let Z=A?Object.values(A).flat().find((X)=>X.id===q):null;if(Z)G(Z),P(!0)},[A]),b=FJ(async(q,Z)=>{await V.moveDeal({dealId:q,stageId:Z})},[V]);if(Q&&!O)return U(DJ,{label:"Loading CRM..."});if(Y)return U(LJ,{title:"Failed to load CRM",description:Y.message,onRetry:W,retryLabel:"Retry"});return h("div",{className:"space-y-6",children:[h("div",{className:"flex items-center justify-between",children:[U("h2",{className:"font-bold text-2xl",children:"CRM Pipeline"}),h(IJ,{onClick:()=>K(!0),children:[U("span",{className:"mr-2",children:"+"})," Create Deal"]})]}),_&&h(CJ,{children:[U(r,{label:"Total Pipeline",value:l(_.totalValue),hint:`${_.total} deals`}),U(r,{label:"Open Deals",value:l(_.openValue),hint:`${_.openCount} active`}),U(r,{label:"Won",value:l(_.wonValue),hint:`${_.wonCount} closed`}),U(r,{label:"Lost",value:_.lostCount,hint:"deals lost"})]}),h(mJ,{defaultValue:"pipeline",className:"w-full",children:[h(SJ,{children:[h(KJ,{value:"pipeline",children:[U("span",{className:"mr-2",children:"\uD83D\uDCCA"}),"Pipeline"]}),h(KJ,{value:"list",children:[U("span",{className:"mr-2",children:"\uD83D\uDCCB"}),"All Deals"]}),h(KJ,{value:"metrics",children:[U("span",{className:"mr-2",children:"\uD83D\uDCC8"}),"Metrics"]})]}),U(JJ,{value:"pipeline",className:"min-h-[400px]",children:U($J,{dealsByStage:A,stages:H,onDealClick:T,onDealMove:b})}),U(JJ,{value:"list",className:"min-h-[400px]",children:U(zJ,{onDealClick:T})}),U(JJ,{value:"metrics",className:"min-h-[400px]",children:U(pJ,{stats:_})})]}),U(qJ,{isOpen:J,onClose:()=>K(!1),onSubmit:async(q)=>{await V.createDeal(q)},stages:H,isLoading:V.createState.loading}),U(QJ,{isOpen:z,deal:N,stages:H,onClose:()=>{P(!1),G(null)},onWin:async(q)=>{await V.winDeal(q)},onLose:async(q)=>{await V.loseDeal(q)},onMove:async(q)=>{await V.moveDeal(q),W()},isLoading:V.isLoading})]})}function pJ({stats:J}){if(!J)return null;return U("div",{className:"space-y-6",children:h("div",{className:"rounded-xl border border-border bg-card p-6",children:[U("h3",{className:"mb-4 font-semibold text-lg",children:"Pipeline Overview"}),h("dl",{className:"grid gap-4 sm:grid-cols-3",children:[h("div",{children:[U("dt",{className:"text-muted-foreground text-sm",children:"Win Rate"}),h("dd",{className:"font-semibold text-2xl",children:[J.total>0?(J.wonCount/J.total*100).toFixed(0):0,"%"]})]}),h("div",{children:[U("dt",{className:"text-muted-foreground text-sm",children:"Avg Deal Size"}),U("dd",{className:"font-semibold text-2xl",children:l(J.total>0?J.totalValue/J.total:0)})]}),h("div",{children:[U("dt",{className:"text-muted-foreground text-sm",children:"Conversion"}),h("dd",{className:"font-semibold text-2xl",children:[J.wonCount," / ",J.total]})]})]})]})})}export{fK as CrmDashboard};
2
+ import{jsx as d,jsxs as ZJ}from"react/jsx-runtime";function AJ(J,K){return new Intl.NumberFormat("en-US",{style:"currency",currency:K,minimumFractionDigits:0,maximumFractionDigits:0}).format(J)}function $J({deal:J,onClick:K}){let W=J.expectedCloseDate?Math.ceil((J.expectedCloseDate.getTime()-Date.now())/86400000):null;return ZJ("div",{onClick:K,className:"cursor-pointer rounded-lg border border-border bg-card p-3 shadow-sm transition-shadow hover:shadow-md",role:"button",tabIndex:0,onKeyDown:(_)=>{if(_.key==="Enter"||_.key===" ")K?.()},children:[d("h4",{className:"font-medium leading-snug",children:J.name}),d("div",{className:"mt-2 font-semibold text-lg text-primary",children:AJ(J.value,J.currency)}),ZJ("div",{className:"mt-3 flex items-center justify-between text-muted-foreground text-xs",children:[W!==null&&d("span",{className:W<0?"text-red-500":W<=7?"text-yellow-600 dark:text-yellow-500":"",children:W<0?`${Math.abs(W)}d overdue`:W===0?"Due today":`${W}d left`}),d("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 NJ}from"react";import{jsx as D,jsxs as C}from"react/jsx-runtime";function UJ(J){if(J>=1e6)return`$${(J/1e6).toFixed(1)}M`;if(J>=1000)return`$${(J/1000).toFixed(0)}K`;return`$${J}`}function GJ({dealsByStage:J,stages:K,onDealClick:W,onDealMove:_}){let[Q,O]=NJ(null),f=[...K].sort((N,z)=>N.position-z.position),A=(N,z)=>{_?.(N,z),O(null)};return D("div",{className:"flex gap-4 overflow-x-auto pb-4",children:f.map((N)=>{let z=J[N.id]??[],R=z.reduce((Z,F)=>Z+F.value,0);return C("div",{className:"flex w-72 flex-shrink-0 flex-col rounded-lg bg-muted/30",children:[C("div",{className:"flex items-center justify-between border-border border-b px-3 py-2",children:[C("div",{children:[D("h3",{className:"font-medium",children:N.name}),C("p",{className:"text-muted-foreground text-xs",children:[z.length," deals \xB7 ",UJ(R)]})]}),D("span",{className:"flex h-6 w-6 items-center justify-center rounded-full bg-muted font-medium text-xs",children:z.length})]}),D("div",{className:"flex flex-1 flex-col gap-2 p-2",children:z.length===0?D("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"}):z.map((Z)=>C("div",{className:"group relative",children:[D($J,{deal:Z,onClick:()=>W?.(Z.id)}),Z.status==="OPEN"&&_&&C("div",{className:"absolute top-1 right-1 opacity-0 transition-opacity group-hover:opacity-100",children:[D("button",{type:"button",onClick:(F)=>{F.stopPropagation(),O(Q===Z.id?null:Z.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"}),Q===Z.id&&C("div",{className:"absolute top-7 right-0 z-20 min-w-[140px] rounded-lg border border-border bg-card py-1 shadow-lg",children:[D("p",{className:"px-3 py-1 font-medium text-muted-foreground text-xs",children:"Move to:"}),f.filter((F)=>F.id!==Z.stageId).map((F)=>D("button",{type:"button",onClick:(U)=>{U.stopPropagation(),A(Z.id,F.id)},className:"w-full px-3 py-1.5 text-left text-sm hover:bg-muted",children:F.name},F.id))]})]})]},Z.id))})]},N.id)})})}import{useTemplateRuntime as WJ}from"@contractspec/lib.example-shared-ui";import{useCallback as wJ,useEffect as PJ,useMemo as VJ,useState as S}from"react";function i(J={}){let{handlers:K,projectId:W}=WJ(),{crm:_}=K,[Q,O]=S(null),[f,A]=S({}),[N,z]=S([]),[R,Z]=S(!0),[F,U]=S(null),[Y,B]=S(0),q=J.pipelineId??"pipeline-1",$=J.pageIndex??Y,X=J.pageSize??J.limit??50,[E]=J.sorting??[],y=E?.id==="deal"?"name":E?.id,v=E?E.desc?"desc":"asc":void 0,c=wJ(async()=>{Z(!0),U(null);try{let[M,H,t]=await Promise.all([_.listDeals({projectId:W,pipelineId:q,stageId:J.stageId,status:J.status==="all"?void 0:J.status,search:J.search,limit:X,offset:$*X,sortBy:y==="name"||y==="value"||y==="status"||y==="expectedCloseDate"||y==="updatedAt"?y:void 0,sortDirection:v}),_.getDealsByStage({projectId:W,pipelineId:q}),_.getPipelineStages({pipelineId:q})]);O(M),A(H),z(t)}catch(M){U(M instanceof Error?M:Error("Unknown error"))}finally{Z(!1)}},[_,W,q,J.stageId,J.status,J.search,$,X,y,v]);PJ(()=>{c()},[c]);let a=VJ(()=>{if(!Q)return null;let M=Q.deals.filter((h)=>h.status==="OPEN"),H=Q.deals.filter((h)=>h.status==="WON"),t=Q.deals.filter((h)=>h.status==="LOST");return{total:Q.total,totalValue:Q.totalValue,openCount:M.length,openValue:M.reduce((h,s)=>h+s.value,0),wonCount:H.length,wonValue:H.reduce((h,s)=>h+s.value,0),lostCount:t.length}},[Q]);return{data:Q,dealsByStage:f,stages:N,loading:R,error:F,stats:a,page:$+1,pageIndex:$,pageSize:X,refetch:c,nextPage:J.pageIndex===void 0?()=>B((M)=>M+1):void 0,prevPage:J.pageIndex===void 0?()=>$>0&&B((M)=>M-1):void 0}}import{useTemplateRuntime as OJ}from"@contractspec/lib.example-shared-ui";import{useCallback as l,useState as r}from"react";function _J(J={}){let{handlers:K,projectId:W}=OJ(),{crm:_}=K,[Q,O]=r({loading:!1,error:null,data:null}),[f,A]=r({loading:!1,error:null,data:null}),[N,z]=r({loading:!1,error:null,data:null}),[R,Z]=r({loading:!1,error:null,data:null}),F=l(async(q)=>{O({loading:!0,error:null,data:null});try{let $=await _.createDeal(q,{projectId:W,ownerId:"user-1"});return O({loading:!1,error:null,data:$}),J.onSuccess?.(),$}catch($){let X=$ instanceof Error?$:Error("Failed to create deal");return O({loading:!1,error:X,data:null}),J.onError?.(X),null}},[_,W,J]),U=l(async(q)=>{A({loading:!0,error:null,data:null});try{let $=await _.moveDeal(q);return A({loading:!1,error:null,data:$}),J.onSuccess?.(),$}catch($){let X=$ instanceof Error?$:Error("Failed to move deal");return A({loading:!1,error:X,data:null}),J.onError?.(X),null}},[_,J]),Y=l(async(q)=>{z({loading:!0,error:null,data:null});try{let $=await _.winDeal(q);return z({loading:!1,error:null,data:$}),J.onSuccess?.(),$}catch($){let X=$ instanceof Error?$:Error("Failed to mark deal as won");return z({loading:!1,error:X,data:null}),J.onError?.(X),null}},[_,J]),B=l(async(q)=>{Z({loading:!0,error:null,data:null});try{let $=await _.loseDeal(q);return Z({loading:!1,error:null,data:$}),J.onSuccess?.(),$}catch($){let X=$ instanceof Error?$:Error("Failed to mark deal as lost");return Z({loading:!1,error:X,data:null}),J.onError?.(X),null}},[_,J]);return{createDeal:F,moveDeal:U,winDeal:Y,loseDeal:B,createState:Q,moveState:f,winState:N,loseState:R,isLoading:Q.loading||f.loading||N.loading||R.loading}}import{Button as qJ,Input as e}from"@contractspec/lib.design-system";import{useState as p}from"react";import{jsx as T,jsxs as I}from"react/jsx-runtime";var RJ=["USD","EUR","GBP","CAD"],fJ="pipeline-1";function QJ({isOpen:J,onClose:K,onSubmit:W,stages:_,isLoading:Q=!1}){let[O,f]=p(""),[A,N]=p(""),[z,R]=p("USD"),[Z,F]=p(_[0]?.id??""),[U,Y]=p(""),[B,q]=p(null),$=async(X)=>{if(X.preventDefault(),q(null),!O.trim()){q("Deal name is required");return}let E=parseFloat(A);if(isNaN(E)||E<=0){q("Value must be a positive number");return}if(!Z){q("Please select a pipeline stage");return}try{await W({name:O.trim(),value:E,currency:z,pipelineId:fJ,stageId:Z,expectedCloseDate:U?new Date(U):void 0}),f(""),N(""),R("USD"),F(_[0]?.id??""),Y(""),K()}catch(y){q(y instanceof Error?y.message:"Failed to create deal")}};if(!J)return null;return I("div",{className:"fixed inset-0 z-50 flex items-center justify-center",children:[T("div",{className:"absolute inset-0 bg-background/80 backdrop-blur-sm",onClick:K,role:"button",tabIndex:0,onKeyDown:(X)=>{if(X.key==="Enter"||X.key===" ")K()},"aria-label":"Close modal"}),I("div",{className:"relative z-10 w-full max-w-md rounded-xl border border-border bg-card p-6 shadow-xl",children:[T("h2",{className:"mb-4 font-semibold text-xl",children:"Create New Deal"}),I("form",{onSubmit:$,className:"space-y-4",children:[I("div",{children:[T("label",{htmlFor:"deal-name",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Deal Name *"}),T(e,{id:"deal-name",value:O,onChange:(X)=>f(X.target.value),placeholder:"e.g., Enterprise License - Acme Corp",disabled:Q})]}),I("div",{className:"flex gap-3",children:[I("div",{className:"flex-1",children:[T("label",{htmlFor:"deal-value",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Value *"}),T(e,{id:"deal-value",type:"number",min:"0",step:"0.01",value:A,onChange:(X)=>N(X.target.value),placeholder:"50000",disabled:Q})]}),I("div",{className:"w-24",children:[T("label",{htmlFor:"deal-currency",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Currency"}),T("select",{id:"deal-currency",value:z,onChange:(X)=>R(X.target.value),disabled:Q,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:RJ.map((X)=>T("option",{value:X,children:X},X))})]})]}),I("div",{children:[T("label",{htmlFor:"deal-stage",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Pipeline Stage *"}),T("select",{id:"deal-stage",value:Z,onChange:(X)=>F(X.target.value),disabled:Q,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:_.map((X)=>T("option",{value:X.id,children:X.name},X.id))})]}),I("div",{children:[T("label",{htmlFor:"deal-close-date",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Expected Close Date"}),T(e,{id:"deal-close-date",type:"date",value:U,onChange:(X)=>Y(X.target.value),disabled:Q})]}),B&&T("div",{className:"rounded-md bg-destructive/10 p-3 text-destructive text-sm",children:B}),I("div",{className:"flex justify-end gap-3 pt-2",children:[T(qJ,{type:"button",variant:"ghost",onPress:K,disabled:Q,children:"Cancel"}),T(qJ,{type:"submit",disabled:Q,children:Q?"Creating...":"Create Deal"})]})]})]})]})}import{Button as L}from"@contractspec/lib.design-system";import{useState as g}from"react";import{jsx as G,jsxs as P,Fragment as TJ}from"react/jsx-runtime";function EJ(J,K){return new Intl.NumberFormat("en-US",{style:"currency",currency:K,minimumFractionDigits:0,maximumFractionDigits:0}).format(J)}function zJ({isOpen:J,deal:K,stages:W,onClose:_,onWin:Q,onLose:O,onMove:f,isLoading:A=!1}){let[N,z]=g("menu"),[R,Z]=g(""),[F,U]=g(""),[Y,B]=g(""),[q,$]=g(""),[X,E]=g(null),y=()=>{z("menu"),Z(""),U(""),B(""),$(""),E(null)},v=()=>{y(),_()},c=async()=>{if(!K)return;E(null);try{await Q({dealId:K.id,wonSource:R.trim()||void 0,notes:Y.trim()||void 0}),v()}catch(H){E(H instanceof Error?H.message:"Failed to mark deal as won")}},a=async()=>{if(!K)return;if(E(null),!F.trim()){E("Please provide a reason for losing the deal");return}try{await O({dealId:K.id,lostReason:F.trim(),notes:Y.trim()||void 0}),v()}catch(H){E(H instanceof Error?H.message:"Failed to mark deal as lost")}},M=async()=>{if(!K)return;if(E(null),!q){E("Please select a stage");return}if(q===K.stageId){E("Deal is already in this stage");return}try{await f({dealId:K.id,stageId:q}),v()}catch(H){E(H instanceof Error?H.message:"Failed to move deal")}};if(!J||!K)return null;return P("div",{className:"fixed inset-0 z-50 flex items-center justify-center",children:[G("div",{className:"absolute inset-0 bg-background/80 backdrop-blur-sm",onClick:v,role:"button",tabIndex:0,onKeyDown:(H)=>{if(H.key==="Enter"||H.key===" ")v()},"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:[P("div",{className:"mb-4 border-border border-b pb-4",children:[G("h2",{className:"font-semibold text-xl",children:K.name}),G("p",{className:"font-medium text-lg text-primary",children:EJ(K.value,K.currency)}),G("span",{className:`mt-2 inline-flex rounded-full px-2 py-0.5 font-medium text-xs ${K.status==="WON"?"bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400":K.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:K.status})]}),N==="menu"&&P("div",{className:"space-y-3",children:[K.status==="OPEN"&&P(TJ,{children:[P(L,{className:"w-full justify-start",variant:"ghost",onPress:()=>z("win"),children:[G("span",{className:"mr-2",children:"\uD83C\uDFC6"})," Mark as Won"]}),P(L,{className:"w-full justify-start",variant:"ghost",onPress:()=>z("lose"),children:[G("span",{className:"mr-2",children:"\u274C"})," Mark as Lost"]}),P(L,{className:"w-full justify-start",variant:"ghost",onPress:()=>{$(K.stageId),z("move")},children:[G("span",{className:"mr-2",children:"\u27A1\uFE0F"})," Move to Stage"]})]}),K.status!=="OPEN"&&P("p",{className:"py-4 text-center text-muted-foreground",children:["This deal is already ",K.status.toLowerCase(),". No actions available."]}),G("div",{className:"border-border border-t pt-3",children:G(L,{className:"w-full",variant:"outline",onPress:v,children:"Close"})})]}),N==="win"&&P("div",{className:"space-y-4",children:[P("div",{children:[G("label",{htmlFor:"won-source",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"How did you win this deal?"}),P("select",{id:"won-source",value:R,onChange:(H)=>Z(H.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:[G("option",{value:"",children:"Select a source..."}),G("option",{value:"referral",children:"Referral"}),G("option",{value:"cold_outreach",children:"Cold Outreach"}),G("option",{value:"inbound",children:"Inbound Lead"}),G("option",{value:"upsell",children:"Upsell"}),G("option",{value:"other",children:"Other"})]})]}),P("div",{children:[G("label",{htmlFor:"win-notes",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Notes (optional)"}),G("textarea",{id:"win-notes",value:Y,onChange:(H)=>B(H.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"})]}),X&&G("div",{className:"rounded-md bg-destructive/10 p-3 text-destructive text-sm",children:X}),P("div",{className:"flex justify-end gap-3 pt-2",children:[G(L,{variant:"ghost",onPress:()=>z("menu"),disabled:A,children:"Back"}),G(L,{onPress:c,disabled:A,children:A?"Processing...":"\uD83C\uDFC6 Confirm Win"})]})]}),N==="lose"&&P("div",{className:"space-y-4",children:[P("div",{children:[G("label",{htmlFor:"lost-reason",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Why was this deal lost? *"}),P("select",{id:"lost-reason",value:F,onChange:(H)=>U(H.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:[G("option",{value:"",children:"Select a reason..."}),G("option",{value:"price",children:"Price too high"}),G("option",{value:"competitor",children:"Lost to competitor"}),G("option",{value:"no_budget",children:"No budget"}),G("option",{value:"no_decision",children:"No decision made"}),G("option",{value:"timing",children:"Bad timing"}),G("option",{value:"product_fit",children:"Product not a fit"}),G("option",{value:"other",children:"Other"})]})]}),P("div",{children:[G("label",{htmlFor:"lose-notes",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Notes (optional)"}),G("textarea",{id:"lose-notes",value:Y,onChange:(H)=>B(H.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"})]}),X&&G("div",{className:"rounded-md bg-destructive/10 p-3 text-destructive text-sm",children:X}),P("div",{className:"flex justify-end gap-3 pt-2",children:[G(L,{variant:"ghost",onPress:()=>z("menu"),disabled:A,children:"Back"}),G(L,{variant:"destructive",onPress:a,disabled:A,children:A?"Processing...":"\u274C Confirm Loss"})]})]}),N==="move"&&P("div",{className:"space-y-4",children:[P("div",{children:[G("label",{htmlFor:"move-stage",className:"mb-1 block font-medium text-muted-foreground text-sm",children:"Move to Stage"}),G("select",{id:"move-stage",value:q,onChange:(H)=>$(H.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:W.map((H)=>P("option",{value:H.id,children:[H.name,H.id===K.stageId?" (current)":""]},H.id))})]}),X&&G("div",{className:"rounded-md bg-destructive/10 p-3 text-destructive text-sm",children:X}),P("div",{className:"flex justify-end gap-3 pt-2",children:[G(L,{variant:"ghost",onPress:()=>z("menu"),disabled:A,children:"Back"}),G(L,{onPress:M,disabled:A,children:A?"Moving...":"\u27A1\uFE0F Move Deal"})]})]})]})]})}import{Button as j,DataTable as bJ,DataTableToolbar as BJ,LoaderBlock as MJ}from"@contractspec/lib.design-system";import{useContractTable as kJ}from"@contractspec/lib.presentation-runtime-react";import{Badge as yJ}from"@contractspec/lib.ui-kit-web/ui/badge";import{HStack as x,VStack as JJ}from"@contractspec/lib.ui-kit-web/ui/stack";import{Text as k}from"@contractspec/lib.ui-kit-web/ui/text";import*as u from"react";import{jsx as V,jsxs as m}from"react/jsx-runtime";function IJ(J,K="USD"){return new Intl.NumberFormat("en-US",{style:"currency",currency:K,minimumFractionDigits:0,maximumFractionDigits:0}).format(J)}function LJ(J){switch(J){case"WON":return"default";case"LOST":return"destructive";case"STALE":return"outline";default:return"secondary"}}function vJ({value:J,onChange:K}){return m(x,{gap:"sm",className:"flex-wrap",children:[V(j,{variant:J==="all"?"secondary":"outline",size:"sm",onPress:()=>K("all"),children:"All Deals"}),V(j,{variant:J==="OPEN"?"secondary":"outline",size:"sm",onPress:()=>K("OPEN"),children:"Open Only"}),V(j,{variant:J==="WON"?"secondary":"outline",size:"sm",onPress:()=>K("WON"),children:"Won Only"}),V(j,{variant:J==="LOST"?"secondary":"outline",size:"sm",onPress:()=>K("LOST"),children:"Lost Only"})]})}function hJ({deals:J,totalItems:K,pageIndex:W,pageSize:_,sorting:Q,search:O,status:f,loading:A,onSortingChange:N,onPaginationChange:z,onSearchChange:R,onStatusChange:Z,onDealClick:F}){let U=kJ({data:J,columns:[{id:"deal",header:"Deal",label:"Deal",accessor:(Y)=>Y.name,cell:({item:Y})=>m(JJ,{gap:"xs",children:[V(k,{className:"font-medium text-sm",children:Y.name}),V(k,{className:"text-muted-foreground text-xs",children:Y.companyId??"Unassigned company"})]}),size:240,minSize:180,canSort:!0,canPin:!0,canResize:!0},{id:"value",header:"Value",label:"Value",accessorKey:"value",cell:({item:Y})=>IJ(Y.value,Y.currency),align:"right",size:140,canSort:!0,canResize:!0},{id:"status",header:"Status",label:"Status",accessorKey:"status",cell:({value:Y})=>V(yJ,{variant:LJ(Y),children:String(Y)}),size:130,canSort:!0,canHide:!0,canPin:!0,canResize:!0},{id:"expectedCloseDate",header:"Expected Close",label:"Expected Close",accessor:(Y)=>Y.expectedCloseDate?.toISOString()??"",cell:({item:Y})=>Y.expectedCloseDate?.toLocaleDateString()??"Not scheduled",size:170,canSort:!0,canHide:!0,canResize:!0},{id:"updatedAt",header:"Updated",label:"Updated",accessor:(Y)=>Y.updatedAt.toISOString(),cell:({item:Y})=>Y.updatedAt.toLocaleDateString(),size:140,canSort:!0,canHide:!0,canResize:!0},{id:"actions",header:"Actions",label:"Actions",accessor:(Y)=>Y.id,cell:({item:Y})=>V(j,{variant:"ghost",size:"sm",onPress:()=>F?.(Y.id),children:"Actions"}),size:120,canSort:!1,canHide:!1,canPin:!1,canResize:!1}],executionMode:"server",selectionMode:"multiple",totalItems:K,state:{sorting:Q,pagination:{pageIndex:W,pageSize:_}},onSortingChange:N,onPaginationChange:z,initialState:{columnVisibility:{updatedAt:!1},columnPinning:{left:["deal","status"],right:[]}},renderExpandedContent:(Y)=>m(JJ,{gap:"sm",className:"py-2",children:[m(x,{justify:"between",children:[V(k,{className:"font-medium text-sm",children:"Owner"}),V(k,{className:"text-muted-foreground text-sm",children:Y.ownerId})]}),m(x,{justify:"between",children:[V(k,{className:"font-medium text-sm",children:"Contact"}),V(k,{className:"text-muted-foreground text-sm",children:Y.contactId??"No linked contact"})]}),Y.wonSource?m(x,{justify:"between",children:[V(k,{className:"font-medium text-sm",children:"Won Source"}),V(k,{className:"text-muted-foreground text-sm",children:Y.wonSource})]}):null,Y.lostReason?m(x,{justify:"between",children:[V(k,{className:"font-medium text-sm",children:"Lost Reason"}),V(k,{className:"text-muted-foreground text-sm",children:Y.lostReason})]}):null,Y.notes?m(JJ,{gap:"xs",children:[V(k,{className:"font-medium text-sm",children:"Notes"}),V(k,{className:"text-muted-foreground text-sm",children:Y.notes})]}):null]}),getCanExpand:()=>!0});return V(bJ,{controller:U,title:"All Deals",description:"Server-mode table using the shared ContractSpec controller.",loading:A,toolbar:V(BJ,{controller:U,searchPlaceholder:"Search deals, companies, contacts, or notes",searchValue:O,onSearchChange:R,activeChips:f==="all"?[]:[{key:"status",label:`Status: ${f}`,onRemove:()=>Z("all")}],onClearAll:()=>{R(""),Z("all")},actionsStart:vJ({value:f,onChange:Z}),actionsEnd:m(k,{className:"text-muted-foreground text-sm",children:[K," total deals"]})}),footer:`Page ${U.pageIndex+1} of ${U.pageCount}`,emptyState:V("div",{className:"rounded-md border border-dashed p-8 text-center text-muted-foreground text-sm",children:"No deals found"})})}function FJ({onDealClick:J}){let[K,W]=u.useState([{id:"value",desc:!0}]),[_,Q]=u.useState({pageIndex:0,pageSize:3}),[O,f]=u.useState(""),[A,N]=u.useState("all"),{data:z,loading:R}=i({pageIndex:_.pageIndex,pageSize:_.pageSize,search:O,status:A,sorting:K});if(R&&!z)return V(MJ,{label:"Loading deals..."});return V(hJ,{deals:z?.deals??[],totalItems:z?.total??0,pageIndex:_.pageIndex,pageSize:_.pageSize,sorting:K,search:O,status:A,loading:R,onSortingChange:(Z)=>{W(Z),Q((F)=>({...F,pageIndex:0}))},onPaginationChange:Q,onSearchChange:(Z)=>{f(Z),Q((F)=>({...F,pageIndex:0}))},onStatusChange:(Z)=>{N(Z),Q((F)=>({...F,pageIndex:0}))},onDealClick:J})}import{Button as DJ,ErrorState as mJ,LoaderBlock as CJ,StatCard as n,StatCardGroup as SJ}from"@contractspec/lib.design-system";import{Tabs as pJ,TabsContent as KJ,TabsList as gJ,TabsTrigger as XJ}from"@contractspec/lib.ui-kit-web/ui/tabs";import{useCallback as HJ,useState as YJ}from"react";import{jsx as w,jsxs as b}from"react/jsx-runtime";function o(J,K="USD"){return new Intl.NumberFormat("en-US",{style:"currency",currency:K,minimumFractionDigits:0,maximumFractionDigits:0}).format(J)}function TK(){let[J,K]=YJ(!1),[W,_]=YJ(null),[Q,O]=YJ(!1),{data:f,dealsByStage:A,stages:N,loading:z,error:R,stats:Z,refetch:F}=i(),U=_J({onSuccess:()=>{F()}}),Y=HJ((q)=>{let $=A?Object.values(A).flat().find((X)=>X.id===q):null;if($)_($),O(!0)},[A]),B=HJ(async(q,$)=>{await U.moveDeal({dealId:q,stageId:$})},[U]);if(z&&!f)return w(CJ,{label:"Loading CRM..."});if(R)return w(mJ,{title:"Failed to load CRM",description:R.message,onRetry:F,retryLabel:"Retry"});return b("div",{className:"space-y-6",children:[b("div",{className:"flex items-center justify-between",children:[w("h2",{className:"font-bold text-2xl",children:"CRM Pipeline"}),b(DJ,{onClick:()=>K(!0),children:[w("span",{className:"mr-2",children:"+"})," Create Deal"]})]}),Z&&b(SJ,{children:[w(n,{label:"Total Pipeline",value:o(Z.totalValue),hint:`${Z.total} deals`}),w(n,{label:"Open Deals",value:o(Z.openValue),hint:`${Z.openCount} active`}),w(n,{label:"Won",value:o(Z.wonValue),hint:`${Z.wonCount} closed`}),w(n,{label:"Lost",value:Z.lostCount,hint:"deals lost"})]}),b(pJ,{defaultValue:"pipeline",className:"w-full",children:[b(gJ,{children:[b(XJ,{value:"pipeline",children:[w("span",{className:"mr-2",children:"\uD83D\uDCCA"}),"Pipeline"]}),b(XJ,{value:"list",children:[w("span",{className:"mr-2",children:"\uD83D\uDCCB"}),"All Deals"]}),b(XJ,{value:"metrics",children:[w("span",{className:"mr-2",children:"\uD83D\uDCC8"}),"Metrics"]})]}),w(KJ,{value:"pipeline",className:"min-h-[400px]",children:w(GJ,{dealsByStage:A,stages:N,onDealClick:Y,onDealMove:B})}),w(KJ,{value:"list",className:"min-h-[400px]",children:w(FJ,{onDealClick:Y})}),w(KJ,{value:"metrics",className:"min-h-[400px]",children:w(cJ,{stats:Z})})]}),w(QJ,{isOpen:J,onClose:()=>K(!1),onSubmit:async(q)=>{await U.createDeal(q)},stages:N,isLoading:U.createState.loading}),w(zJ,{isOpen:Q,deal:W,stages:N,onClose:()=>{O(!1),_(null)},onWin:async(q)=>{await U.winDeal(q)},onLose:async(q)=>{await U.loseDeal(q)},onMove:async(q)=>{await U.moveDeal(q),F()},isLoading:U.isLoading})]})}function cJ({stats:J}){if(!J)return null;return w("div",{className:"space-y-6",children:b("div",{className:"rounded-xl border border-border bg-card p-6",children:[w("h3",{className:"mb-4 font-semibold text-lg",children:"Pipeline Overview"}),b("dl",{className:"grid gap-4 sm:grid-cols-3",children:[b("div",{children:[w("dt",{className:"text-muted-foreground text-sm",children:"Win Rate"}),b("dd",{className:"font-semibold text-2xl",children:[J.total>0?(J.wonCount/J.total*100).toFixed(0):0,"%"]})]}),b("div",{children:[w("dt",{className:"text-muted-foreground text-sm",children:"Avg Deal Size"}),w("dd",{className:"font-semibold text-2xl",children:o(J.total>0?J.totalValue/J.total:0)})]}),b("div",{children:[w("dt",{className:"text-muted-foreground text-sm",children:"Conversion"}),b("dd",{className:"font-semibold text-2xl",children:[J.wonCount," / ",J.total]})]})]})]})})}export{TK as CrmDashboard};
@@ -1,2 +1,2 @@
1
1
  // @bun
2
- import{useTemplateRuntime as x}from"@contractspec/lib.example-shared-ui";import{useCallback as B,useEffect as C,useMemo as y,useState as V}from"react";function D(A={}){let{handlers:k,projectId:Q}=x(),{crm:G}=k,[J,X]=V(null),[_,Y]=V({}),[$,Z]=V([]),[T,U]=V(!0),[w,E]=V(null),[M,P]=V(0),H=A.pipelineId??"pipeline-1",q=A.pageIndex??M,F=A.pageSize??A.limit??50,[R]=A.sorting??[],O=R?.id,b=R?R.desc?"desc":"asc":void 0,f=B(async()=>{U(!0),E(null);try{let[K,W,v]=await Promise.all([G.listDeals({projectId:Q,pipelineId:H,stageId:A.stageId,status:A.status==="all"?void 0:A.status,search:A.search,limit:F,offset:q*F,sortBy:O==="name"||O==="value"||O==="status"||O==="expectedCloseDate"||O==="updatedAt"?O:void 0,sortDirection:b}),G.getDealsByStage({projectId:Q,pipelineId:H}),G.getPipelineStages({pipelineId:H})]);X(K),Y(W),Z(v)}catch(K){E(K instanceof Error?K:Error("Unknown error"))}finally{U(!1)}},[G,Q,H,A.stageId,A.status,A.search,q,F,O,b]);C(()=>{f()},[f]);let h=y(()=>{if(!J)return null;let K=J.deals.filter((N)=>N.status==="OPEN"),W=J.deals.filter((N)=>N.status==="WON"),v=J.deals.filter((N)=>N.status==="LOST");return{total:J.total,totalValue:J.totalValue,openCount:K.length,openValue:K.reduce((N,L)=>N+L.value,0),wonCount:W.length,wonValue:W.reduce((N,L)=>N+L.value,0),lostCount:v.length}},[J]);return{data:J,dealsByStage:_,stages:$,loading:T,error:w,stats:h,page:q+1,pageIndex:q,pageSize:F,refetch:f,nextPage:A.pageIndex===void 0?()=>P((K)=>K+1):void 0,prevPage:A.pageIndex===void 0?()=>q>0&&P((K)=>K-1):void 0}}import{useTemplateRuntime as I}from"@contractspec/lib.example-shared-ui";import{useCallback as z,useState as j}from"react";function g(A={}){let{handlers:k,projectId:Q}=I(),{crm:G}=k,[J,X]=j({loading:!1,error:null,data:null}),[_,Y]=j({loading:!1,error:null,data:null}),[$,Z]=j({loading:!1,error:null,data:null}),[T,U]=j({loading:!1,error:null,data:null}),w=z(async(H)=>{X({loading:!0,error:null,data:null});try{let q=await G.createDeal(H,{projectId:Q,ownerId:"user-1"});return X({loading:!1,error:null,data:q}),A.onSuccess?.(),q}catch(q){let F=q instanceof Error?q:Error("Failed to create deal");return X({loading:!1,error:F,data:null}),A.onError?.(F),null}},[G,Q,A]),E=z(async(H)=>{Y({loading:!0,error:null,data:null});try{let q=await G.moveDeal(H);return Y({loading:!1,error:null,data:q}),A.onSuccess?.(),q}catch(q){let F=q instanceof Error?q:Error("Failed to move deal");return Y({loading:!1,error:F,data:null}),A.onError?.(F),null}},[G,A]),M=z(async(H)=>{Z({loading:!0,error:null,data:null});try{let q=await G.winDeal(H);return Z({loading:!1,error:null,data:q}),A.onSuccess?.(),q}catch(q){let F=q instanceof Error?q:Error("Failed to mark deal as won");return Z({loading:!1,error:F,data:null}),A.onError?.(F),null}},[G,A]),P=z(async(H)=>{U({loading:!0,error:null,data:null});try{let q=await G.loseDeal(H);return U({loading:!1,error:null,data:q}),A.onSuccess?.(),q}catch(q){let F=q instanceof Error?q:Error("Failed to mark deal as lost");return U({loading:!1,error:F,data:null}),A.onError?.(F),null}},[G,A]);return{createDeal:w,moveDeal:E,winDeal:M,loseDeal:P,createState:J,moveState:_,winState:$,loseState:T,isLoading:J.loading||_.loading||$.loading||T.loading}}export{g as useDealMutations,D as useDealList};
2
+ import{useTemplateRuntime as x}from"@contractspec/lib.example-shared-ui";import{useCallback as B,useEffect as C,useMemo as y,useState as V}from"react";function D(A={}){let{handlers:w,projectId:Q}=x(),{crm:G}=w,[J,X]=V(null),[_,Y]=V({}),[$,Z]=V([]),[T,U]=V(!0),[M,E]=V(null),[R,P]=V(0),H=A.pipelineId??"pipeline-1",q=A.pageIndex??R,F=A.pageSize??A.limit??50,[W]=A.sorting??[],O=W?.id==="deal"?"name":W?.id,b=W?W.desc?"desc":"asc":void 0,f=B(async()=>{U(!0),E(null);try{let[K,z,v]=await Promise.all([G.listDeals({projectId:Q,pipelineId:H,stageId:A.stageId,status:A.status==="all"?void 0:A.status,search:A.search,limit:F,offset:q*F,sortBy:O==="name"||O==="value"||O==="status"||O==="expectedCloseDate"||O==="updatedAt"?O:void 0,sortDirection:b}),G.getDealsByStage({projectId:Q,pipelineId:H}),G.getPipelineStages({pipelineId:H})]);X(K),Y(z),Z(v)}catch(K){E(K instanceof Error?K:Error("Unknown error"))}finally{U(!1)}},[G,Q,H,A.stageId,A.status,A.search,q,F,O,b]);C(()=>{f()},[f]);let h=y(()=>{if(!J)return null;let K=J.deals.filter((N)=>N.status==="OPEN"),z=J.deals.filter((N)=>N.status==="WON"),v=J.deals.filter((N)=>N.status==="LOST");return{total:J.total,totalValue:J.totalValue,openCount:K.length,openValue:K.reduce((N,L)=>N+L.value,0),wonCount:z.length,wonValue:z.reduce((N,L)=>N+L.value,0),lostCount:v.length}},[J]);return{data:J,dealsByStage:_,stages:$,loading:T,error:M,stats:h,page:q+1,pageIndex:q,pageSize:F,refetch:f,nextPage:A.pageIndex===void 0?()=>P((K)=>K+1):void 0,prevPage:A.pageIndex===void 0?()=>q>0&&P((K)=>K-1):void 0}}import{useTemplateRuntime as I}from"@contractspec/lib.example-shared-ui";import{useCallback as j,useState as k}from"react";function g(A={}){let{handlers:w,projectId:Q}=I(),{crm:G}=w,[J,X]=k({loading:!1,error:null,data:null}),[_,Y]=k({loading:!1,error:null,data:null}),[$,Z]=k({loading:!1,error:null,data:null}),[T,U]=k({loading:!1,error:null,data:null}),M=j(async(H)=>{X({loading:!0,error:null,data:null});try{let q=await G.createDeal(H,{projectId:Q,ownerId:"user-1"});return X({loading:!1,error:null,data:q}),A.onSuccess?.(),q}catch(q){let F=q instanceof Error?q:Error("Failed to create deal");return X({loading:!1,error:F,data:null}),A.onError?.(F),null}},[G,Q,A]),E=j(async(H)=>{Y({loading:!0,error:null,data:null});try{let q=await G.moveDeal(H);return Y({loading:!1,error:null,data:q}),A.onSuccess?.(),q}catch(q){let F=q instanceof Error?q:Error("Failed to move deal");return Y({loading:!1,error:F,data:null}),A.onError?.(F),null}},[G,A]),R=j(async(H)=>{Z({loading:!0,error:null,data:null});try{let q=await G.winDeal(H);return Z({loading:!1,error:null,data:q}),A.onSuccess?.(),q}catch(q){let F=q instanceof Error?q:Error("Failed to mark deal as won");return Z({loading:!1,error:F,data:null}),A.onError?.(F),null}},[G,A]),P=j(async(H)=>{U({loading:!0,error:null,data:null});try{let q=await G.loseDeal(H);return U({loading:!1,error:null,data:q}),A.onSuccess?.(),q}catch(q){let F=q instanceof Error?q:Error("Failed to mark deal as lost");return U({loading:!1,error:F,data:null}),A.onError?.(F),null}},[G,A]);return{createDeal:M,moveDeal:E,winDeal:R,loseDeal:P,createState:J,moveState:_,winState:$,loseState:T,isLoading:J.loading||_.loading||$.loading||T.loading}}export{g as useDealMutations,D as useDealList};