@paro.io/expert-shared-components 1.12.20 → 1.12.22

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 (70) 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/chat.d.ts +22 -22
  14. package/lib/components/DiscussionThread/chat.js +106 -106
  15. package/lib/components/DocumentCenter/DocumentTable.d.ts +15 -15
  16. package/lib/components/DocumentCenter/DocumentTable.js +350 -350
  17. package/lib/components/DocumentCenter/UploadFilesButton.d.ts +6 -6
  18. package/lib/components/DocumentCenter/UploadFilesButton.js +29 -29
  19. package/lib/components/EarningsTracker/ActiveProjectCard.d.ts +52 -52
  20. package/lib/components/EarningsTracker/ActiveProjectCard.js +161 -161
  21. package/lib/components/EarningsTracker/CenterCardUI.d.ts +13 -13
  22. package/lib/components/EarningsTracker/CenterCardUI.js +134 -134
  23. package/lib/components/EarningsTracker/EarningsTracker.d.ts +52 -52
  24. package/lib/components/EarningsTracker/EarningsTracker.js +508 -508
  25. package/lib/components/EarningsTracker/EditDateModal.d.ts +22 -22
  26. package/lib/components/EarningsTracker/EditDateModal.js +149 -149
  27. package/lib/components/EarningsTracker/EmailModal.d.ts +14 -14
  28. package/lib/components/EarningsTracker/EmailModal.js +79 -79
  29. package/lib/components/EarningsTracker/EndProjectModal.d.ts +56 -56
  30. package/lib/components/EarningsTracker/EndProjectModal.js +221 -221
  31. package/lib/components/EarningsTracker/LeftCardUI.d.ts +18 -18
  32. package/lib/components/EarningsTracker/LeftCardUI.js +189 -189
  33. package/lib/components/EarningsTracker/LogTimeModalAuthenticated.d.ts +52 -52
  34. package/lib/components/EarningsTracker/LogTimeModalAuthenticated.js +358 -358
  35. package/lib/components/EarningsTracker/ProgressBar.d.ts +4 -4
  36. package/lib/components/EarningsTracker/ProgressBar.js +66 -66
  37. package/lib/components/EarningsTracker/ReviewRequestModal.d.ts +17 -17
  38. package/lib/components/EarningsTracker/ReviewRequestModal.js +135 -135
  39. package/lib/components/EarningsTracker/RightCardUI.d.ts +46 -46
  40. package/lib/components/EarningsTracker/RightCardUI.js +231 -231
  41. package/lib/components/EarningsTracker/index.d.ts +1 -1
  42. package/lib/components/EarningsTracker/index.js +5 -5
  43. package/lib/components/ExpertProfileHeader/ActionButtonSection.js +6 -6
  44. package/lib/components/ExpertProfileHeader/ProfileSection.js +7 -7
  45. package/lib/components/Invoices/DecisionSection.js +6 -4
  46. package/lib/components/Invoices/InvoiceHeader.js +22 -19
  47. package/lib/components/Invoices/ProjectHoursAdjustmentModal.js +57 -47
  48. package/lib/components/Invoices/TestDecisionSection.d.ts +1 -1
  49. package/lib/components/Invoices/TestDecisionSection.js +126 -126
  50. package/lib/components/OrganizationChart/OrganizationChart.d.ts +15 -15
  51. package/lib/components/OrganizationChart/OrganizationChart.js +312 -312
  52. package/lib/components/OrganizationChart/PersonCard.js +5 -5
  53. package/lib/components/OrganizationChart/utils.js +79 -79
  54. package/lib/components/ProjectCard/ActiveProjectCard.js +7 -23
  55. package/lib/components/ProjectCard/ProgressBar.js +4 -4
  56. package/lib/components/ProjectCard/ReviewRequestModal.js +5 -5
  57. package/lib/components/ProjectCard/RightCardUI.d.ts +1 -2
  58. package/lib/components/ProjectCard/RightCardUI.js +2 -2
  59. package/lib/components/Reviews/Pagination.js +6 -6
  60. package/lib/components/ReviewsTab/RatingHeader.js +6 -6
  61. package/lib/components/ReviewsTab/expert-shared-components.code-workspace +20 -20
  62. package/lib/components/ReviewsTab/reviewRequestModal.js +5 -5
  63. package/lib/components/shared/Image.js +13 -13
  64. package/lib/components/shared/ProfileTextField.d.ts +18 -18
  65. package/lib/components/shared/ProfileTextField.js +16 -16
  66. package/lib/components/shared/StyledActionButtons.d.ts +7 -7
  67. package/lib/components/shared/StyledActionButtons.js +15 -15
  68. package/lib/components/shared/ToastNotification.d.ts +10 -10
  69. package/lib/components/shared/ToastNotification.js +63 -63
  70. package/package.json +61 -61
