@paro.io/expert-shared-components 1.13.11 → 1.13.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/components/Escalations/AccountSuspensionModal.js +1 -1
- package/lib/components/Escalations/EscalationSubmitForm.js +0 -1
- package/lib/components/Escalations/MarkResolvedModal.js +1 -1
- package/lib/components/FileUploader/index.d.ts +1 -1
- package/lib/components/ProjectIntelligence/EngagementHeader/index.d.ts +18 -0
- package/lib/components/ProjectIntelligence/EngagementHeader/index.js +47 -0
- package/lib/components/ProjectIntelligence/FocusAreas/index.d.ts +18 -0
- package/lib/components/ProjectIntelligence/FocusAreas/index.js +156 -0
- package/lib/components/ProjectIntelligence/KeyMetrics/index.d.ts +12 -0
- package/lib/components/ProjectIntelligence/KeyMetrics/index.js +148 -0
- package/lib/components/ProjectIntelligence/MissingInformation/index.d.ts +24 -0
- package/lib/components/ProjectIntelligence/MissingInformation/index.js +218 -0
- package/lib/components/ProjectIntelligence/ProgressTracker/index.d.ts +8 -0
- package/lib/components/ProjectIntelligence/ProgressTracker/index.js +21 -0
- package/lib/components/ProjectIntelligence/ProjectHealth/index.d.ts +25 -0
- package/lib/components/ProjectIntelligence/ProjectHealth/index.js +42 -0
- package/lib/components/ProjectIntelligence/TeamSection/index.d.ts +25 -0
- package/lib/components/ProjectIntelligence/TeamSection/index.js +41 -0
- package/lib/components/ProjectIntelligence/index.d.ts +17 -0
- package/lib/components/ProjectIntelligence/index.js +146 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +3 -1
- package/package.json +1 -1
|
@@ -73,7 +73,7 @@ const AccountSuspensionModal = ({ showSuspensionModal, onClose }) => {
|
|
|
73
73
|
react_1.default.createElement("b", null, "Note: "),
|
|
74
74
|
"Warnings are counted within this level, not as a total. Suspension is triggered after 1 warning at this severity. ",
|
|
75
75
|
react_1.default.createElement("b", null, "After 1 warning in this level"),
|
|
76
|
-
":
|
|
76
|
+
": Permanent suspension")))),
|
|
77
77
|
react_1.default.createElement("div", { className: "text-xs" },
|
|
78
78
|
react_1.default.createElement("b", null, "\u26A0\uFE0F Suspension thresholds are based on the number of warnings within each severity level, not total warnings across all levels"))),
|
|
79
79
|
react_1.default.createElement("div", { className: "mt-6 bg-gray-50 rounded-lg p-4" },
|
|
@@ -161,7 +161,6 @@ const EscalationSubmitForm = ({ goBack, goHome, expertsOrClients, projects, docu
|
|
|
161
161
|
const isFormValid = () => {
|
|
162
162
|
const requiredProjectSelection = isExpert || (!isExpert && selectedProjects.length > 0);
|
|
163
163
|
const requireEscalationTo = (isExpert || (!isExpert && clientEscalationTo !== ''));
|
|
164
|
-
console.log({ issueStartDate });
|
|
165
164
|
return (selectedUser !== null &&
|
|
166
165
|
issueType !== '' &&
|
|
167
166
|
severity !== '' &&
|
|
@@ -98,6 +98,6 @@ const MarkResolvedModal = ({ escalationId, expertName, open, onClose, updateProj
|
|
|
98
98
|
react_1.default.createElement("div", { className: "flex space-x-2" },
|
|
99
99
|
react_1.default.createElement(base_ui_1.Button, { label: "Cancel", onClick: onClose, disabled: submitting })),
|
|
100
100
|
react_1.default.createElement("div", { className: "flex space-x-2" },
|
|
101
|
-
react_1.default.createElement(base_ui_1.Button, { label: "Submit", color: "primary", onClick: handleSubmit, isLoading: submitting, disabled: expertResponsibility === '' || resolutionText === '' }))))));
|
|
101
|
+
react_1.default.createElement(base_ui_1.Button, { label: "Submit", color: "primary", onClick: handleSubmit, isLoading: submitting, disabled: expertResponsibility === '' || !resolutionText || resolutionText.trim() === '' }))))));
|
|
102
102
|
};
|
|
103
103
|
exports.default = MarkResolvedModal;
|
|
@@ -11,7 +11,7 @@ interface UploadFileParams {
|
|
|
11
11
|
updateProjectEscalation?: any;
|
|
12
12
|
uploadExpertClientFiles?: any;
|
|
13
13
|
createProjectEscalation?: any;
|
|
14
|
-
previousFiles?: string | string[]
|
|
14
|
+
previousFiles?: string | string[];
|
|
15
15
|
isExpert: boolean;
|
|
16
16
|
extraData?: {
|
|
17
17
|
clientId: number;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
interface ClientTab {
|
|
3
|
+
id: string;
|
|
4
|
+
label: string;
|
|
5
|
+
clientId: string;
|
|
6
|
+
}
|
|
7
|
+
interface EngagementHeaderProps {
|
|
8
|
+
clientName: string;
|
|
9
|
+
expertName: string;
|
|
10
|
+
expertTitle: string;
|
|
11
|
+
startDate: string;
|
|
12
|
+
status: 'Discovery' | 'Kickoff_Complete' | 'In_Progress' | 'Completed' | 'Paused' | 'Cancelled';
|
|
13
|
+
clientTabs: ClientTab[];
|
|
14
|
+
selectedClientId: string | null;
|
|
15
|
+
onClientChange: (opportunityId: string) => void;
|
|
16
|
+
}
|
|
17
|
+
export declare const EngagementHeader: React.FC<EngagementHeaderProps>;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.EngagementHeader = void 0;
|
|
7
|
+
const react_1 = __importDefault(require("react"));
|
|
8
|
+
const date_fns_1 = require("date-fns");
|
|
9
|
+
const base_ui_1 = require("@paro.io/base-ui");
|
|
10
|
+
const EscalationIssueCard_1 = require("../../Escalations/EscalationIssueCard");
|
|
11
|
+
const STATUS_COLORS = {
|
|
12
|
+
Discovery: 'warning',
|
|
13
|
+
Kickoff_Complete: 'success',
|
|
14
|
+
In_Progress: 'info',
|
|
15
|
+
Completed: 'neutral',
|
|
16
|
+
Paused: 'warning',
|
|
17
|
+
Cancelled: 'danger',
|
|
18
|
+
};
|
|
19
|
+
const STATUS_LABELS = {
|
|
20
|
+
Discovery: 'DISCOVERY',
|
|
21
|
+
Kickoff_Complete: 'KICKOFF COMPLETE',
|
|
22
|
+
In_Progress: 'IN PROGRESS',
|
|
23
|
+
Completed: 'COMPLETED',
|
|
24
|
+
Paused: 'PAUSED',
|
|
25
|
+
Cancelled: 'CANCELLED',
|
|
26
|
+
};
|
|
27
|
+
const EngagementHeader = ({ clientName, expertName, expertTitle, startDate, status, clientTabs, selectedClientId, onClientChange }) => {
|
|
28
|
+
const formattedDate = startDate ? (0, date_fns_1.format)(new Date(startDate), 'MMMM dd, yyyy') : (0, date_fns_1.format)(new Date(), 'MMMM dd, yyyy');
|
|
29
|
+
return (react_1.default.createElement("div", { className: "bg-white rounded-lg shadow-sm border border-gray-200" },
|
|
30
|
+
react_1.default.createElement("div", { className: "px-6 pt-4" },
|
|
31
|
+
react_1.default.createElement("p", { className: "text-sm font-medium text-gray-700 mb-3" }, "Select Client:"),
|
|
32
|
+
react_1.default.createElement("div", { className: "flex flex-wrap gap-2 mb-4" }, clientTabs.map((client) => (react_1.default.createElement(base_ui_1.Button, { key: client.id, label: client.label, onClick: () => onClientChange(client.id), color: "primary" })))),
|
|
33
|
+
react_1.default.createElement("hr", { className: "border-gray-200" })),
|
|
34
|
+
react_1.default.createElement("div", { className: "px-6 py-4" },
|
|
35
|
+
react_1.default.createElement("div", { className: "flex items-center justify-between" },
|
|
36
|
+
react_1.default.createElement("div", null,
|
|
37
|
+
react_1.default.createElement("h1", { className: "text-2xl font-bold text-gray-900 mb-1" },
|
|
38
|
+
clientName,
|
|
39
|
+
" \u00D7 ",
|
|
40
|
+
expertName),
|
|
41
|
+
react_1.default.createElement("p", { className: "text-gray-600" },
|
|
42
|
+
expertTitle,
|
|
43
|
+
" \u2022 Started ",
|
|
44
|
+
formattedDate)),
|
|
45
|
+
react_1.default.createElement(EscalationIssueCard_1.CustomTag, { label: STATUS_LABELS[status] === 'GOOD' ? 'On track' : STATUS_LABELS[status] === 'ATTENTION_NEEDED' ? 'Needs attention' : 'Critical', customColor: `bg-${STATUS_COLORS[status]} border-${STATUS_COLORS[status]}` })))));
|
|
46
|
+
};
|
|
47
|
+
exports.EngagementHeader = EngagementHeader;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
interface FocusArea {
|
|
3
|
+
id: string;
|
|
4
|
+
title: string;
|
|
5
|
+
description: string;
|
|
6
|
+
status: 'CONFIRMED' | 'IN_PROGRESS' | 'COMPLETED' | 'AT_RISK';
|
|
7
|
+
dueDate: string;
|
|
8
|
+
priority: 'HIGH' | 'MEDIUM' | 'LOW';
|
|
9
|
+
}
|
|
10
|
+
interface FocusAreasProps {
|
|
11
|
+
focusAreas: FocusArea[];
|
|
12
|
+
onEditFocusAreas: () => void;
|
|
13
|
+
updateOpportunityFocusArea: any;
|
|
14
|
+
GetOpportunityInsightsDocument: any;
|
|
15
|
+
opportunityId: string;
|
|
16
|
+
}
|
|
17
|
+
export declare const FocusAreas: React.FC<FocusAreasProps>;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.FocusAreas = void 0;
|
|
27
|
+
const react_1 = __importStar(require("react"));
|
|
28
|
+
const date_fns_1 = require("date-fns");
|
|
29
|
+
const utils_1 = require("../../shared/utils");
|
|
30
|
+
const core_1 = require("@material-ui/core");
|
|
31
|
+
const base_icons_1 = require("@paro.io/base-icons");
|
|
32
|
+
const base_ui_1 = require("@paro.io/base-ui");
|
|
33
|
+
const EscalationIssueCard_1 = require("../../Escalations/EscalationIssueCard");
|
|
34
|
+
const TeamSection_1 = require("../TeamSection");
|
|
35
|
+
const STATUS_COLORS = {
|
|
36
|
+
CONFIRMED: "success",
|
|
37
|
+
IN_PROGRESS: "info",
|
|
38
|
+
COMPLETED: "neutral",
|
|
39
|
+
AT_RISK: "warning",
|
|
40
|
+
};
|
|
41
|
+
const STATUS_ICONS = {
|
|
42
|
+
CONFIRMED: react_1.default.createElement(base_icons_1.IconCheck, { size: "sm" }),
|
|
43
|
+
IN_PROGRESS: react_1.default.createElement(base_icons_1.IconDotsHorizontal, { size: "sm" }),
|
|
44
|
+
COMPLETED: react_1.default.createElement(base_icons_1.IconCheck, { size: "sm" }),
|
|
45
|
+
AT_RISK: react_1.default.createElement(base_icons_1.IconExclamation, { size: "sm" }),
|
|
46
|
+
};
|
|
47
|
+
const PRIORITY_COLORS = {
|
|
48
|
+
HIGH: 'danger',
|
|
49
|
+
MEDIUM: 'warning',
|
|
50
|
+
LOW: 'info',
|
|
51
|
+
};
|
|
52
|
+
// Modal Component
|
|
53
|
+
const EditFocusAreaModal = ({ isOpen, focusArea, onClose, onSave, updateOpportunityFocusArea, GetOpportunityInsightsDocument, opportunityId, }) => {
|
|
54
|
+
const [title, setTitle] = (0, react_1.useState)((focusArea === null || focusArea === void 0 ? void 0 : focusArea.title) || '');
|
|
55
|
+
const [description, setDescription] = (0, react_1.useState)((focusArea === null || focusArea === void 0 ? void 0 : focusArea.description) || '');
|
|
56
|
+
react_1.default.useEffect(() => {
|
|
57
|
+
setTitle((focusArea === null || focusArea === void 0 ? void 0 : focusArea.title) || '');
|
|
58
|
+
setDescription((focusArea === null || focusArea === void 0 ? void 0 : focusArea.description) || '');
|
|
59
|
+
}, [focusArea]);
|
|
60
|
+
const handleSave = () => {
|
|
61
|
+
if (focusArea) {
|
|
62
|
+
try {
|
|
63
|
+
updateOpportunityFocusArea({
|
|
64
|
+
variables: {
|
|
65
|
+
opportunityId: `0063l00000nqlm2AAA`,
|
|
66
|
+
input: {
|
|
67
|
+
id: focusArea.id,
|
|
68
|
+
title,
|
|
69
|
+
description
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
refetchQueries: [
|
|
73
|
+
{ query: GetOpportunityInsightsDocument, variables: { opportunityId } }
|
|
74
|
+
]
|
|
75
|
+
});
|
|
76
|
+
(0, utils_1.showToast)('success', 'Focus area updated successfully');
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
console.error('Error updating focus area:', error);
|
|
80
|
+
(0, utils_1.showToast)('warning', 'Error updating focus area. Please try again.');
|
|
81
|
+
}
|
|
82
|
+
onClose();
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
const handleCancel = () => {
|
|
86
|
+
setTitle((focusArea === null || focusArea === void 0 ? void 0 : focusArea.title) || '');
|
|
87
|
+
setDescription((focusArea === null || focusArea === void 0 ? void 0 : focusArea.description) || '');
|
|
88
|
+
onClose();
|
|
89
|
+
};
|
|
90
|
+
if (!isOpen || !focusArea)
|
|
91
|
+
return null;
|
|
92
|
+
return (react_1.default.createElement(core_1.Dialog, { open: isOpen, onClose: onClose, fullWidth: true },
|
|
93
|
+
react_1.default.createElement(core_1.DialogTitle, null,
|
|
94
|
+
react_1.default.createElement("div", { className: "text-black mb-1 p-2 pl-4 absolute top-0 left-0 w-full flex flex-row justify-between items-center z-50" },
|
|
95
|
+
"Edit Focus Area",
|
|
96
|
+
react_1.default.createElement(core_1.IconButton, { onClick: onClose },
|
|
97
|
+
react_1.default.createElement(base_icons_1.IconX, null)))),
|
|
98
|
+
react_1.default.createElement(core_1.DialogContent, null,
|
|
99
|
+
react_1.default.createElement("div", { className: "space-y-4 mt-4" },
|
|
100
|
+
react_1.default.createElement("div", null,
|
|
101
|
+
react_1.default.createElement("label", { htmlFor: "title", className: "block text-sm font-medium text-gray-700 mb-2" }, "Title"),
|
|
102
|
+
react_1.default.createElement("textarea", { id: "title", value: title, onChange: (e) => setTitle(e.target.value), className: "w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500", placeholder: "Enter focus area title..." })),
|
|
103
|
+
react_1.default.createElement("div", null,
|
|
104
|
+
react_1.default.createElement("label", { htmlFor: "description", className: "block text-sm font-medium text-gray-700 mb-2" }, "Description"),
|
|
105
|
+
react_1.default.createElement("textarea", { id: "description", value: description, onChange: (e) => setDescription(e.target.value), rows: 4, className: "w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500", placeholder: "Enter focus area description..." }))),
|
|
106
|
+
react_1.default.createElement("div", { className: "px-6 py-4 flex justify-end space-x-3" },
|
|
107
|
+
react_1.default.createElement(base_ui_1.Button, { label: "cancel", onClick: handleCancel }),
|
|
108
|
+
react_1.default.createElement(base_ui_1.Button, { label: "save", onClick: handleSave, color: "primary" })))));
|
|
109
|
+
};
|
|
110
|
+
const FocusAreas = ({ focusAreas, onEditFocusAreas, updateOpportunityFocusArea, GetOpportunityInsightsDocument, opportunityId, }) => {
|
|
111
|
+
const [editingFocusArea, setEditingFocusArea] = (0, react_1.useState)(null);
|
|
112
|
+
const [isModalOpen, setIsModalOpen] = (0, react_1.useState)(false);
|
|
113
|
+
const [localFocusAreas, setLocalFocusAreas] = (0, react_1.useState)(focusAreas || []);
|
|
114
|
+
react_1.default.useEffect(() => {
|
|
115
|
+
setLocalFocusAreas(focusAreas);
|
|
116
|
+
}, [focusAreas]);
|
|
117
|
+
const handleEditClick = (focusArea) => {
|
|
118
|
+
setEditingFocusArea(focusArea);
|
|
119
|
+
setIsModalOpen(true);
|
|
120
|
+
};
|
|
121
|
+
const handleCloseModal = () => {
|
|
122
|
+
setIsModalOpen(false);
|
|
123
|
+
setEditingFocusArea(null);
|
|
124
|
+
};
|
|
125
|
+
const handleSaveFocusArea = (id, newTitle, newDescription) => {
|
|
126
|
+
setLocalFocusAreas(prev => prev.map(area => area.id === id
|
|
127
|
+
? Object.assign(Object.assign({}, area), { title: newTitle, description: newDescription }) : area));
|
|
128
|
+
// Here you would typically call an API to update the focus area
|
|
129
|
+
console.log('Saving focus area:', id, newTitle, newDescription);
|
|
130
|
+
};
|
|
131
|
+
return (react_1.default.createElement(react_1.default.Fragment, null,
|
|
132
|
+
react_1.default.createElement("div", { className: "bg-white rounded-lg shadow-sm border border-gray-200" },
|
|
133
|
+
react_1.default.createElement("div", { className: "flex items-center justify-between px-6 py-4 border-b border-gray-200" },
|
|
134
|
+
react_1.default.createElement(TeamSection_1.Heading, { icon: react_1.default.createElement(base_icons_1.IconTarget, { size: "sm", className: "mr-4" }), headerText: "Focus Areas" }),
|
|
135
|
+
react_1.default.createElement("p", { className: "text-sm text-gray-600 mt-1" }, "Time-sensitive tasks that need attention today/this week")),
|
|
136
|
+
react_1.default.createElement("div", { className: "p-6" },
|
|
137
|
+
react_1.default.createElement("div", { className: "space-y-4" }, localFocusAreas.map((area, index) => (react_1.default.createElement("div", { key: area.id, className: "flex items-start space-x-4 p-4 border border-gray-200 rounded-lg hover:shadow-sm transition-shadow" },
|
|
138
|
+
react_1.default.createElement("div", { className: "flex-1 min-w-0" },
|
|
139
|
+
react_1.default.createElement("div", { className: "flex items-center justify-between mb-1" },
|
|
140
|
+
react_1.default.createElement("div", { className: "flex items-center space-x-2" },
|
|
141
|
+
react_1.default.createElement("div", { className: "text-sm font-bold" }, area.title),
|
|
142
|
+
react_1.default.createElement(EscalationIssueCard_1.CustomTag, { label: area.status.replace('_', ' '), iconLeft: STATUS_ICONS[area.status], customColor: `bg-${STATUS_COLORS[area.status]} border-${STATUS_COLORS[area.status]}` })),
|
|
143
|
+
react_1.default.createElement(base_ui_1.Button, { label: "Edit focus area", iconLeft: react_1.default.createElement(base_icons_1.IconPencil, { size: "sm" }), onClick: () => handleEditClick(area), color: "success", size: 'sm' })),
|
|
144
|
+
react_1.default.createElement("p", { className: "text-sm text-gray-600 mb-2" }, area.description),
|
|
145
|
+
react_1.default.createElement("div", { className: "flex items-center text-sm font-medium space-x-2" },
|
|
146
|
+
react_1.default.createElement("span", null, (0, date_fns_1.format)(new Date(area.dueDate), 'MMM dd, yyyy')),
|
|
147
|
+
react_1.default.createElement("span", null, "\u2022"),
|
|
148
|
+
react_1.default.createElement("span", { className: `text-${PRIORITY_COLORS[area.priority]}` },
|
|
149
|
+
area.priority,
|
|
150
|
+
" Priority"),
|
|
151
|
+
area.status === 'IN_PROGRESS' && (react_1.default.createElement(react_1.default.Fragment, null,
|
|
152
|
+
react_1.default.createElement("span", null, "\u2022"),
|
|
153
|
+
react_1.default.createElement("span", { className: "text-info" }, "In progress"))))))))))),
|
|
154
|
+
react_1.default.createElement(EditFocusAreaModal, { isOpen: isModalOpen, focusArea: editingFocusArea, onClose: handleCloseModal, onSave: handleSaveFocusArea, updateOpportunityFocusArea: updateOpportunityFocusArea, GetOpportunityInsightsDocument: GetOpportunityInsightsDocument, opportunityId: opportunityId })));
|
|
155
|
+
};
|
|
156
|
+
exports.FocusAreas = FocusAreas;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
interface KeyMetricsData {
|
|
3
|
+
targetAchievements: number;
|
|
4
|
+
hourlyRate: number;
|
|
5
|
+
daysSinceGoal: number;
|
|
6
|
+
totalContractLength: number;
|
|
7
|
+
}
|
|
8
|
+
interface KeyMetricsProps {
|
|
9
|
+
metrics: KeyMetricsData;
|
|
10
|
+
}
|
|
11
|
+
export declare const KeyMetrics: React.FC<KeyMetricsProps>;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.KeyMetrics = void 0;
|
|
27
|
+
const react_1 = __importStar(require("react"));
|
|
28
|
+
const EscalationIssueCard_1 = require("../../Escalations/EscalationIssueCard");
|
|
29
|
+
const base_icons_1 = require("@paro.io/base-icons");
|
|
30
|
+
const TeamSection_1 = require("../TeamSection");
|
|
31
|
+
// Edit Metrics Modal Component
|
|
32
|
+
const EditMetricsModal = ({ isOpen, metrics, onClose, onSave }) => {
|
|
33
|
+
const [formData, setFormData] = (0, react_1.useState)(metrics);
|
|
34
|
+
react_1.default.useEffect(() => {
|
|
35
|
+
setFormData(metrics);
|
|
36
|
+
}, [metrics]);
|
|
37
|
+
const handleSave = () => {
|
|
38
|
+
onSave(formData);
|
|
39
|
+
onClose();
|
|
40
|
+
};
|
|
41
|
+
const handleCancel = () => {
|
|
42
|
+
setFormData(metrics);
|
|
43
|
+
onClose();
|
|
44
|
+
};
|
|
45
|
+
const handleInputChange = (field, value) => {
|
|
46
|
+
const numericValue = parseFloat(value) || 0;
|
|
47
|
+
setFormData(prev => (Object.assign(Object.assign({}, prev), { [field]: numericValue })));
|
|
48
|
+
};
|
|
49
|
+
if (!isOpen)
|
|
50
|
+
return null;
|
|
51
|
+
return (react_1.default.createElement("div", { className: "fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50" },
|
|
52
|
+
react_1.default.createElement("div", { className: "bg-white rounded-lg shadow-xl w-full max-w-md mx-4" },
|
|
53
|
+
react_1.default.createElement("div", { className: "px-6 py-4 border-b border-gray-200" },
|
|
54
|
+
react_1.default.createElement("div", { className: "flex items-center justify-between" },
|
|
55
|
+
react_1.default.createElement("h2", { className: "text-lg font-semibold text-gray-900" }, "Edit Engagement Metrics"),
|
|
56
|
+
react_1.default.createElement("button", { onClick: onClose, className: "text-gray-400 hover:text-gray-600 transition-colors" },
|
|
57
|
+
react_1.default.createElement("svg", { className: "w-6 h-6", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24" },
|
|
58
|
+
react_1.default.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M6 18L18 6M6 6l12 12" }))))),
|
|
59
|
+
react_1.default.createElement("div", { className: "px-6 py-4 space-y-4" },
|
|
60
|
+
react_1.default.createElement("div", null,
|
|
61
|
+
react_1.default.createElement("label", { htmlFor: "targetAchievements", className: "block text-sm font-medium text-gray-700 mb-1" }, "Target Hours/Month"),
|
|
62
|
+
react_1.default.createElement("input", { id: "targetAchievements", type: "number", value: formData.targetAchievements, onChange: (e) => handleInputChange('targetAchievements', e.target.value), className: "w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-sm", placeholder: "Enter target hours per month" })),
|
|
63
|
+
react_1.default.createElement("div", null,
|
|
64
|
+
react_1.default.createElement("label", { htmlFor: "hourlyRate", className: "block text-sm font-medium text-gray-700 mb-1" }, "Hourly Rate ($)"),
|
|
65
|
+
react_1.default.createElement("input", { id: "hourlyRate", type: "number", step: "0.01", value: formData.hourlyRate, onChange: (e) => handleInputChange('hourlyRate', e.target.value), className: "w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-sm", placeholder: "Enter hourly rate" })),
|
|
66
|
+
react_1.default.createElement("div", null,
|
|
67
|
+
react_1.default.createElement("label", { htmlFor: "daysSinceGoal", className: "block text-sm font-medium text-gray-700 mb-1" }, "Day Close Goal"),
|
|
68
|
+
react_1.default.createElement("input", { id: "daysSinceGoal", type: "number", value: formData.daysSinceGoal, onChange: (e) => handleInputChange('daysSinceGoal', e.target.value), className: "w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-sm", placeholder: "Enter day close goal" })),
|
|
69
|
+
react_1.default.createElement("div", null,
|
|
70
|
+
react_1.default.createElement("label", { htmlFor: "totalContractLength", className: "block text-sm font-medium text-gray-700 mb-1" }, "Total Contract Length (months)"),
|
|
71
|
+
react_1.default.createElement("input", { id: "totalContractLength", type: "number", value: formData.totalContractLength, onChange: (e) => handleInputChange('totalContractLength', e.target.value), className: "w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-sm", placeholder: "Enter contract length in months" }))),
|
|
72
|
+
react_1.default.createElement("div", { className: "px-6 py-4 border-t border-gray-200 flex justify-end space-x-3" },
|
|
73
|
+
react_1.default.createElement("button", { onClick: handleCancel, className: "px-4 py-2 text-sm font-medium text-gray-700 bg-white border border-gray-300 rounded-md hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-gray-500 transition-colors" }, "Cancel"),
|
|
74
|
+
react_1.default.createElement("button", { onClick: handleSave, className: "px-4 py-2 text-sm font-medium text-white bg-blue-600 border border-transparent rounded-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 transition-colors" }, "Save Changes")))));
|
|
75
|
+
};
|
|
76
|
+
const KeyMetrics = ({ metrics }) => {
|
|
77
|
+
const [isModalOpen, setIsModalOpen] = (0, react_1.useState)(false);
|
|
78
|
+
const [localMetrics, setLocalMetrics] = (0, react_1.useState)(metrics);
|
|
79
|
+
react_1.default.useEffect(() => {
|
|
80
|
+
setLocalMetrics(metrics);
|
|
81
|
+
}, [metrics]);
|
|
82
|
+
const handleOpenModal = () => {
|
|
83
|
+
setIsModalOpen(true);
|
|
84
|
+
};
|
|
85
|
+
const handleCloseModal = () => {
|
|
86
|
+
setIsModalOpen(false);
|
|
87
|
+
};
|
|
88
|
+
const handleSaveMetrics = (newMetrics) => {
|
|
89
|
+
setLocalMetrics(newMetrics);
|
|
90
|
+
// Here you would typically call an API to update the metrics
|
|
91
|
+
console.log('Saving metrics:', newMetrics);
|
|
92
|
+
};
|
|
93
|
+
return (react_1.default.createElement("div", { className: "flex flex-col space-y-4" },
|
|
94
|
+
react_1.default.createElement("div", { className: "bg-white rounded-lg shadow-sm border border-gray-200 p-6" },
|
|
95
|
+
react_1.default.createElement(TeamSection_1.Heading, { icon: react_1.default.createElement(base_icons_1.IconViewGrid, { size: "sm", className: "mr-4" }), headerText: "Engagement Metrics" }),
|
|
96
|
+
react_1.default.createElement("div", { className: "grid grid-cols-2 gap-4 mt-2" },
|
|
97
|
+
react_1.default.createElement("div", { className: "text-center" },
|
|
98
|
+
react_1.default.createElement("div", { className: "text-2xl font-bold text-gray-900" }, localMetrics.targetAchievements),
|
|
99
|
+
react_1.default.createElement("div", { className: "text-sm text-gray-600" }, "Target Hours/Month")),
|
|
100
|
+
react_1.default.createElement("div", { className: "text-center" },
|
|
101
|
+
react_1.default.createElement("div", { className: "text-2xl font-bold text-gray-900" },
|
|
102
|
+
"$",
|
|
103
|
+
localMetrics.hourlyRate),
|
|
104
|
+
react_1.default.createElement("div", { className: "text-sm text-gray-600" }, "Hourly Rate")),
|
|
105
|
+
react_1.default.createElement("div", { className: "text-center" },
|
|
106
|
+
react_1.default.createElement("div", { className: "text-2xl font-bold text-gray-900" },
|
|
107
|
+
localMetrics.daysSinceGoal,
|
|
108
|
+
"-5"),
|
|
109
|
+
react_1.default.createElement("div", { className: "text-sm text-gray-600" }, "Day Close Goal")),
|
|
110
|
+
react_1.default.createElement("div", { className: "text-center" },
|
|
111
|
+
react_1.default.createElement("div", { className: "text-2xl font-bold text-gray-900" }, localMetrics.totalContractLength),
|
|
112
|
+
react_1.default.createElement("div", { className: "text-sm text-gray-600" }, "Total Contract Length")))),
|
|
113
|
+
react_1.default.createElement("div", { className: "bg-white rounded-lg shadow-sm border border-gray-200 p-4" },
|
|
114
|
+
react_1.default.createElement(TeamSection_1.Heading, { icon: react_1.default.createElement(base_icons_1.IconChat, { size: "sm", className: "mr-4" }), headerText: "Communication" }),
|
|
115
|
+
react_1.default.createElement("div", { className: "space-y-2 text-sm text-gray-600 mt-2" },
|
|
116
|
+
react_1.default.createElement("div", { className: "flex justify-between" },
|
|
117
|
+
react_1.default.createElement("span", null, "Daily"),
|
|
118
|
+
react_1.default.createElement("span", { className: "font-medium" }, "Microsoft Teams")),
|
|
119
|
+
react_1.default.createElement("div", { className: "flex justify-between" },
|
|
120
|
+
react_1.default.createElement("span", null, "Formal"),
|
|
121
|
+
react_1.default.createElement("span", { className: "font-medium" }, "Email")),
|
|
122
|
+
react_1.default.createElement("div", { className: "flex justify-between" },
|
|
123
|
+
react_1.default.createElement("span", null, "Weekly"),
|
|
124
|
+
react_1.default.createElement("span", { className: "font-medium" }, "Mondays 11 AM EST")),
|
|
125
|
+
react_1.default.createElement("div", { className: "flex justify-between" },
|
|
126
|
+
react_1.default.createElement("span", null, "Languages"),
|
|
127
|
+
react_1.default.createElement("span", { className: "font-medium" }, "English & Spanish")))),
|
|
128
|
+
react_1.default.createElement("div", { className: "bg-white rounded-lg shadow-sm border border-gray-200 p-4" },
|
|
129
|
+
react_1.default.createElement(TeamSection_1.Heading, { icon: react_1.default.createElement(base_icons_1.IconChartBar, { size: "sm", className: "mr-4" }), headerText: "Business Context" }),
|
|
130
|
+
react_1.default.createElement("div", { className: "space-y-2 text-sm text-gray-600 mt-2" },
|
|
131
|
+
react_1.default.createElement("div", { className: "flex justify-between" },
|
|
132
|
+
react_1.default.createElement("span", null, "Industry:"),
|
|
133
|
+
react_1.default.createElement("span", { className: "font-medium" }, "NetSuite software development")),
|
|
134
|
+
react_1.default.createElement("div", { className: "flex justify-between" },
|
|
135
|
+
react_1.default.createElement("span", null, "Structure:"),
|
|
136
|
+
react_1.default.createElement("span", { className: "font-medium" }, "US clients, Colombian delivery team")),
|
|
137
|
+
react_1.default.createElement("div", { className: "flex justify-between" },
|
|
138
|
+
react_1.default.createElement("span", null, "Challenge:"),
|
|
139
|
+
react_1.default.createElement("span", { className: "font-medium" }, "Manual consolidation processes")),
|
|
140
|
+
react_1.default.createElement("div", { className: "flex justify-between" },
|
|
141
|
+
react_1.default.createElement("span", null, "Opportunity:"),
|
|
142
|
+
react_1.default.createElement("span", { className: "font-medium" }, "NetSuite automation & efficiency"))),
|
|
143
|
+
react_1.default.createElement("div", { className: "flex items-center space-x-2 mt-4" },
|
|
144
|
+
react_1.default.createElement(EscalationIssueCard_1.CustomTag, { label: "NetSuite", customColor: `bg-info border-info` }),
|
|
145
|
+
react_1.default.createElement(EscalationIssueCard_1.CustomTag, { label: "Bilingual Team", customColor: `bg-success border-success` }))),
|
|
146
|
+
react_1.default.createElement(EditMetricsModal, { isOpen: isModalOpen, metrics: localMetrics, onClose: handleCloseModal, onSave: handleSaveMetrics })));
|
|
147
|
+
};
|
|
148
|
+
exports.KeyMetrics = KeyMetrics;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
interface MissingInfoItem {
|
|
3
|
+
id: string;
|
|
4
|
+
title: string;
|
|
5
|
+
description: string;
|
|
6
|
+
priority: 'HIGH' | 'MEDIUM' | 'LOW';
|
|
7
|
+
category: string;
|
|
8
|
+
status?: string;
|
|
9
|
+
clientResponse?: string;
|
|
10
|
+
}
|
|
11
|
+
interface MissingInformationProps {
|
|
12
|
+
items: MissingInfoItem[];
|
|
13
|
+
className?: string;
|
|
14
|
+
opportunityId: string;
|
|
15
|
+
GetOpportunityInsightsDocument: any;
|
|
16
|
+
updateOpportunityMissingInformation: any;
|
|
17
|
+
documentCenterController: {
|
|
18
|
+
openDocumentCenter: ((open: boolean) => void) | null;
|
|
19
|
+
} | null;
|
|
20
|
+
isInternal: boolean;
|
|
21
|
+
handleRouteToDocuments?: () => void;
|
|
22
|
+
}
|
|
23
|
+
export declare const MissingInformation: React.FC<MissingInformationProps>;
|
|
24
|
+
export {};
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
26
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
27
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
28
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
29
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
30
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
31
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
35
|
+
exports.MissingInformation = void 0;
|
|
36
|
+
const react_1 = __importStar(require("react"));
|
|
37
|
+
const utils_1 = require("../../shared/utils");
|
|
38
|
+
const core_1 = require("@material-ui/core");
|
|
39
|
+
const base_icons_1 = require("@paro.io/base-icons");
|
|
40
|
+
const base_ui_1 = require("@paro.io/base-ui");
|
|
41
|
+
const TeamSection_1 = require("../TeamSection");
|
|
42
|
+
// Category options for the dropdown
|
|
43
|
+
const INFORMATION_CATEGORIES = [
|
|
44
|
+
'Technical Integration Details',
|
|
45
|
+
'Financial Data Requirements',
|
|
46
|
+
'Process Documentation',
|
|
47
|
+
'Team Assessment',
|
|
48
|
+
'System Access Requirements',
|
|
49
|
+
'Compliance Information',
|
|
50
|
+
'Other'
|
|
51
|
+
];
|
|
52
|
+
// Priority levels for the dropdown
|
|
53
|
+
const PRIORITY_LEVELS = [
|
|
54
|
+
'Urgent - Blocks current work',
|
|
55
|
+
'High - Needed this week',
|
|
56
|
+
'Medium - Needed next week',
|
|
57
|
+
'Low - Needed eventually'
|
|
58
|
+
];
|
|
59
|
+
// Request Information Modal Component
|
|
60
|
+
const RequestInfoModal = ({ isOpen, missingInfoItem, onClose, onSubmit, documentCenterController, isInternal, handleRouteToDocuments, }) => {
|
|
61
|
+
const [submitting, setSubmitting] = (0, react_1.useState)(false);
|
|
62
|
+
const [formData, setFormData] = (0, react_1.useState)({
|
|
63
|
+
informationCategory: '',
|
|
64
|
+
priorityLevel: '',
|
|
65
|
+
specificInformation: '',
|
|
66
|
+
});
|
|
67
|
+
react_1.default.useEffect(() => {
|
|
68
|
+
if (missingInfoItem) {
|
|
69
|
+
setFormData({
|
|
70
|
+
informationCategory: missingInfoItem.category || '',
|
|
71
|
+
priorityLevel: missingInfoItem.priority === 'HIGH' ? 'Urgent - Blocks current work' :
|
|
72
|
+
missingInfoItem.priority === 'MEDIUM' ? 'High - Needed this week' :
|
|
73
|
+
'Medium - Needed next week',
|
|
74
|
+
specificInformation: missingInfoItem.description || '',
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}, [missingInfoItem]);
|
|
78
|
+
const handleSubmit = () => {
|
|
79
|
+
setSubmitting(true);
|
|
80
|
+
try {
|
|
81
|
+
onSubmit(Object.assign(Object.assign({}, formData), { itemId: missingInfoItem === null || missingInfoItem === void 0 ? void 0 : missingInfoItem.id, itemTitle: missingInfoItem === null || missingInfoItem === void 0 ? void 0 : missingInfoItem.title }));
|
|
82
|
+
}
|
|
83
|
+
catch (e) {
|
|
84
|
+
console.error("Error while submitting request", e);
|
|
85
|
+
}
|
|
86
|
+
finally {
|
|
87
|
+
setSubmitting(false);
|
|
88
|
+
}
|
|
89
|
+
onClose();
|
|
90
|
+
};
|
|
91
|
+
const handleCancel = () => {
|
|
92
|
+
// Reset form data when canceling
|
|
93
|
+
if (missingInfoItem) {
|
|
94
|
+
setFormData({
|
|
95
|
+
informationCategory: missingInfoItem.category || '',
|
|
96
|
+
priorityLevel: missingInfoItem.priority === 'HIGH' ? 'Urgent - Blocks current work' :
|
|
97
|
+
missingInfoItem.priority === 'MEDIUM' ? 'High - Needed this week' :
|
|
98
|
+
'Medium - Needed next week',
|
|
99
|
+
specificInformation: missingInfoItem.description || '',
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
onClose();
|
|
103
|
+
};
|
|
104
|
+
const handleOpenDocumentCenter = () => {
|
|
105
|
+
if (isInternal && handleRouteToDocuments) {
|
|
106
|
+
handleRouteToDocuments && handleRouteToDocuments();
|
|
107
|
+
onClose();
|
|
108
|
+
}
|
|
109
|
+
if (!isInternal && documentCenterController && documentCenterController.openDocumentCenter) {
|
|
110
|
+
documentCenterController.openDocumentCenter(true);
|
|
111
|
+
onClose();
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
if (!isOpen || !missingInfoItem)
|
|
115
|
+
return null;
|
|
116
|
+
const isClientResponded = missingInfoItem.status === "CLIENT_RESPONDED";
|
|
117
|
+
const buttonText = isClientResponded ? "Update Request" : "Send Request";
|
|
118
|
+
const hasUploadedDocuments = missingInfoItem.clientResponse && missingInfoItem.clientResponse.includes("Client Uploaded");
|
|
119
|
+
return (react_1.default.createElement(core_1.Dialog, { open: isOpen, onClose: onClose, fullWidth: true },
|
|
120
|
+
react_1.default.createElement(core_1.DialogTitle, null,
|
|
121
|
+
react_1.default.createElement("div", { className: "text-black mb-1 p-2 pl-4 absolute top-0 left-0 w-full flex flex-row justify-between items-center z-50" },
|
|
122
|
+
isClientResponded ? "View Client Response" : "Request Additional Information",
|
|
123
|
+
react_1.default.createElement(core_1.IconButton, { onClick: onClose },
|
|
124
|
+
react_1.default.createElement(base_icons_1.IconX, null)))),
|
|
125
|
+
react_1.default.createElement(core_1.DialogContent, null,
|
|
126
|
+
react_1.default.createElement("div", { className: "space-y-4 mt-4" },
|
|
127
|
+
isClientResponded && missingInfoItem.clientResponse && (react_1.default.createElement("div", null,
|
|
128
|
+
react_1.default.createElement("label", { className: "block text-sm font-medium text-gray-700 mb-1" }, "Client Response"),
|
|
129
|
+
react_1.default.createElement("div", { className: "w-full px-3 py-2 border border-gray-300 rounded-md bg-gray-50 text-sm" },
|
|
130
|
+
react_1.default.createElement("p", { className: "whitespace-pre-wrap" }, missingInfoItem.clientResponse)),
|
|
131
|
+
hasUploadedDocuments && (react_1.default.createElement("div", { className: "mt-2" },
|
|
132
|
+
react_1.default.createElement("button", { onClick: handleOpenDocumentCenter, className: "text-blue-600 hover:text-blue-800 font-medium flex items-center text-sm" },
|
|
133
|
+
react_1.default.createElement("svg", { className: "w-4 h-4 mr-1", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", xmlns: "http://www.w3.org/2000/svg" },
|
|
134
|
+
react_1.default.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" })),
|
|
135
|
+
isInternal ? 'View Documents under Documents tab' : 'View Documents in Document Center'))))),
|
|
136
|
+
react_1.default.createElement("div", null,
|
|
137
|
+
react_1.default.createElement("label", { htmlFor: "informationCategory", className: "block text-sm font-medium text-gray-700 mb-1" }, "Information Category"),
|
|
138
|
+
react_1.default.createElement(core_1.Select, { fullWidth: true, id: "informationCategory", value: formData.informationCategory, onChange: (e) => setFormData((prev) => (Object.assign(Object.assign({}, prev), { informationCategory: e.target.value }))), displayEmpty: true, variant: "outlined", placeholder: 'Select a category' },
|
|
139
|
+
react_1.default.createElement(core_1.MenuItem, { disabled: true, value: "" },
|
|
140
|
+
react_1.default.createElement("em", null, `Select a category`)),
|
|
141
|
+
INFORMATION_CATEGORIES.map((category) => (react_1.default.createElement(core_1.MenuItem, { key: category, value: category }, category))))),
|
|
142
|
+
react_1.default.createElement("div", null,
|
|
143
|
+
react_1.default.createElement("label", { htmlFor: "priorityLevel", className: "block text-sm font-medium text-gray-700 mb-1" }, "Priority Level"),
|
|
144
|
+
react_1.default.createElement(core_1.Select, { fullWidth: true, id: "priorityLevel", value: formData.priorityLevel, onChange: (e) => setFormData((prev) => (Object.assign(Object.assign({}, prev), { priorityLevel: e.target.value }))), displayEmpty: true, variant: "outlined", placeholder: 'Select priority level' },
|
|
145
|
+
react_1.default.createElement(core_1.MenuItem, { disabled: true, value: "" },
|
|
146
|
+
react_1.default.createElement("em", null, `Select priority level`)),
|
|
147
|
+
PRIORITY_LEVELS.map((level) => (react_1.default.createElement(core_1.MenuItem, { key: level, value: level }, level))))),
|
|
148
|
+
react_1.default.createElement("div", null,
|
|
149
|
+
react_1.default.createElement("label", { htmlFor: "specificInformation", className: "block text-sm font-medium text-gray-700 mb-1" }, "Specific Information Needed"),
|
|
150
|
+
react_1.default.createElement("textarea", { id: "specificInformation", value: formData.specificInformation, onChange: (e) => setFormData(prev => (Object.assign(Object.assign({}, prev), { specificInformation: e.target.value }))), rows: 4, className: "w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500 text-sm", placeholder: "Need NetSuite configuration details:\r\n\r\n1. US vs Colombian entity setup\r\n2. Consolidation process and requirements" }))),
|
|
151
|
+
react_1.default.createElement("div", { className: "px-6 py-4 flex justify-end space-x-3" },
|
|
152
|
+
react_1.default.createElement(base_ui_1.Button, { label: "cancel", onClick: handleCancel, disabled: submitting }),
|
|
153
|
+
react_1.default.createElement(base_ui_1.Button, { label: buttonText, onClick: handleSubmit, color: "primary", isLoading: submitting })))));
|
|
154
|
+
};
|
|
155
|
+
const MissingInformation = ({ items, className = '', opportunityId, GetOpportunityInsightsDocument, updateOpportunityMissingInformation, documentCenterController, isInternal, handleRouteToDocuments, }) => {
|
|
156
|
+
const [selectedItem, setSelectedItem] = (0, react_1.useState)(null);
|
|
157
|
+
const [isModalOpen, setIsModalOpen] = (0, react_1.useState)(false);
|
|
158
|
+
const [requestedItems, setRequestedItems] = (0, react_1.useState)({});
|
|
159
|
+
const handleRequestInfo = (item) => {
|
|
160
|
+
setSelectedItem(item);
|
|
161
|
+
setIsModalOpen(true);
|
|
162
|
+
};
|
|
163
|
+
const handleCloseModal = () => {
|
|
164
|
+
setIsModalOpen(false);
|
|
165
|
+
setSelectedItem(null);
|
|
166
|
+
};
|
|
167
|
+
const handleSubmitRequest = (requestData) => __awaiter(void 0, void 0, void 0, function* () {
|
|
168
|
+
if (!selectedItem)
|
|
169
|
+
return;
|
|
170
|
+
try {
|
|
171
|
+
yield updateOpportunityMissingInformation({
|
|
172
|
+
variables: {
|
|
173
|
+
opportunityId,
|
|
174
|
+
input: {
|
|
175
|
+
id: selectedItem.id,
|
|
176
|
+
description: requestData.specificInformation,
|
|
177
|
+
category: requestData.informationCategory,
|
|
178
|
+
status: "EXPERT_REQUESTED",
|
|
179
|
+
priority: requestData.priority || "HIGH"
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
refetchQueries: [
|
|
183
|
+
{ query: GetOpportunityInsightsDocument, variables: { opportunityId } }
|
|
184
|
+
]
|
|
185
|
+
});
|
|
186
|
+
(0, utils_1.showToast)('success', 'Information request submitted successfully');
|
|
187
|
+
// Update local state to reflect the change immediately
|
|
188
|
+
setRequestedItems(prev => (Object.assign(Object.assign({}, prev), { [selectedItem.id]: true })));
|
|
189
|
+
console.log('Information request submitted successfully');
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
console.error('Error submitting information request:', error);
|
|
193
|
+
(0, utils_1.showToast)('warning', 'Error submitting information request. Please try again.');
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
return (react_1.default.createElement(react_1.default.Fragment, null,
|
|
197
|
+
react_1.default.createElement("div", { className: `bg-white rounded-lg shadow-sm border border-gray-200 p-6 ${className}` },
|
|
198
|
+
react_1.default.createElement(TeamSection_1.Heading, { icon: react_1.default.createElement(base_icons_1.IconExclamation, { size: "sm", className: "mr-4" }), headerText: "Missing Information" }),
|
|
199
|
+
react_1.default.createElement("div", { className: "space-y-4 mt-2" }, items.map((item) => {
|
|
200
|
+
const isRequested = requestedItems[item.id] || item.status === "EXPERT_REQUESTED";
|
|
201
|
+
const isClientResponded = item.status === "CLIENT_RESPONDED";
|
|
202
|
+
let displayPriority = item.priority;
|
|
203
|
+
if (isRequested) {
|
|
204
|
+
displayPriority = "REQUESTED";
|
|
205
|
+
}
|
|
206
|
+
if (isClientResponded) {
|
|
207
|
+
displayPriority = "CLIENT_RESPONDED";
|
|
208
|
+
}
|
|
209
|
+
return (react_1.default.createElement("div", { key: item.id, className: "border border-gray-200 rounded-lg p-4 hover:shadow-sm transition-shadow" },
|
|
210
|
+
react_1.default.createElement("div", { className: "text-sm font-bold" }, item.title),
|
|
211
|
+
react_1.default.createElement("p", { className: "text-sm text-gray-600 mb-2" }, item.description),
|
|
212
|
+
react_1.default.createElement("div", { className: "flex items-center justify-between" },
|
|
213
|
+
react_1.default.createElement("span", { className: "text-xs text-gray-500" }, item.category),
|
|
214
|
+
isClientResponded ? (react_1.default.createElement(base_ui_1.Button, { label: "Show Response", iconRight: react_1.default.createElement(base_icons_1.IconArrowRight, { size: "sm" }), onClick: () => handleRequestInfo(item), color: "success", size: 'sm' })) : !isRequested ? (react_1.default.createElement(base_ui_1.Button, { label: "Request Info", iconRight: react_1.default.createElement(base_icons_1.IconArrowRight, { size: "sm" }), onClick: () => handleRequestInfo(item), color: "success", size: 'sm' })) : (react_1.default.createElement(base_ui_1.Button, { label: "Information Requested", iconLeft: react_1.default.createElement(base_icons_1.IconCheckCircle, { size: "sm" }), disabled: true, size: 'sm' })))));
|
|
215
|
+
}))),
|
|
216
|
+
react_1.default.createElement(RequestInfoModal, { isOpen: isModalOpen, missingInfoItem: selectedItem, onClose: handleCloseModal, onSubmit: handleSubmitRequest, documentCenterController: documentCenterController, isInternal: isInternal, handleRouteToDocuments: handleRouteToDocuments })));
|
|
217
|
+
};
|
|
218
|
+
exports.MissingInformation = MissingInformation;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ProgressTracker = void 0;
|
|
7
|
+
const react_1 = __importDefault(require("react"));
|
|
8
|
+
const base_ui_1 = require("@paro.io/base-ui");
|
|
9
|
+
const PHASE_TABS = [
|
|
10
|
+
{ id: 'discovery', label: 'Discovery', active: true },
|
|
11
|
+
{ id: 'kickoff', label: 'Kickoff', active: false },
|
|
12
|
+
{ id: 'setup', label: 'Setup', active: false },
|
|
13
|
+
{ id: 'active', label: 'Active', active: false },
|
|
14
|
+
{ id: 'renewal', label: 'Renewal', active: false },
|
|
15
|
+
];
|
|
16
|
+
const ProgressTracker = ({ percentage, currentPhase, nextSteps }) => {
|
|
17
|
+
return (react_1.default.createElement("div", { className: "bg-white rounded-lg shadow-sm border border-gray-200 mt-6" },
|
|
18
|
+
react_1.default.createElement("div", { className: "px-6 py-4 border-b border-gray-200" },
|
|
19
|
+
react_1.default.createElement("div", { className: "flex space-x-2" }, PHASE_TABS.map((phase) => (react_1.default.createElement(base_ui_1.Button, { key: phase.id, disabled: !phase.active, label: phase.label, color: phase.active ? 'primary' : 'default' })))))));
|
|
20
|
+
};
|
|
21
|
+
exports.ProgressTracker = ProgressTracker;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
interface ProjectHealthData {
|
|
3
|
+
communicationFlow: {
|
|
4
|
+
status: 'GOOD' | 'ATTENTION_NEEDED' | 'CRITICAL';
|
|
5
|
+
description: string;
|
|
6
|
+
};
|
|
7
|
+
performanceTracking: {
|
|
8
|
+
status: 'GOOD' | 'ATTENTION_NEEDED' | 'CRITICAL';
|
|
9
|
+
description: string;
|
|
10
|
+
};
|
|
11
|
+
processHealth: {
|
|
12
|
+
status: 'GOOD' | 'ATTENTION_NEEDED' | 'CRITICAL';
|
|
13
|
+
description: string;
|
|
14
|
+
};
|
|
15
|
+
riskManagement: {
|
|
16
|
+
status: 'GOOD' | 'ATTENTION_NEEDED' | 'CRITICAL';
|
|
17
|
+
description: string;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
interface ProjectHealthProps {
|
|
21
|
+
healthData: ProjectHealthData;
|
|
22
|
+
className?: string;
|
|
23
|
+
}
|
|
24
|
+
export declare const ProjectHealth: React.FC<ProjectHealthProps>;
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ProjectHealth = void 0;
|
|
7
|
+
const react_1 = __importDefault(require("react"));
|
|
8
|
+
const TeamSection_1 = require("../TeamSection");
|
|
9
|
+
const base_icons_1 = require("@paro.io/base-icons");
|
|
10
|
+
const EscalationIssueCard_1 = require("../../Escalations/EscalationIssueCard");
|
|
11
|
+
const STATUS_COLORS = {
|
|
12
|
+
GOOD: "success",
|
|
13
|
+
CRITICAL: "danger",
|
|
14
|
+
ATTENTION_NEEDED: "warning",
|
|
15
|
+
};
|
|
16
|
+
const STATUS_ICONS = {
|
|
17
|
+
GOOD: react_1.default.createElement(base_icons_1.IconCheck, { size: "sm" }),
|
|
18
|
+
CRITICAL: react_1.default.createElement(base_icons_1.IconX, { size: "sm" }),
|
|
19
|
+
ATTENTION_NEEDED: react_1.default.createElement(base_icons_1.IconExclamation, { size: "sm" }),
|
|
20
|
+
};
|
|
21
|
+
const HealthIndicator = ({ title, status, description, icon }) => {
|
|
22
|
+
return (react_1.default.createElement("div", { className: "flex items-start space-x-3 p-3 rounded-lg border border-gray-200 hover:shadow-sm transition-shadow" },
|
|
23
|
+
react_1.default.createElement("div", { className: "flex-1 min-w-0" },
|
|
24
|
+
react_1.default.createElement("div", { className: "flex items-center space-x-2 mb-1" },
|
|
25
|
+
react_1.default.createElement("span", { className: "text-sm" }, icon),
|
|
26
|
+
react_1.default.createElement("div", { className: "text-sm font-bold" }, title),
|
|
27
|
+
react_1.default.createElement(EscalationIssueCard_1.CustomTag, { label: status === 'GOOD' ? 'On track' : status === 'ATTENTION_NEEDED' ? 'Needs attention' : 'Critical', iconLeft: STATUS_ICONS[status], customColor: `bg-${STATUS_COLORS[status]} border-${STATUS_COLORS[status]}` })),
|
|
28
|
+
react_1.default.createElement("p", { className: "text-sm text-gray-600" }, description))));
|
|
29
|
+
};
|
|
30
|
+
const ProjectHealth = ({ healthData, className = '' }) => {
|
|
31
|
+
return (react_1.default.createElement("div", { className: `bg-white rounded-lg shadow-sm border border-gray-200 ${className}` },
|
|
32
|
+
react_1.default.createElement("div", { className: "px-6 py-4 border-b border-gray-200 mb-2" },
|
|
33
|
+
react_1.default.createElement(TeamSection_1.Heading, { icon: react_1.default.createElement(base_icons_1.IconChartBar, { size: "sm", className: "mr-4" }), headerText: "Project Health" }),
|
|
34
|
+
react_1.default.createElement("p", { className: "text-sm text-gray-600 mt-1" }, "Automated insights from client communications and project data")),
|
|
35
|
+
react_1.default.createElement("div", { className: "p-6" },
|
|
36
|
+
react_1.default.createElement("div", { className: "space-y-4" },
|
|
37
|
+
react_1.default.createElement(HealthIndicator, { title: "Communication Flow", status: healthData.communicationFlow.status, description: healthData.communicationFlow.description, icon: react_1.default.createElement(base_icons_1.IconChat, { size: "sm" }) }),
|
|
38
|
+
react_1.default.createElement(HealthIndicator, { title: "Performance Tracking", status: healthData.performanceTracking.status, description: healthData.performanceTracking.description, icon: react_1.default.createElement(base_icons_1.IconLightningBolt, { size: "sm" }) }),
|
|
39
|
+
react_1.default.createElement(HealthIndicator, { title: "Process Health", status: healthData.processHealth.status, description: healthData.processHealth.description, icon: react_1.default.createElement(base_icons_1.IconCog, { size: "sm" }) }),
|
|
40
|
+
react_1.default.createElement(HealthIndicator, { title: "Risk Management", status: healthData.riskManagement.status, description: healthData.riskManagement.description, icon: react_1.default.createElement(base_icons_1.IconExclamationCircle, { size: "sm" }) })))));
|
|
41
|
+
};
|
|
42
|
+
exports.ProjectHealth = ProjectHealth;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
interface TeamMember {
|
|
3
|
+
id: string;
|
|
4
|
+
name: string;
|
|
5
|
+
role: string;
|
|
6
|
+
avatar?: string;
|
|
7
|
+
availability?: string;
|
|
8
|
+
}
|
|
9
|
+
interface Expert {
|
|
10
|
+
id: string;
|
|
11
|
+
name: string;
|
|
12
|
+
title: string;
|
|
13
|
+
avatar: string;
|
|
14
|
+
}
|
|
15
|
+
interface TeamSectionProps {
|
|
16
|
+
clientTeam: TeamMember[];
|
|
17
|
+
expert: Expert;
|
|
18
|
+
paroTeam: TeamMember[];
|
|
19
|
+
}
|
|
20
|
+
export declare const Heading: ({ icon, headerText }: {
|
|
21
|
+
icon: any;
|
|
22
|
+
headerText: string;
|
|
23
|
+
}) => React.JSX.Element;
|
|
24
|
+
export declare const TeamSection: React.FC<TeamSectionProps>;
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.TeamSection = exports.Heading = void 0;
|
|
7
|
+
const react_1 = __importDefault(require("react"));
|
|
8
|
+
const base_icons_1 = require("@paro.io/base-icons");
|
|
9
|
+
const base_ui_1 = require("@paro.io/base-ui");
|
|
10
|
+
const Heading = ({ icon, headerText }) => {
|
|
11
|
+
return (react_1.default.createElement("div", { className: "flex flex-row justify-start items-center" },
|
|
12
|
+
icon,
|
|
13
|
+
react_1.default.createElement("div", { className: "text-lg font-semibold" }, headerText)));
|
|
14
|
+
};
|
|
15
|
+
exports.Heading = Heading;
|
|
16
|
+
const TeamSection = ({ clientTeam, expert, paroTeam }) => {
|
|
17
|
+
return (react_1.default.createElement("div", { className: "space-y-6" },
|
|
18
|
+
react_1.default.createElement("div", { className: "bg-white rounded-lg shadow-sm border border-gray-200 p-6" },
|
|
19
|
+
react_1.default.createElement(exports.Heading, { icon: react_1.default.createElement(base_icons_1.IconUserGroup, { size: "sm", className: "mr-4" }), headerText: "Client Team" }),
|
|
20
|
+
react_1.default.createElement("div", { className: "space-y-3 mt-2" }, clientTeam === null || clientTeam === void 0 ? void 0 : clientTeam.map((member) => (react_1.default.createElement("div", { key: member.id, className: "flex items-center space-x-3" },
|
|
21
|
+
react_1.default.createElement(base_ui_1.Avatar, { src: member.avatar || '', name: member.name }),
|
|
22
|
+
react_1.default.createElement("div", { className: "flex-1 min-w-0" },
|
|
23
|
+
react_1.default.createElement("p", { className: "text-sm font-bold" }, member.name),
|
|
24
|
+
react_1.default.createElement("p", { className: "text-sm text-gray-600" }, member.role),
|
|
25
|
+
member.availability && (react_1.default.createElement("p", { className: "text-xs text-gray-500" }, member.availability)))))))),
|
|
26
|
+
react_1.default.createElement("div", { className: "bg-white rounded-lg shadow-sm border border-gray-200 p-6" },
|
|
27
|
+
react_1.default.createElement(exports.Heading, { icon: react_1.default.createElement(base_icons_1.IconStar, { size: "sm", className: "mr-4" }), headerText: "Your Expert" }),
|
|
28
|
+
react_1.default.createElement("div", { className: "flex items-center space-x-3 mt-2" },
|
|
29
|
+
react_1.default.createElement(base_ui_1.Avatar, { src: expert.avatar, name: expert.name }),
|
|
30
|
+
react_1.default.createElement("div", { className: "flex-1 min-w-0" },
|
|
31
|
+
react_1.default.createElement("p", { className: "text-sm font-bold" }, expert.name),
|
|
32
|
+
react_1.default.createElement("p", { className: "text-sm text-gray-600" }, expert.title)))),
|
|
33
|
+
react_1.default.createElement("div", { className: "bg-white rounded-lg shadow-sm border border-gray-200 p-6" },
|
|
34
|
+
react_1.default.createElement(exports.Heading, { icon: react_1.default.createElement(base_icons_1.IconUsers, { size: "sm", className: "mr-4" }), headerText: "Your Paro Team" }),
|
|
35
|
+
react_1.default.createElement("div", { className: "space-y-3 mt-2" }, paroTeam === null || paroTeam === void 0 ? void 0 : paroTeam.map((member) => (react_1.default.createElement("div", { key: member.id || member.name, className: "flex items-center space-x-3" },
|
|
36
|
+
react_1.default.createElement(base_ui_1.Avatar, { src: member.avatar || '', name: member.name }),
|
|
37
|
+
react_1.default.createElement("div", { className: "flex-1 min-w-0" },
|
|
38
|
+
react_1.default.createElement("p", { className: "text-sm font-bold" }, member.name),
|
|
39
|
+
react_1.default.createElement("p", { className: "text-sm text-gray-600" }, member.role)))))))));
|
|
40
|
+
};
|
|
41
|
+
exports.TeamSection = TeamSection;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
interface ProjectIntelligenceProps {
|
|
3
|
+
checkIfOpportunityInsightsExistData: any;
|
|
4
|
+
opportunityInsightsData: any;
|
|
5
|
+
selectedOpportunityId: string | null;
|
|
6
|
+
setSelectedOpportunityId: (id: string | null) => void;
|
|
7
|
+
GetOpportunityInsightsDocument: any;
|
|
8
|
+
updateOpportunityMissingInformation: any;
|
|
9
|
+
documentCenterController?: {
|
|
10
|
+
openDocumentCenter: ((open: boolean) => void) | null;
|
|
11
|
+
} | null;
|
|
12
|
+
updateOpportunityFocusArea: any;
|
|
13
|
+
isInternal?: boolean;
|
|
14
|
+
handleRouteToDocuments?: () => void;
|
|
15
|
+
}
|
|
16
|
+
export declare const ProjectIntelligence: React.FC<ProjectIntelligenceProps>;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ProjectIntelligence = void 0;
|
|
7
|
+
const react_1 = __importDefault(require("react"));
|
|
8
|
+
const EngagementHeader_1 = require("./EngagementHeader");
|
|
9
|
+
const ProgressTracker_1 = require("./ProgressTracker");
|
|
10
|
+
const FocusAreas_1 = require("./FocusAreas");
|
|
11
|
+
const TeamSection_1 = require("./TeamSection");
|
|
12
|
+
const ProjectHealth_1 = require("./ProjectHealth");
|
|
13
|
+
const KeyMetrics_1 = require("./KeyMetrics");
|
|
14
|
+
const MissingInformation_1 = require("./MissingInformation");
|
|
15
|
+
const ProjectIntelligence = ({ checkIfOpportunityInsightsExistData, opportunityInsightsData, selectedOpportunityId, setSelectedOpportunityId, GetOpportunityInsightsDocument, updateOpportunityMissingInformation, documentCenterController = null, updateOpportunityFocusArea, isInternal = false, handleRouteToDocuments, }) => {
|
|
16
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0;
|
|
17
|
+
const opportunityInsights = opportunityInsightsData === null || opportunityInsightsData === void 0 ? void 0 : opportunityInsightsData.getOpportunityInsights;
|
|
18
|
+
// Create client tabs from checkIfOpportunityInsightsExistData
|
|
19
|
+
const clientTabs = ((_a = checkIfOpportunityInsightsExistData === null || checkIfOpportunityInsightsExistData === void 0 ? void 0 : checkIfOpportunityInsightsExistData.checkIfOpportunityInsightsExist) === null || _a === void 0 ? void 0 : _a.map((insight) => ({
|
|
20
|
+
id: insight.opportunityId,
|
|
21
|
+
label: insight.clientName || `Client ${insight.clientId}`,
|
|
22
|
+
clientId: insight.clientId
|
|
23
|
+
}))) || [];
|
|
24
|
+
// Handle client tab change
|
|
25
|
+
const handleClientChange = (opportunityId) => {
|
|
26
|
+
setSelectedOpportunityId(opportunityId);
|
|
27
|
+
};
|
|
28
|
+
// Map status values from API to component expected values
|
|
29
|
+
const mapFocusAreaStatus = (status) => {
|
|
30
|
+
if (!status)
|
|
31
|
+
return 'IN_PROGRESS';
|
|
32
|
+
switch (status.toUpperCase()) {
|
|
33
|
+
case 'NEEDS_INPUT':
|
|
34
|
+
return 'AT_RISK';
|
|
35
|
+
case 'BLOCKED':
|
|
36
|
+
return 'AT_RISK';
|
|
37
|
+
case 'CONFIRMED':
|
|
38
|
+
return 'CONFIRMED';
|
|
39
|
+
case 'COMPLETED':
|
|
40
|
+
return 'COMPLETED';
|
|
41
|
+
case 'IN_PROGRESS':
|
|
42
|
+
default:
|
|
43
|
+
return 'IN_PROGRESS';
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
// Map health status from API to component expected values
|
|
47
|
+
const mapHealthStatus = (status) => {
|
|
48
|
+
if (!status)
|
|
49
|
+
return 'ATTENTION_NEEDED';
|
|
50
|
+
switch (status.toUpperCase()) {
|
|
51
|
+
case 'ON_TRACK':
|
|
52
|
+
return 'GOOD';
|
|
53
|
+
case 'NEEDS_ATTENTION':
|
|
54
|
+
return 'ATTENTION_NEEDED';
|
|
55
|
+
case 'CRITICAL':
|
|
56
|
+
return 'CRITICAL';
|
|
57
|
+
default:
|
|
58
|
+
return 'ATTENTION_NEEDED';
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
// Helper function to map API priority to component priority
|
|
62
|
+
const mapPriority = (priority) => {
|
|
63
|
+
if (!priority)
|
|
64
|
+
return 'MEDIUM';
|
|
65
|
+
if (typeof priority === 'string') {
|
|
66
|
+
switch (priority.toUpperCase()) {
|
|
67
|
+
case 'HIGH':
|
|
68
|
+
return 'HIGH';
|
|
69
|
+
case 'LOW':
|
|
70
|
+
return 'LOW';
|
|
71
|
+
case 'MEDIUM':
|
|
72
|
+
default:
|
|
73
|
+
return 'MEDIUM';
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return 'MEDIUM';
|
|
77
|
+
};
|
|
78
|
+
// Convert API focus areas to component format
|
|
79
|
+
const convertFocusAreas = () => {
|
|
80
|
+
if (!(opportunityInsights === null || opportunityInsights === void 0 ? void 0 : opportunityInsights.focusAreas))
|
|
81
|
+
return [];
|
|
82
|
+
return opportunityInsights.focusAreas.map((area, index) => ({
|
|
83
|
+
id: (area === null || area === void 0 ? void 0 : area.id) || `focus-${index}`,
|
|
84
|
+
title: (area === null || area === void 0 ? void 0 : area.title) || 'N/A',
|
|
85
|
+
description: (area === null || area === void 0 ? void 0 : area.description) || 'N/A',
|
|
86
|
+
status: mapFocusAreaStatus(area === null || area === void 0 ? void 0 : area.status),
|
|
87
|
+
dueDate: (area === null || area === void 0 ? void 0 : area.date) || new Date().toISOString(),
|
|
88
|
+
priority: mapPriority(area === null || area === void 0 ? void 0 : area.priority)
|
|
89
|
+
}));
|
|
90
|
+
};
|
|
91
|
+
return (react_1.default.createElement(react_1.default.Fragment, null,
|
|
92
|
+
react_1.default.createElement("div", { className: "w-full bg-info-light py-6 px-4 rounded-lg shadow mb-8" },
|
|
93
|
+
react_1.default.createElement("h1", { className: "text-2xl font-bold" }, (opportunityInsights === null || opportunityInsights === void 0 ? void 0 : opportunityInsights.opportunityName) || 'Client Engagement Dashboard'),
|
|
94
|
+
react_1.default.createElement("p", { className: "text-gray-600 mt-1" },
|
|
95
|
+
"Engagement dashboard for ",
|
|
96
|
+
(opportunityInsights === null || opportunityInsights === void 0 ? void 0 : opportunityInsights.opportunityName) || 'this client')),
|
|
97
|
+
react_1.default.createElement("div", { className: "max-w-[80%] mx-auto px-4 py-8" },
|
|
98
|
+
react_1.default.createElement(EngagementHeader_1.EngagementHeader, { clientName: (opportunityInsights === null || opportunityInsights === void 0 ? void 0 : opportunityInsights.opportunityName) || '', expertName: ((_b = opportunityInsights === null || opportunityInsights === void 0 ? void 0 : opportunityInsights.expertInfo) === null || _b === void 0 ? void 0 : _b.name) || '', expertTitle: ((_c = opportunityInsights === null || opportunityInsights === void 0 ? void 0 : opportunityInsights.expertInfo) === null || _c === void 0 ? void 0 : _c.role) || '', startDate: (opportunityInsights === null || opportunityInsights === void 0 ? void 0 : opportunityInsights.startDate) || '', status: (opportunityInsights === null || opportunityInsights === void 0 ? void 0 : opportunityInsights.phase) || 'In_Progress', clientTabs: clientTabs, selectedClientId: selectedOpportunityId, onClientChange: handleClientChange }),
|
|
99
|
+
react_1.default.createElement(ProgressTracker_1.ProgressTracker, { percentage: 0, currentPhase: "Kickoff Phase (Day 1 of 3)", nextSteps: "System access & team introductions" }),
|
|
100
|
+
react_1.default.createElement("div", { className: "flex flex-row space-x-4 mt-8" },
|
|
101
|
+
react_1.default.createElement("div", { className: "w-2/3 space-7-4" },
|
|
102
|
+
react_1.default.createElement(FocusAreas_1.FocusAreas, { focusAreas: convertFocusAreas(), onEditFocusAreas: () => { }, updateOpportunityFocusArea: updateOpportunityFocusArea, GetOpportunityInsightsDocument: GetOpportunityInsightsDocument, opportunityId: selectedOpportunityId }),
|
|
103
|
+
react_1.default.createElement(ProjectHealth_1.ProjectHealth, { healthData: {
|
|
104
|
+
communicationFlow: {
|
|
105
|
+
status: mapHealthStatus((_e = (_d = opportunityInsights === null || opportunityInsights === void 0 ? void 0 : opportunityInsights.opportunityHealth) === null || _d === void 0 ? void 0 : _d.communicationFlow) === null || _e === void 0 ? void 0 : _e.status),
|
|
106
|
+
description: ((_g = (_f = opportunityInsights === null || opportunityInsights === void 0 ? void 0 : opportunityInsights.opportunityHealth) === null || _f === void 0 ? void 0 : _f.communicationFlow) === null || _g === void 0 ? void 0 : _g.description) || 'N/A'
|
|
107
|
+
},
|
|
108
|
+
performanceTracking: {
|
|
109
|
+
status: mapHealthStatus((_j = (_h = opportunityInsights === null || opportunityInsights === void 0 ? void 0 : opportunityInsights.opportunityHealth) === null || _h === void 0 ? void 0 : _h.performanceTracking) === null || _j === void 0 ? void 0 : _j.status),
|
|
110
|
+
description: ((_l = (_k = opportunityInsights === null || opportunityInsights === void 0 ? void 0 : opportunityInsights.opportunityHealth) === null || _k === void 0 ? void 0 : _k.performanceTracking) === null || _l === void 0 ? void 0 : _l.description) || 'N/A'
|
|
111
|
+
},
|
|
112
|
+
processHealth: {
|
|
113
|
+
status: mapHealthStatus((_o = (_m = opportunityInsights === null || opportunityInsights === void 0 ? void 0 : opportunityInsights.opportunityHealth) === null || _m === void 0 ? void 0 : _m.processHealth) === null || _o === void 0 ? void 0 : _o.status),
|
|
114
|
+
description: ((_q = (_p = opportunityInsights === null || opportunityInsights === void 0 ? void 0 : opportunityInsights.opportunityHealth) === null || _p === void 0 ? void 0 : _p.processHealth) === null || _q === void 0 ? void 0 : _q.description) || 'N/A'
|
|
115
|
+
},
|
|
116
|
+
riskManagement: {
|
|
117
|
+
status: mapHealthStatus((_s = (_r = opportunityInsights === null || opportunityInsights === void 0 ? void 0 : opportunityInsights.opportunityHealth) === null || _r === void 0 ? void 0 : _r.riskManagement) === null || _s === void 0 ? void 0 : _s.status),
|
|
118
|
+
description: ((_u = (_t = opportunityInsights === null || opportunityInsights === void 0 ? void 0 : opportunityInsights.opportunityHealth) === null || _t === void 0 ? void 0 : _t.riskManagement) === null || _u === void 0 ? void 0 : _u.description) || 'N/A'
|
|
119
|
+
}
|
|
120
|
+
}, className: "mt-8" }),
|
|
121
|
+
react_1.default.createElement(MissingInformation_1.MissingInformation, { items: (opportunityInsights === null || opportunityInsights === void 0 ? void 0 : opportunityInsights.missingInformation) ?
|
|
122
|
+
opportunityInsights.missingInformation.map((item, index) => ({
|
|
123
|
+
id: (item === null || item === void 0 ? void 0 : item.id) || `missing-${index}`,
|
|
124
|
+
title: (item === null || item === void 0 ? void 0 : item.title) || 'N/A',
|
|
125
|
+
description: (item === null || item === void 0 ? void 0 : item.description) || 'N/A',
|
|
126
|
+
priority: mapPriority(item === null || item === void 0 ? void 0 : item.priority),
|
|
127
|
+
category: (item === null || item === void 0 ? void 0 : item.category) || 'N/A',
|
|
128
|
+
status: (item === null || item === void 0 ? void 0 : item.status) || undefined,
|
|
129
|
+
clientResponse: (item === null || item === void 0 ? void 0 : item.clientResponse) || undefined
|
|
130
|
+
})) : [], className: "mt-8", opportunityId: selectedOpportunityId, GetOpportunityInsightsDocument: GetOpportunityInsightsDocument, updateOpportunityMissingInformation: updateOpportunityMissingInformation, documentCenterController: documentCenterController, isInternal: isInternal, handleRouteToDocuments: handleRouteToDocuments })),
|
|
131
|
+
react_1.default.createElement("div", { className: "w-1/3 space-y-4" },
|
|
132
|
+
react_1.default.createElement(TeamSection_1.TeamSection, { clientTeam: opportunityInsights === null || opportunityInsights === void 0 ? void 0 : opportunityInsights.clientTeam, expert: {
|
|
133
|
+
id: 'expert-1',
|
|
134
|
+
name: ((_v = opportunityInsights === null || opportunityInsights === void 0 ? void 0 : opportunityInsights.expertInfo) === null || _v === void 0 ? void 0 : _v.name) || '',
|
|
135
|
+
title: ((_w = opportunityInsights === null || opportunityInsights === void 0 ? void 0 : opportunityInsights.expertInfo) === null || _w === void 0 ? void 0 : _w.role) || '',
|
|
136
|
+
avatar: ''
|
|
137
|
+
}, paroTeam: opportunityInsights === null || opportunityInsights === void 0 ? void 0 : opportunityInsights.paroTeam }),
|
|
138
|
+
react_1.default.createElement(KeyMetrics_1.KeyMetrics, { metrics: {
|
|
139
|
+
targetAchievements: ((_x = opportunityInsights === null || opportunityInsights === void 0 ? void 0 : opportunityInsights.engagementMetrics) === null || _x === void 0 ? void 0 : _x.targetHours) || 0,
|
|
140
|
+
hourlyRate: ((_y = opportunityInsights === null || opportunityInsights === void 0 ? void 0 : opportunityInsights.engagementMetrics) === null || _y === void 0 ? void 0 : _y.hourlyRate) || 0,
|
|
141
|
+
daysSinceGoal: ((_z = opportunityInsights === null || opportunityInsights === void 0 ? void 0 : opportunityInsights.engagementMetrics) === null || _z === void 0 ? void 0 : _z.dayCloseGoal) ?
|
|
142
|
+
parseInt(opportunityInsights.engagementMetrics.dayCloseGoal) : 0,
|
|
143
|
+
totalContractLength: ((_0 = opportunityInsights === null || opportunityInsights === void 0 ? void 0 : opportunityInsights.engagementMetrics) === null || _0 === void 0 ? void 0 : _0.contractLength) || 0
|
|
144
|
+
} }))))));
|
|
145
|
+
};
|
|
146
|
+
exports.ProjectIntelligence = ProjectIntelligence;
|
package/lib/index.d.ts
CHANGED
|
@@ -15,3 +15,4 @@ export { fileUploader } from './components/FileUploader';
|
|
|
15
15
|
export { DiscussionSection } from './components/Invoices/DiscussionSection';
|
|
16
16
|
export { fileDownloader } from './components/FileDownloader';
|
|
17
17
|
export { Escalations } from './components/Escalations';
|
|
18
|
+
export { ProjectIntelligence } from "./components/ProjectIntelligence";
|
package/lib/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Escalations = exports.fileDownloader = exports.DiscussionSection = exports.fileUploader = exports.InvoiceCard = exports.ActiveProjectCard = exports.sharedUtils = exports.ServiceLinesTemplate = exports.HeaderNavBar = exports.DocumentCenter = exports.ProfileCompletedPercentage = exports.ExpertProfileHeader = exports.OrganizationChart = exports.FirmEmployeeSection = exports.ClientReferenceSection = exports.Reviews = exports.ReviewsTab = void 0;
|
|
3
|
+
exports.ProjectIntelligence = exports.Escalations = exports.fileDownloader = exports.DiscussionSection = exports.fileUploader = exports.InvoiceCard = exports.ActiveProjectCard = exports.sharedUtils = exports.ServiceLinesTemplate = exports.HeaderNavBar = exports.DocumentCenter = exports.ProfileCompletedPercentage = exports.ExpertProfileHeader = exports.OrganizationChart = exports.FirmEmployeeSection = exports.ClientReferenceSection = exports.Reviews = exports.ReviewsTab = void 0;
|
|
4
4
|
var ReviewsTab_1 = require("./components/ReviewsTab");
|
|
5
5
|
Object.defineProperty(exports, "ReviewsTab", { enumerable: true, get: function () { return ReviewsTab_1.ReviewsTab; } });
|
|
6
6
|
var Reviews_1 = require("./components/Reviews");
|
|
@@ -35,3 +35,5 @@ var FileDownloader_1 = require("./components/FileDownloader");
|
|
|
35
35
|
Object.defineProperty(exports, "fileDownloader", { enumerable: true, get: function () { return FileDownloader_1.fileDownloader; } });
|
|
36
36
|
var Escalations_1 = require("./components/Escalations");
|
|
37
37
|
Object.defineProperty(exports, "Escalations", { enumerable: true, get: function () { return Escalations_1.Escalations; } });
|
|
38
|
+
var ProjectIntelligence_1 = require("./components/ProjectIntelligence");
|
|
39
|
+
Object.defineProperty(exports, "ProjectIntelligence", { enumerable: true, get: function () { return ProjectIntelligence_1.ProjectIntelligence; } });
|