@paro.io/expert-shared-components 1.13.7 → 1.13.9
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/AccountSuspensionBanner.d.ts +6 -2
- package/lib/components/Escalations/AccountSuspensionBanner.js +59 -5
- package/lib/components/Escalations/AccountSuspensionModal.js +2 -2
- package/lib/components/Escalations/EscalationIssueCard.js +22 -15
- package/lib/components/Escalations/EscalationReportBanner.d.ts +2 -1
- package/lib/components/Escalations/EscalationReportBanner.js +7 -2
- package/lib/components/Escalations/EscalationRespondForm.d.ts +10 -0
- package/lib/components/Escalations/EscalationRespondForm.js +22 -3
- package/lib/components/Escalations/EscalationSubmitForm.js +39 -15
- package/lib/components/Escalations/EscalationTabsContent.js +18 -7
- package/lib/components/Escalations/Escalations.d.ts +2 -1
- package/lib/components/Escalations/Escalations.js +17 -10
- package/lib/components/Escalations/MarkResolvedModal.js +16 -1
- package/lib/components/Escalations/ViewResponseModal.js +7 -5
- package/lib/components/ExpertProfileHeader/EarningsSection.d.ts +1 -0
- package/lib/components/ExpertProfileHeader/EarningsSection.js +6 -4
- package/lib/components/ExpertProfileHeader/ExpertProfileHeader.d.ts +3 -1
- package/lib/components/ExpertProfileHeader/ExpertProfileHeader.js +4 -4
- package/lib/components/ExpertProfileHeader/ExpertStatusCalculator.d.ts +1 -1
- package/lib/components/ExpertProfileHeader/ExpertStatusCalculator.js +14 -3
- package/lib/components/ExpertProfileHeader/NetworkSection.d.ts +3 -1
- package/lib/components/ExpertProfileHeader/NetworkSection.js +49 -3
- package/package.json +1 -1
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
declare const
|
|
1
|
+
export declare const suspensionLevels: {
|
|
2
|
+
value: string;
|
|
3
|
+
label: string;
|
|
4
|
+
}[];
|
|
5
|
+
declare const AccountSuspensionBanner: ({ setShowSuspensionModal, escalationDisputeStatistics, }: {
|
|
2
6
|
setShowSuspensionModal: (showSuspensionModal: boolean) => void;
|
|
3
|
-
|
|
7
|
+
escalationDisputeStatistics: any;
|
|
4
8
|
}) => JSX.Element;
|
|
5
9
|
export default AccountSuspensionBanner;
|
|
@@ -3,17 +3,63 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.suspensionLevels = void 0;
|
|
6
7
|
const react_1 = __importDefault(require("react"));
|
|
7
8
|
const base_icons_1 = require("@paro.io/base-icons");
|
|
8
9
|
const base_ui_1 = require("@paro.io/base-ui");
|
|
9
|
-
const
|
|
10
|
-
|
|
10
|
+
const dayjs_1 = __importDefault(require("dayjs"));
|
|
11
|
+
exports.suspensionLevels = [
|
|
12
|
+
{ value: "LEVEL_1", label: "Low" },
|
|
13
|
+
{ value: "LEVEL_2", label: "Medium" },
|
|
14
|
+
{ value: "LEVEL_3", label: "High" },
|
|
15
|
+
{ value: "LEVEL_4", label: "Critical" },
|
|
16
|
+
];
|
|
17
|
+
const getSuspensionDuration = (label) => {
|
|
18
|
+
switch (label) {
|
|
19
|
+
case "Medium":
|
|
20
|
+
return "30 days";
|
|
21
|
+
case "High":
|
|
22
|
+
return "60 days";
|
|
23
|
+
case "Critical":
|
|
24
|
+
return "Permanent";
|
|
25
|
+
case "Low":
|
|
26
|
+
default:
|
|
27
|
+
return "Low";
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
const AccountSuspensionBanner = ({ setShowSuspensionModal, escalationDisputeStatistics, }) => {
|
|
31
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
32
|
+
const suspensionLevel = (_b = (_a = exports.suspensionLevels.find((obj) => obj.value === escalationDisputeStatistics.suspensionLevel)) === null || _a === void 0 ? void 0 : _a.label) !== null && _b !== void 0 ? _b : "Low";
|
|
33
|
+
const suspensionStartDate = (_c = (0, dayjs_1.default)(escalationDisputeStatistics.suspensionStartDate).format("MM/DD/YYYY")) !== null && _c !== void 0 ? _c : "N/A";
|
|
34
|
+
const suspensionEndDate = (_d = (0, dayjs_1.default)(escalationDisputeStatistics.suspensionEndDate).format("MM/DD/YYYY")) !== null && _d !== void 0 ? _d : "N/A";
|
|
35
|
+
const mediumWarnings = (_e = escalationDisputeStatistics.mediumSeverityEscalationCount) !== null && _e !== void 0 ? _e : 0;
|
|
36
|
+
const highWarnings = (_f = escalationDisputeStatistics.highSeverityEscalationCount) !== null && _f !== void 0 ? _f : 0;
|
|
37
|
+
const criticalWarnings = (_g = escalationDisputeStatistics.criticalSeverityEscalationCount) !== null && _g !== void 0 ? _g : 0;
|
|
38
|
+
return (react_1.default.createElement("div", { className: "bg-[#EF5360] rounded-lg p-4 mb-6 shadow-lg" }, suspensionLevel !== "Low" ?
|
|
11
39
|
react_1.default.createElement("div", { className: "flex items-center" },
|
|
12
40
|
react_1.default.createElement("div", { className: "bg-white bg-opacity-20 rounded-full p-2 mr-3" },
|
|
13
41
|
react_1.default.createElement(base_icons_1.IconExclamation, { className: "h-6 w-6 text-white" })),
|
|
14
42
|
react_1.default.createElement("div", { className: "flex-1" },
|
|
15
|
-
react_1.default.createElement("h3", { className: "text-lg font-bold text-white" }, "\uD83D\uDEAB Account Suspended"),
|
|
16
|
-
react_1.default.createElement("p", { className: "text-red-100 text-sm mt-1" },
|
|
43
|
+
react_1.default.createElement("h3", { className: "text-lg font-bold text-white" }, "\uD83D\uDEAB Account Suspended due to: Engagement Issues"),
|
|
44
|
+
react_1.default.createElement("p", { className: "text-red-100 text-sm mt-1" },
|
|
45
|
+
"Your account has been suspended due to to engagement issues. if you have any problems with the suspension, please go to the Engagement Support section to respond to your issues or reach out to",
|
|
46
|
+
' ',
|
|
47
|
+
react_1.default.createElement("a", { href: "mailto:expertsupport@paro.io", className: "underline text-white hover:text-red-200 cursor-pointer" }, "expertsupport@paro.io"),
|
|
48
|
+
"."),
|
|
49
|
+
react_1.default.createElement("div", { className: "mt-3 bg-white bg-opacity-10 rounded p-3" },
|
|
50
|
+
react_1.default.createElement("div", { className: "flex items-center justify-between text-sm" },
|
|
51
|
+
react_1.default.createElement("div", null,
|
|
52
|
+
react_1.default.createElement("span", { className: "text-red-100" }, "Suspension: "),
|
|
53
|
+
react_1.default.createElement("span", { className: "text-white font-medium" },
|
|
54
|
+
getSuspensionDuration(suspensionLevel),
|
|
55
|
+
' ',
|
|
56
|
+
suspensionLevel)),
|
|
57
|
+
react_1.default.createElement("div", null,
|
|
58
|
+
react_1.default.createElement("span", { className: "text-red-100" }, "Suspension Started Date: "),
|
|
59
|
+
react_1.default.createElement("span", { className: "text-white font-medium" }, suspensionStartDate)),
|
|
60
|
+
react_1.default.createElement("div", null,
|
|
61
|
+
react_1.default.createElement("span", { className: "text-red-100" }, "Suspension End Date: "),
|
|
62
|
+
react_1.default.createElement("span", { className: "text-white font-medium" }, suspensionEndDate))))))
|
|
17
63
|
:
|
|
18
64
|
react_1.default.createElement("div", { className: "flex items-center justify-between" },
|
|
19
65
|
react_1.default.createElement("div", { className: "flex items-center" },
|
|
@@ -21,7 +67,15 @@ const AccountSuspensionBanner = ({ setShowSuspensionModal, suspended = false })
|
|
|
21
67
|
react_1.default.createElement(base_icons_1.IconExclamation, { className: "h-6 w-6 text-white" })),
|
|
22
68
|
react_1.default.createElement("div", null,
|
|
23
69
|
react_1.default.createElement("h3", { className: "text-lg font-bold text-white" }, "Account Suspension Risk"),
|
|
24
|
-
react_1.default.createElement("p", { className: "text-red-100 text-sm mt-1" }, "Unresolved escalations can lead to automatic suspension. Response times matter for client satisfaction.")
|
|
70
|
+
react_1.default.createElement("p", { className: "text-red-100 text-sm mt-1" }, "Unresolved escalations can lead to automatic suspension. Response times matter for client satisfaction."),
|
|
71
|
+
react_1.default.createElement("div", { className: "flex items-center mt-2 text-red-100 text-xs" },
|
|
72
|
+
react_1.default.createElement("span", null,
|
|
73
|
+
mediumWarnings,
|
|
74
|
+
"/3 warnings: Medium = 30 days \u2022 ",
|
|
75
|
+
highWarnings,
|
|
76
|
+
"/2 warnings: High = 60 days \u2022 ",
|
|
77
|
+
criticalWarnings,
|
|
78
|
+
"/1 offense: Critical = Permanent")))),
|
|
25
79
|
react_1.default.createElement(base_ui_1.Button, { label: "view details", onClick: () => setShowSuspensionModal(true), iconLeft: react_1.default.createElement(base_icons_1.IconInfoCircle, { size: "sm" }), color: "danger", className: "bg-white hover:bg-danger text-danger-dark hover:text-white" }))));
|
|
26
80
|
};
|
|
27
81
|
exports.default = AccountSuspensionBanner;
|
|
@@ -29,7 +29,7 @@ const AccountSuspensionModal = ({ showSuspensionModal, onClose }) => {
|
|
|
29
29
|
react_1.default.createElement(base_icons_1.IconCheckCircle, { className: "h-5 w-5 text-white" })),
|
|
30
30
|
react_1.default.createElement("div", { className: "flex-1 pb-8" },
|
|
31
31
|
react_1.default.createElement("div", { className: "flex justify-between items-start mb-2" },
|
|
32
|
-
react_1.default.createElement("h4", { className: "font-semibold text-green-800" }, "Level 1:
|
|
32
|
+
react_1.default.createElement("h4", { className: "font-semibold text-green-800" }, "Level 1: Low Issues"),
|
|
33
33
|
react_1.default.createElement("div", { className: "text-xs bg-green-100 text-green-700 px-2 py-1 rounded-full" }, "Coaching Only")),
|
|
34
34
|
react_1.default.createElement("p", { className: "text-sm text-gray-600 mb-2" }, "Email coaching and guidance. No account restrictions."),
|
|
35
35
|
react_1.default.createElement("div", { className: "text-xs text-gray-500" },
|
|
@@ -104,7 +104,7 @@ const AccountSuspensionModal = ({ showSuspensionModal, onClose }) => {
|
|
|
104
104
|
react_1.default.createElement("div", { className: "mt-6 bg-gray-50 rounded-lg p-4" },
|
|
105
105
|
react_1.default.createElement("h4", { className: "font-medium text-gray-900 mb-2" }, "Important Reminders"),
|
|
106
106
|
react_1.default.createElement("ul", { className: "text-sm text-gray-600 space-y-1" },
|
|
107
|
-
react_1.default.createElement("li", null, "\u2022 All
|
|
107
|
+
react_1.default.createElement("li", null, "\u2022 All issues are tracked and contribute to your warning count"),
|
|
108
108
|
react_1.default.createElement("li", null, "\u2022 Training completion is required before suspension can be lifted"),
|
|
109
109
|
react_1.default.createElement("li", null, "\u2022 Warnings are cumulative across all severity levels"),
|
|
110
110
|
react_1.default.createElement("li", null, "\u2022 All actions are logged and affect your expert rating")))))));
|
|
@@ -69,24 +69,19 @@ const EscalationIssueCard = ({ issues, isExpert, openEscalationChat, showRespond
|
|
|
69
69
|
return "View Response";
|
|
70
70
|
}
|
|
71
71
|
else {
|
|
72
|
-
return issue.expertResponse ? "View Response" : "Awaiting Response";
|
|
72
|
+
return issue.expertResponse || issue.clientResponse ? "View Response" : "Awaiting Response";
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
if (userTypeId === 1) { // Submitted by Expert
|
|
76
76
|
if (isExpert) {
|
|
77
|
-
return issue.clientResponse ? "View Response" : "Awaiting Response";
|
|
77
|
+
return issue.clientResponse || issue.expertResponse ? "View Response" : "Awaiting Response";
|
|
78
78
|
}
|
|
79
79
|
else {
|
|
80
80
|
return "View Response";
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
83
|
if (userTypeId === 2) { // Submitted by Paro internal
|
|
84
|
-
|
|
85
|
-
return issue.clientResponse ? "View Response" : "Awaiting Response";
|
|
86
|
-
}
|
|
87
|
-
else {
|
|
88
|
-
return issue.expertResponse ? "View Response" : "Awaiting Response";
|
|
89
|
-
}
|
|
84
|
+
return issue.clientResponse || issue.expertResponse ? "View Response" : "Awaiting Response";
|
|
90
85
|
}
|
|
91
86
|
return "Awaiting Response";
|
|
92
87
|
};
|
|
@@ -94,7 +89,7 @@ const EscalationIssueCard = ({ issues, isExpert, openEscalationChat, showRespond
|
|
|
94
89
|
return react_1.default.createElement("div", null, "No issues.");
|
|
95
90
|
return (react_1.default.createElement("div", { className: "space-y-4" },
|
|
96
91
|
issues.map((issue) => {
|
|
97
|
-
var _a, _b, _c, _d, _e
|
|
92
|
+
var _a, _b, _c, _d, _e;
|
|
98
93
|
const project = issue.projectDetails && Array.isArray(issue.projectDetails) && issue.projectDetails.length > 0
|
|
99
94
|
? `${(_a = issue.projectDetails[0]) === null || _a === void 0 ? void 0 : _a.projectName}${issue.projectDetails.length > 1 ? ` +${issue.projectDetails.length - 1} more` : ''} `
|
|
100
95
|
: '';
|
|
@@ -104,12 +99,10 @@ const EscalationIssueCard = ({ issues, isExpert, openEscalationChat, showRespond
|
|
|
104
99
|
react_1.default.createElement("div", { className: "flex items-center justify-between mb-3" },
|
|
105
100
|
react_1.default.createElement("div", { className: "flex-1 ml-2" },
|
|
106
101
|
react_1.default.createElement("div", { className: "flex flex-row justify-start gap-2" },
|
|
107
|
-
react_1.default.createElement("div", { className: "font-bold" },
|
|
102
|
+
react_1.default.createElement("div", { className: "font-bold" }, project),
|
|
108
103
|
react_1.default.createElement(exports.CustomTag, { label: issue.severityLevel })),
|
|
109
104
|
react_1.default.createElement("div", { className: "text-xs text-gray-500 font-bold" },
|
|
110
|
-
isExpert ? (
|
|
111
|
-
" \u2022 ",
|
|
112
|
-
project,
|
|
105
|
+
isExpert ? `${(_b = issue === null || issue === void 0 ? void 0 : issue.client) === null || _b === void 0 ? void 0 : _b.name} (Client)` : `${(_c = issue === null || issue === void 0 ? void 0 : issue.freelancer) === null || _c === void 0 ? void 0 : _c.name} (Expert)`,
|
|
113
106
|
" \u2022 Case #",
|
|
114
107
|
issue.escalationNumber)),
|
|
115
108
|
react_1.default.createElement(exports.CustomTag, { label: issue.escalationType, customColor: `bg-[#B0B5D3] border-[#181027]` })),
|
|
@@ -117,11 +110,25 @@ const EscalationIssueCard = ({ issues, isExpert, openEscalationChat, showRespond
|
|
|
117
110
|
react_1.default.createElement("div", { className: "flex items-center justify-between mb-2" },
|
|
118
111
|
react_1.default.createElement("div", { className: "text-sm text-gray-500" },
|
|
119
112
|
"Submitted by: ",
|
|
120
|
-
react_1.default.createElement("span", { className: "font-bold" },
|
|
113
|
+
react_1.default.createElement("span", { className: "font-bold" },
|
|
114
|
+
((_d = issue === null || issue === void 0 ? void 0 : issue.submittedByUser) === null || _d === void 0 ? void 0 : _d.firstName) + " " + ((_e = issue === null || issue === void 0 ? void 0 : issue.submittedByUser) === null || _e === void 0 ? void 0 : _e.lastName),
|
|
115
|
+
" ",
|
|
116
|
+
issue.submittedByUser.userTypeId === 1 ? '(Expert)' : issue.submittedByUser.userTypeId === 2 ? '(Paro)' : '(Client)'),
|
|
121
117
|
(issue === null || issue === void 0 ? void 0 : issue.createdAt) && react_1.default.createElement(react_1.default.Fragment, null,
|
|
122
118
|
" \u2022 Submitted on: ",
|
|
123
119
|
react_1.default.createElement("span", { className: "font-bold" }, (0, dayjs_1.default)(issue === null || issue === void 0 ? void 0 : issue.createdAt).format("MM-DD-YYYY"))))),
|
|
124
|
-
react_1.default.createElement("div", { className: "text-sm text-gray-600 mb-
|
|
120
|
+
react_1.default.createElement("div", { className: "text-sm text-gray-600 mb-2" },
|
|
121
|
+
react_1.default.createElement("span", { className: "font-bold" }, "Projects Affected:"),
|
|
122
|
+
" ",
|
|
123
|
+
issue.projectDetails.map((p) => p.projectName).join(", ")),
|
|
124
|
+
react_1.default.createElement("div", { className: "text-sm text-gray-600 mb-2" },
|
|
125
|
+
react_1.default.createElement("span", { className: "font-bold" }, "Problem:"),
|
|
126
|
+
" ",
|
|
127
|
+
issue.problem),
|
|
128
|
+
react_1.default.createElement("div", { className: "text-sm text-gray-600 mb-2" },
|
|
129
|
+
react_1.default.createElement("span", { className: "font-bold" }, "Desired Outcome:"),
|
|
130
|
+
" ",
|
|
131
|
+
issue.outcome),
|
|
125
132
|
processedDocs && processedDocs.length > 0 && (react_1.default.createElement("div", { className: "flex flex-wrap gap-2" },
|
|
126
133
|
react_1.default.createElement("span", { className: "text-sm font-bold text-gray-500 items-center" }, "Supporting Documents: "),
|
|
127
134
|
processedDocs.map((d, idx) => (react_1.default.createElement(exports.CustomTag, { key: idx, customColor: `bg-success border-success`, label: d.split('%2F')[1], iconLeft: react_1.default.createElement(base_icons_1.IconDocumentDownload, { size: "xs" }), onClick: () => {
|
|
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
const react_1 = __importDefault(require("react"));
|
|
7
7
|
const base_icons_1 = require("@paro.io/base-icons");
|
|
8
8
|
const base_ui_1 = require("@paro.io/base-ui");
|
|
9
|
-
const EscalationReportBanner = ({ onReport }) => {
|
|
9
|
+
const EscalationReportBanner = ({ onReport, isExpert }) => {
|
|
10
10
|
return (react_1.default.createElement("div", { className: "bg-[#EF5360] rounded-lg p-2 mb-4 shadow-lg" },
|
|
11
11
|
react_1.default.createElement("div", { className: "flex items-center justify-between" },
|
|
12
12
|
react_1.default.createElement("div", { className: "flex items-center" },
|
|
@@ -14,7 +14,12 @@ const EscalationReportBanner = ({ onReport }) => {
|
|
|
14
14
|
react_1.default.createElement(base_icons_1.IconExclamation, { className: "text-white" })),
|
|
15
15
|
react_1.default.createElement("div", null,
|
|
16
16
|
react_1.default.createElement("h3", { className: "text-xl font-bold text-white" }, "Having Issues with Your Expert?"),
|
|
17
|
-
react_1.default.createElement("p", { className: "text-red-100 text-sm mt-1" },
|
|
17
|
+
react_1.default.createElement("p", { className: "text-red-100 text-sm mt-1" }, isExpert ? (`Report communication problems, quality concerns, or missed deadlines. We'll resolve it within 4-8 hours.`) : (react_1.default.createElement(react_1.default.Fragment, null,
|
|
18
|
+
"Report communication problems, quality concerns, or missed deadlines. All issues right now will be submitted to the Expert and Paro. If you want to submit issues directly to Paro please email",
|
|
19
|
+
' ',
|
|
20
|
+
react_1.default.createElement("a", { href: "mailto:clientsupport@paro.io", className: "underline text-white hover:text-red-200 cursor-pointer" }, "clientsupport@paro.io"),
|
|
21
|
+
' ',
|
|
22
|
+
"or your Account Executive."))))),
|
|
18
23
|
react_1.default.createElement(base_ui_1.Button, { label: "Report issue now", onClick: onReport, size: "sm", color: "danger", className: "bg-white hover:bg-danger text-danger-dark hover:text-white" }))));
|
|
19
24
|
};
|
|
20
25
|
exports.default = EscalationReportBanner;
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
export declare const ACCEPTED_FILE_TYPES: string[];
|
|
2
2
|
export declare const validateFileUpload: (file: File | null) => boolean;
|
|
3
|
+
export declare const clientResponseTypes: {
|
|
4
|
+
label: string;
|
|
5
|
+
description: string;
|
|
6
|
+
value: string;
|
|
7
|
+
}[];
|
|
8
|
+
export declare const expertResponseTypes: {
|
|
9
|
+
label: string;
|
|
10
|
+
description: string;
|
|
11
|
+
value: string;
|
|
12
|
+
}[];
|
|
3
13
|
declare const EscalationRespondForm: ({ goBack, selectedIssue, documentUploadUrl, downloadDocumentUrl, uploadExpertClientFiles, updateProjectEscalation, bucketName, goHome, isExpert, userId, }: {
|
|
4
14
|
goBack: () => void;
|
|
5
15
|
selectedIssue: any;
|
|
@@ -32,7 +32,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
32
32
|
});
|
|
33
33
|
};
|
|
34
34
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
35
|
-
exports.validateFileUpload = exports.ACCEPTED_FILE_TYPES = void 0;
|
|
35
|
+
exports.expertResponseTypes = exports.clientResponseTypes = exports.validateFileUpload = exports.ACCEPTED_FILE_TYPES = void 0;
|
|
36
36
|
const react_1 = __importStar(require("react"));
|
|
37
37
|
const base_icons_1 = require("@paro.io/base-icons");
|
|
38
38
|
const base_ui_1 = require("@paro.io/base-ui");
|
|
@@ -58,7 +58,7 @@ const validateFileUpload = (file) => {
|
|
|
58
58
|
return true;
|
|
59
59
|
};
|
|
60
60
|
exports.validateFileUpload = validateFileUpload;
|
|
61
|
-
|
|
61
|
+
exports.clientResponseTypes = [
|
|
62
62
|
{
|
|
63
63
|
label: "Acknowledge issue and provide resolution plan",
|
|
64
64
|
description: "Explain circumstances and apologize",
|
|
@@ -66,7 +66,7 @@ const responseTypes = [
|
|
|
66
66
|
},
|
|
67
67
|
{
|
|
68
68
|
label: "Dispute issue and provide counter-evidence",
|
|
69
|
-
description: "Challenge the
|
|
69
|
+
description: "Challenge the expert's concerns with documentation",
|
|
70
70
|
value: "Disputed",
|
|
71
71
|
},
|
|
72
72
|
{
|
|
@@ -75,7 +75,25 @@ const responseTypes = [
|
|
|
75
75
|
value: "NoResponse",
|
|
76
76
|
},
|
|
77
77
|
];
|
|
78
|
+
exports.expertResponseTypes = [
|
|
79
|
+
{
|
|
80
|
+
label: "Acknowledge issue and provide resolution plan",
|
|
81
|
+
description: "Explain circumstances and apologize",
|
|
82
|
+
value: "Acknowledged",
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
label: "Challenge the client's concerns with documentation",
|
|
86
|
+
description: "Challenge the client's concerns with documentation",
|
|
87
|
+
value: "NeedsHelp",
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
label: "Request clarification",
|
|
91
|
+
description: "Ask for more details about the issue",
|
|
92
|
+
value: "NoResponse",
|
|
93
|
+
},
|
|
94
|
+
];
|
|
78
95
|
const EscalationRespondForm = ({ goBack, selectedIssue, documentUploadUrl, downloadDocumentUrl, uploadExpertClientFiles, updateProjectEscalation, bucketName, goHome, isExpert, userId, }) => {
|
|
96
|
+
const responseTypes = isExpert ? exports.expertResponseTypes : exports.clientResponseTypes;
|
|
79
97
|
const [selectedType, setSelectedType] = (0, react_1.useState)(responseTypes[0].value);
|
|
80
98
|
const [responseInput, setResponseInput] = (0, react_1.useState)('');
|
|
81
99
|
const [uploadFiles, setUploadFiles] = (0, react_1.useState)([]);
|
|
@@ -149,6 +167,7 @@ const EscalationRespondForm = ({ goBack, selectedIssue, documentUploadUrl, downl
|
|
|
149
167
|
variables: {
|
|
150
168
|
input: {
|
|
151
169
|
escalationId: selectedIssue.escalationId,
|
|
170
|
+
[isExpert ? 'expertResponseType' : 'clientResponseType']: selectedType,
|
|
152
171
|
[isExpert ? 'expertResponse' : 'clientResponse']: responseInput,
|
|
153
172
|
[isExpert ? 'expertSupportingDocuments' : 'clientSupportingDocuments']: uploadFiles.length > 0 ? uploadFiles.join(", ") : '',
|
|
154
173
|
statusChangedBy: userId,
|
|
@@ -44,6 +44,7 @@ const dayjs_1 = __importDefault(require("dayjs"));
|
|
|
44
44
|
const EscalationRespondForm_1 = require("./EscalationRespondForm");
|
|
45
45
|
const FileUploader_1 = require("../FileUploader");
|
|
46
46
|
const utils_1 = require("../shared/utils");
|
|
47
|
+
const EscalationIssueCard_1 = require("./EscalationIssueCard");
|
|
47
48
|
const issueTypeOptions = [
|
|
48
49
|
{ value: 'CommunicationIssues', label: 'Communication Issues' },
|
|
49
50
|
{ value: 'Professionalism', label: 'Professionalism' },
|
|
@@ -176,7 +177,7 @@ const EscalationSubmitForm = ({ goBack, goHome, expertsOrClients, projects, docu
|
|
|
176
177
|
escalationId: escalationId,
|
|
177
178
|
freelancerId: isExpert ? user.userId : selectedUser === null || selectedUser === void 0 ? void 0 : selectedUser.id,
|
|
178
179
|
clientId: isExpert ? selectedUser === null || selectedUser === void 0 ? void 0 : selectedUser.id : clientId,
|
|
179
|
-
projectDetails: selectedProjects.map((p) => { return { projectId: p.id, projectName: p.name }; }),
|
|
180
|
+
projectDetails: selectedProjects.filter((p) => p !== undefined).map((p) => { return { projectId: p.id, projectName: p.name }; }),
|
|
180
181
|
issueStartDate: issueStartDate,
|
|
181
182
|
escalationType: issueType,
|
|
182
183
|
severityLevel: severity,
|
|
@@ -219,18 +220,27 @@ const EscalationSubmitForm = ({ goBack, goHome, expertsOrClients, projects, docu
|
|
|
219
220
|
react_1.default.createElement("h2", { className: "text-xl font-bold" },
|
|
220
221
|
"Report Issue with Your ",
|
|
221
222
|
isExpert ? 'Client' : 'Expert'),
|
|
222
|
-
react_1.default.createElement("p", { className: "text-gray-600 mt-1" },
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
react_1.default.createElement("
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
223
|
+
react_1.default.createElement("p", { className: "text-gray-600 mt-1" }, isExpert ? (`Provide details about the issue you're experiencing and we'll resolve it within 4-8 hours</p>`) : (react_1.default.createElement(react_1.default.Fragment, null,
|
|
224
|
+
"Report communication problems, quality concerns, or missed deadlines. All issues right now will be submitted to the Expert and Paro. If you want to submit issues directly to Paro please email",
|
|
225
|
+
' ',
|
|
226
|
+
react_1.default.createElement("a", { href: "mailto:clientsupport@paro.io", className: "underline text-white hover:text-red-200 cursor-pointer" }, "clientsupport@paro.io"),
|
|
227
|
+
' ',
|
|
228
|
+
"or your Account Executive.")))),
|
|
229
|
+
react_1.default.createElement("div", null,
|
|
230
|
+
react_1.default.createElement("h4", { className: "font-medium text-gray-900 mb-2" }, "Priority Levels for Issues"),
|
|
231
|
+
react_1.default.createElement("div", { className: "space-y-2 text-sm" },
|
|
232
|
+
react_1.default.createElement("div", { className: "flex items-center gap-x-2" },
|
|
233
|
+
react_1.default.createElement(EscalationIssueCard_1.CustomTag, { label: "Critical" }),
|
|
234
|
+
react_1.default.createElement("span", null, "All deliverables are currently blocked; the project cannot move forward.")),
|
|
235
|
+
react_1.default.createElement("div", { className: "flex items-center gap-x-2" },
|
|
236
|
+
react_1.default.createElement(EscalationIssueCard_1.CustomTag, { label: "High" }),
|
|
237
|
+
react_1.default.createElement("span", null, "Key deliverables are blocked, preventing meaningful progress.")),
|
|
238
|
+
react_1.default.createElement("div", { className: "flex items-center gap-x-2" },
|
|
239
|
+
react_1.default.createElement(EscalationIssueCard_1.CustomTag, { label: "Medium" }),
|
|
240
|
+
react_1.default.createElement("span", null, "Deliverables are still in progress, but timelines are at risk.")),
|
|
241
|
+
react_1.default.createElement("div", { className: "flex items-center gap-x-2" },
|
|
242
|
+
react_1.default.createElement(EscalationIssueCard_1.CustomTag, { label: "Low" }),
|
|
243
|
+
react_1.default.createElement("span", null, "Minor issue or inconvenience that does not affect overall project flow.")))),
|
|
234
244
|
react_1.default.createElement("div", { className: "border rounded-lg overflow-hidden mb-6" },
|
|
235
245
|
react_1.default.createElement("div", { className: "bg-gray-50 p-3 border-b" },
|
|
236
246
|
react_1.default.createElement("h3", { className: "font-medium" }, "Issue Details")),
|
|
@@ -263,10 +273,24 @@ const EscalationSubmitForm = ({ goBack, goHome, expertsOrClients, projects, docu
|
|
|
263
273
|
.filter(opt => selected.includes(opt.value))
|
|
264
274
|
.map(opt => opt.label)
|
|
265
275
|
.join(', ');
|
|
266
|
-
}, placeholder: `Select Project(s)
|
|
276
|
+
}, placeholder: `Select Project(s)`, MenuProps: {
|
|
277
|
+
PaperProps: {
|
|
278
|
+
style: {
|
|
279
|
+
maxHeight: 300,
|
|
280
|
+
},
|
|
281
|
+
},
|
|
282
|
+
} },
|
|
267
283
|
react_1.default.createElement(core_1.MenuItem, { disabled: true, value: "" },
|
|
268
284
|
react_1.default.createElement("em", null, "Select Project(s)...")),
|
|
269
|
-
projectOptions.map(option => (react_1.default.createElement(core_1.MenuItem, { key: option.value.id, value: option.value }, option.label)))
|
|
285
|
+
projectOptions.map(option => (react_1.default.createElement(core_1.MenuItem, { key: option.value.id, value: option.value }, option.label))),
|
|
286
|
+
react_1.default.createElement(core_1.MenuItem, { disableRipple: true, disableGutters: true },
|
|
287
|
+
react_1.default.createElement(base_ui_1.Button, { label: "save & continue", className: "mx-4", color: "primary", onClick: (e) => {
|
|
288
|
+
const menu = e.target.closest('[role="presentation"]');
|
|
289
|
+
if (menu) {
|
|
290
|
+
const escEvent = new KeyboardEvent('keydown', { key: 'Escape', bubbles: true });
|
|
291
|
+
menu.dispatchEvent(escEvent);
|
|
292
|
+
}
|
|
293
|
+
} })))),
|
|
270
294
|
react_1.default.createElement("div", { className: "grid grid-cols-1 md:grid-cols-2 gap-4" },
|
|
271
295
|
react_1.default.createElement("div", null,
|
|
272
296
|
react_1.default.createElement("label", { className: "block text-sm font-medium text-gray-700 mb-1" }, "Issue Type"),
|
|
@@ -58,24 +58,35 @@ const EscalationTabsContent = ({ activeTab, openEscalationChat, setSelectedIssue
|
|
|
58
58
|
react_1.default.createElement("div", { className: "flex items-start" },
|
|
59
59
|
react_1.default.createElement("div", null,
|
|
60
60
|
react_1.default.createElement("div", { className: "flex flex-row justify-start items-center mb-3" },
|
|
61
|
-
react_1.default.createElement("h4", { className: "font-medium text-gray-900 mb-1 mr-4" },
|
|
61
|
+
react_1.default.createElement("h4", { className: "font-medium text-gray-900 mb-1 mr-4" }, project),
|
|
62
62
|
react_1.default.createElement(EscalationIssueCard_1.CustomTag, { label: issue.severityLevel })),
|
|
63
63
|
react_1.default.createElement("div", { className: "flex items-center text-sm text-gray-600 mb-2" },
|
|
64
|
-
react_1.default.createElement("span", null,
|
|
65
|
-
|
|
66
|
-
|
|
64
|
+
react_1.default.createElement("span", null,
|
|
65
|
+
"Submitted By: ",
|
|
66
|
+
issue.submittedByUser.firstName + " " + issue.submittedByUser.lastName),
|
|
67
67
|
react_1.default.createElement("span", { className: "mx-2" }, "\u2022"),
|
|
68
68
|
react_1.default.createElement("span", null,
|
|
69
69
|
"Case #",
|
|
70
70
|
issue.escalationNumber)),
|
|
71
|
-
react_1.default.createElement("
|
|
71
|
+
react_1.default.createElement("div", { className: "text-sm text-gray-600 mb-2" },
|
|
72
|
+
react_1.default.createElement("span", { className: "font-bold" }, "Problem:"),
|
|
73
|
+
" ",
|
|
74
|
+
issue.problem),
|
|
75
|
+
react_1.default.createElement("div", { className: "text-sm text-gray-600 mb-2" },
|
|
76
|
+
react_1.default.createElement("span", { className: "font-bold" }, "Desired Outcome:"),
|
|
77
|
+
" ",
|
|
78
|
+
issue.outcome),
|
|
79
|
+
react_1.default.createElement("div", { className: "text-sm text-gray-600 mb-2" },
|
|
80
|
+
react_1.default.createElement("span", { className: "font-bold" }, "Resolution:"),
|
|
81
|
+
" ",
|
|
82
|
+
issue.internalResolution ? issue.internalResolution : issue.clientResolution),
|
|
72
83
|
react_1.default.createElement("div", { className: "flex items-center text-sm text-gray-600 mb-2" },
|
|
73
84
|
react_1.default.createElement("span", { className: "text-xs text-gray-500" },
|
|
74
|
-
"Resolved ",
|
|
85
|
+
"Resolved: ",
|
|
75
86
|
(0, dayjs_1.default)(issue.updatedAt).format("MM-DD-YYYY")),
|
|
76
87
|
react_1.default.createElement("span", { className: "mx-2" }, "\u2022"),
|
|
77
88
|
react_1.default.createElement("span", { className: "text-xs text-gray-500" },
|
|
78
|
-
"Resolved By ",
|
|
89
|
+
"Resolved By: ",
|
|
79
90
|
issue.statusChangedByUser.firstName + " " + issue.statusChangedByUser.lastName,
|
|
80
91
|
" ",
|
|
81
92
|
issue.statusChangedByUser.userTypeId === 2 && '(Paro)')))),
|
|
@@ -12,6 +12,7 @@ interface EscalationsProps {
|
|
|
12
12
|
bucketName: string;
|
|
13
13
|
user: any;
|
|
14
14
|
clientId?: number;
|
|
15
|
+
escalationDisputeStatistics?: any;
|
|
15
16
|
}
|
|
16
|
-
export declare const Escalations: ({ expertsOrClients, projects, isExpert, escalations, documentUploadUrl, downloadDocumentUrl, uploadExpertClientFiles, createProjectEscalation, updateProjectEscalation, createEscalationChatMessage, bucketName, user, clientId, }: EscalationsProps) => JSX.Element;
|
|
17
|
+
export declare const Escalations: ({ expertsOrClients, projects, isExpert, escalations, documentUploadUrl, downloadDocumentUrl, uploadExpertClientFiles, createProjectEscalation, updateProjectEscalation, createEscalationChatMessage, bucketName, user, clientId, escalationDisputeStatistics, }: EscalationsProps) => JSX.Element;
|
|
17
18
|
export {};
|
|
@@ -40,7 +40,7 @@ const AccountSuspensionModal_1 = __importDefault(require("./AccountSuspensionMod
|
|
|
40
40
|
const base_icons_1 = require("@paro.io/base-icons");
|
|
41
41
|
const EscalationIssueCard_1 = require("./EscalationIssueCard");
|
|
42
42
|
const react_hot_toast_1 = require("react-hot-toast");
|
|
43
|
-
const Escalations = ({ expertsOrClients, projects, isExpert = false, escalations, documentUploadUrl, downloadDocumentUrl, uploadExpertClientFiles, createProjectEscalation, updateProjectEscalation, createEscalationChatMessage, bucketName, user, clientId = 0, }) => {
|
|
43
|
+
const Escalations = ({ expertsOrClients, projects, isExpert = false, escalations, documentUploadUrl, downloadDocumentUrl, uploadExpertClientFiles, createProjectEscalation, updateProjectEscalation, createEscalationChatMessage, bucketName, user, clientId = 0, escalationDisputeStatistics, }) => {
|
|
44
44
|
const [activeSection, setActiveSection] = (0, react_1.useState)('support');
|
|
45
45
|
const [selectedProject, setSelectedProject] = (0, react_1.useState)(null);
|
|
46
46
|
const [selectedIssueId, setSelectedIssueId] = (0, react_1.useState)(null); // using selectedIssueId 0 for new escalation submission
|
|
@@ -53,13 +53,20 @@ const Escalations = ({ expertsOrClients, projects, isExpert = false, escalations
|
|
|
53
53
|
const userTypeId = (_a = issue === null || issue === void 0 ? void 0 : issue.submittedByUser) === null || _a === void 0 ? void 0 : _a.userTypeId;
|
|
54
54
|
const internalEscalationTo = issue === null || issue === void 0 ? void 0 : issue.internalEscalationTo;
|
|
55
55
|
const status = issue === null || issue === void 0 ? void 0 : issue.status;
|
|
56
|
-
|
|
57
|
-
const
|
|
58
|
-
?
|
|
59
|
-
:
|
|
60
|
-
|
|
61
|
-
(
|
|
62
|
-
|
|
56
|
+
const isDifferentUserType = userTypeId !== user.userTypeId;
|
|
57
|
+
const isPendingResponse = isExpert
|
|
58
|
+
? issue.expertResponse === null
|
|
59
|
+
: issue.clientResponse === null;
|
|
60
|
+
const isValidInternalEscalation = userTypeId === 2
|
|
61
|
+
? (isExpert
|
|
62
|
+
? internalEscalationTo !== "client" // expert should NOT see escalations to client
|
|
63
|
+
: internalEscalationTo !== "expert") // client should NOT see escalations to expert
|
|
64
|
+
: userTypeId !== user.userTypeId;
|
|
65
|
+
const shouldInclude = isDifferentUserType &&
|
|
66
|
+
isPendingResponse &&
|
|
67
|
+
isValidInternalEscalation &&
|
|
68
|
+
status === 'InProgress';
|
|
69
|
+
return shouldInclude;
|
|
63
70
|
});
|
|
64
71
|
const inProgressIssues = escalations.filter(issue => { var _a; return ((((_a = issue === null || issue === void 0 ? void 0 : issue.submittedByUser) === null || _a === void 0 ? void 0 : _a.userTypeId) === user.userTypeId) || (isExpert ? issue.expertResponse !== null : issue.clientResponse !== null)) && (issue === null || issue === void 0 ? void 0 : issue.status) === 'InProgress'; });
|
|
65
72
|
const resolvedIssues = escalations.filter(issue => (issue === null || issue === void 0 ? void 0 : issue.status) === 'Resolved');
|
|
@@ -149,9 +156,9 @@ const Escalations = ({ expertsOrClients, projects, isExpert = false, escalations
|
|
|
149
156
|
react_1.default.createElement("p", { className: "text-xs" }, "Report payment, scope, or communication activeIssues")))),
|
|
150
157
|
react_1.default.createElement(base_ui_1.Button, { label: "Report issue", onClick: () => setSelectedIssueId(0), iconLeft: react_1.default.createElement(base_icons_1.IconExclamation, { size: "sm", className: "text-white" }), color: "primary" }))),
|
|
151
158
|
isExpert &&
|
|
152
|
-
react_1.default.createElement(AccountSuspensionBanner_1.default, { setShowSuspensionModal: setShowSuspensionModal,
|
|
159
|
+
react_1.default.createElement(AccountSuspensionBanner_1.default, { setShowSuspensionModal: setShowSuspensionModal, escalationDisputeStatistics: escalationDisputeStatistics }),
|
|
153
160
|
!isExpert &&
|
|
154
|
-
react_1.default.createElement(EscalationReportBanner_1.default, { onReport: () => setSelectedIssueId(0) }),
|
|
161
|
+
react_1.default.createElement(EscalationReportBanner_1.default, { onReport: () => setSelectedIssueId(0), isExpert: isExpert }),
|
|
155
162
|
escalations.length > 0 ? react_1.default.createElement(EscalationTabs_1.default, { activeTab: activeEscalationTab, setActiveTab: setActiveEscalationTab, activeIssues: activeIssues.length, inProgressIssues: inProgressIssues.length, resolvedIssues: resolvedIssues.length }) : null,
|
|
156
163
|
react_1.default.createElement(EscalationTabsContent_1.default, { activeTab: activeEscalationTab, openEscalationChat: openEscalationChat, setSelectedIssueId: setSelectedIssueId, activeIssues: activeIssues, inProgressIssues: inProgressIssues, resolvedIssues: resolvedIssues, updateProjectEscalation: updateProjectEscalation, isExpert: isExpert, downloadDocumentUrl: downloadDocumentUrl, bucketName: bucketName, userId: user.userId })),
|
|
157
164
|
activeSection === 'support' && activeIssues.filter((issue) => issue.escalationNumber === selectedIssueId).length > 0 && (react_1.default.createElement(EscalationRespondForm_1.default, { goBack: goBack, selectedIssue: activeIssues.find((issue) => issue.escalationNumber === selectedIssueId), documentUploadUrl: documentUploadUrl, downloadDocumentUrl: downloadDocumentUrl, bucketName: bucketName, uploadExpertClientFiles: uploadExpertClientFiles, updateProjectEscalation: updateProjectEscalation, goHome: goHome, isExpert: isExpert, userId: user.userId })),
|
|
@@ -40,6 +40,7 @@ const utils_1 = require("../shared/utils");
|
|
|
40
40
|
const MarkResolvedModal = ({ escalationId, expertName, open, onClose, updateProjectEscalation, userId, }) => {
|
|
41
41
|
const [resolutionText, setResolutionText] = (0, react_1.useState)(null);
|
|
42
42
|
const [submitting, setSubmitting] = (0, react_1.useState)(false);
|
|
43
|
+
const [expertResponsibility, setExpertResponsibility] = (0, react_1.useState)('');
|
|
43
44
|
const handleSubmit = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
44
45
|
setSubmitting(true);
|
|
45
46
|
try {
|
|
@@ -50,6 +51,7 @@ const MarkResolvedModal = ({ escalationId, expertName, open, onClose, updateProj
|
|
|
50
51
|
clientResolution: resolutionText,
|
|
51
52
|
status: 'Resolved',
|
|
52
53
|
statusChangedBy: userId,
|
|
54
|
+
isExpertAtFault: expertResponsibility === 'atFault' ? true : false,
|
|
53
55
|
}
|
|
54
56
|
}
|
|
55
57
|
});
|
|
@@ -78,10 +80,23 @@ const MarkResolvedModal = ({ escalationId, expertName, open, onClose, updateProj
|
|
|
78
80
|
react_1.default.createElement("div", { className: "mt-6 mb-4" },
|
|
79
81
|
react_1.default.createElement("label", { className: "block text-sm font-medium text-gray-700 mb-1" }, "Please provide additional details before marking this issue as resolved."),
|
|
80
82
|
react_1.default.createElement(base_ui_1.Input, { type: "text", value: resolutionText, placeholder: "Enter details here...", onChange: (e) => setResolutionText(e.target.value) })),
|
|
83
|
+
react_1.default.createElement("p", null, "Please provide additional details before marking this issue as resolved."),
|
|
84
|
+
react_1.default.createElement("div", { style: { marginTop: '1em', marginBottom: '1em' } },
|
|
85
|
+
react_1.default.createElement("label", { className: "block text-sm font-normal mb-1" },
|
|
86
|
+
"Expert Responsibility ",
|
|
87
|
+
react_1.default.createElement("span", { className: "text-sm text-red-800" }, "*")),
|
|
88
|
+
react_1.default.createElement("div", null,
|
|
89
|
+
react_1.default.createElement("label", null,
|
|
90
|
+
react_1.default.createElement("input", { type: "radio", name: "expertResponsibility", value: "atFault", checked: expertResponsibility === 'atFault', onChange: () => setExpertResponsibility('atFault'), required: true }),
|
|
91
|
+
"Expert is at fault for this issue")),
|
|
92
|
+
react_1.default.createElement("div", null,
|
|
93
|
+
react_1.default.createElement("label", null,
|
|
94
|
+
react_1.default.createElement("input", { type: "radio", name: "expertResponsibility", value: "notAtFault", checked: expertResponsibility === 'notAtFault', onChange: () => setExpertResponsibility('notAtFault'), required: true }),
|
|
95
|
+
"Expert is NOT at fault"))),
|
|
81
96
|
react_1.default.createElement("div", { className: "flex items-center justify-between" },
|
|
82
97
|
react_1.default.createElement("div", { className: "flex space-x-2" },
|
|
83
98
|
react_1.default.createElement(base_ui_1.Button, { label: "Cancel", onClick: onClose, disabled: submitting })),
|
|
84
99
|
react_1.default.createElement("div", { className: "flex space-x-2" },
|
|
85
|
-
react_1.default.createElement(base_ui_1.Button, { label: "Submit", color: "primary", onClick: handleSubmit, isLoading: submitting }))))));
|
|
100
|
+
react_1.default.createElement(base_ui_1.Button, { label: "Submit", color: "primary", onClick: handleSubmit, isLoading: submitting, disabled: expertResponsibility === '' }))))));
|
|
86
101
|
};
|
|
87
102
|
exports.default = MarkResolvedModal;
|
|
@@ -8,9 +8,14 @@ const base_icons_1 = require("@paro.io/base-icons");
|
|
|
8
8
|
const core_1 = require("@material-ui/core");
|
|
9
9
|
const utils_1 = require("../shared/utils");
|
|
10
10
|
const EscalationIssueCard_1 = require("./EscalationIssueCard");
|
|
11
|
+
const EscalationRespondForm_1 = require("./EscalationRespondForm");
|
|
11
12
|
const ViewResponseModal = ({ selectedIssue, open, onClose, isExpert }) => {
|
|
13
|
+
var _a, _b, _c, _d, _e;
|
|
12
14
|
const docs = [selectedIssue.expertSupportingDocuments, selectedIssue.clientSupportingDocuments, selectedIssue.internalSupportingDocuments];
|
|
13
15
|
const processedDocs = (0, utils_1.processDocs)(docs);
|
|
16
|
+
const responseType = isExpert ? (_a = selectedIssue.clientResponseType) !== null && _a !== void 0 ? _a : selectedIssue.expertResponseType : (_b = selectedIssue.expertResponseType) !== null && _b !== void 0 ? _b : selectedIssue.clientResponseType;
|
|
17
|
+
const responseTypeLabel = ((_c = [...new Set([...EscalationRespondForm_1.clientResponseTypes, ...EscalationRespondForm_1.expertResponseTypes])].find(opt => opt.value === responseType)) === null || _c === void 0 ? void 0 : _c.label) || responseType || "";
|
|
18
|
+
const response = isExpert ? (_d = selectedIssue.clientResponse) !== null && _d !== void 0 ? _d : selectedIssue.expertResponse : (_e = selectedIssue.expertResponse) !== null && _e !== void 0 ? _e : selectedIssue.clientResponse;
|
|
14
19
|
return (react_1.default.createElement(core_1.Dialog, { open: open, onClose: onClose, maxWidth: 'md', PaperProps: {
|
|
15
20
|
style: { minWidth: '40%' }
|
|
16
21
|
} },
|
|
@@ -23,15 +28,12 @@ const ViewResponseModal = ({ selectedIssue, open, onClose, isExpert }) => {
|
|
|
23
28
|
react_1.default.createElement(base_icons_1.IconX, null))))),
|
|
24
29
|
react_1.default.createElement(core_1.DialogContent, null,
|
|
25
30
|
react_1.default.createElement("div", { className: "bg-green-50 border-green-800 border border-l-4 rounded p-4 mt-4 mb-4" },
|
|
26
|
-
react_1.default.createElement("p", { className: "text-sm font-medium" },
|
|
27
|
-
"Status: ",
|
|
28
|
-
react_1.default.createElement("span", { className: "font-normal" }, "Responded")),
|
|
29
31
|
react_1.default.createElement("p", { className: "text-sm font-medium" },
|
|
30
32
|
"Response Type: ",
|
|
31
|
-
react_1.default.createElement("span", { className: "font-normal" },
|
|
33
|
+
react_1.default.createElement("span", { className: "font-normal" }, responseTypeLabel)),
|
|
32
34
|
react_1.default.createElement("p", { className: "text-sm font-medium" },
|
|
33
35
|
"Response: ",
|
|
34
|
-
react_1.default.createElement("span", { className: "font-normal" },
|
|
36
|
+
react_1.default.createElement("span", { className: "font-normal" }, response))),
|
|
35
37
|
processedDocs && processedDocs.length > 0 && (react_1.default.createElement("div", { className: "flex flex-wrap gap-2 mt-4 mb-2" },
|
|
36
38
|
react_1.default.createElement("span", { className: "text-sm font-bold text-gray-500 items-center" }, "Supporting Documents: "),
|
|
37
39
|
processedDocs.map((d, idx) => (react_1.default.createElement(EscalationIssueCard_1.CustomTag, { key: idx, label: d.split('%2F')[1], customColor: `bg-success border-success` }))))))));
|
|
@@ -16,5 +16,6 @@ interface EarningsSectionProps {
|
|
|
16
16
|
hasCompletedProjects: boolean;
|
|
17
17
|
lifetimeFSV: any;
|
|
18
18
|
}
|
|
19
|
+
export declare const EarningsInfo: ({ label, value, textColor }: any) => JSX.Element;
|
|
19
20
|
declare const EarningsSection: ({ expertServiceLinesPlus, firmTags, haveATeamProp, expertLevels, currentMonthGoalHours, expertIRPRRatio, winRate, expertIRPRRatioPrev, winRatePrev, earnings, upsell, crossSell, lifetimeIRPR, lifetimeWinRate, hasCompletedProjects, lifetimeFSV }: EarningsSectionProps) => JSX.Element;
|
|
20
21
|
export default EarningsSection;
|
|
@@ -14,6 +14,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
14
14
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.EarningsInfo = void 0;
|
|
17
18
|
const core_1 = require("@material-ui/core");
|
|
18
19
|
const styles_1 = require("@material-ui/core/styles");
|
|
19
20
|
const react_1 = __importDefault(require("react"));
|
|
@@ -64,6 +65,7 @@ const FirmTags = (_a) => {
|
|
|
64
65
|
const EarningsInfo = ({ label, value, textColor = 'black' }) => (react_1.default.createElement(core_1.Box, { mr: 2, display: "flex", justifyContent: "space-between", alignItems: "center" },
|
|
65
66
|
react_1.default.createElement(core_1.Typography, { variant: "body2" }, label),
|
|
66
67
|
react_1.default.createElement("b", { style: { color: textColor } }, value)));
|
|
68
|
+
exports.EarningsInfo = EarningsInfo;
|
|
67
69
|
const DataCell = ({ label, value }) => {
|
|
68
70
|
//@ts-ignore
|
|
69
71
|
const classes = useStyles();
|
|
@@ -129,16 +131,16 @@ const EarningsSection = ({ expertServiceLinesPlus, firmTags, haveATeamProp, expe
|
|
|
129
131
|
react_1.default.createElement(core_1.Box, { mb: 1 },
|
|
130
132
|
react_1.default.createElement("b", null, "Expert Earnings :")),
|
|
131
133
|
hasCompletedProjects ? (react_1.default.createElement(react_1.default.Fragment, null,
|
|
132
|
-
react_1.default.createElement(EarningsInfo, { label: "Monthly Goal", value: currentMonthGoalHours > 0 ? `$ ${ToLocalStringFn(currentMonthGoalHours)}` : 'N/A' }),
|
|
134
|
+
react_1.default.createElement(exports.EarningsInfo, { label: "Monthly Goal", value: currentMonthGoalHours > 0 ? `$ ${ToLocalStringFn(currentMonthGoalHours)}` : 'N/A' }),
|
|
133
135
|
react_1.default.createElement(core_1.Box, { mb: 2 },
|
|
134
136
|
react_1.default.createElement(core_1.Divider, null)),
|
|
135
|
-
react_1.default.createElement(EarningsInfo, { label: "Monthly Earnings", value: (earnings === null || earnings === void 0 ? void 0 : earnings.length) > 0 ? `$ ${ToLocalStringFn(earning)}` : 'N/A' }),
|
|
137
|
+
react_1.default.createElement(exports.EarningsInfo, { label: "Monthly Earnings", value: (earnings === null || earnings === void 0 ? void 0 : earnings.length) > 0 ? `$ ${ToLocalStringFn(earning)}` : 'N/A' }),
|
|
136
138
|
react_1.default.createElement(core_1.Box, { mb: 2 },
|
|
137
139
|
react_1.default.createElement(core_1.Divider, null)),
|
|
138
|
-
react_1.default.createElement(EarningsInfo, { label: "% to Goal", value: currentMonthGoalHours && earning ? `${ToLocalStringFn(((earning / currentMonthGoalHours) * 100).toFixed(2))} %` : 'N/A' }),
|
|
140
|
+
react_1.default.createElement(exports.EarningsInfo, { label: "% to Goal", value: currentMonthGoalHours && earning ? `${ToLocalStringFn(((earning / currentMonthGoalHours) * 100).toFixed(2))} %` : 'N/A' }),
|
|
139
141
|
react_1.default.createElement(core_1.Box, { mb: 2 },
|
|
140
142
|
react_1.default.createElement(core_1.Divider, null)),
|
|
141
|
-
react_1.default.createElement(EarningsInfo, { label: "Lifetime FSV", value: lifetimeFSV ? ToLocalStringFn(lifetimeFSV) : 'N/A', textColor: backgroundColor }),
|
|
143
|
+
react_1.default.createElement(exports.EarningsInfo, { label: "Lifetime FSV", value: lifetimeFSV ? ToLocalStringFn(lifetimeFSV) : 'N/A', textColor: backgroundColor }),
|
|
142
144
|
react_1.default.createElement(core_1.Box, { mb: 2 },
|
|
143
145
|
react_1.default.createElement(core_1.Divider, null)),
|
|
144
146
|
react_1.default.createElement(ExpertStatsTable, { expertIRPRRatioPrev: expertIRPRRatioPrev, expertIRPRRatio: expertIRPRRatio, winRatePrev: winRatePrev, winRate: winRate, lifetimeIRPR: lifetimeIRPR, lifetimeWinRate: lifetimeWinRate }))) : (react_1.default.createElement(core_1.Typography, { variant: "body1" }, "This Expert has not billed yet")))));
|
|
@@ -64,6 +64,8 @@ type QueryAndMutationProps = {
|
|
|
64
64
|
getExpertVanityTitles?: any;
|
|
65
65
|
getExpertRates?: any;
|
|
66
66
|
updateExpert?: any;
|
|
67
|
+
escalationDisputeStatistics?: any;
|
|
68
|
+
escalationDisputeStatisticsLoading?: any;
|
|
67
69
|
};
|
|
68
70
|
type ChangeEmailPasswordProps = {
|
|
69
71
|
getUserByEmail?: any;
|
|
@@ -79,5 +81,5 @@ type ProfileCompletedPercentageProps = {
|
|
|
79
81
|
handleScrollToBottom?: () => void;
|
|
80
82
|
clientReferencesSectionCompleted: boolean;
|
|
81
83
|
};
|
|
82
|
-
export declare const ExpertProfileHeader: ({ expertId, legacyFreelancerId, address: addressForReducer, companyName: companyNameForReducer, legacyMetadata: legacyMetadataForReducer, legacyMetadata: { primaryServiceLine, applicationStatus }, user: userForReducer, user: { id, imageUrl, email }, firmTags, detailsSectionCompleted, clientReferencesSectionCompleted, preferenceTasks, isWhiteLabel, handleScrollToBottom, haveATeamProp, expertFirms, paroAppUrl, isExpertOps, internalUserId, getExpertByLegacyFreelancerIdDocument, createExpertPublicProfileTrackingRecord, updateFreelancerServiceLine, serviceLineLoading, serviceLineData, serviceLineError, reviewData, profileReviewError, availabilityData, nextMonthAvailabilityData, expertStatusData, expertStatusLoading, expertLevelsData, projectsData, paroProjectsLoading, expertMetricsData, expertMetricsLoading, upSellCrossSellData, upSellCrossSellLoading, projectChangeLogData, projectChangeLogLoading, updateAddressMutation, addressUpdateLoading, updateUserMutation, userUpdateLoading, updateUserError, updateFreelancerMutation, updateFreelancerLoading, updateFreelancerError, getFreelancerEarnings, getQualityKPIs, expertLevelsTrainings, trainingsDataLoading, assignLetterTrainingToExpert, assignTrainingLoading, assignTrainingError, getUserDocument, getExpertStatusDocument, uploadUserPhotoMutation, loadingNewImage, imageUpdateError, getUserByEmail, updateUserEmail, getUserByEmailLazyQuery, updateUserPassword, verifyUserPassword, openServiceLinesTemplate, setOpenServiceLinesTemplate, getExpertVanityTitles, getExpertRates, updateExpert, vanityTitle, }: SectionContents & PersonalInformationType & ProfileCompletedPercentageProps & QueryAndMutationProps & ChangeEmailPasswordProps) => JSX.Element;
|
|
84
|
+
export declare const ExpertProfileHeader: ({ expertId, legacyFreelancerId, address: addressForReducer, companyName: companyNameForReducer, legacyMetadata: legacyMetadataForReducer, legacyMetadata: { primaryServiceLine, applicationStatus }, user: userForReducer, user: { id, imageUrl, email }, firmTags, detailsSectionCompleted, clientReferencesSectionCompleted, preferenceTasks, isWhiteLabel, handleScrollToBottom, haveATeamProp, expertFirms, paroAppUrl, isExpertOps, internalUserId, getExpertByLegacyFreelancerIdDocument, createExpertPublicProfileTrackingRecord, updateFreelancerServiceLine, serviceLineLoading, serviceLineData, serviceLineError, reviewData, profileReviewError, availabilityData, nextMonthAvailabilityData, expertStatusData, expertStatusLoading, expertLevelsData, projectsData, paroProjectsLoading, expertMetricsData, expertMetricsLoading, upSellCrossSellData, upSellCrossSellLoading, projectChangeLogData, projectChangeLogLoading, updateAddressMutation, addressUpdateLoading, updateUserMutation, userUpdateLoading, updateUserError, updateFreelancerMutation, updateFreelancerLoading, updateFreelancerError, getFreelancerEarnings, getQualityKPIs, expertLevelsTrainings, trainingsDataLoading, assignLetterTrainingToExpert, assignTrainingLoading, assignTrainingError, getUserDocument, getExpertStatusDocument, uploadUserPhotoMutation, loadingNewImage, imageUpdateError, getUserByEmail, updateUserEmail, getUserByEmailLazyQuery, updateUserPassword, verifyUserPassword, openServiceLinesTemplate, setOpenServiceLinesTemplate, getExpertVanityTitles, getExpertRates, updateExpert, vanityTitle, escalationDisputeStatistics, escalationDisputeStatisticsLoading, }: SectionContents & PersonalInformationType & ProfileCompletedPercentageProps & QueryAndMutationProps & ChangeEmailPasswordProps) => JSX.Element;
|
|
83
85
|
export {};
|
|
@@ -76,7 +76,7 @@ const convertRegionToStateAbbreviation = (region) => {
|
|
|
76
76
|
const personalInformationReducer = (state, updatedState) => {
|
|
77
77
|
return Object.assign(Object.assign({}, state), updatedState);
|
|
78
78
|
};
|
|
79
|
-
const ExpertProfileHeader = ({ expertId, legacyFreelancerId, address: addressForReducer, companyName: companyNameForReducer, legacyMetadata: legacyMetadataForReducer, legacyMetadata: { primaryServiceLine, applicationStatus }, user: userForReducer, user: { id, imageUrl, email }, firmTags, detailsSectionCompleted, clientReferencesSectionCompleted, preferenceTasks, isWhiteLabel, handleScrollToBottom, haveATeamProp, expertFirms, paroAppUrl, isExpertOps, internalUserId, getExpertByLegacyFreelancerIdDocument, createExpertPublicProfileTrackingRecord, updateFreelancerServiceLine, serviceLineLoading, serviceLineData, serviceLineError, reviewData, profileReviewError, availabilityData, nextMonthAvailabilityData, expertStatusData, expertStatusLoading, expertLevelsData, projectsData, paroProjectsLoading, expertMetricsData, expertMetricsLoading, upSellCrossSellData, upSellCrossSellLoading, projectChangeLogData, projectChangeLogLoading, updateAddressMutation, addressUpdateLoading, updateUserMutation, userUpdateLoading, updateUserError, updateFreelancerMutation, updateFreelancerLoading, updateFreelancerError, getFreelancerEarnings, getQualityKPIs, expertLevelsTrainings, trainingsDataLoading, assignLetterTrainingToExpert, assignTrainingLoading, assignTrainingError, getUserDocument, getExpertStatusDocument, uploadUserPhotoMutation, loadingNewImage, imageUpdateError, getUserByEmail, updateUserEmail, getUserByEmailLazyQuery, updateUserPassword, verifyUserPassword, openServiceLinesTemplate, setOpenServiceLinesTemplate, getExpertVanityTitles, getExpertRates, updateExpert, vanityTitle, }) => {
|
|
79
|
+
const ExpertProfileHeader = ({ expertId, legacyFreelancerId, address: addressForReducer, companyName: companyNameForReducer, legacyMetadata: legacyMetadataForReducer, legacyMetadata: { primaryServiceLine, applicationStatus }, user: userForReducer, user: { id, imageUrl, email }, firmTags, detailsSectionCompleted, clientReferencesSectionCompleted, preferenceTasks, isWhiteLabel, handleScrollToBottom, haveATeamProp, expertFirms, paroAppUrl, isExpertOps, internalUserId, getExpertByLegacyFreelancerIdDocument, createExpertPublicProfileTrackingRecord, updateFreelancerServiceLine, serviceLineLoading, serviceLineData, serviceLineError, reviewData, profileReviewError, availabilityData, nextMonthAvailabilityData, expertStatusData, expertStatusLoading, expertLevelsData, projectsData, paroProjectsLoading, expertMetricsData, expertMetricsLoading, upSellCrossSellData, upSellCrossSellLoading, projectChangeLogData, projectChangeLogLoading, updateAddressMutation, addressUpdateLoading, updateUserMutation, userUpdateLoading, updateUserError, updateFreelancerMutation, updateFreelancerLoading, updateFreelancerError, getFreelancerEarnings, getQualityKPIs, expertLevelsTrainings, trainingsDataLoading, assignLetterTrainingToExpert, assignTrainingLoading, assignTrainingError, getUserDocument, getExpertStatusDocument, uploadUserPhotoMutation, loadingNewImage, imageUpdateError, getUserByEmail, updateUserEmail, getUserByEmailLazyQuery, updateUserPassword, verifyUserPassword, openServiceLinesTemplate, setOpenServiceLinesTemplate, getExpertVanityTitles, getExpertRates, updateExpert, vanityTitle, escalationDisputeStatistics, escalationDisputeStatisticsLoading, }) => {
|
|
80
80
|
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
|
|
81
81
|
const [leftSideStatus, setLeftSideStatus] = react_1.default.useState("");
|
|
82
82
|
const [rightSideStatus, setRightSideStatus] = react_1.default.useState("");
|
|
@@ -142,7 +142,7 @@ const ExpertProfileHeader = ({ expertId, legacyFreelancerId, address: addressFor
|
|
|
142
142
|
const expertServiceLinesPlus = (serviceLineData === null || serviceLineData === void 0 ? void 0 : serviceLineData.getExpertByLegacyFreelancerId.serviceLinesPlus) || [];
|
|
143
143
|
const expertLevels = expertLevelsData === null || expertLevelsData === void 0 ? void 0 : expertLevelsData.getExpertLevelsData;
|
|
144
144
|
if ((expertStatuses && expertMetricsData && leftSideStatus === "" && rightSideStatus === "") || triggerNewStatus) {
|
|
145
|
-
(0, ExpertStatusCalculator_1.default)(expertStatuses, setLeftSideStatus, setRightSideStatus, setInfoColor, expertMetricsData, earnings, applicationStatus, setTriggerNewStatus);
|
|
145
|
+
(0, ExpertStatusCalculator_1.default)(expertStatuses, setLeftSideStatus, setRightSideStatus, setInfoColor, expertMetricsData, earnings, applicationStatus, setTriggerNewStatus, escalationDisputeStatistics);
|
|
146
146
|
}
|
|
147
147
|
const [open, setOpen] = react_1.default.useState(false);
|
|
148
148
|
const [reducedPersonalInformation, dispatchUpdatedPersonalInformationUpdate] = react_1.default.useReducer(personalInformationReducer, {
|
|
@@ -165,7 +165,7 @@ const ExpertProfileHeader = ({ expertId, legacyFreelancerId, address: addressFor
|
|
|
165
165
|
return react_1.default.createElement(ParoError_1.ParoError, null);
|
|
166
166
|
}
|
|
167
167
|
const publicProfileLink = `${paroAppUrl}/public/${id}`;
|
|
168
|
-
if (expertStatusLoading || expertMetricsLoading || serviceLineLoading || paroProjectsLoading || projectChangeLogLoading || upSellCrossSellLoading) {
|
|
168
|
+
if (expertStatusLoading || expertMetricsLoading || serviceLineLoading || paroProjectsLoading || projectChangeLogLoading || upSellCrossSellLoading || escalationDisputeStatisticsLoading) {
|
|
169
169
|
return react_1.default.createElement(Loader_1.default, { size: '30px', text: 'Loading Expert Stats...' });
|
|
170
170
|
}
|
|
171
171
|
const ServiceLines = [
|
|
@@ -219,7 +219,7 @@ const ExpertProfileHeader = ({ expertId, legacyFreelancerId, address: addressFor
|
|
|
219
219
|
react_1.default.createElement(ProfileSection_1.default, { legacyFreelancerId: Number(legacyFreelancerId), imageUrl: imageUrl, shouldAllowEditProfile: isExpertOps !== null && isExpertOps !== void 0 ? isExpertOps : false, firstName: firstName, lastName: lastName, primaryServiceLine: primaryServiceLine, editServiceLine: editServiceLine, city: city, stateAbbreviation: stateAbbreviation, email: email, phone: phone, setOpen: setOpen, paroTenure: paroTenure, hourlyRate: defaultHourlyRate, paroProjectsData: paroProjectsData, getUserDocument: getUserDocument, uploadUserPhotoMutation: uploadUserPhotoMutation, loadingNewImage: loadingNewImage, imageUpdateError: imageUpdateError, isInternal: isInternal, getUserByEmail: getUserByEmail, updateUserEmail: updateUserEmail, updateUserMutation: updateUserMutation, getUserByEmailLazyQuery: getUserByEmailLazyQuery, updateUserPassword: updateUserPassword, verifyUserPassword: verifyUserPassword, getExpertRates: getExpertRates }),
|
|
220
220
|
react_1.default.createElement(core_1.Divider, { orientation: isSmallScreen ? 'horizontal' : 'vertical', style: { marginLeft: '0px !important' } })),
|
|
221
221
|
react_1.default.createElement(core_1.Grid, { item: true, container: true, direction: 'column', justify: 'space-between', xs: 12, md: true, style: { flex: '1', width: 'auto' } },
|
|
222
|
-
react_1.default.createElement(NetworkSection_1.default, { handleScrollToBottom: isInternal && handleScrollToBottom ? handleScrollToBottom : () => { }, preferenceTasks: preferenceTasks, isWhiteLabel: isWhiteLabel, detailsSectionCompleted: detailsSectionCompleted, clientReferencesSectionCompleted: clientReferencesSectionCompleted, infoColor: infoColor, leftSideStatus: leftSideStatus, rightSideStatus: rightSideStatus, isInternal: isInternal, paroTenure: paroTenure, getExpertVanityTitles: getExpertVanityTitles, updateExpert: updateExpert, expertId: expertId, legacyFreelancerId: Number(legacyFreelancerId), vanityTitle: vanityTitle, getExpertByLegacyFreelancerIdDocument: getExpertByLegacyFreelancerIdDocument }),
|
|
222
|
+
react_1.default.createElement(NetworkSection_1.default, { handleScrollToBottom: isInternal && handleScrollToBottom ? handleScrollToBottom : () => { }, preferenceTasks: preferenceTasks, isWhiteLabel: isWhiteLabel, detailsSectionCompleted: detailsSectionCompleted, clientReferencesSectionCompleted: clientReferencesSectionCompleted, infoColor: infoColor, leftSideStatus: leftSideStatus, rightSideStatus: rightSideStatus, isInternal: isInternal, paroTenure: paroTenure, getExpertVanityTitles: getExpertVanityTitles, updateExpert: updateExpert, expertId: expertId, legacyFreelancerId: Number(legacyFreelancerId), vanityTitle: vanityTitle, getExpertByLegacyFreelancerIdDocument: getExpertByLegacyFreelancerIdDocument, escalationDisputeStatistics: escalationDisputeStatistics, paroAppUrl: paroAppUrl }),
|
|
223
223
|
react_1.default.createElement(core_1.Divider, { orientation: isSmallScreen ? 'horizontal' : 'vertical' })),
|
|
224
224
|
react_1.default.createElement(core_1.Grid, { item: true, container: true, direction: 'column', justify: 'space-between', xs: 12, md: true, style: { flex: '1', width: 'auto' } },
|
|
225
225
|
react_1.default.createElement(EarningsSection_1.default, { expertServiceLinesPlus: expertServiceLinesPlus, firmTags: firmTags, haveATeamProp: haveATeamProp, expertLevels: expertLevels, currentMonthGoalHours: currentMonthGoalHours, expertIRPRRatio: expertIRPRRatio, winRate: winRate, expertIRPRRatioPrev: expertIRPRRatioPrev, winRatePrev: winRatePrev, earnings: earnings, upsell: upsell, crossSell: crossSell, lifetimeIRPR: lifetimeIRPR, lifetimeWinRate: lifetimeWinRate, hasCompletedProjects: (_k = expertMetricsData === null || expertMetricsData === void 0 ? void 0 : expertMetricsData.getExpertMetrics) === null || _k === void 0 ? void 0 : _k.lastProjectCompleted, lifetimeFSV: lifetimeFSV }),
|
|
@@ -20,5 +20,5 @@ export type ExpertStatusData = {
|
|
|
20
20
|
hasValidInsurance: boolean;
|
|
21
21
|
insuranceExpiryDate: Date;
|
|
22
22
|
};
|
|
23
|
-
declare const ExpertStatusCalculator: (statusData: ExpertStatusData, setLeftSideStatus: (leftSideStatus: string) => void, setRightSideStatus: (rightSideStatus: string) => void, setInfoColor: ({ leftSideStatus, rightSideStatus, Matching, FaF, JobBoard, Available }: any) => void, expertMetricsData: any, earningsData: any, applicationStatus: any, setTriggerNewStatus: any) => void;
|
|
23
|
+
declare const ExpertStatusCalculator: (statusData: ExpertStatusData, setLeftSideStatus: (leftSideStatus: string) => void, setRightSideStatus: (rightSideStatus: string) => void, setInfoColor: ({ leftSideStatus, rightSideStatus, Matching, FaF, JobBoard, Available }: any) => void, expertMetricsData: any, earningsData: any, applicationStatus: any, setTriggerNewStatus: any, escalationDisputeStatistics: any) => void;
|
|
24
24
|
export default ExpertStatusCalculator;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const utils_1 = require("../shared/utils");
|
|
4
|
+
const AccountSuspensionBanner_1 = require("../Escalations/AccountSuspensionBanner");
|
|
4
5
|
const isDespiseExpert = (earningsToShow, lastPitchDate, lastLoginDate, applicationStatus) => {
|
|
5
6
|
// Expert is in Despise Status IF:
|
|
6
7
|
// Active Expert who has no logins, no pitches, no earnings in the last 30 days
|
|
@@ -11,18 +12,28 @@ const isDespiseExpert = (earningsToShow, lastPitchDate, lastLoginDate, applicati
|
|
|
11
12
|
const earnedInLast2Months = earningsToShow.filter((earning) => earning.earnings_type === 'actual' && (earning.month === formattedPreviousMonthDate || earning.month === formattedCurrentMonthDate));
|
|
12
13
|
return (0, utils_1.isOlderThan30Days)(lastPitchDate) && (0, utils_1.isOlderThan30Days)(lastLoginDate) && earnedInLast2Months.length === 0 && applicationStatus === 'ACTIVE';
|
|
13
14
|
};
|
|
14
|
-
const ExpertStatusCalculator = (statusData, setLeftSideStatus, setRightSideStatus, setInfoColor, expertMetricsData, earningsData, applicationStatus, setTriggerNewStatus) => {
|
|
15
|
+
const ExpertStatusCalculator = (statusData, setLeftSideStatus, setRightSideStatus, setInfoColor, expertMetricsData, earningsData, applicationStatus, setTriggerNewStatus, escalationDisputeStatistics) => {
|
|
16
|
+
var _a, _b;
|
|
15
17
|
const suspensionStatus = (statusData.suspensionEndDate && statusData.suspensionEndDate !== "")
|
|
16
18
|
? `! Suspended till ${statusData.suspensionEndDate}, see notes.`
|
|
17
19
|
: '! Suspended, see notes.';
|
|
20
|
+
const suspensionLevel = (_b = (_a = AccountSuspensionBanner_1.suspensionLevels.find((obj) => obj.value === escalationDisputeStatistics.suspensionLevel)) === null || _a === void 0 ? void 0 : _a.label) !== null && _b !== void 0 ? _b : "Low";
|
|
21
|
+
const startDate = escalationDisputeStatistics.suspensionStartDate ? new Date(escalationDisputeStatistics.suspensionStartDate).toLocaleDateString() : '';
|
|
22
|
+
const endDate = escalationDisputeStatistics.suspensionEndDate ? new Date(escalationDisputeStatistics.suspensionEndDate).toLocaleDateString() : '';
|
|
18
23
|
if (statusData.churnedPermanently) {
|
|
19
24
|
const formattedDate = new Date(statusData.churnedDate).toLocaleDateString();
|
|
20
25
|
setLeftSideStatus('This Expert has been churned from the Network and cannot take on new projects.');
|
|
21
26
|
setRightSideStatus(`! Churned ${formattedDate} - Not eligible for rehire. See notes.`);
|
|
22
27
|
}
|
|
23
28
|
else if (statusData.suspended) {
|
|
24
|
-
|
|
25
|
-
|
|
29
|
+
if (suspensionLevel !== "Low" && startDate && endDate) { // suspension due to escalations
|
|
30
|
+
setLeftSideStatus(`The Expert is on ${suspensionLevel} severity level for Escalations.`);
|
|
31
|
+
setRightSideStatus(`This Expert has been suspended due to severity strikes on escalations and was suspended on ${startDate} and will come off suspension on ${endDate} due.`);
|
|
32
|
+
}
|
|
33
|
+
else { // suspension due to other reasons
|
|
34
|
+
setLeftSideStatus('This Expert is currently suspended and is unable to take on new projects.');
|
|
35
|
+
setRightSideStatus(`${suspensionStatus}`);
|
|
36
|
+
}
|
|
26
37
|
}
|
|
27
38
|
else if (statusData.churned) {
|
|
28
39
|
const formattedDate = new Date(statusData.churnedDate).toLocaleDateString();
|
|
@@ -15,6 +15,8 @@ interface NetworkSectionProps {
|
|
|
15
15
|
expertId: string;
|
|
16
16
|
vanityTitle: string;
|
|
17
17
|
getExpertByLegacyFreelancerIdDocument: any;
|
|
18
|
+
escalationDisputeStatistics: any;
|
|
19
|
+
paroAppUrl: string;
|
|
18
20
|
}
|
|
19
|
-
declare const NetworkSection: ({ handleScrollToBottom, preferenceTasks, isWhiteLabel, detailsSectionCompleted, clientReferencesSectionCompleted, infoColor, leftSideStatus, rightSideStatus, isInternal, paroTenure, getExpertVanityTitles, updateExpert, expertId, legacyFreelancerId, vanityTitle, getExpertByLegacyFreelancerIdDocument, }: NetworkSectionProps) => JSX.Element;
|
|
21
|
+
declare const NetworkSection: ({ handleScrollToBottom, preferenceTasks, isWhiteLabel, detailsSectionCompleted, clientReferencesSectionCompleted, infoColor, leftSideStatus, rightSideStatus, isInternal, paroTenure, getExpertVanityTitles, updateExpert, expertId, legacyFreelancerId, vanityTitle, getExpertByLegacyFreelancerIdDocument, escalationDisputeStatistics, paroAppUrl, }: NetworkSectionProps) => JSX.Element;
|
|
20
22
|
export default NetworkSection;
|
|
@@ -39,6 +39,10 @@ const ProfileCompletedPercentange_1 = require("../ProfileCompletedPercentange");
|
|
|
39
39
|
const icons_1 = require("@material-ui/icons");
|
|
40
40
|
const ReviewsTab_1 = require("../ReviewsTab/ReviewsTab");
|
|
41
41
|
const base_ui_1 = require("@paro.io/base-ui");
|
|
42
|
+
const EarningsSection_1 = require("./EarningsSection");
|
|
43
|
+
const base_icons_1 = require("@paro.io/base-icons");
|
|
44
|
+
const EscalationIssueCard_1 = require("../Escalations/EscalationIssueCard");
|
|
45
|
+
const AccountSuspensionBanner_1 = require("../Escalations/AccountSuspensionBanner");
|
|
42
46
|
const useStyles = (0, styles_1.makeStyles)((theme) => ({
|
|
43
47
|
textSuccess: {
|
|
44
48
|
color: '#12756F',
|
|
@@ -75,11 +79,32 @@ const ExpertiseTitleModal = ({ openModal, updateTitle, titleOptions, selectedTit
|
|
|
75
79
|
react_1.default.createElement(base_ui_1.Button, { label: "UPDATE PROFILE", onClick: handleUpdateExpertTitle, type: "button", isLoading: updateTitle, disabled: selectedTitle === null || (selectedTitle === 'Other' && (customTitle === null || customTitle === '')), color: "primary" }),
|
|
76
80
|
react_1.default.createElement(base_ui_1.Button, { label: "CANCEL", onClick: handleOnClose, type: "button", disabled: updateTitle, isLoading: updateTitle })))));
|
|
77
81
|
};
|
|
78
|
-
const
|
|
82
|
+
const PriorityLevelModal = ({ openModal, onClose }) => {
|
|
83
|
+
return (react_1.default.createElement(base_ui_1.Modal, { open: openModal, size: "sm", onClose: onClose },
|
|
84
|
+
react_1.default.createElement("div", { className: "border border-1 border-gray-300 p-4 rounded-md gap-y-2" },
|
|
85
|
+
react_1.default.createElement("h4", { className: "font-medium text-gray-900 mb-2" }, "Severity Levels for Issues"),
|
|
86
|
+
react_1.default.createElement("div", { className: "space-y-2 text-sm" },
|
|
87
|
+
react_1.default.createElement("div", { className: "flex items-center gap-x-2" },
|
|
88
|
+
react_1.default.createElement(EscalationIssueCard_1.CustomTag, { label: "Critical" }),
|
|
89
|
+
react_1.default.createElement("span", null, "All deliverables are currently blocked; the project cannot move forward.")),
|
|
90
|
+
react_1.default.createElement("div", { className: "flex items-center gap-x-2" },
|
|
91
|
+
react_1.default.createElement(EscalationIssueCard_1.CustomTag, { label: "High" }),
|
|
92
|
+
react_1.default.createElement("span", null, "Key deliverables are blocked, preventing meaningful progress.")),
|
|
93
|
+
react_1.default.createElement("div", { className: "flex items-center gap-x-2" },
|
|
94
|
+
react_1.default.createElement(EscalationIssueCard_1.CustomTag, { label: "Medium" }),
|
|
95
|
+
react_1.default.createElement("span", null, "Deliverables are still in progress, but timelines are at risk.")),
|
|
96
|
+
react_1.default.createElement("div", { className: "flex items-center gap-x-2" },
|
|
97
|
+
react_1.default.createElement(EscalationIssueCard_1.CustomTag, { label: "Low" }),
|
|
98
|
+
react_1.default.createElement("span", null, "Minor issue or inconvenience that does not affect overall project flow."))))));
|
|
99
|
+
};
|
|
100
|
+
const NetworkSection = ({ handleScrollToBottom, preferenceTasks, isWhiteLabel, detailsSectionCompleted, clientReferencesSectionCompleted, infoColor, leftSideStatus, rightSideStatus, isInternal, paroTenure, getExpertVanityTitles, updateExpert, expertId, legacyFreelancerId, vanityTitle, getExpertByLegacyFreelancerIdDocument, escalationDisputeStatistics, paroAppUrl, }) => {
|
|
101
|
+
var _a, _b, _c, _d, _e, _f;
|
|
79
102
|
const [openModal, setOpenModal] = (0, react_1.useState)(false);
|
|
80
103
|
const [selectedTitle, setSelectedTitle] = (0, react_1.useState)(vanityTitle);
|
|
81
104
|
const [customTitle, setCustomTitle] = (0, react_1.useState)(null);
|
|
82
105
|
const [updateTitle, setUpdateTitle] = (0, react_1.useState)(false);
|
|
106
|
+
const [showPriorityLevelModal, setShowPriorityLevelModal] = (0, react_1.useState)(false);
|
|
107
|
+
const suspensionLevel = (_b = (_a = AccountSuspensionBanner_1.suspensionLevels.find((obj) => obj.value === escalationDisputeStatistics.suspensionLevel)) === null || _a === void 0 ? void 0 : _a.label) !== null && _b !== void 0 ? _b : "Low";
|
|
83
108
|
const handleUpdateExpertTitle = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
84
109
|
setUpdateTitle(true);
|
|
85
110
|
updateExpert({
|
|
@@ -129,6 +154,21 @@ const NetworkSection = ({ handleScrollToBottom, preferenceTasks, isWhiteLabel, d
|
|
|
129
154
|
react_1.default.createElement(icons_1.Cached, null))),
|
|
130
155
|
react_1.default.createElement(core_1.Box, { mt: 1, mb: 1, mr: 2 },
|
|
131
156
|
react_1.default.createElement(core_1.Divider, null))),
|
|
157
|
+
escalationDisputeStatistics &&
|
|
158
|
+
react_1.default.createElement(react_1.default.Fragment, null,
|
|
159
|
+
react_1.default.createElement(EarningsSection_1.EarningsInfo, { label: "# of Escalations in last 3 months", value: (_c = escalationDisputeStatistics.recentResolvedEscalations) !== null && _c !== void 0 ? _c : 0 }),
|
|
160
|
+
react_1.default.createElement(core_1.Box, { mb: 2 },
|
|
161
|
+
react_1.default.createElement(core_1.Divider, null)),
|
|
162
|
+
react_1.default.createElement(EarningsSection_1.EarningsInfo, { label: "Total # of Escalations", value: (_d = escalationDisputeStatistics.totalResolvedEscalations) !== null && _d !== void 0 ? _d : 0 }),
|
|
163
|
+
react_1.default.createElement(core_1.Box, { mb: 2 },
|
|
164
|
+
react_1.default.createElement(core_1.Divider, null)),
|
|
165
|
+
react_1.default.createElement(EarningsSection_1.EarningsInfo, { label: "# of Disputes in last 3 months", value: (_e = escalationDisputeStatistics.recentResolvedDisputes) !== null && _e !== void 0 ? _e : 0 }),
|
|
166
|
+
react_1.default.createElement(core_1.Box, { mb: 2 },
|
|
167
|
+
react_1.default.createElement(core_1.Divider, null)),
|
|
168
|
+
react_1.default.createElement(EarningsSection_1.EarningsInfo, { label: "Total # of Disputes", value: (_f = escalationDisputeStatistics.totalResolvedDisputes) !== null && _f !== void 0 ? _f : 0 }),
|
|
169
|
+
react_1.default.createElement(core_1.Box, { mb: 0.5 },
|
|
170
|
+
react_1.default.createElement(core_1.Divider, null)),
|
|
171
|
+
!isInternal && react_1.default.createElement("a", { href: `${paroAppUrl}/engagement-support`, className: "text-xs underline text-[#1878BD] cursor-pointer block text-right mb-2", target: "_blank", rel: "noopener noreferrer" }, "See more Disputes & Escalations")),
|
|
132
172
|
react_1.default.createElement(core_1.Grid, { style: { marginRight: '8px', marginLeft: '2px' } },
|
|
133
173
|
react_1.default.createElement(core_1.Box, { mb: 1 },
|
|
134
174
|
react_1.default.createElement("b", null, "Network Availability :")),
|
|
@@ -148,9 +188,15 @@ const NetworkSection = ({ handleScrollToBottom, preferenceTasks, isWhiteLabel, d
|
|
|
148
188
|
react_1.default.createElement(core_1.Box, null,
|
|
149
189
|
react_1.default.createElement("b", null, "Reason: "),
|
|
150
190
|
react_1.default.createElement("ul", null,
|
|
151
|
-
react_1.default.createElement("li", null,
|
|
191
|
+
react_1.default.createElement("li", null,
|
|
192
|
+
leftSideStatus,
|
|
193
|
+
" ",
|
|
194
|
+
suspensionLevel !== "Low" && react_1.default.createElement(core_1.IconButton, { onClick: () => { setShowPriorityLevelModal(true); } },
|
|
195
|
+
react_1.default.createElement(base_icons_1.IconInfoCircle, { size: "sm", className: "block text-right" }))),
|
|
152
196
|
react_1.default.createElement("li", null, rightSideStatus)))),
|
|
153
197
|
openModal &&
|
|
154
|
-
react_1.default.createElement(ExpertiseTitleModal, { openModal: openModal, updateTitle: updateTitle, titleOptions: [...getExpertVanityTitles], selectedTitle: selectedTitle, customTitle: customTitle, setCustomTitle: setCustomTitle, handleChangeTitle: (event) => { setSelectedTitle(event.target.value); }, handleOnClose: () => setOpenModal(false), handleUpdateExpertTitle: handleUpdateExpertTitle })
|
|
198
|
+
react_1.default.createElement(ExpertiseTitleModal, { openModal: openModal, updateTitle: updateTitle, titleOptions: [...getExpertVanityTitles], selectedTitle: selectedTitle, customTitle: customTitle, setCustomTitle: setCustomTitle, handleChangeTitle: (event) => { setSelectedTitle(event.target.value); }, handleOnClose: () => setOpenModal(false), handleUpdateExpertTitle: handleUpdateExpertTitle }),
|
|
199
|
+
showPriorityLevelModal &&
|
|
200
|
+
react_1.default.createElement(PriorityLevelModal, { openModal: showPriorityLevelModal, onClose: () => setShowPriorityLevelModal(false) })));
|
|
155
201
|
};
|
|
156
202
|
exports.default = NetworkSection;
|