@@ -73,6 +73,9 @@ const DecisionSection = ({ dispute, onUpdateDispute, updateInvoiceMutation, upda
73
73
  };
74
74
  const getTotalApprovedHours = () => {
75
75
  var _a;
76
+ if (resolution === 'DECLINED') {
77
+ return 0;
78
+ }
76
79
  if (resolution === 'PARTIAL' && ((_a = dispute === null || dispute === void 0 ? void 0 : dispute.disputeProjects) === null || _a === void 0 ? void 0 : _a.length) > 0) {
77
80
  return Object.values(projectApprovedHours).reduce((sum, hours) => sum + hours, 0);
78
81
  }
@@ -106,7 +109,7 @@ const DecisionSection = ({ dispute, onUpdateDispute, updateInvoiceMutation, upda
106
109
  const disputeProjectUpdates = ((_a = dispute === null || dispute === void 0 ? void 0 : dispute.disputeProjects) === null || _a === void 0 ? void 0 : _a.map((project) => ({
107
110
  projectId: project.projectId,
108
111
  resolutionMode: 'Direct',
109
- resolutionType: resolutionType
112
+ resolutionType: resolution === 'DECLINED' ? 'Canceled' : resolutionType
110
113
  }))) || [];
111
114
  // Calculate approved amount based on dispute types
112
115
  const hasNonHourlyProjects = (_b = dispute === null || dispute === void 0 ? void 0 : dispute.disputeProjects) === null || _b === void 0 ? void 0 : _b.some((project) => project.disputeType !== 'Hourly');
@@ -181,9 +184,9 @@ const DecisionSection = ({ dispute, onUpdateDispute, updateInvoiceMutation, upda
181
184
  react_1.default.createElement("div", { className: "text-xs text-gray-500" }, project.disputeType === 'Hourly'
182
185
  ? `Disputed Hours: ${project.disputeHours}`
183
186
  : `Dispute Amount: $${((_a = project.disputeAmount) === null || _a === void 0 ? void 0 : _a.toFixed(2)) || '0.00'}`)),
184
- react_1.default.createElement("div", { className: "w-32" }, project.disputeType === 'Hourly' ? (react_1.default.createElement("input", { type: "number", step: "0.01", min: "0", max: project.disputeHours, value: projectApprovedHours[project.projectId] || 0, onChange: (e) => handleProjectHoursChange(project.projectId, parseFloat(e.target.value) || 0), className: "w-full px-2 py-1 border border-[#CCCCCC] rounded text-sm focus:outline-none focus:ring-2 focus:ring-[#248384]", placeholder: "Hours" })) : (react_1.default.createElement("div", { className: "relative" },
187
+ react_1.default.createElement("div", { className: "w-32" }, project.disputeType === 'Hourly' ? (react_1.default.createElement("input", { type: "number", step: "1", min: "0", max: project.disputeHours, value: projectApprovedHours[project.projectId] || '', onChange: (e) => handleProjectHoursChange(project.projectId, parseFloat(e.target.value) || 0), className: "w-full px-2 py-1 border border-[#CCCCCC] rounded text-sm focus:outline-none focus:ring-2 focus:ring-[#248384]", placeholder: "Hours" })) : (react_1.default.createElement("div", { className: "relative" },
185
188
  react_1.default.createElement("span", { className: "absolute left-2 top-1/2 transform -translate-y-1/2 text-gray-500" }, "$"),
186
- react_1.default.createElement("input", { type: "number", step: "0.01", min: "0", max: project.disputeAmount, value: projectApprovedHours[project.projectId] || 0, onChange: (e) => handleProjectHoursChange(project.projectId, parseFloat(e.target.value) || 0), className: "w-full pl-2 pr-2 py-1 border border-[#CCCCCC] rounded text-sm focus:outline-none focus:ring-2 focus:ring-[#248384]", placeholder: "0.00" }))))));
189
+ react_1.default.createElement("input", { type: "number", step: "1", min: "0", max: project.disputeAmount, value: projectApprovedHours[project.projectId] || '', onChange: (e) => handleProjectHoursChange(project.projectId, parseFloat(e.target.value) || 0), className: "w-full pl-2 pr-2 py-1 border border-[#CCCCCC] rounded text-sm focus:outline-none focus:ring-2 focus:ring-[#248384]", placeholder: "0.00" }))))));
187
190
  })),
188
191
  react_1.default.createElement("div", { className: "text-sm text-gray-600" }, dispute.disputeProjects.some((p) => p.disputeType === 'Hourly') &&
189
192
  dispute.disputeProjects.some((p) => p.disputeType !== 'Hourly') ? (
@@ -215,7 +218,6 @@ const DecisionSection = ({ dispute, onUpdateDispute, updateInvoiceMutation, upda
215
218
  // All non-hourly disputes
216
219
  `Total Approved Amount: $${getTotalApprovedHours().toFixed(2)} / $${dispute.disputeProjects.reduce((sum, p) => sum + p.disputeAmount, 0).toFixed(2)}`)))),
217
220
  react_1.default.createElement("div", { className: "flex justify-end space-x-4" },
218
- react_1.default.createElement(base_ui_1.Button, { label: "Decline Dispute", color: "error", onClick: () => handleResolutionChange('DECLINED'), disabled: isSubmitting, size: "md" }),
219
221
  react_1.default.createElement(base_ui_1.Button, { label: "Approve & Resolve", color: "primary", onClick: handleApproveClick, isLoading: isSubmitting, disabled: !resolution || (resolution === 'PARTIAL' && !isValidPartialApproval()), size: "md" }))),
220
222
  react_1.default.createElement(ProjectHoursAdjustmentModal_1.ProjectHoursAdjustmentModal, { isOpen: showHoursModal, onClose: () => setShowHoursModal(false), disputeProjects: (dispute === null || dispute === void 0 ? void 0 : dispute.disputeProjects) || [], dispute: dispute, onSubmit: handleHoursSubmit, isSubmitting: isSubmitting, getClientInvoiceSummaryByMonth: getClientInvoiceSummaryByMonth, invoiceSummary: invoiceSummary, approvedHours: resolution === 'PARTIAL' ? getTotalApprovedHours() : totalDisputeHours, projectApprovedHours: isPartialWithMultipleProjects ? projectApprovedHours : {}, user: user })));
221
223
  };
