@paro.io/expert-shared-components 1.14.41 → 1.14.43

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.
Files changed (83) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +2 -2
  3. package/lib/components/ClientReferencesSection/DeleteButton.js +11 -11
  4. package/lib/components/ClientReferencesSection/ParoError.js +10 -10
  5. package/lib/components/ClientReferencesSection/TagsSection.js +2 -2
  6. package/lib/components/ClientReferencesSection/styles/BrandedTypography.js +2 -2
  7. package/lib/components/ClientReferencesSection/styles/Buttons.js +15 -15
  8. package/lib/components/ClientReferencesSection/styles/Name.js +5 -5
  9. package/lib/components/ClientReferencesSection/styles/NullContentConditionalColor.js +4 -4
  10. package/lib/components/ClientReferencesSection/styles/SectionBody.js +11 -11
  11. package/lib/components/ClientReferencesSection/styles/SectionTitle.js +6 -6
  12. package/lib/components/ClientReferencesSection/styles/Tags.js +2 -2
  13. package/lib/components/DiscussionThread/DiscussionThread.d.ts +25 -25
  14. package/lib/components/DiscussionThread/DiscussionThread.js +137 -137
  15. package/lib/components/DiscussionThread/chat.d.ts +22 -22
  16. package/lib/components/DiscussionThread/chat.js +106 -106
  17. package/lib/components/DiscussionThread/index.d.ts +1 -1
  18. package/lib/components/DiscussionThread/index.js +5 -5
  19. package/lib/components/DocumentCenter/ClientDocumentsTable.d.ts +3 -1
  20. package/lib/components/DocumentCenter/ClientDocumentsTable.js +6 -16
  21. package/lib/components/DocumentCenter/DocumentCenter.js +8 -1
  22. package/lib/components/DocumentCenter/DocumentTable.d.ts +15 -15
  23. package/lib/components/DocumentCenter/DocumentTable.js +350 -350
  24. package/lib/components/DocumentCenter/DragDropUpload.d.ts +10 -0
  25. package/lib/components/DocumentCenter/DragDropUpload.js +124 -0
  26. package/lib/components/DocumentCenter/MultiFileUploadSection.d.ts +17 -0
  27. package/lib/components/DocumentCenter/MultiFileUploadSection.js +639 -0
  28. package/lib/components/DocumentCenter/ParoDocumentsTable.js +2 -2
  29. package/lib/components/DocumentCenter/UploadFilesButton.d.ts +6 -6
  30. package/lib/components/DocumentCenter/UploadFilesButton.js +29 -29
  31. package/lib/components/DocumentCenter/index.d.ts +3 -1
  32. package/lib/components/DocumentCenter/index.js +5 -1
  33. package/lib/components/EarningsTracker/ActiveProjectCard.d.ts +52 -52
  34. package/lib/components/EarningsTracker/ActiveProjectCard.js +161 -161
  35. package/lib/components/EarningsTracker/CenterCardUI.d.ts +13 -13
  36. package/lib/components/EarningsTracker/CenterCardUI.js +134 -134
  37. package/lib/components/EarningsTracker/EarningsTracker.d.ts +52 -52
  38. package/lib/components/EarningsTracker/EarningsTracker.js +508 -508
  39. package/lib/components/EarningsTracker/EditDateModal.d.ts +22 -22
  40. package/lib/components/EarningsTracker/EditDateModal.js +149 -149
  41. package/lib/components/EarningsTracker/EmailModal.d.ts +14 -14
  42. package/lib/components/EarningsTracker/EmailModal.js +79 -79
  43. package/lib/components/EarningsTracker/EndProjectModal.d.ts +56 -56
  44. package/lib/components/EarningsTracker/EndProjectModal.js +221 -221
  45. package/lib/components/EarningsTracker/LeftCardUI.d.ts +18 -18
  46. package/lib/components/EarningsTracker/LeftCardUI.js +189 -189
  47. package/lib/components/EarningsTracker/LogTimeModalAuthenticated.d.ts +52 -52
  48. package/lib/components/EarningsTracker/LogTimeModalAuthenticated.js +358 -358
  49. package/lib/components/EarningsTracker/ProgressBar.d.ts +4 -4
  50. package/lib/components/EarningsTracker/ProgressBar.js +66 -66
  51. package/lib/components/EarningsTracker/ReviewRequestModal.d.ts +17 -17
  52. package/lib/components/EarningsTracker/ReviewRequestModal.js +135 -135
  53. package/lib/components/EarningsTracker/RightCardUI.d.ts +46 -46
  54. package/lib/components/EarningsTracker/RightCardUI.js +231 -231
  55. package/lib/components/EarningsTracker/index.d.ts +1 -1
  56. package/lib/components/EarningsTracker/index.js +5 -5
  57. package/lib/components/Escalations/CustomTag.d.ts +3 -3
  58. package/lib/components/Escalations/CustomTag.js +25 -25
  59. package/lib/components/Escalations/ViewReponseModal.d.ts +8 -8
  60. package/lib/components/Escalations/ViewReponseModal.js +27 -27
  61. package/lib/components/ExpertProfileHeader/ActionButtonSection.js +6 -6
  62. package/lib/components/ExpertProfileHeader/ProfileSection.js +7 -7
  63. package/lib/components/Invoices/TestDecisionSection.d.ts +1 -1
  64. package/lib/components/Invoices/TestDecisionSection.js +126 -126
  65. package/lib/components/OrganizationChart/OrganizationChart.d.ts +15 -15
  66. package/lib/components/OrganizationChart/OrganizationChart.js +312 -312
  67. package/lib/components/OrganizationChart/PersonCard.js +5 -5
  68. package/lib/components/OrganizationChart/utils.js +79 -79
  69. package/lib/components/ProjectCard/ProgressBar.js +4 -4
  70. package/lib/components/ProjectCard/ReviewRequestModal.js +5 -5
  71. package/lib/components/ProjectIntelligence/MissingInformation/index.js +1 -1
  72. package/lib/components/Reviews/Pagination.js +6 -6
  73. package/lib/components/ReviewsTab/RatingHeader.js +6 -6
  74. package/lib/components/ReviewsTab/expert-shared-components.code-workspace +20 -20
  75. package/lib/components/ReviewsTab/reviewRequestModal.js +5 -5
  76. package/lib/components/shared/Image.js +13 -13
  77. package/lib/components/shared/ProfileTextField.d.ts +18 -18
  78. package/lib/components/shared/ProfileTextField.js +16 -16
  79. package/lib/components/shared/StyledActionButtons.d.ts +7 -7
  80. package/lib/components/shared/StyledActionButtons.js +15 -15
  81. package/lib/components/shared/ToastNotification.d.ts +10 -10
  82. package/lib/components/shared/ToastNotification.js +63 -63
  83. package/package.json +67 -67
