|
| 1 | +'use client' |
| 2 | + |
| 3 | +import React, { useState, useCallback } from 'react' |
| 4 | +import type { BoardData, Deal, gecoStatus, CRMStatus, Customer, EditableDeal, PartialComment } from './types' |
| 5 | +import { DealDetails } from './DealDetails' |
| 6 | +import { KanbanColumn } from './KanbanColumn' |
| 7 | +import { |
| 8 | + DragDropContext, |
| 9 | + type DropResult, |
| 10 | +} from '@atlaskit/pragmatic-drag-and-drop-react-beautiful-dnd-migration' |
| 11 | + |
| 12 | +const gcstatuses: gecoStatus[] = ['firm', 'forecast', 'other'] |
| 13 | +const statuses: CRMStatus[] = ['Cold', 'Qualified', 'Proposal Made', 'SoW Submitted', 'Won', 'Lost'] |
| 14 | + |
| 15 | +type CogeBoardProps = { |
| 16 | + initialData: BoardData |
| 17 | + // onDragEnd: (result: DropResult) => void |
| 18 | + // addNewDeal: (newDeal: Deal) => Promise<{ success: boolean; errors?: Record<string, string> }>; |
| 19 | + updateDeal: (updatedDeal: Partial<EditableDeal>) => Promise<{ success: boolean; errors?: Record<string, string> }>; |
| 20 | + addComment: (dealId: string, comment: PartialComment) => Promise<{ success: boolean; errors?: Record<string, string> }>; |
| 21 | + // addNewCustomer: (newCustomer: Partial<Customer>) => Promise<{ success: boolean; errors?: Record<string, string> }>; |
| 22 | +} |
| 23 | + |
| 24 | +export function CRMCogeBoard({ |
| 25 | + initialData, |
| 26 | + // addNewDeal, |
| 27 | + updateDeal, |
| 28 | + addComment, |
| 29 | + // addNewCustomer, |
| 30 | +}: CogeBoardProps) { |
| 31 | + const [selectedDeal, setSelectedDeal] = useState<Deal | null>(null) |
| 32 | + // const [boardData, setBoardData] = useState<BoardData>(initialData) |
| 33 | + |
| 34 | + const getColumnDeals = (status: gecoStatus) => { |
| 35 | + return (initialData.deals ?? []) |
| 36 | + .filter((deal) => deal.gecoStatus === status) |
| 37 | + .filter((deal) => deal.status !== 'Lost' && deal.status !== 'Cold') |
| 38 | +} |
| 39 | + |
| 40 | + const calculateColumnValue = (deals: Deal[]) => { |
| 41 | + return deals.reduce((sum, deal) => sum + (deal.value || 0), 0) |
| 42 | + } |
| 43 | + |
| 44 | + const calculateWeightedValue = (deals: Deal[], status: CRMStatus | gecoStatus) => { |
| 45 | + const weightMap: Record<CRMStatus, number> = { |
| 46 | + Cold: 0, |
| 47 | + Qualified: 0.2, |
| 48 | + 'Proposal Made': 0.5, |
| 49 | + 'SoW Submitted': 0.8, |
| 50 | + Won: 1, |
| 51 | + Lost: 0, |
| 52 | + } |
| 53 | + return deals.reduce((sum, deal) => sum + (deal.value || 0) * (weightMap[deal.status] ?? 0), 0) |
| 54 | + } |
| 55 | + |
| 56 | + const onDragEnd = useCallback( |
| 57 | + (result: DropResult) => { |
| 58 | + const { source, destination } = result |
| 59 | + // console.log('onDragEnd', result, destination) |
| 60 | + if (!destination) { |
| 61 | + return |
| 62 | + } |
| 63 | + |
| 64 | + if (source.droppableId === destination.droppableId && source.index === destination.index) { |
| 65 | + return |
| 66 | + } |
| 67 | + |
| 68 | + const sourceStatus = source.droppableId as gecoStatus |
| 69 | + const destinationStatus = destination.droppableId as gecoStatus |
| 70 | + // console.log('Moving from ', sourceStatus, 'to', destinationStatus) |
| 71 | + // find the deal with id source.index |
| 72 | + // |
| 73 | + let movedDeal = initialData.deals?.filter((deal) => deal.id === source.index)[0] |
| 74 | + // console.log('deal', movedDeal) |
| 75 | + |
| 76 | + const updatedDeals = [...(initialData.deals ?? [])] |
| 77 | + // const [movedDeal] = updatedDeals.splice(source.index, 1) |
| 78 | + if (movedDeal) { |
| 79 | + movedDeal.gecoStatus = destinationStatus |
| 80 | + // updatedDeals.splice(destination.index, 0, movedDeal) |
| 81 | + |
| 82 | + // Update the deal using the updateDeal function |
| 83 | + updateDeal(movedDeal as EditableDeal) |
| 84 | + } |
| 85 | + }, |
| 86 | + [updateDeal], |
| 87 | + ) |
| 88 | + |
| 89 | + return ( |
| 90 | + <DragDropContext onDragEnd={onDragEnd}> |
| 91 | + <div className="p-4 h-full overflow-auto"> |
| 92 | + {/* <h1 className="text-3xl font-bold mb-8">Cortex Sales Pipeline</h1> */} |
| 93 | + <div className="flex space-x-4 pb-4"> |
| 94 | + {gcstatuses |
| 95 | + |
| 96 | + .map((status) => { |
| 97 | + const deals = getColumnDeals(status) |
| 98 | + return ( |
| 99 | + <KanbanColumn |
| 100 | + key={status} |
| 101 | + status={status} |
| 102 | + deals={deals} |
| 103 | + customers={initialData.customers ?? []} |
| 104 | + users={initialData.users} |
| 105 | + categories={initialData.categories ?? []} |
| 106 | + onDealClick={setSelectedDeal} |
| 107 | + calculateColumnValue={calculateColumnValue} |
| 108 | + calculateWeightedValue={calculateWeightedValue} |
| 109 | + compact={true} |
| 110 | + /> |
| 111 | + ) |
| 112 | + })} |
| 113 | + </div> |
| 114 | + |
| 115 | + {selectedDeal && ( |
| 116 | + <DealDetails |
| 117 | + deal={selectedDeal} |
| 118 | + users={initialData.users} |
| 119 | + customer={selectedDeal.customer as Customer} |
| 120 | + categories={initialData.categories ?? []} |
| 121 | + onClose={() => setSelectedDeal(null)} |
| 122 | + onSave={updateDeal} |
| 123 | + onAddComment={(comment) => addComment(selectedDeal.id.toString(), comment)} |
| 124 | + /> |
| 125 | + )} |
| 126 | + </div> |
| 127 | + </DragDropContext> |
| 128 | + ) |
| 129 | +} |
0 commit comments