@@ -10,35 +10,38 @@ const InvoiceHeader = ({ invoice, isInternal = false, isClient = false }) => {
10
10
  var _a, _b, _c, _d, _e, _f;
11
11
  const disputeAmount = (_a = invoice === null || invoice === void 0 ? void 0 : invoice.disputeProjects) === null || _a === void 0 ? void 0 : _a.reduce((acc, project) => acc + (project === null || project === void 0 ? void 0 : project.disputeAmount), 0);
12
12
  return (react_1.default.createElement("div", { className: "bg-[#0F172A] w-full text-white px-6 py-4 rounded-t-lg flex items-center justify-between font-arial" },
13
- react_1.default.createElement("div", { className: "flex items-center space-x-8" },
13
+ react_1.default.createElement("div", { className: "flex items-center space-x-4 overflow-x-auto" },
14
+ react_1.default.createElement("div", { className: "flex-shrink-0" },
15
+ react_1.default.createElement("span", { className: "text-sm font-bold" }, "Dispute #:"),
16
+ react_1.default.createElement("span", { className: "ml-1" }, invoice === null || invoice === void 0 ? void 0 : invoice.disputeId)),
17
+ react_1.default.createElement("div", { className: "flex-shrink-0" },
18
+ react_1.default.createElement("span", { className: "text-sm font-bold" }, "Invoice #:"),
19
+ react_1.default.createElement("span", { className: "ml-1" }, (_b = invoice === null || invoice === void 0 ? void 0 : invoice.invoice) === null || _b === void 0 ? void 0 : _b.id)),
20
+ !isClient && (react_1.default.createElement("div", { className: "flex-shrink-0" },
21
+ react_1.default.createElement("span", { className: "text-sm font-bold" }, "Client:"),
22
+ react_1.default.createElement("span", { className: "ml-1" }, (_c = invoice === null || invoice === void 0 ? void 0 : invoice.client) === null || _c === void 0 ? void 0 : _c.name))),
14
23
  react_1.default.createElement("div", null,
15
- react_1.default.createElement("span", { className: "text-sm font-bold" }, "Dispute Invoice #: "),
16
- react_1.default.createElement("span", { className: "ml-2" }, (_b = invoice === null || invoice === void 0 ? void 0 : invoice.invoice) === null || _b === void 0 ? void 0 : _b.id)),
17
- !isClient && (react_1.default.createElement("div", null,
18
- react_1.default.createElement("span", { className: "text-sm font-bold" }, "Client: "),
19
- react_1.default.createElement("span", { className: "ml-2" }, (_c = invoice === null || invoice === void 0 ? void 0 : invoice.client) === null || _c === void 0 ? void 0 : _c.name))),
20
- react_1.default.createElement("div", null,
21
- react_1.default.createElement("span", { className: "text-sm font-bold" }, "Amount: "),
22
- react_1.default.createElement("span", { className: "ml-2" },
24
+ react_1.default.createElement("span", { className: "text-sm font-bold" }, "Amount:"),
25
+ react_1.default.createElement("span", { className: "ml-1" },
23
26
  "$",
24
27
  disputeAmount)),
25
28
  react_1.default.createElement("div", null,
26
- react_1.default.createElement("span", { className: "text-sm font-bold" }, "Dispute Date: "),
27
- react_1.default.createElement("span", { className: "ml-2" }, (0, dayjs_1.default)(invoice === null || invoice === void 0 ? void 0 : invoice.disputeDate).format('MMM D, YYYY'))),
29
+ react_1.default.createElement("span", { className: "text-sm font-bold" }, "Date:"),
30
+ react_1.default.createElement("span", { className: "ml-1" }, (0, dayjs_1.default)(invoice === null || invoice === void 0 ? void 0 : invoice.disputeDate).format('MMM D, YYYY'))),
28
31
  react_1.default.createElement("div", null,
29
- react_1.default.createElement("span", { className: "text-sm font-bold" }, "Invoice Date: "),
30
- react_1.default.createElement("span", { className: "ml-2" }, (0, dayjs_1.default)((_d = invoice === null || invoice === void 0 ? void 0 : invoice.invoice) === null || _d === void 0 ? void 0 : _d.dateGenerated).format('MMM D, YYYY'))),
32
+ react_1.default.createElement("span", { className: "text-sm font-bold" }, "Invoice Date:"),
33
+ react_1.default.createElement("span", { className: "ml-1" }, (0, dayjs_1.default)((_d = invoice === null || invoice === void 0 ? void 0 : invoice.invoice) === null || _d === void 0 ? void 0 : _d.dateGenerated).format('MMM D, YYYY'))),
31
34
  react_1.default.createElement("div", null,
32
- react_1.default.createElement("span", { className: "text-sm font-bold" }, "Dispute Status: "),
33
- react_1.default.createElement("span", { className: "ml-2" }, (invoice === null || invoice === void 0 ? void 0 : invoice.status) === "InProgress" ? "In Progress" : invoice === null || invoice === void 0 ? void 0 : invoice.status)),
34
- (invoice === null || invoice === void 0 ? void 0 : invoice.approvedAmount) && (react_1.default.createElement("div", null,
35
- react_1.default.createElement("span", { className: "text-sm font-bold" }, "Approved Amount: "),
36
- react_1.default.createElement("span", { className: "ml-2" },
35
+ react_1.default.createElement("span", { className: "text-sm font-bold" }, "Status:"),
36
+ react_1.default.createElement("span", { className: "ml-1" }, (invoice === null || invoice === void 0 ? void 0 : invoice.status) === "InProgress" ? "In Progress" : invoice === null || invoice === void 0 ? void 0 : invoice.status)),
37
+ (invoice === null || invoice === void 0 ? void 0 : invoice.approvedAmount) >= 0 && (invoice === null || invoice === void 0 ? void 0 : invoice.status) === 'Resolved' && (react_1.default.createElement("div", null,
38
+ react_1.default.createElement("span", { className: "text-sm font-bold" }, "Approved Amount:"),
39
+ react_1.default.createElement("span", { className: "ml-1" },
37
40
  "$", invoice === null || invoice === void 0 ? void 0 :
38
41
  invoice.approvedAmount))),
39
42
  !isInternal && (react_1.default.createElement("div", null,
40
43
  react_1.default.createElement("span", { className: "text-sm font-bold" }, "Expert Name: "),
41
- react_1.default.createElement("span", { className: "ml-2" }, (_e = invoice === null || invoice === void 0 ? void 0 : invoice.freelancer) === null || _e === void 0 ? void 0 :
44
+ react_1.default.createElement("span", { className: "ml-1" }, (_e = invoice === null || invoice === void 0 ? void 0 : invoice.freelancer) === null || _e === void 0 ? void 0 :
42
45
  _e.firstName,
43
46
  " ", (_f = invoice === null || invoice === void 0 ? void 0 : invoice.freelancer) === null || _f === void 0 ? void 0 :
44
47
  _f.lastName))))));
@@ -69,12 +69,9 @@ const ProjectHoursAdjustmentModal = ({ isOpen, onClose, disputeProjects, dispute
69
69
  setLoading(true);
70
70
  try {
71
71
  if ((_a = invoiceSummary === null || invoiceSummary === void 0 ? void 0 : invoiceSummary.getClientInvoiceSummaryByMonth) === null || _a === void 0 ? void 0 : _a.projects) {
72
- // Group tasks by project based on disputeProjects
73
72
  const projectSectionsData = [];
74
73
  disputeProjects.forEach(disputeProject => {
75
- // Find matching project in invoice summary
76
74
  const invoiceProject = invoiceSummary.getClientInvoiceSummaryByMonth.projects.find((proj) => {
77
- // Match by project name since we don't have direct projectId mapping
78
75
  return proj.projectName === disputeProject.project.name;
79
76
  });
80
77
  if (invoiceProject) {
@@ -90,14 +87,23 @@ const ProjectHoursAdjustmentModal = ({ isOpen, onClose, disputeProjects, dispute
90
87
  }
91
88
  });
92
89
  setProjectSections(projectSectionsData);
93
- // Initialize adjusted hours for all tasks
94
90
  const initialAdjustedHours = {};
95
91
  projectSectionsData.forEach(section => {
96
92
  section.tasks.forEach(task => {
97
- initialAdjustedHours[task.projectHourId] = {
98
- originalHours: task.hours,
99
- newHours: task.hours
100
- };
93
+ if (section.disputeType !== 'Hourly') {
94
+ initialAdjustedHours[task.projectHourId] = {
95
+ originalAmount: parseFloat((task.hours * task.rate).toFixed(2)),
96
+ newAmount: parseFloat((task.hours * task.rate).toFixed(2)),
97
+ originalHours: parseFloat(task.hours.toFixed(2)),
98
+ newHours: parseFloat(task.hours.toFixed(2))
99
+ };
100
+ }
101
+ else {
102
+ initialAdjustedHours[task.projectHourId] = {
103
+ originalHours: parseFloat(task.hours.toFixed(2)),
104
+ newHours: parseFloat(task.hours.toFixed(2))
105
+ };
106
+ }
101
107
  });
102
108
  });
103
109
  setAdjustedHours(initialAdjustedHours);
@@ -115,12 +121,10 @@ const ProjectHoursAdjustmentModal = ({ isOpen, onClose, disputeProjects, dispute
115
121
  });
116
122
  const handleHourChange = (projectHourId, newHours) => {
117
123
  var _a;
118
- const numericValue = parseFloat(newHours) || 0;
124
+ const numericValue = parseFloat(newHours);
119
125
  const originalHours = ((_a = adjustedHours[projectHourId]) === null || _a === void 0 ? void 0 : _a.originalHours) || 0;
120
126
  setAdjustedHours(prev => (Object.assign(Object.assign({}, prev), { [projectHourId]: Object.assign(Object.assign({}, prev[projectHourId]), { newHours: numericValue }) })));
121
- // Clear individual hour error
122
127
  setErrors(prev => (Object.assign(Object.assign({}, prev), { [projectHourId]: '' })));
123
- // Validate individual hour
124
128
  if (numericValue > originalHours) {
125
129
  setErrors(prev => (Object.assign(Object.assign({}, prev), { [projectHourId]: 'New hours cannot exceed original hours' })));
126
130
  }
@@ -128,6 +132,19 @@ const ProjectHoursAdjustmentModal = ({ isOpen, onClose, disputeProjects, dispute
128
132
  setErrors(prev => (Object.assign(Object.assign({}, prev), { [projectHourId]: 'Hours cannot be negative' })));
129
133
  }
130
134
  };
135
+ const handleAmountChange = (projectHourId, newAmount) => {
136
+ var _a;
137
+ const numericValue = parseFloat(newAmount);
138
+ const originalAmount = ((_a = adjustedHours[projectHourId]) === null || _a === void 0 ? void 0 : _a.originalAmount) || 0;
139
+ setAdjustedHours(prev => (Object.assign(Object.assign({}, prev), { [projectHourId]: Object.assign(Object.assign({}, prev[projectHourId]), { newAmount: numericValue }) })));
140
+ setErrors(prev => (Object.assign(Object.assign({}, prev), { [projectHourId]: '' })));
141
+ if (numericValue > originalAmount) {
142
+ setErrors(prev => (Object.assign(Object.assign({}, prev), { [projectHourId]: 'New Dispute Amount cannot exceed original Amount' })));
143
+ }
144
+ else if (numericValue < 0) {
145
+ setErrors(prev => (Object.assign(Object.assign({}, prev), { [projectHourId]: 'Dispute Amount cannot be negative' })));
146
+ }
147
+ };
131
148
  const getProjectTotalHours = (projectId, type) => {
132
149
  const section = projectSections.find(s => s.projectId === projectId);
133
150
  if (!section)
@@ -136,7 +153,7 @@ const ProjectHoursAdjustmentModal = ({ isOpen, onClose, disputeProjects, dispute
136
153
  const adjustment = adjustedHours[task.projectHourId];
137
154
  if (!adjustment)
138
155
  return total;
139
- return total + (type === 'original' ? adjustment.originalHours : adjustment.newHours);
156
+ return total + (type === 'original' ? adjustment.originalHours || 0 : adjustment.newHours || 0);
140
157
  }, 0);
141
158
  };
142
159
  const getProjectTotalAmount = (projectId, type) => {
@@ -147,8 +164,8 @@ const ProjectHoursAdjustmentModal = ({ isOpen, onClose, disputeProjects, dispute
147
164
  const adjustment = adjustedHours[task.projectHourId];
148
165
  if (!adjustment)
149
166
  return total;
150
- const hours = type === 'original' ? adjustment.originalHours : adjustment.newHours;
151
- return total + (hours * task.rate); // Calculate amount based on hours * rate
167
+ const newAmount = type === 'original' ? adjustment.originalAmount || 0 : adjustment.newAmount || 0;
168
+ return total + newAmount; // Calculate amount based on hours * rate
152
169
  }, 0);
153
170
  };
154
171
  const getProjectReduction = (projectId) => {
@@ -167,32 +184,37 @@ const ProjectHoursAdjustmentModal = ({ isOpen, onClose, disputeProjects, dispute
167
184
  let hasErrors = false;
168
185
  const newErrors = {};
169
186
  const newProjectErrors = {};
170
- // Validate each project section
171
187
  projectSections.forEach(section => {
172
188
  const projectId = section.projectId;
173
- const disputeHours = section.disputeHours;
174
- // Use project-specific approved hours if available, otherwise use dispute hours
175
189
  const maxAllowedReduction = projectApprovedHours[projectId] !== undefined
176
190
  ? projectApprovedHours[projectId]
177
191
  : section.disputeType === 'Hourly'
178
192
  ? section.disputeHours
179
193
  : section.disputeAmount;
180
- // Validate individual hours within this project
181
194
  section.tasks.forEach(task => {
182
- var _a, _b;
195
+ var _a, _b, _c, _d;
183
196
  const projectHourId = task.projectHourId;
184
197
  const originalHours = ((_a = adjustedHours[projectHourId]) === null || _a === void 0 ? void 0 : _a.originalHours) || 0;
185
- const newHours = ((_b = adjustedHours[projectHourId]) === null || _b === void 0 ? void 0 : _b.newHours) || 0;
186
- if (newHours > originalHours) {
198
+ const newHours = (_b = adjustedHours[projectHourId]) === null || _b === void 0 ? void 0 : _b.newHours;
199
+ const newAmount = (_c = adjustedHours[projectHourId]) === null || _c === void 0 ? void 0 : _c.newAmount;
200
+ const originalAmount = ((_d = adjustedHours[projectHourId]) === null || _d === void 0 ? void 0 : _d.originalAmount) || 0;
201
+ if (newHours && newHours > originalHours) {
187
202
  newErrors[projectHourId] = 'New hours cannot exceed original hours';
188
203
  hasErrors = true;
189
204
  }
190
- else if (newHours < 0) {
205
+ else if (newHours && newHours < 0) {
191
206
  newErrors[projectHourId] = 'Hours cannot be negative';
192
207
  hasErrors = true;
193
208
  }
209
+ else if (newAmount && newAmount < 0) {
210
+ newErrors[projectHourId] = 'Dispute Amount cannot be negative';
211
+ hasErrors = true;
212
+ }
213
+ else if (newAmount && newAmount > originalAmount) {
214
+ newErrors[projectHourId] = 'New Dispute Amount cannot exceed original Amount';
215
+ hasErrors = true;
216
+ }
194
217
  });
195
- // Validate project-level hour reduction
196
218
  const projectReduction = getProjectReduction(projectId);
197
219
  if (Math.abs(projectReduction - maxAllowedReduction) > 0.01) {
198
220
  const reductionType = projectApprovedHours[projectId] !== undefined ? 'approved' : 'disputed';
@@ -204,40 +226,30 @@ const ProjectHoursAdjustmentModal = ({ isOpen, onClose, disputeProjects, dispute
204
226
  setErrors(newErrors);
205
227
  setProjectErrors(newProjectErrors);
206
228
  if (!hasErrors) {
207
- // Generate the submission data in the required format
208
229
  let freelancerRate = 0;
209
230
  const inputArray = [];
210
231
  projectSections.forEach(section => {
211
232
  section.tasks.forEach(task => {
212
233
  const projectHourId = task.projectHourId;
213
234
  const adjustedHour = adjustedHours[projectHourId];
235
+ const newHours = adjustedHour === null || adjustedHour === void 0 ? void 0 : adjustedHour.newHours;
214
236
  freelancerRate = task.freelancerRate;
215
- // Calculate hours based on dispute type
216
- let calculatedHours = (adjustedHour === null || adjustedHour === void 0 ? void 0 : adjustedHour.newHours) || 0;
237
+ let calculatedHours = newHours || 0;
217
238
  if (section.disputeType !== 'Hourly') {
218
- // For non-hourly projects, calculate hour equivalent based on approved dispute amount ratio
219
239
  const approvedDisputeAmount = projectApprovedHours[section.projectId] !== undefined
220
240
  ? projectApprovedHours[section.projectId]
221
241
  : section.disputeAmount;
222
242
  const totalProjectAmount = section.totalProjectAmount;
223
243
  if (totalProjectAmount > 0) {
224
- // Calculate the ratio of approved dispute amount to total project amount
225
244
  const disputeRatio = approvedDisputeAmount / totalProjectAmount;
226
- // Apply this ratio to the original hours for this task
227
245
  const originalTaskHours = (adjustedHour === null || adjustedHour === void 0 ? void 0 : adjustedHour.originalHours) || 0;
228
- // The reduction in hours should be proportional to the dispute ratio
229
246
  const hourReduction = originalTaskHours * disputeRatio;
230
247
  calculatedHours = originalTaskHours - hourReduction;
231
- // Ensure hours don't go negative
232
- calculatedHours = Math.max(0, calculatedHours);
248
+ calculatedHours = Number(Math.max(0, calculatedHours).toFixed(4));
233
249
  }
234
250
  }
235
- // Generate expert name
236
- const expertName = (dispute === null || dispute === void 0 ? void 0 : dispute.freelancer) ?
237
- `${dispute.freelancer.firstName} ${dispute.freelancer.lastName}` : '';
238
- // Generate internal user name
239
- const internalUser = user ?
240
- `${user.firstName} ${user.lastName}` : '';
251
+ const expertName = (dispute === null || dispute === void 0 ? void 0 : dispute.freelancer) ? `${dispute.freelancer.firstName} ${dispute.freelancer.lastName}` : '';
252
+ const internalUser = user ? `${user.firstName} ${user.lastName}` : '';
241
253
  inputArray.push({
242
254
  clientRate: task.rate,
243
255
  freelancerRate: task.freelancerRate,
@@ -301,7 +313,7 @@ const ProjectHoursAdjustmentModal = ({ isOpen, onClose, disputeProjects, dispute
301
313
  : "Please enter the new hours to settle the dispute for each of the project time log entries below"),
302
314
  react_1.default.createElement("div", { className: "space-y-8" }, projectSections.map((section) => {
303
315
  var _a, _b;
304
- const projectReduction = getProjectReduction(section.projectId);
316
+ const projectReduction = getProjectReduction(section.projectId) || 0;
305
317
  const maxAllowedReduction = projectApprovedHours[section.projectId] !== undefined
306
318
  ? projectApprovedHours[section.projectId]
307
319
  : section.disputeType === 'Hourly'
@@ -359,26 +371,24 @@ const ProjectHoursAdjustmentModal = ({ isOpen, onClose, disputeProjects, dispute
359
371
  react_1.default.createElement("th", { className: "border border-gray-300 px-4 py-2 text-left" }, section.disputeType === 'Hourly' ? 'Original Hours' : 'Original Dispute $'),
360
372
  react_1.default.createElement("th", { className: "border border-gray-300 px-4 py-2 text-left" }, section.disputeType === 'Hourly' ? 'Adjusted Hours' : 'Adjusted $ Amount'))),
361
373
  react_1.default.createElement("tbody", null, section.tasks.map((task) => {
362
- var _a, _b;
374
+ var _a, _b, _c;
363
375
  const projectHourId = task.projectHourId;
364
376
  const originalHours = task.hours;
365
377
  const originalAmount = task.hours * task.rate;
366
- const newHours = ((_a = adjustedHours[projectHourId]) === null || _a === void 0 ? void 0 : _a.newHours) || 0;
367
- const newAmount = newHours * task.rate;
378
+ const newHours = (_a = adjustedHours[projectHourId]) === null || _a === void 0 ? void 0 : _a.newHours;
379
+ const newAmount = (_b = adjustedHours[projectHourId]) === null || _b === void 0 ? void 0 : _b.newAmount;
368
380
  const hasError = errors[projectHourId];
369
381
  return (react_1.default.createElement("tr", { key: projectHourId, className: hasError ? 'bg-red-50' : '' },
370
382
  react_1.default.createElement("td", { className: "border border-gray-300 px-4 py-2" }, formatDate(task.date)),
371
383
  react_1.default.createElement("td", { className: "border border-gray-300 px-4 py-2" }, task.description),
372
384
  react_1.default.createElement("td", { className: "border border-gray-300 px-4 py-2" }, section.disputeType === 'Hourly'
373
385
  ? originalHours.toFixed(2)
374
- : `$${((_b = section.disputeAmount) === null || _b === void 0 ? void 0 : _b.toFixed(2)) || '0.00'}`),
386
+ : `$${((_c = section.disputeAmount) === null || _c === void 0 ? void 0 : _c.toFixed(2)) || '0.00'}`),
375
387
  react_1.default.createElement("td", { className: "border border-gray-300 px-4 py-2" },
376
388
  react_1.default.createElement("div", null,
377
- section.disputeType === 'Hourly' ? (react_1.default.createElement("input", { type: "number", step: "0.01", min: "0", max: originalHours, value: newHours, onChange: (e) => handleHourChange(projectHourId, e.target.value), className: `w-full px-2 py-1 border rounded focus:outline-none focus:ring-2 focus:ring-blue-500 ${hasError ? 'border-red-500' : 'border-gray-300'}` })) : (react_1.default.createElement("div", { className: "flex items-center" },
389
+ section.disputeType === 'Hourly' ? (react_1.default.createElement("input", { type: "number", step: "1", max: originalHours, value: newHours !== undefined && newHours >= 0 ? newHours : '', onChange: (e) => handleHourChange(projectHourId, e.target.value), className: `w-full px-2 py-1 border rounded focus:outline-none focus:ring-2 focus:ring-blue-500 ${hasError ? 'border-red-500' : 'border-gray-300'}` })) : (react_1.default.createElement("div", { className: "flex items-center" },
378
390
  react_1.default.createElement("span", { className: "mr-1" }, "$"),
379
- react_1.default.createElement("span", null, newAmount.toFixed(2)),
380
- react_1.default.createElement("input", { type: "number", step: "0.01", min: "0", max: originalHours, value: newHours, onChange: (e) => handleHourChange(projectHourId, e.target.value), className: `ml-2 w-20 px-2 py-1 border rounded focus:outline-none focus:ring-2 focus:ring-blue-500 ${hasError ? 'border-red-500' : 'border-gray-300'}`, placeholder: "hrs" }),
381
- react_1.default.createElement("span", { className: "ml-1 text-xs text-gray-500" }, "hrs"))),
391
+ react_1.default.createElement("input", { type: "number", step: "1", min: "0", max: originalAmount, value: newAmount !== undefined && newAmount >= 0 ? newAmount : '', onChange: (e) => handleAmountChange(projectHourId, (e.target.value).toString()), className: `w-32 px-2 py-1 border rounded focus:outline-none focus:ring-2 focus:ring-blue-500 ${hasError ? 'border-red-500' : 'border-gray-300'}`, placeholder: "$0.00" }))),
382
392
  hasError && (react_1.default.createElement("div", { className: "text-red-500 text-xs mt-1" }, hasError))))));
383
393
  }))))));
384
394
  })),
@@ -1 +1 @@
1
- export declare const TestDecisionSection: () => JSX.Element;
1
+ export declare const TestDecisionSection: () => JSX.Element;
@@ -1,126 +1,126 @@
1
- "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
- var __importDefault = (this && this.__importDefault) || function (mod) {
12
- return (mod && mod.__esModule) ? mod : { "default": mod };
13
- };
14
- Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.TestDecisionSection = void 0;
16
- const react_1 = __importDefault(require("react"));
17
- const DecisionSection_1 = require("./DecisionSection");
18
- // Test data from the provided JSON
19
- const testDispute = {
20
- "__typename": "ClientInvoiceDispute",
21
- "disputeId": 35,
22
- "approvedAmount": 450,
23
- "chatEnabled": true,
24
- "disputeDate": "2025-05-22",
25
- "disputeUpdatedDate": "2025-05-23",
26
- "freelancerId": 8081,
27
- "freelancer": {
28
- "__typename": "User",
29
- "id": 8081,
30
- "email": "andpande@gmail.com",
31
- "firstName": "William",
32
- "lastName": "Annon"
33
- },
34
- "status": "Resolved",
35
- "finalDecisionOwnerId": 29005,
36
- "invoice": {
37
- "__typename": "ClientInvoice",
38
- "amount": 450,
39
- "balanceDue": 450,
40
- "clientId": 4433,
41
- "dateGenerated": "2025-04-14",
42
- "id": 175325,
43
- "month": "2025-04-01"
44
- },
45
- "disputeProjects": [
46
- {
47
- "__typename": "ClientInvoiceDisputeProject",
48
- "id": 37,
49
- "projectId": 19158,
50
- "disputeAmount": 450,
51
- "disputeHours": 30,
52
- "disputeType": "Hourly",
53
- "disputeReasonCode": "Incorrect Hours Logged",
54
- "clientExplanation": "I have a dispute over this expert for the current invoice 175325",
55
- "clientDocumentLinks": "https://expert-client-dispute-files.s3.amazonaws.com/project-19158%2Ftesting.pdf,https://expert-client-dispute-files.s3.amazonaws.com/project-19158%2F_Carmelita_Resume-Graduation__1_.docx",
56
- "expertDocumentLinks": "https://expert-client-dispute-files.s3.amazonaws.com/project-19158%2F0eb4b31b-b6d8-4cd1-b6ac-39b807ea4577.pdf",
57
- "resolutionMode": null,
58
- "resolutionType": null,
59
- "project": {
60
- "__typename": "Project",
61
- "name": "Ongoing Ad-Hoc Support",
62
- "freelancerRate": null,
63
- "clientRate": null,
64
- "projectHours": [
65
- {
66
- "__typename": "ProjectHour",
67
- "id": 136605,
68
- "date": "2022-02-11",
69
- "description": "Ate Tacos",
70
- "hours": 8
71
- },
72
- {
73
- "__typename": "ProjectHour",
74
- "id": 136606,
75
- "date": "2022-02-12",
76
- "description": "Worked on project analysis",
77
- "hours": 6
78
- },
79
- {
80
- "__typename": "ProjectHour",
81
- "id": 136607,
82
- "date": "2022-02-13",
83
- "description": "Client meeting and documentation",
84
- "hours": 4
85
- },
86
- {
87
- "__typename": "ProjectHour",
88
- "id": 136608,
89
- "date": "2022-02-14",
90
- "description": "Code review and testing",
91
- "hours": 7
92
- },
93
- {
94
- "__typename": "ProjectHour",
95
- "id": 136609,
96
- "date": "2022-02-15",
97
- "description": "Final deliverable preparation",
98
- "hours": 5
99
- }
100
- ]
101
- }
102
- }
103
- ],
104
- "lastMessageAt": "2025-05-23T10:56:53.000Z",
105
- "messageCount": 5,
106
- "client": {
107
- "__typename": "Client",
108
- "name": "Vessel Advisors"
109
- }
110
- };
111
- const testUser = {
112
- userId: 29005,
113
- firstName: "Test",
114
- lastName: "User"
115
- };
116
- const TestDecisionSection = () => {
117
- const mockOnUpdateDispute = (variables) => __awaiter(void 0, void 0, void 0, function* () {
118
- console.log('Mock update dispute called with:', variables);
119
- return Promise.resolve({ success: true });
120
- });
121
- const mockUpdateInvoiceMutation = {};
122
- return (react_1.default.createElement("div", { className: "p-8 max-w-4xl mx-auto" },
123
- react_1.default.createElement("h1", { className: "text-2xl font-bold mb-6" }, "Test Decision Section with Hours Modal"),
124
- react_1.default.createElement(DecisionSection_1.DecisionSection, { dispute: testDispute, onUpdateDispute: mockOnUpdateDispute, updateInvoiceMutation: mockUpdateInvoiceMutation, user: testUser })));
125
- };
126
- exports.TestDecisionSection = TestDecisionSection;
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __importDefault = (this && this.__importDefault) || function (mod) {
12
+ return (mod && mod.__esModule) ? mod : { "default": mod };
13
+ };
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.TestDecisionSection = void 0;
16
+ const react_1 = __importDefault(require("react"));
17
+ const DecisionSection_1 = require("./DecisionSection");
18
+ // Test data from the provided JSON
19
+ const testDispute = {
20
+ "__typename": "ClientInvoiceDispute",
21
+ "disputeId": 35,
22
+ "approvedAmount": 450,
23
+ "chatEnabled": true,
24
+ "disputeDate": "2025-05-22",
25
+ "disputeUpdatedDate": "2025-05-23",
26
+ "freelancerId": 8081,
27
+ "freelancer": {
28
+ "__typename": "User",
29
+ "id": 8081,
30
+ "email": "andpande@gmail.com",
31
+ "firstName": "William",
32
+ "lastName": "Annon"
33
+ },
34
+ "status": "Resolved",
35
+ "finalDecisionOwnerId": 29005,
36
+ "invoice": {
37
+ "__typename": "ClientInvoice",
38
+ "amount": 450,
39
+ "balanceDue": 450,
40
+ "clientId": 4433,
41
+ "dateGenerated": "2025-04-14",
42
+ "id": 175325,
43
+ "month": "2025-04-01"
44
+ },
45
+ "disputeProjects": [
46
+ {
47
+ "__typename": "ClientInvoiceDisputeProject",
48
+ "id": 37,
49
+ "projectId": 19158,
50
+ "disputeAmount": 450,
51
+ "disputeHours": 30,
52
+ "disputeType": "Hourly",
53
+ "disputeReasonCode": "Incorrect Hours Logged",
54
+ "clientExplanation": "I have a dispute over this expert for the current invoice 175325",
55
+ "clientDocumentLinks": "https://expert-client-dispute-files.s3.amazonaws.com/project-19158%2Ftesting.pdf,https://expert-client-dispute-files.s3.amazonaws.com/project-19158%2F_Carmelita_Resume-Graduation__1_.docx",
56
+ "expertDocumentLinks": "https://expert-client-dispute-files.s3.amazonaws.com/project-19158%2F0eb4b31b-b6d8-4cd1-b6ac-39b807ea4577.pdf",
57
+ "resolutionMode": null,
58
+ "resolutionType": null,
59
+ "project": {
60
+ "__typename": "Project",
61
+ "name": "Ongoing Ad-Hoc Support",
62
+ "freelancerRate": null,
63
+ "clientRate": null,
64
+ "projectHours": [
65
+ {
66
+ "__typename": "ProjectHour",
67
+ "id": 136605,
68
+ "date": "2022-02-11",
69
+ "description": "Ate Tacos",
70
+ "hours": 8
71
+ },
72
+ {
73
+ "__typename": "ProjectHour",
74
+ "id": 136606,
75
+ "date": "2022-02-12",
76
+ "description": "Worked on project analysis",
77
+ "hours": 6
78
+ },
79
+ {
80
+ "__typename": "ProjectHour",
81
+ "id": 136607,
82
+ "date": "2022-02-13",
83
+ "description": "Client meeting and documentation",
84
+ "hours": 4
85
+ },
86
+ {
87
+ "__typename": "ProjectHour",
88
+ "id": 136608,
89
+ "date": "2022-02-14",
90
+ "description": "Code review and testing",
91
+ "hours": 7
92
+ },
93
+ {
94
+ "__typename": "ProjectHour",
95
+ "id": 136609,
96
+ "date": "2022-02-15",
97
+ "description": "Final deliverable preparation",
98
+ "hours": 5
99
+ }
100
+ ]
101
+ }
102
+ }
103
+ ],
104
+ "lastMessageAt": "2025-05-23T10:56:53.000Z",
105
+ "messageCount": 5,
106
+ "client": {
107
+ "__typename": "Client",
108
+ "name": "Vessel Advisors"
109
+ }
110
+ };
111
+ const testUser = {
112
+ userId: 29005,
113
+ firstName: "Test",
114
+ lastName: "User"
115
+ };
116
+ const TestDecisionSection = () => {
117
+ const mockOnUpdateDispute = (variables) => __awaiter(void 0, void 0, void 0, function* () {
118
+ console.log('Mock update dispute called with:', variables);
119
+ return Promise.resolve({ success: true });
120
+ });
121
+ const mockUpdateInvoiceMutation = {};
122
+ return (react_1.default.createElement("div", { className: "p-8 max-w-4xl mx-auto" },
123
+ react_1.default.createElement("h1", { className: "text-2xl font-bold mb-6" }, "Test Decision Section with Hours Modal"),
124
+ react_1.default.createElement(DecisionSection_1.DecisionSection, { dispute: testDispute, onUpdateDispute: mockOnUpdateDispute, updateInvoiceMutation: mockUpdateInvoiceMutation, user: testUser })));
125
+ };
126
+ exports.TestDecisionSection = TestDecisionSection;