@@ -0,0 +1,639 @@
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
+ var __importDefault = (this && this.__importDefault) || function (mod) {
35
+ return (mod && mod.__esModule) ? mod : { "default": mod };
36
+ };
37
+ Object.defineProperty(exports, "__esModule", { value: true });
38
+ exports.MultiFileUploadSection = void 0;
39
+ const react_1 = __importStar(require("react"));
40
+ const core_1 = require("@material-ui/core");
41
+ const ExpandMore_1 = __importDefault(require("@material-ui/icons/ExpandMore"));
42
+ const ExpandLess_1 = __importDefault(require("@material-ui/icons/ExpandLess"));
43
+ const date_fns_1 = require("date-fns");
44
+ const base_ui_1 = require("@paro.io/base-ui");
45
+ const base_icons_1 = require("@paro.io/base-icons");
46
+ const DragDropUpload_1 = require("./DragDropUpload");
47
+ const utils_1 = require("../shared/utils");
48
+ const UploadClient_1 = __importDefault(require("../shared/UploadClient"));
49
+ const ClientDocumentTypes = [
50
+ "Financial Documents",
51
+ "Contractual and Legal Documents",
52
+ "Operational and Management Documents",
53
+ "Tax and Compliance Documents",
54
+ "Project Specific Documents",
55
+ ];
56
+ const ParoDocumentTypes = [
57
+ "EO Insurance",
58
+ "Resume",
59
+ "General Insurance",
60
+ "Certification",
61
+ ];
62
+ const CHUNK_SIZE = 5 * 1024 * 1024;
63
+ const generateOptions = (array, clientOptions, isClientPortal) => {
64
+ return array.map((item, index) => {
65
+ var _a, _b;
66
+ const name = isClientPortal ? (_a = item === null || item === void 0 ? void 0 : item.freelancer) === null || _a === void 0 ? void 0 : _a.name : (_b = item === null || item === void 0 ? void 0 : item.client) === null || _b === void 0 ? void 0 : _b.name;
67
+ const projectName = item === null || item === void 0 ? void 0 : item.name;
68
+ let newText = "";
69
+ const itemKey = (item === null || item === void 0 ? void 0 : item.id)
70
+ ? `${item === null || item === void 0 ? void 0 : item.id}_${index}`
71
+ : item === null || item === void 0 ? void 0 : item.split(" ").join("_").concat(index);
72
+ if (clientOptions) {
73
+ if (name && projectName) {
74
+ newText = `${name} - ${projectName}`;
75
+ }
76
+ else if (name) {
77
+ newText = name;
78
+ }
79
+ else if (projectName) {
80
+ newText = projectName;
81
+ }
82
+ else {
83
+ newText = "N/A";
84
+ }
85
+ return Object.assign({ id: itemKey, value: newText, label: newText }, item);
86
+ }
87
+ else {
88
+ return {
89
+ id: itemKey,
90
+ value: item,
91
+ label: item,
92
+ };
93
+ }
94
+ });
95
+ };
96
+ const MultiFileUploadSection = ({ paroDocuments, clientAndProjectsList, uploadExpertClientFiles, uploadInternalFile, foldername, refetchFiles, legacyFreelancerId, freelancerName, freelancerEmail, isClientPortal = false, documentUploadUrl, showAsPage, }) => {
97
+ var _a;
98
+ const [uploadItems, setUploadItems] = (0, react_1.useState)([]);
99
+ const [isUploading, setIsUploading] = (0, react_1.useState)(false);
100
+ const [showUploadSection, setShowUploadSection] = (0, react_1.useState)(true);
101
+ const [selectedExpert, setSelectedExpert] = (0, react_1.useState)(null);
102
+ const documentTypeOptions = paroDocuments
103
+ ? generateOptions(ParoDocumentTypes, false, isClientPortal)
104
+ : generateOptions(ClientDocumentTypes, false, isClientPortal);
105
+ const clientOptions = clientAndProjectsList
106
+ ? generateOptions(clientAndProjectsList, true, isClientPortal)
107
+ : [];
108
+ const defaultDocType = documentTypeOptions[0].value;
109
+ // Group projects by expert for client portal OR by client for expert portal
110
+ const expertGroups = react_1.default.useMemo(() => {
111
+ if (!clientAndProjectsList)
112
+ return [];
113
+ const groups = new Map();
114
+ clientAndProjectsList.forEach((project) => {
115
+ var _a, _b, _c, _d;
116
+ const id = isClientPortal ? (_a = project === null || project === void 0 ? void 0 : project.freelancer) === null || _a === void 0 ? void 0 : _a.id : (_b = project === null || project === void 0 ? void 0 : project.client) === null || _b === void 0 ? void 0 : _b.id;
117
+ const name = isClientPortal
118
+ ? (_c = project === null || project === void 0 ? void 0 : project.freelancer) === null || _c === void 0 ? void 0 : _c.name
119
+ : (_d = project === null || project === void 0 ? void 0 : project.client) === null || _d === void 0 ? void 0 : _d.name;
120
+ const projectStatus = (project === null || project === void 0 ? void 0 : project.status) || (project === null || project === void 0 ? void 0 : project.projectStatus);
121
+ // Exclude completed, DOA, and churned projects
122
+ const excludedStatuses = ["complete", "completed", "doa", "churned"];
123
+ const isValidProject = projectStatus &&
124
+ !excludedStatuses.includes(projectStatus.toLowerCase());
125
+ if (id && name) {
126
+ if (!groups.has(id)) {
127
+ groups.set(id, {
128
+ expert: { id, name },
129
+ projects: [],
130
+ });
131
+ }
132
+ // Only add valid projects (not completed, DOA, or churned)
133
+ if (isValidProject) {
134
+ groups.get(id).projects.push(project);
135
+ }
136
+ }
137
+ });
138
+ // Filter out experts/clients with zero valid projects
139
+ return Array.from(groups.values()).filter((group) => group.projects.length > 0);
140
+ }, [isClientPortal, clientAndProjectsList]);
141
+ const handleExpertSelection = (expert) => {
142
+ if ((selectedExpert === null || selectedExpert === void 0 ? void 0 : selectedExpert.id) === expert.id) {
143
+ // Deselect if clicking the same expert
144
+ setSelectedExpert(null);
145
+ // Clear all project assignments
146
+ setUploadItems((prev) => prev.map((item) => (Object.assign(Object.assign({}, item), { clientAndProject: undefined }))));
147
+ }
148
+ else {
149
+ // Select new expert
150
+ setSelectedExpert(expert);
151
+ // Get expert's/client's valid projects (not completed, DOA, or churned)
152
+ const expertProjects = clientOptions.filter((opt) => {
153
+ var _a, _b;
154
+ const isMatch = isClientPortal
155
+ ? ((_a = opt.freelancer) === null || _a === void 0 ? void 0 : _a.id) === expert.id
156
+ : ((_b = opt.client) === null || _b === void 0 ? void 0 : _b.id) === expert.id;
157
+ const projectStatus = opt.status || opt.projectStatus;
158
+ const excludedStatuses = ["complete", "completed", "doa", "churned"];
159
+ const isValidProject = projectStatus &&
160
+ !excludedStatuses.includes(projectStatus.toLowerCase());
161
+ return isMatch && isValidProject;
162
+ });
163
+ // If expert has only one active project, auto-assign it to all files
164
+ if (expertProjects.length === 1) {
165
+ setUploadItems((prev) => prev.map((item) => (Object.assign(Object.assign({}, item), { clientAndProject: expertProjects[0] }))));
166
+ }
167
+ else {
168
+ // Clear all project assignments when switching to multi-project expert
169
+ setUploadItems((prev) => prev.map((item) => (Object.assign(Object.assign({}, item), { clientAndProject: undefined }))));
170
+ }
171
+ }
172
+ };
173
+ // Get selected expert's/client's projects and count
174
+ const selectedExpertProjects = react_1.default.useMemo(() => {
175
+ if (!selectedExpert)
176
+ return [];
177
+ return clientOptions.filter((opt) => {
178
+ var _a, _b;
179
+ const isMatch = isClientPortal
180
+ ? ((_a = opt.freelancer) === null || _a === void 0 ? void 0 : _a.id) === selectedExpert.id
181
+ : ((_b = opt.client) === null || _b === void 0 ? void 0 : _b.id) === selectedExpert.id;
182
+ const projectStatus = opt.status || opt.projectStatus;
183
+ const excludedStatuses = ["complete", "completed", "doa", "churned"];
184
+ const isValidProject = projectStatus &&
185
+ !excludedStatuses.includes(projectStatus.toLowerCase());
186
+ return isMatch && isValidProject;
187
+ });
188
+ }, [selectedExpert, clientOptions, isClientPortal]);
189
+ const hasMultipleProjects = selectedExpertProjects.length > 1;
190
+ const hasSingleProject = selectedExpertProjects.length === 1;
191
+ // Filtered project options for the selected expert/client
192
+ const filteredProjectOptions = react_1.default.useMemo(() => {
193
+ if (!selectedExpert)
194
+ return [];
195
+ return clientOptions.filter((opt) => {
196
+ var _a, _b;
197
+ const isMatch = isClientPortal
198
+ ? ((_a = opt.freelancer) === null || _a === void 0 ? void 0 : _a.id) === selectedExpert.id
199
+ : ((_b = opt.client) === null || _b === void 0 ? void 0 : _b.id) === selectedExpert.id;
200
+ const projectStatus = opt.status || opt.projectStatus;
201
+ const excludedStatuses = ["complete", "completed", "doa", "churned"];
202
+ const isValidProject = projectStatus &&
203
+ !excludedStatuses.includes(projectStatus.toLowerCase());
204
+ return isMatch && isValidProject;
205
+ });
206
+ }, [selectedExpert, clientOptions, isClientPortal]);
207
+ // Check if all files have projects assigned (for multi-project experts/clients)
208
+ const allFilesHaveProjects = react_1.default.useMemo(() => {
209
+ if (!hasMultipleProjects)
210
+ return true;
211
+ return uploadItems.every((item) => { var _a; return (_a = item.clientAndProject) === null || _a === void 0 ? void 0 : _a.value; });
212
+ }, [uploadItems, hasMultipleProjects]);
213
+ const handleFilesSelected = (files) => {
214
+ const newItems = files.map((file) => ({
215
+ id: `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
216
+ file,
217
+ documentName: file.name.substring(0, file.name.lastIndexOf(".")),
218
+ docType: defaultDocType,
219
+ clientAndProject: undefined, // Don't auto-assign, let user select
220
+ expiryDate: null,
221
+ status: "pending",
222
+ progress: 0,
223
+ }));
224
+ setUploadItems((prev) => [...prev, ...newItems]);
225
+ (0, utils_1.showToast)("success", `${files.length} file(s) added to upload queue`);
226
+ };
227
+ const removeItem = (id) => {
228
+ setUploadItems((prev) => prev.filter((item) => item.id !== id));
229
+ };
230
+ const updateItem = (id, updates) => {
231
+ setUploadItems((prev) => prev.map((item) => (item.id === id ? Object.assign(Object.assign({}, item), updates) : item)));
232
+ };
233
+ const validateItem = (item) => {
234
+ var _a;
235
+ if (!item.documentName.trim()) {
236
+ (0, utils_1.showToast)("warning", `Document name is required for ${item.file.name}`);
237
+ return false;
238
+ }
239
+ if (!paroDocuments && !((_a = item.clientAndProject) === null || _a === void 0 ? void 0 : _a.value)) {
240
+ (0, utils_1.showToast)("warning", `Client & Project is required for ${item.file.name}`);
241
+ return false;
242
+ }
243
+ if (paroDocuments && item.docType === "EO Insurance") {
244
+ if (!item.expiryDate) {
245
+ (0, utils_1.showToast)("warning", `Expiry date is required for EO Insurance documents`);
246
+ return false;
247
+ }
248
+ if (item.dateError) {
249
+ (0, utils_1.showToast)("warning", `Please fix the date error: ${item.dateError}`);
250
+ return false;
251
+ }
252
+ }
253
+ return true;
254
+ };
255
+ const uploadSingleFile = (item) => __awaiter(void 0, void 0, void 0, function* () {
256
+ try {
257
+ const fileExtension = item.file.name.substring(item.file.name.lastIndexOf(".") + 1);
258
+ const reader = new FileReader();
259
+ return new Promise((resolve) => {
260
+ reader.onloadend = () => __awaiter(void 0, void 0, void 0, function* () {
261
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
262
+ const fileForUpload = String(reader.result).split(";base64,")[1];
263
+ const isLargeFile = item.file.size >= CHUNK_SIZE;
264
+ const fileNameWithExtension = `${item.documentName}.${fileExtension}`;
265
+ updateItem(item.id, { status: "uploading", progress: 10 });
266
+ try {
267
+ if (isLargeFile) {
268
+ const uploadClient = new UploadClient_1.default({
269
+ fileSelected: item.file,
270
+ fileName: fileNameWithExtension,
271
+ projectId: (_a = item.clientAndProject) === null || _a === void 0 ? void 0 : _a.id,
272
+ documentUploadUrl,
273
+ });
274
+ yield uploadClient.triggerMultipartUpload();
275
+ }
276
+ updateItem(item.id, { progress: 50 });
277
+ if (paroDocuments) {
278
+ const result = yield uploadInternalFile({
279
+ variables: {
280
+ input: {
281
+ foldername,
282
+ subfolder: item.docType,
283
+ filename: `${item.documentName}.${fileExtension}`,
284
+ data: fileForUpload,
285
+ expirationDate: item.docType === "EO Insurance" && item.expiryDate
286
+ ? item.expiryDate.toISOString()
287
+ : "",
288
+ valid: true,
289
+ sourceOfEOInsurance: "Dashboard",
290
+ },
291
+ },
292
+ });
293
+ if ((_b = result === null || result === void 0 ? void 0 : result.data) === null || _b === void 0 ? void 0 : _b.uploadFileData) {
294
+ updateItem(item.id, { status: "success", progress: 100 });
295
+ resolve(true);
296
+ }
297
+ else {
298
+ throw new Error("Upload failed");
299
+ }
300
+ }
301
+ else {
302
+ const result = yield uploadExpertClientFiles({
303
+ variables: {
304
+ input: {
305
+ clientId: (_d = (_c = item.clientAndProject) === null || _c === void 0 ? void 0 : _c.client) === null || _d === void 0 ? void 0 : _d.id,
306
+ clientName: (_f = (_e = item.clientAndProject) === null || _e === void 0 ? void 0 : _e.client) === null || _f === void 0 ? void 0 : _f.name,
307
+ email: isClientPortal
308
+ ? (_h = (_g = item.clientAndProject) === null || _g === void 0 ? void 0 : _g.freelancer) === null || _h === void 0 ? void 0 : _h.name
309
+ : freelancerEmail,
310
+ fileName: `${item.documentName}.${fileExtension}`,
311
+ fileSize: item.file.size,
312
+ fileType: utils_1.DOCUMENT_TYPE_CONSTANTS[item.docType],
313
+ freelancerId: isClientPortal
314
+ ? (_k = (_j = item.clientAndProject) === null || _j === void 0 ? void 0 : _j.freelancer) === null || _k === void 0 ? void 0 : _k.id
315
+ : legacyFreelancerId,
316
+ freelancerName: isClientPortal
317
+ ? (_m = (_l = item.clientAndProject) === null || _l === void 0 ? void 0 : _l.freelancer) === null || _m === void 0 ? void 0 : _m.name
318
+ : freelancerName,
319
+ projectId: (_o = item.clientAndProject) === null || _o === void 0 ? void 0 : _o.id,
320
+ projectName: (_p = item.clientAndProject) === null || _p === void 0 ? void 0 : _p.name,
321
+ uploadedBy: isClientPortal ? "CLIENT" : "FREELANCER",
322
+ data: isLargeFile ? "" : fileForUpload,
323
+ },
324
+ },
325
+ });
326
+ if ((_q = result === null || result === void 0 ? void 0 : result.data) === null || _q === void 0 ? void 0 : _q.uploadExpertClientFilesData) {
327
+ updateItem(item.id, { status: "success", progress: 100 });
328
+ resolve(true);
329
+ }
330
+ else {
331
+ throw new Error("Upload failed");
332
+ }
333
+ }
334
+ }
335
+ catch (error) {
336
+ updateItem(item.id, {
337
+ status: "error",
338
+ progress: 0,
339
+ error: "Upload failed",
340
+ });
341
+ resolve(false);
342
+ }
343
+ });
344
+ reader.readAsDataURL(item.file);
345
+ });
346
+ }
347
+ catch (error) {
348
+ updateItem(item.id, { status: "error", error: "Failed to process file" });
349
+ return false;
350
+ }
351
+ });
352
+ const handleUploadAll = () => __awaiter(void 0, void 0, void 0, function* () {
353
+ const validItems = uploadItems.filter((item) => {
354
+ if (item.status === "success")
355
+ return false;
356
+ return validateItem(item);
357
+ });
358
+ if (validItems.length === 0) {
359
+ (0, utils_1.showToast)("warning", "No valid files to upload");
360
+ return;
361
+ }
362
+ setIsUploading(true);
363
+ let successCount = 0;
364
+ let failCount = 0;
365
+ for (const item of validItems) {
366
+ const success = yield uploadSingleFile(item);
367
+ if (success) {
368
+ successCount++;
369
+ }
370
+ else {
371
+ failCount++;
372
+ }
373
+ }
374
+ setIsUploading(false);
375
+ if (successCount > 0) {
376
+ (0, utils_1.showToast)("success", `${successCount} file(s) uploaded successfully!`);
377
+ if (refetchFiles) {
378
+ yield refetchFiles();
379
+ }
380
+ }
381
+ if (failCount > 0) {
382
+ (0, utils_1.showToast)("warning", `${failCount} file(s) failed to upload`);
383
+ }
384
+ if (failCount === 0) {
385
+ setUploadItems([]);
386
+ }
387
+ });
388
+ const formatFileSize = (bytes) => {
389
+ if (bytes === 0)
390
+ return "0 Bytes";
391
+ const k = 1024;
392
+ const sizes = ["Bytes", "KB", "MB", "GB"];
393
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
394
+ return Math.round((bytes / Math.pow(k, i)) * 100) / 100 + " " + sizes[i];
395
+ };
396
+ return (react_1.default.createElement(core_1.Box, { mb: 3 },
397
+ react_1.default.createElement(core_1.Paper, { elevation: 2, style: { padding: "20px", borderRadius: "12px" } },
398
+ react_1.default.createElement(core_1.Box, { display: "flex", justifyContent: "space-between", alignItems: "center", mb: 2 },
399
+ react_1.default.createElement(core_1.Box, { display: "flex", alignItems: "center" },
400
+ react_1.default.createElement(core_1.Typography, { variant: "h6", className: "font-semibold" }, "Upload Documents"),
401
+ uploadItems.length > 0 && (react_1.default.createElement(core_1.Chip, { label: `${uploadItems.length} ${uploadItems.length === 1 ? "file" : "files"}`, color: "primary", size: "small", style: { marginLeft: "12px" } }))),
402
+ react_1.default.createElement(core_1.Tooltip, { title: showUploadSection ? "Collapse" : "Expand" },
403
+ react_1.default.createElement(core_1.IconButton, { onClick: () => setShowUploadSection(!showUploadSection), size: "small" }, showUploadSection ? react_1.default.createElement(ExpandLess_1.default, null) : react_1.default.createElement(ExpandMore_1.default, null)))),
404
+ react_1.default.createElement(core_1.Collapse, { in: showUploadSection },
405
+ react_1.default.createElement(core_1.Box, { mb: 3 },
406
+ react_1.default.createElement(DragDropUpload_1.DragDropUpload, { onFilesSelected: handleFilesSelected, maxFileSize: 10, maxFiles: 10, acceptedFileTypes: [
407
+ ".pdf",
408
+ ".doc",
409
+ ".xls",
410
+ ".csv",
411
+ ".jpg",
412
+ ".png",
413
+ ], disabled: isUploading })),
414
+ uploadItems.length > 0 &&
415
+ !paroDocuments &&
416
+ expertGroups.length > 0 && (react_1.default.createElement(core_1.Box, { mb: 3, p: 2, style: { backgroundColor: "#F7FAFC", borderRadius: "8px" } },
417
+ react_1.default.createElement(core_1.Box, { display: "flex", alignItems: "center", mb: 2 },
418
+ react_1.default.createElement(core_1.Box, { style: {
419
+ width: "32px",
420
+ height: "32px",
421
+ borderRadius: "50%",
422
+ backgroundColor: "#48BB78",
423
+ color: "white",
424
+ display: "flex",
425
+ alignItems: "center",
426
+ justifyContent: "center",
427
+ fontWeight: "bold",
428
+ marginRight: "12px",
429
+ } }, "1"),
430
+ react_1.default.createElement(core_1.Typography, { variant: "subtitle1", style: { fontWeight: 600 } }, isClientPortal
431
+ ? "SEND TO EXPERT — select who these files are for"
432
+ : "SEND TO CLIENT — select which client these files are for")),
433
+ react_1.default.createElement(core_1.Box, { display: "flex", flexWrap: "wrap", style: { gap: "8px" } }, expertGroups.map((group) => (react_1.default.createElement(core_1.Chip, { key: group.expert.id, label: react_1.default.createElement(core_1.Box, null,
434
+ react_1.default.createElement("strong", null, group.expert.name),
435
+ react_1.default.createElement(core_1.Box, { component: "span", ml: 1, style: { color: "#718096" } },
436
+ group.projects.length,
437
+ " ",
438
+ group.projects.length === 1
439
+ ? "project"
440
+ : "projects")), onClick: () => handleExpertSelection(group.expert), clickable: true, disabled: group.projects.length === 0, color: "default", style: {
441
+ padding: "8px 12px",
442
+ height: "auto",
443
+ borderRadius: "20px",
444
+ border: (selectedExpert === null || selectedExpert === void 0 ? void 0 : selectedExpert.id) === group.expert.id
445
+ ? "2px solid #3182CE"
446
+ : "1px solid #CBD5E0",
447
+ backgroundColor: (selectedExpert === null || selectedExpert === void 0 ? void 0 : selectedExpert.id) === group.expert.id
448
+ ? "#EBF8FF"
449
+ : "white",
450
+ opacity: group.projects.length === 0 ? 0.5 : 1,
451
+ cursor: group.projects.length === 0
452
+ ? "not-allowed"
453
+ : "pointer",
454
+ } })))),
455
+ selectedExpert && hasSingleProject && (react_1.default.createElement(core_1.Box, { mt: 2 },
456
+ react_1.default.createElement(core_1.Chip, { label: "AUTO-ASSIGNED", size: "small", style: {
457
+ backgroundColor: "#C6F6D5",
458
+ color: "#22543D",
459
+ fontWeight: 600,
460
+ } }),
461
+ react_1.default.createElement(core_1.Typography, { variant: "caption", style: { marginLeft: "8px", color: "#718096" } },
462
+ ((_a = selectedExpertProjects[0]) === null || _a === void 0 ? void 0 : _a.name) || "Project",
463
+ " \u2014 only project with ",
464
+ selectedExpert.name))),
465
+ selectedExpert && hasMultipleProjects && (react_1.default.createElement(core_1.Box, { mt: 2 },
466
+ react_1.default.createElement(core_1.Chip, { label: `${selectedExpertProjects.length} PROJECTS`, size: "small", style: {
467
+ backgroundColor: "#FEF5E7",
468
+ color: "#D97706",
469
+ fontWeight: 600,
470
+ }, icon: react_1.default.createElement(base_icons_1.IconExclamation, { stroke: "#D97706" }) }),
471
+ react_1.default.createElement(core_1.Typography, { variant: "caption", style: { marginLeft: "8px", color: "#718096" } }, "Select which project each file is for below"))))),
472
+ uploadItems.length > 0 && (react_1.default.createElement(core_1.Box, null,
473
+ react_1.default.createElement(core_1.Box, null,
474
+ react_1.default.createElement(core_1.Grid, { container: true, spacing: 2 }, uploadItems.map((item, index) => {
475
+ var _a;
476
+ return (react_1.default.createElement(core_1.Grid, { item: true, xs: 12, key: item.id },
477
+ react_1.default.createElement(core_1.Paper, { elevation: 1, style: {
478
+ padding: "12px 16px",
479
+ backgroundColor: "white",
480
+ border: "1px solid #E2E8F0",
481
+ borderRadius: "4px",
482
+ } },
483
+ react_1.default.createElement(core_1.Grid, { container: true, spacing: 2 },
484
+ react_1.default.createElement(core_1.Grid, { item: true, xs: 12, sm: "auto", style: { display: "flex", alignItems: "center" } },
485
+ react_1.default.createElement(base_icons_1.IconDocument, { size: "lg" })),
486
+ react_1.default.createElement(core_1.Grid, { item: true, xs: 12, sm: true },
487
+ react_1.default.createElement(base_ui_1.Input, { type: "text", value: item.documentName, onChange: (e) => updateItem(item.id, {
488
+ documentName: e.target.value,
489
+ }), disabled: item.status === "uploading" ||
490
+ item.status === "success", placeholder: "Document name" }),
491
+ react_1.default.createElement(core_1.Typography, { variant: "caption", style: {
492
+ color: "#718096",
493
+ display: "block",
494
+ marginTop: "4px",
495
+ } },
496
+ item.file.name,
497
+ " \u2022",
498
+ " ",
499
+ formatFileSize(item.file.size))),
500
+ react_1.default.createElement(core_1.Grid, { item: true, xs: 12, sm: 3 },
501
+ react_1.default.createElement(base_ui_1.Select, { value: item.docType, onChange: (e) => {
502
+ updateItem(item.id, {
503
+ docType: e.target.value,
504
+ });
505
+ // Clear expiry date and error if switching away from EO Insurance
506
+ if (e.target.value !== "EO Insurance") {
507
+ updateItem(item.id, {
508
+ expiryDate: null,
509
+ dateError: null,
510
+ });
511
+ }
512
+ }, options: documentTypeOptions, disabled: item.status === "uploading" ||
513
+ item.status === "success", required: false })),
514
+ paroDocuments && item.docType === "EO Insurance" && (react_1.default.createElement(core_1.Grid, { item: true, xs: 12, sm: 3, style: { marginTop: "5px" } },
515
+ react_1.default.createElement(core_1.TextField, { type: "date", label: "Expiration Date", value: item.expiryDate
516
+ ? new Date(item.expiryDate)
517
+ .toISOString()
518
+ .split("T")[0]
519
+ : "", color: "primary", onChange: (e) => {
520
+ const dateValue = e.target.value
521
+ ? new Date(e.target.value)
522
+ : null;
523
+ if (dateValue) {
524
+ const currentDate = (0, date_fns_1.startOfDay)(new Date());
525
+ if ((0, date_fns_1.isBefore)(dateValue, currentDate)) {
526
+ updateItem(item.id, {
527
+ expiryDate: dateValue,
528
+ dateError: "The expiration date cannot be in the past.",
529
+ });
530
+ }
531
+ else {
532
+ updateItem(item.id, {
533
+ expiryDate: dateValue,
534
+ dateError: null,
535
+ });
536
+ }
537
+ }
538
+ else {
539
+ updateItem(item.id, {
540
+ expiryDate: null,
541
+ dateError: null,
542
+ });
543
+ }
544
+ }, InputLabelProps: {
545
+ shrink: true,
546
+ }, disabled: item.status === "uploading" ||
547
+ item.status === "success", fullWidth: true, variant: "outlined", size: "small", required: true, error: !!item.dateError, helperText: item.dateError }))),
548
+ !paroDocuments && !hasSingleProject && (react_1.default.createElement(core_1.Grid, { item: true, xs: 12, sm: 3 },
549
+ react_1.default.createElement(base_ui_1.Select, { value: ((_a = item.clientAndProject) === null || _a === void 0 ? void 0 : _a.value) || "", onChange: (e) => {
550
+ // Handle empty selection
551
+ if (e.target.value === "") {
552
+ updateItem(item.id, {
553
+ clientAndProject: undefined,
554
+ });
555
+ return;
556
+ }
557
+ const selected = filteredProjectOptions.find((opt) => opt.value === e.target.value);
558
+ updateItem(item.id, {
559
+ clientAndProject: selected,
560
+ });
561
+ }, options: [
562
+ {
563
+ id: "placeholder",
564
+ value: "",
565
+ label: "Which project?",
566
+ },
567
+ ...filteredProjectOptions,
568
+ ], disabled: item.status === "uploading" ||
569
+ item.status === "success" ||
570
+ !selectedExpert, required: false }))),
571
+ react_1.default.createElement(core_1.Grid, { item: true, xs: 12, sm: "auto", style: {
572
+ display: "flex",
573
+ alignItems: "center",
574
+ justifyContent: "flex-end",
575
+ } },
576
+ react_1.default.createElement(core_1.Tooltip, { title: "Remove file" },
577
+ react_1.default.createElement(core_1.IconButton, { onClick: () => removeItem(item.id), disabled: item.status === "uploading", size: "small" },
578
+ react_1.default.createElement(base_icons_1.IconTrash, { size: "md" }))))),
579
+ item.status === "uploading" && (react_1.default.createElement(core_1.Box, { mt: 1, ml: { xs: 0, sm: 6 } },
580
+ react_1.default.createElement(core_1.Typography, { variant: "caption", style: { color: "#3182CE" } },
581
+ "Uploading ",
582
+ item.progress,
583
+ "%..."))),
584
+ item.status === "success" && (react_1.default.createElement(core_1.Box, { mt: 1, ml: { xs: 0, sm: 6 } },
585
+ react_1.default.createElement(core_1.Typography, { variant: "caption", style: { color: "#38A169" } }, "\u2713 Upload successful"))),
586
+ item.status === "error" && (react_1.default.createElement(core_1.Box, { mt: 1, ml: { xs: 0, sm: 6 } },
587
+ react_1.default.createElement(core_1.Typography, { variant: "caption", style: { color: "#E53E3E" } }, "\u2717 Upload failed"))))));
588
+ }))),
589
+ react_1.default.createElement(core_1.Divider, { style: { marginTop: "24px" } }),
590
+ react_1.default.createElement(core_1.Box, { display: "flex", justifyContent: "space-between", alignItems: "center", mb: 2, mt: 2 },
591
+ react_1.default.createElement(core_1.Box, null,
592
+ react_1.default.createElement(core_1.Typography, { variant: "body2", style: { color: "#718096" } }, paroDocuments ? (react_1.default.createElement(react_1.default.Fragment, null,
593
+ react_1.default.createElement("span", { style: { fontWeight: "bold" } }, uploadItems.filter((i) => i.status !== "success")
594
+ .length),
595
+ " ",
596
+ uploadItems.filter((i) => i.status !== "success")
597
+ .length === 1
598
+ ? "file"
599
+ : "files")) : (react_1.default.createElement(react_1.default.Fragment, null,
600
+ react_1.default.createElement("span", { style: { fontWeight: "bold" } }, uploadItems.filter((i) => i.status !== "success")
601
+ .length),
602
+ " ",
603
+ "files \u2022",
604
+ " ",
605
+ react_1.default.createElement("span", { style: { color: "#718096" } },
606
+ react_1.default.createElement("span", { style: { fontWeight: "bold" } }, uploadItems.filter((i) => {
607
+ var _a;
608
+ return ((_a = i.clientAndProject) === null || _a === void 0 ? void 0 : _a.value) &&
609
+ i.status !== "success";
610
+ }).length),
611
+ " ",
612
+ "project-assigned"),
613
+ " ",
614
+ "\u2022",
615
+ " ",
616
+ react_1.default.createElement("span", { style: { color: "#D97706" } },
617
+ react_1.default.createElement("span", { style: { fontWeight: "bold" } }, uploadItems.filter((i) => {
618
+ var _a;
619
+ return !((_a = i.clientAndProject) === null || _a === void 0 ? void 0 : _a.value) &&
620
+ i.status !== "success";
621
+ }).length),
622
+ " ",
623
+ "need project"))))),
624
+ react_1.default.createElement(core_1.Box, null,
625
+ react_1.default.createElement(core_1.Box, { component: "span", ml: 1 },
626
+ react_1.default.createElement(base_ui_1.Button, { label: isUploading
627
+ ? "Uploading..."
628
+ : `Upload ${uploadItems.filter((i) => i.status !== "success").length} Files`, onClick: handleUploadAll, disabled: uploadItems.length === 0 ||
629
+ isUploading ||
630
+ uploadItems.every((i) => i.status === "success") ||
631
+ (!paroDocuments && !selectedExpert) ||
632
+ (hasMultipleProjects && !allFilesHaveProjects) ||
633
+ uploadItems.some((i) => paroDocuments &&
634
+ i.docType === "EO Insurance" &&
635
+ (!i.expiryDate || !!i.dateError) &&
636
+ i.status !== "success"), color: "primary", isLoading: isUploading }))))))))));
637
+ };
638
+ exports.MultiFileUploadSection = MultiFileUploadSection;
639
+ exports.default = exports.MultiFileUploadSection;