@paro.io/expert-shared-components 1.11.3 → 1.11.5

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.
@@ -0,0 +1,10 @@
1
+ interface UploadFileParams {
2
+ file: File;
3
+ documentName: string;
4
+ disputeId?: number;
5
+ projectId: number;
6
+ documentUploadUrl: string;
7
+ uploadExpertClientDisputeFilesData: any;
8
+ }
9
+ export declare const fileUploader: ({ file, documentName, disputeId, projectId, documentUploadUrl, uploadExpertClientDisputeFilesData, }: UploadFileParams) => Promise<boolean>;
10
+ export {};
@@ -0,0 +1,70 @@
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.fileUploader = void 0;
16
+ const UploadClient_1 = __importDefault(require("../shared/UploadClient"));
17
+ const utils_1 = require("../shared/utils");
18
+ const CHUNK_SIZE = 5 * 1024 * 1024;
19
+ const fileUploader = (_a) => __awaiter(void 0, [_a], void 0, function* ({ file, documentName, disputeId, projectId, documentUploadUrl, uploadExpertClientDisputeFilesData, }) {
20
+ var _b;
21
+ const fileExtension = file.name.split('.').pop() || '';
22
+ const fileNameWithExtension = `${documentName}.${fileExtension}`;
23
+ const isLargeFile = file.size >= CHUNK_SIZE;
24
+ try {
25
+ // Read file as base64
26
+ const fileForUpload = yield new Promise((resolve, reject) => {
27
+ const reader = new FileReader();
28
+ reader.onload = () => { var _a; return resolve(((_a = reader.result) === null || _a === void 0 ? void 0 : _a.toString()) || ''); };
29
+ reader.onerror = reject;
30
+ reader.readAsDataURL(file);
31
+ });
32
+ // Upload large file via multipart
33
+ if (isLargeFile) {
34
+ (0, utils_1.showToast)('warning', 'Uploading a large file — this may take longer than usual. Thank you for your patience!');
35
+ const uploadClient = new UploadClient_1.default({
36
+ fileSelected: file,
37
+ fileName: fileNameWithExtension,
38
+ projectId,
39
+ documentUploadUrl,
40
+ });
41
+ yield uploadClient.triggerMultipartUpload();
42
+ }
43
+ const inputData = {
44
+ disputeId: disputeId ? disputeId : projectId, // initially we don't have disputeId. clarify and update
45
+ fileName: documentName,
46
+ data: isLargeFile ? '' : fileForUpload,
47
+ fileSize: file.size,
48
+ };
49
+ console.log({ inputData, disputeId, projectId });
50
+ // Upload metadata to backend
51
+ const response = yield uploadExpertClientDisputeFilesData({
52
+ variables: {
53
+ input: inputData,
54
+ },
55
+ });
56
+ if ((_b = response === null || response === void 0 ? void 0 : response.data) === null || _b === void 0 ? void 0 : _b.uploadExpertClientDisputeFilesData) {
57
+ (0, utils_1.showToast)('success', 'Supporting document uploaded successfully!');
58
+ return true;
59
+ }
60
+ else {
61
+ (0, utils_1.showToast)('warning', 'Failed to upload supporting document');
62
+ return false;
63
+ }
64
+ }
65
+ catch (error) {
66
+ (0, utils_1.showToast)('warning', 'Error uploading file. Please try again.');
67
+ return false;
68
+ }
69
+ });
70
+ exports.fileUploader = fileUploader;
@@ -0,0 +1,7 @@
1
+ interface DecisionSectionProps {
2
+ dispute: any;
3
+ onUpdateDispute: (variables: any) => Promise<any>;
4
+ onRefetch: () => Promise<void>;
5
+ }
6
+ export declare const DecisionSection: ({ dispute, onUpdateDispute, }: DecisionSectionProps) => JSX.Element;
7
+ export {};
@@ -0,0 +1,95 @@
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.DecisionSection = void 0;
39
+ const react_1 = __importStar(require("react"));
40
+ const base_ui_1 = require("@paro.io/base-ui");
41
+ const react_hot_toast_1 = __importDefault(require("react-hot-toast"));
42
+ const RESOLUTION_OPTIONS = [
43
+ { value: 'Resolved', label: 'Approve Dispute' },
44
+ { value: 'Resolved', label: 'Decline Dispute' },
45
+ { value: 'Resolved', label: 'Partial Approval' },
46
+ ];
47
+ const DecisionSection = ({ dispute, onUpdateDispute, }) => {
48
+ const [resolution, setResolution] = (0, react_1.useState)('');
49
+ const [amount, setAmount] = (0, react_1.useState)('');
50
+ const [isSubmitting, setIsSubmitting] = (0, react_1.useState)(false);
51
+ const handleSubmit = () => __awaiter(void 0, void 0, void 0, function* () {
52
+ if (!resolution)
53
+ return;
54
+ setIsSubmitting(true);
55
+ try {
56
+ yield onUpdateDispute({
57
+ variables: {
58
+ input: {
59
+ disputeId: dispute.disputeId,
60
+ status: resolution,
61
+ approvedAmount: amount ? parseFloat(amount) : undefined,
62
+ },
63
+ },
64
+ });
65
+ react_hot_toast_1.default.success('Thank you for resolving the dispute');
66
+ }
67
+ catch (error) {
68
+ console.error('Failed to update dispute:', error);
69
+ react_hot_toast_1.default.error('Failed to update dispute');
70
+ }
71
+ finally {
72
+ setIsSubmitting(false);
73
+ }
74
+ });
75
+ return (react_1.default.createElement("div", { className: "space-y-6" },
76
+ react_1.default.createElement("div", { className: "flex items-center justify-between" },
77
+ react_1.default.createElement("div", { className: "text-lg font-bold text-[#333333]" }, "Make Decision")),
78
+ react_1.default.createElement("div", { className: "p-6 space-y-6", style: { backgroundColor: '#F8F9FA' } },
79
+ react_1.default.createElement("div", { className: "grid grid-cols-2 gap-6" },
80
+ react_1.default.createElement("div", null,
81
+ react_1.default.createElement("label", { className: "block text-sm text-[#666666] font-bold mb-2" }, "Resolution:"),
82
+ react_1.default.createElement("div", { className: "w-full" },
83
+ react_1.default.createElement("select", { value: resolution, onChange: (e) => setResolution(e.target.value), className: "w-full px-3 py-2 border border-[#CCCCCC] rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-[#248384] bg-white" },
84
+ react_1.default.createElement("option", { value: "" }, "Select Resolution"),
85
+ RESOLUTION_OPTIONS.map((option) => (react_1.default.createElement("option", { key: option.value, value: option.value }, option.label)))))),
86
+ resolution === 'PARTIAL' && (react_1.default.createElement("div", null,
87
+ react_1.default.createElement("label", { className: "block text-sm text-[#666666] font-bold mb-2" }, "Approved Amount:"),
88
+ react_1.default.createElement("div", { className: "relative" },
89
+ react_1.default.createElement("span", { className: "absolute left-3 top-1/2 transform -translate-y-1/2 text-[#333333]" }, "$"),
90
+ react_1.default.createElement("input", { type: "number", value: amount, onChange: (e) => setAmount(e.target.value), className: "w-full pl-8 pr-3 py-2 border border-[#CCCCCC] rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-[#248384]", placeholder: "Enter amount" }))))),
91
+ react_1.default.createElement("div", { className: "flex justify-end space-x-4" },
92
+ react_1.default.createElement(base_ui_1.Button, { label: "Decline Dispute", color: "error", onClick: () => setResolution('DECLINED'), disabled: isSubmitting, size: "md" }),
93
+ react_1.default.createElement(base_ui_1.Button, { label: "Approve & Resolve", color: "primary", onClick: handleSubmit, isLoading: isSubmitting, disabled: !resolution || (resolution === 'PARTIAL' && !amount), size: "md" })))));
94
+ };
95
+ exports.DecisionSection = DecisionSection;
@@ -0,0 +1,19 @@
1
+ interface Message {
2
+ id: string;
3
+ sender: {
4
+ firstName: string;
5
+ lastName: string;
6
+ role: string;
7
+ userTypeId: number;
8
+ };
9
+ messageText: string;
10
+ createdAt: string;
11
+ }
12
+ interface DiscussionSectionProps {
13
+ disputeId: string;
14
+ currentUser: any;
15
+ messages: Message[];
16
+ onCreateMessage: (variables: any) => Promise<any>;
17
+ }
18
+ export declare const DiscussionSection: ({ disputeId, currentUser, messages, onCreateMessage, }: DiscussionSectionProps) => JSX.Element;
19
+ export {};
@@ -0,0 +1,177 @@
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.DiscussionSection = void 0;
39
+ const react_1 = __importStar(require("react"));
40
+ const base_ui_1 = require("@paro.io/base-ui");
41
+ const dayjs_1 = __importDefault(require("dayjs"));
42
+ const DiscussionSection = ({ disputeId, currentUser, messages, onCreateMessage, }) => {
43
+ var _a;
44
+ const [newMessage, setNewMessage] = (0, react_1.useState)('');
45
+ const [visibility, setVisibility] = (0, react_1.useState)('ALL');
46
+ const [isLoading, setIsLoading] = (0, react_1.useState)(false);
47
+ const threadsEndRef = (0, react_1.useRef)(null);
48
+ const didMountRef = (0, react_1.useRef)(false);
49
+ // Auto scroll to bottom when messages change
50
+ (0, react_1.useEffect)(() => {
51
+ if (didMountRef.current) {
52
+ if (threadsEndRef.current) {
53
+ threadsEndRef.current.scrollIntoView({ behavior: "smooth" });
54
+ }
55
+ }
56
+ else {
57
+ didMountRef.current = true;
58
+ }
59
+ }, [messages]);
60
+ // Get the sequence of unique userTypeIds to determine alignment
61
+ const userSequence = (0, react_1.useMemo)(() => {
62
+ const uniqueUsers = Array.from(new Set(messages.map(m => m.sender.userTypeId)));
63
+ return uniqueUsers;
64
+ }, [messages]);
65
+ const handleSendMessage = () => __awaiter(void 0, void 0, void 0, function* () {
66
+ var _a;
67
+ if (!newMessage.trim())
68
+ return;
69
+ try {
70
+ setIsLoading(true);
71
+ yield onCreateMessage({
72
+ variables: {
73
+ input: {
74
+ disputeId,
75
+ messageText: newMessage,
76
+ visibility,
77
+ senderId: (_a = currentUser === null || currentUser === void 0 ? void 0 : currentUser.app_metadata) === null || _a === void 0 ? void 0 : _a.userId,
78
+ },
79
+ },
80
+ });
81
+ setNewMessage('');
82
+ setIsLoading(false);
83
+ }
84
+ catch (error) {
85
+ console.error('Failed to send message:', error);
86
+ }
87
+ });
88
+ const getSenderInitial = (sender) => {
89
+ if (sender.userTypeId === 3)
90
+ return 'C';
91
+ if (sender.userTypeId === 1)
92
+ return 'E';
93
+ return 'P';
94
+ };
95
+ const getSenderTextColor = (userTypeId) => {
96
+ switch (userTypeId) {
97
+ case 1:
98
+ return '#195C5C';
99
+ case 2:
100
+ return '#2196F3';
101
+ case 3:
102
+ return '#643E0C';
103
+ default:
104
+ return '#333333';
105
+ }
106
+ };
107
+ const getSenderBackgroundColor = (userTypeId) => {
108
+ switch (userTypeId) {
109
+ case 1:
110
+ return '#D9EFEF';
111
+ case 2:
112
+ return '#E4F0FF';
113
+ case 3:
114
+ return '#FDD7A5';
115
+ default:
116
+ return '#F8F9FA';
117
+ }
118
+ };
119
+ const getMessageAlignment = (userTypeId) => {
120
+ // First user in sequence is left-aligned, second is right-aligned
121
+ const userIndex = userSequence.indexOf(userTypeId);
122
+ return userIndex === 0 ? 'mr-auto' : 'ml-auto';
123
+ };
124
+ const getMessageOrder = (userTypeId) => {
125
+ // First user in sequence shows avatar on left, second on right
126
+ const userIndex = userSequence.indexOf(userTypeId);
127
+ return userIndex === 0 ? 'flex-row' : 'flex-row-reverse';
128
+ };
129
+ const getHeaderAlignment = (userTypeId) => {
130
+ // First user in sequence aligns text left, second aligns right
131
+ const userIndex = userSequence.indexOf(userTypeId);
132
+ return userIndex === 0 ? 'justify-start' : 'justify-end';
133
+ };
134
+ return (react_1.default.createElement("div", { className: "space-y-6" },
135
+ react_1.default.createElement("div", { className: "flex items-center justify-between" },
136
+ react_1.default.createElement("div", { className: "text-lg font-bold text-[#333333]" }, "Discussion Thread")),
137
+ react_1.default.createElement("div", { className: "rounded" },
138
+ react_1.default.createElement("div", { className: "space-y-6" }, messages.map((message) => (react_1.default.createElement("div", { key: message.id, className: `flex ${getMessageAlignment(message.sender.userTypeId)}` },
139
+ react_1.default.createElement("div", { className: `flex space-x-4 ${getMessageOrder(message.sender.userTypeId)} ${getMessageAlignment(message.sender.userTypeId)}` },
140
+ react_1.default.createElement("div", { style: {
141
+ backgroundColor: getSenderBackgroundColor(message.sender.userTypeId),
142
+ color: getSenderTextColor(message.sender.userTypeId)
143
+ }, className: "w-8 h-8 rounded-full flex items-center justify-center flex-shrink-0" },
144
+ react_1.default.createElement("span", { className: "text-sm font-bold" }, getSenderInitial(message.sender))),
145
+ react_1.default.createElement("div", { className: "flex-1" },
146
+ react_1.default.createElement("div", { className: `flex items-center ${getHeaderAlignment(message.sender.userTypeId)} space-x-2` },
147
+ react_1.default.createElement("span", { className: "text-sm font-bold text-[#333333]" },
148
+ message.sender.firstName,
149
+ " ",
150
+ message.sender.lastName),
151
+ react_1.default.createElement("span", { className: "text-xs text-[#666666]" }, (0, dayjs_1.default)(message.createdAt).format('MMM D, YYYY • h:mm A'))),
152
+ react_1.default.createElement("div", { style: { backgroundColor: getSenderBackgroundColor(message.sender.userTypeId) }, className: "mt-1 p-3 rounded-lg" },
153
+ react_1.default.createElement("p", { className: `text-sm ${getHeaderAlignment(message.sender.userTypeId)}`, style: { color: getSenderTextColor(message.sender.userTypeId) } }, message.messageText)))))))),
154
+ react_1.default.createElement("div", { ref: threadsEndRef }),
155
+ react_1.default.createElement("div", { className: "space-y-4 p-6 space-y-6 rounded mt-4", style: { backgroundColor: '#F5F7F9' } },
156
+ react_1.default.createElement("div", null,
157
+ react_1.default.createElement("div", { className: "font-bold text-[#333333] mb-2" }, "Add Comment"),
158
+ react_1.default.createElement("textarea", { value: newMessage, onChange: (e) => setNewMessage(e.target.value), placeholder: "Type your comment here...", className: "w-full h-24 p-3 border border-[#CCCCCC] rounded-lg text-sm resize-none focus:outline-none focus:ring-2 focus:ring-[#248384]" })),
159
+ react_1.default.createElement("div", { className: "flex items-center justify-between" },
160
+ react_1.default.createElement("div", { className: "flex items-center space-x-4" }, ((_a = currentUser === null || currentUser === void 0 ? void 0 : currentUser.app_metadata) === null || _a === void 0 ? void 0 : _a.userTypeId) === 1 && (react_1.default.createElement(react_1.default.Fragment, null,
161
+ react_1.default.createElement("span", { className: "text-sm text-[#666666]" }, "Visible to:"),
162
+ react_1.default.createElement("div", { className: "flex items-center space-x-2" },
163
+ react_1.default.createElement("label", { className: "flex items-center space-x-2 cursor-pointer" },
164
+ react_1.default.createElement("input", { type: "radio", checked: visibility === 'EXPERT_INTERNAL_ONLY', onChange: () => setVisibility('EXPERT_INTERNAL_ONLY'), className: "form-radio text-[#248384]" }),
165
+ react_1.default.createElement("span", { className: "text-sm text-[#333333]" }, "Expert Only")),
166
+ react_1.default.createElement("label", { className: "flex items-center space-x-2 cursor-pointer" },
167
+ react_1.default.createElement("input", { type: "radio", checked: visibility === 'CLIENT_INTERNAL_ONLY', onChange: () => setVisibility('CLIENT_INTERNAL_ONLY'), className: "form-radio text-[#248384]" }),
168
+ react_1.default.createElement("span", { className: "text-sm text-[#333333]" }, "Client Only")),
169
+ react_1.default.createElement("label", { className: "flex items-center space-x-2 cursor-pointer" },
170
+ react_1.default.createElement("input", { type: "radio", checked: visibility === 'INTERNAL_ONLY', onChange: () => setVisibility('INTERNAL_ONLY'), className: "form-radio text-[#248384]" }),
171
+ react_1.default.createElement("span", { className: "text-sm text-[#333333]" }, "Internal Only")),
172
+ react_1.default.createElement("label", { className: "flex items-center space-x-2 cursor-pointer" },
173
+ react_1.default.createElement("input", { type: "radio", checked: visibility === 'ALL', onChange: () => setVisibility('ALL'), className: "form-radio text-[#248384]" }),
174
+ react_1.default.createElement("span", { className: "text-sm text-[#333333]" }, "All")))))),
175
+ react_1.default.createElement(base_ui_1.Button, { label: "Reply", color: "primary", isLoading: isLoading, onClick: handleSendMessage, disabled: !newMessage.trim() }))))));
176
+ };
177
+ exports.DiscussionSection = DiscussionSection;
@@ -0,0 +1,20 @@
1
+ interface DisputeProjectCardProps {
2
+ project: {
3
+ project: {
4
+ name: string;
5
+ };
6
+ disputeAmount: number;
7
+ disputeHours: number;
8
+ disputeType: string;
9
+ disputeReasonCode: string;
10
+ resolutionMode?: 'Direct' | 'Mediated' | 'Escalated';
11
+ resolutionType?: 'Upheld' | 'Reduced' | 'FutureCredit' | 'Canceled';
12
+ };
13
+ client: {
14
+ name: string;
15
+ };
16
+ disputeDate: string;
17
+ disputeUpdatedDate: string;
18
+ }
19
+ export declare const DisputeProjectCard: ({ project, client, disputeDate, disputeUpdatedDate }: DisputeProjectCardProps) => JSX.Element;
20
+ export {};
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.DisputeProjectCard = void 0;
7
+ const react_1 = __importDefault(require("react"));
8
+ const base_ui_1 = require("@paro.io/base-ui");
9
+ const dayjs_1 = __importDefault(require("dayjs"));
10
+ const DisputeProjectCard = ({ project, client, disputeDate, disputeUpdatedDate }) => {
11
+ var _a, _b;
12
+ const getResolutionModeColor = (mode) => {
13
+ switch (mode) {
14
+ case 'Direct':
15
+ return 'success';
16
+ case 'Mediated':
17
+ return 'warning';
18
+ case 'Escalated':
19
+ return 'danger';
20
+ default:
21
+ return 'default';
22
+ }
23
+ };
24
+ const getResolutionTypeColor = (type) => {
25
+ switch (type) {
26
+ case 'Upheld':
27
+ return 'success';
28
+ case 'Reduced':
29
+ return 'warning';
30
+ case 'FutureCredit':
31
+ return 'info';
32
+ case 'Canceled':
33
+ return 'danger';
34
+ default:
35
+ return 'default';
36
+ }
37
+ };
38
+ return (react_1.default.createElement("div", { className: "bg-white rounded-lg shadow-sm border border-[#E5E7EB] p-6 space-y-4" },
39
+ react_1.default.createElement("div", { className: "flex items-center justify-between" },
40
+ react_1.default.createElement("div", { className: "flex items-center space-x-2" },
41
+ project.resolutionMode && (react_1.default.createElement("div", null,
42
+ react_1.default.createElement("span", { className: "text-sm text-[#666666] font-bold" }, "Resolution Mode: "),
43
+ react_1.default.createElement(base_ui_1.Tag, { label: project.resolutionMode, variant: "subtle", color: getResolutionModeColor(project.resolutionMode) }))),
44
+ project.resolutionType && (react_1.default.createElement("div", null,
45
+ react_1.default.createElement("span", { className: "text-sm text-[#666666] font-bold" }, "Resolution Type: "),
46
+ react_1.default.createElement(base_ui_1.Tag, { label: project.resolutionType, variant: "subtle", color: getResolutionTypeColor(project.resolutionType) }))))),
47
+ react_1.default.createElement("div", { className: "grid grid-cols-2 gap-6" },
48
+ react_1.default.createElement("div", { className: "space-y-4" },
49
+ react_1.default.createElement("div", null,
50
+ react_1.default.createElement("span", { className: "text-sm text-[#666666] font-bold" }, "Client: "),
51
+ react_1.default.createElement("span", { className: "text-sm text-[#333333]" }, client === null || client === void 0 ? void 0 : client.name)),
52
+ react_1.default.createElement("div", null,
53
+ react_1.default.createElement("span", { className: "text-sm text-[#666666] font-bold" }, "Project: "),
54
+ react_1.default.createElement("span", { className: "text-sm text-[#333333]" }, (_a = project === null || project === void 0 ? void 0 : project.project) === null || _a === void 0 ? void 0 : _a.name)),
55
+ react_1.default.createElement("div", null,
56
+ react_1.default.createElement("span", { className: "text-sm text-[#666666] font-bold" }, "Type: "),
57
+ react_1.default.createElement("span", { className: "text-sm text-[#333333]" }, project === null || project === void 0 ? void 0 : project.disputeType)),
58
+ react_1.default.createElement("div", null,
59
+ react_1.default.createElement("span", { className: "text-sm text-[#666666] font-bold" }, "Hours Disputed: "),
60
+ react_1.default.createElement("span", { className: "text-sm text-[#333333]" }, project === null || project === void 0 ? void 0 :
61
+ project.disputeHours,
62
+ " hours"))),
63
+ react_1.default.createElement("div", { className: "space-y-4" },
64
+ react_1.default.createElement("div", null,
65
+ react_1.default.createElement("span", { className: "text-sm text-[#666666] font-bold" }, "Amount: "),
66
+ react_1.default.createElement("span", { className: "text-sm text-[#F44336] font-bold" },
67
+ "$", (_b = project === null || project === void 0 ? void 0 : project.disputeAmount) === null || _b === void 0 ? void 0 :
68
+ _b.toLocaleString())),
69
+ react_1.default.createElement("div", null,
70
+ react_1.default.createElement("span", { className: "text-sm text-[#666666] font-bold" }, "Submitted: "),
71
+ react_1.default.createElement("span", { className: "text-sm text-[#333333]" }, (0, dayjs_1.default)(disputeDate).format('MMM D, YYYY'))),
72
+ react_1.default.createElement("div", null,
73
+ react_1.default.createElement("span", { className: "text-sm text-[#666666] font-bold" }, "Last Updated: "),
74
+ react_1.default.createElement("span", { className: "text-sm text-[#333333]" }, (0, dayjs_1.default)(disputeUpdatedDate).format('MMM D, YYYY'))),
75
+ react_1.default.createElement("div", null,
76
+ react_1.default.createElement("span", { className: "text-sm text-[#666666] font-bold" }, "Days Left: "),
77
+ react_1.default.createElement("span", { className: "text-sm text-[#F44336] font-bold" }, "5 days left to resolve"))))));
78
+ };
79
+ exports.DisputeProjectCard = DisputeProjectCard;
@@ -0,0 +1,9 @@
1
+ interface DisputeSectionProps {
2
+ dispute: any;
3
+ onUpdateDispute: (variables: any) => Promise<any>;
4
+ onRefetch: () => Promise<void>;
5
+ documentUploadUrl: string;
6
+ uploadExpertClientDisputeFiles: (variables: any) => Promise<any>;
7
+ }
8
+ export declare const DisputeSection: ({ dispute, onUpdateDispute, onRefetch, documentUploadUrl, uploadExpertClientDisputeFiles, }: DisputeSectionProps) => JSX.Element;
9
+ export {};
@@ -0,0 +1,126 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
26
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
27
+ return new (P || (P = Promise))(function (resolve, reject) {
28
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
29
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
30
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
31
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
32
+ });
33
+ };
34
+ Object.defineProperty(exports, "__esModule", { value: true });
35
+ exports.DisputeSection = void 0;
36
+ const react_1 = __importStar(require("react"));
37
+ const base_ui_1 = require("@paro.io/base-ui");
38
+ const base_icons_1 = require("@paro.io/base-icons");
39
+ const FileUploader_1 = require("../FileUploader");
40
+ const ACCEPTED_FILE_TYPES = [
41
+ 'application/pdf',
42
+ 'application/msword',
43
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
44
+ 'image/jpeg',
45
+ 'image/png',
46
+ 'text/csv',
47
+ ];
48
+ const DisputeSection = ({ dispute, onUpdateDispute, onRefetch, documentUploadUrl, uploadExpertClientDisputeFiles, }) => {
49
+ var _a, _b, _c;
50
+ const [files, setFiles] = (0, react_1.useState)((dispute === null || dispute === void 0 ? void 0 : dispute.clientDocumentLinks) || []);
51
+ const fileInputRef = (0, react_1.useRef)(null);
52
+ const validateFileUpload = (file) => {
53
+ return ACCEPTED_FILE_TYPES.includes(file.type);
54
+ };
55
+ const handleUpload = (event) => {
56
+ const selectedFiles = event.target.files;
57
+ if (!selectedFiles)
58
+ return;
59
+ const validFiles = Array.from(selectedFiles)
60
+ .filter(file => validateFileUpload(file))
61
+ .map(file => file.name);
62
+ const getFileNames = selectedFiles && Array.from(selectedFiles).map((file) => {
63
+ if (!validateFileUpload(file)) {
64
+ return null;
65
+ }
66
+ if (file) {
67
+ return file.name;
68
+ }
69
+ }).filter((name) => name !== null);
70
+ if (getFileNames && getFileNames.length > 0) {
71
+ Array.from(selectedFiles).forEach((selectedFile) => {
72
+ const reader = new FileReader();
73
+ reader.onloadend = () => __awaiter(void 0, void 0, void 0, function* () {
74
+ var _a, _b;
75
+ yield (0, FileUploader_1.fileUploader)({
76
+ file: selectedFile,
77
+ documentName: selectedFile.name,
78
+ projectId: Number((_b = (_a = dispute === null || dispute === void 0 ? void 0 : dispute.disputeProjects) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.projectId),
79
+ documentUploadUrl: documentUploadUrl,
80
+ uploadExpertClientDisputeFilesData: uploadExpertClientDisputeFiles,
81
+ });
82
+ });
83
+ reader.readAsDataURL(selectedFile);
84
+ });
85
+ setFiles(prev => [...prev, ...validFiles]);
86
+ }
87
+ if (fileInputRef.current) {
88
+ fileInputRef.current.value = '';
89
+ }
90
+ };
91
+ const handleRemoveFile = (fileName) => {
92
+ setFiles(prev => prev.filter(f => f !== fileName));
93
+ };
94
+ const disputeProject = (_a = dispute === null || dispute === void 0 ? void 0 : dispute.disputeProjects) === null || _a === void 0 ? void 0 : _a[0];
95
+ return (react_1.default.createElement("div", { className: "grid grid-cols-2 gap-6" },
96
+ react_1.default.createElement("div", { className: "space-y-4" },
97
+ react_1.default.createElement("div", { className: "flex items-center justify-between" },
98
+ react_1.default.createElement("div", { className: "text-lg font-bold text-[#333333]" }, "Client Dispute")),
99
+ react_1.default.createElement("div", { className: "p-6 space-y-4 rounded", style: { backgroundColor: '#D9EFEF', minHeight: '230px' } },
100
+ react_1.default.createElement("div", { className: "py-1 text-[#195C5C] font-bold inline-block mb-2" },
101
+ "Reason: ", disputeProject === null || disputeProject === void 0 ? void 0 :
102
+ disputeProject.disputeReasonCode),
103
+ react_1.default.createElement("div", null,
104
+ react_1.default.createElement("div", { className: "text-sm font-bold text-[#333333] mb-2" }, "Client Explanation:"),
105
+ react_1.default.createElement("p", { className: "text-sm text-[#333333]" }, disputeProject === null || disputeProject === void 0 ? void 0 : disputeProject.clientExplanation)),
106
+ react_1.default.createElement("div", null,
107
+ react_1.default.createElement("div", { className: "text-sm font-bold text-[#333333] mb-2" }, "Supporting Documents:"),
108
+ react_1.default.createElement("div", { className: "flex flex-wrap gap-2" },
109
+ react_1.default.createElement("input", { ref: fileInputRef, type: "file", multiple: true, accept: ACCEPTED_FILE_TYPES.join(','), className: "hidden", onChange: handleUpload }),
110
+ react_1.default.createElement(base_ui_1.Button, { label: "Add File", iconLeft: react_1.default.createElement(base_icons_1.IconPlus, { size: "sm" }), onClick: () => { var _a; return (_a = fileInputRef.current) === null || _a === void 0 ? void 0 : _a.click(); }, size: "sm", color: "info", className: "bg-white border border-[#248384] text-[#248384]" }), (_b = disputeProject === null || disputeProject === void 0 ? void 0 : disputeProject.clientDocumentLinks) === null || _b === void 0 ? void 0 :
111
+ _b.map((file, index) => (react_1.default.createElement(base_ui_1.Tag, { key: index, label: file, variant: "subtle", color: "success", iconRight: react_1.default.createElement(base_icons_1.IconXCircle, null), onClick: () => handleRemoveFile(file) }))))))),
112
+ react_1.default.createElement("div", { className: "space-y-4" },
113
+ react_1.default.createElement("div", { className: "flex items-center justify-between" },
114
+ react_1.default.createElement("div", { className: "text-lg font-bold text-[#333333]" }, "Expert Rebuttal")),
115
+ react_1.default.createElement("div", { className: "p-6 space-y-4 rounded", style: { backgroundColor: '#FDD7A5', minHeight: '230px' } },
116
+ react_1.default.createElement("div", null,
117
+ react_1.default.createElement("div", { className: "text-sm font-bold text-[#333333] mb-2" }, "Expert Explanation:"),
118
+ react_1.default.createElement("p", { className: "text-sm text-[#333333]" }, disputeProject === null || disputeProject === void 0 ? void 0 : disputeProject.expertExplanation)),
119
+ react_1.default.createElement("div", null,
120
+ react_1.default.createElement("div", { className: "text-sm font-bold text-[#333333] mb-2" }, "Supporting Documents:"),
121
+ react_1.default.createElement("div", { className: "flex flex-wrap gap-2" },
122
+ react_1.default.createElement("input", { ref: fileInputRef, type: "file", multiple: true, accept: ACCEPTED_FILE_TYPES.join(','), className: "hidden", onChange: handleUpload }),
123
+ react_1.default.createElement(base_ui_1.Button, { label: "Add File", iconLeft: react_1.default.createElement(base_icons_1.IconPlus, { size: "sm" }), onClick: () => { var _a; return (_a = fileInputRef.current) === null || _a === void 0 ? void 0 : _a.click(); }, size: "sm", color: "info", className: "bg-white border border-[#248384] text-[#248384]" }), (_c = disputeProject === null || disputeProject === void 0 ? void 0 : disputeProject.expertDocumentLinks) === null || _c === void 0 ? void 0 :
124
+ _c.map((file, index) => (react_1.default.createElement(base_ui_1.Tag, { key: index, label: file, variant: "subtle", color: "success", iconRight: react_1.default.createElement(base_icons_1.IconXCircle, null), onClick: () => handleRemoveFile(file) })))))))));
125
+ };
126
+ exports.DisputeSection = DisputeSection;
@@ -4,6 +4,9 @@ interface InvoiceCardProps {
4
4
  user: any;
5
5
  chatMessages: any[];
6
6
  updateClientInvoiceMutation: any;
7
+ refetchInvoiceDisputes: any;
8
+ documentUploadUrl: string;
9
+ uploadExpertClientDisputeFiles: any;
7
10
  }
8
- export declare const InvoiceCard: ({ clientInvoice, createDisputeChatMessage, user, chatMessages, updateClientInvoiceMutation }: InvoiceCardProps) => JSX.Element;
11
+ export declare const InvoiceCard: ({ clientInvoice, createDisputeChatMessage, user, chatMessages, updateClientInvoiceMutation, refetchInvoiceDisputes, documentUploadUrl, uploadExpertClientDisputeFiles, }: InvoiceCardProps) => JSX.Element;
9
12
  export {};
@@ -1,263 +1,25 @@
1
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
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
35
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
36
4
  };
37
5
  Object.defineProperty(exports, "__esModule", { value: true });
38
6
  exports.InvoiceCard = void 0;
39
- const react_1 = __importStar(require("react"));
7
+ const react_1 = __importDefault(require("react"));
40
8
  const base_ui_1 = require("@paro.io/base-ui");
41
- const base_ui_2 = require("@paro.io/base-ui");
42
- const core_1 = require("@material-ui/core");
43
- const base_icons_1 = require("@paro.io/base-icons");
44
- const DiscussionThread_1 = require("../DiscussionThread");
45
- const dayjs_1 = __importDefault(require("dayjs"));
46
- const utils_1 = require("../shared/utils");
47
- const useStyles = (0, core_1.makeStyles)((theme) => ({
48
- root: {
49
- width: "100%",
50
- },
51
- paper: {
52
- width: "100%",
53
- },
54
- table: {
55
- minWidth: '95%',
56
- border: "1px solid",
57
- borderColor: "#E2E8F0",
58
- },
59
- visuallyHidden: {
60
- border: 0,
61
- clip: "rect(0 0 0 0)",
62
- height: 1,
63
- margin: -1,
64
- overflow: "hidden",
65
- padding: 0,
66
- position: "absolute",
67
- top: 20,
68
- width: 1,
69
- },
70
- tableCell: {
71
- width: "auto",
72
- },
73
- collapse: {
74
- backgroundColor: "#FFFFFF",
75
- width: "100%",
76
- borderRadius: 6,
77
- marginLeft: 12,
78
- marginTop: 12,
79
- },
80
- tableHead: {
81
- backgroundColor: "#F1F5F9",
82
- borderRadius: 6,
83
- width: "100%",
84
- },
85
- }));
86
- const headCells = [
87
- { id: 'project', label: 'Project' },
88
- { id: 'projectType', label: 'Project Type' },
89
- { id: 'hours', label: 'Hours' },
90
- { id: 'amount', label: 'Amount' },
91
- { id: 'reason', label: 'Reason' },
92
- ];
93
- const ACCEPTED_FILE_TYPES = [
94
- 'application/pdf',
95
- 'application/msword',
96
- 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
97
- 'image/jpeg',
98
- 'image/png',
99
- 'text/csv',
100
- ];
101
- const validateFileUpload = (file) => {
102
- if (!file) {
103
- return false;
104
- }
105
- if (!ACCEPTED_FILE_TYPES.includes(file.type)) {
106
- return false;
107
- }
108
- return true;
109
- };
110
- const InvoiceCard = ({ clientInvoice, createDisputeChatMessage, user, chatMessages, updateClientInvoiceMutation }) => {
111
- var _a, _b, _c, _d, _e, _f;
112
- const [expandRow, setExpandRow] = (0, react_1.useState)(null);
113
- const classes = useStyles();
114
- const [projects, setProjects] = (0, react_1.useState)(clientInvoice === null || clientInvoice === void 0 ? void 0 : clientInvoice.disputeProjects);
115
- const [editingRowId, setEditingRowId] = (0, react_1.useState)(null);
116
- const [editedExplanation, setEditedExplanation] = (0, react_1.useState)('');
117
- const [updatedDispute, setUpdateDispute] = (0, react_1.useState)(false);
118
- const [updatingDispute, setUpdatingDispute] = (0, react_1.useState)(false);
119
- (0, react_1.useEffect)(() => {
120
- if (JSON.stringify(clientInvoice === null || clientInvoice === void 0 ? void 0 : clientInvoice.disputeProjects) !== JSON.stringify(projects)) {
121
- setUpdateDispute(true);
122
- }
123
- }, [projects]);
124
- const fileInputRef = (0, react_1.useRef)(null);
125
- const handleChange = (e, field) => {
126
- const updatedProject = field === "clientDocumentLinks" ? projects.map((project) => {
127
- if (project.projectId === expandRow) {
128
- return Object.assign(Object.assign({}, project), { clientDocumentLinks: [...project.clientDocumentLinks, ...e.filter((name) => !project.clientDocumentLinks.includes(name))] });
129
- }
130
- return project;
131
- }) : projects.map((project) => {
132
- return Object.assign(Object.assign({}, project), { [field]: e.target.value });
133
- });
134
- //upload files mutation
135
- setProjects(updatedProject);
136
- };
137
- const handleUpload = (event) => {
138
- const selectedFiles = event.target.files;
139
- if (!selectedFiles)
140
- return;
141
- const validFiles = Array.from(selectedFiles)
142
- .filter(file => validateFileUpload(file))
143
- .map(file => file.name);
144
- if (validFiles.length === 0)
145
- return;
146
- handleChange(validFiles, "clientDocumentLinks");
147
- if (fileInputRef.current) {
148
- fileInputRef.current.value = '';
149
- }
150
- };
151
- const handleEditExplanation = (projectId) => __awaiter(void 0, void 0, void 0, function* () {
152
- setProjects((prev) => prev.map((p) => p.projectId === projectId
153
- ? Object.assign(Object.assign({}, p), { clientExplanation: editedExplanation }) : p));
154
- setEditingRowId(null);
155
- });
156
- const handleSubmitDispute = () => __awaiter(void 0, void 0, void 0, function* () {
157
- setUpdatingDispute(true);
158
- try {
159
- yield updateClientInvoiceMutation({
160
- variables: {
161
- input: {
162
- disputeId: projects[0].id,
163
- projectDisputes: projects.map((project) => ({
164
- projectId: project.projectId,
165
- disputeReasonCode: project.disputeReasonCode,
166
- disputeType: project.disputeType,
167
- disputeHours: project.disputeHours,
168
- disputeAmount: project.disputeAmount,
169
- clientExplanation: project.clientExplanation,
170
- clientDocumentLinks: project.clientDocumentLinks,
171
- })),
172
- }
173
- }
174
- }).then(() => {
175
- (0, utils_1.showToast)('success', 'Dispute updation successfull!');
176
- });
177
- }
178
- catch (error) {
179
- console.error("Failed to update dispute", error);
180
- (0, utils_1.showToast)('warning', 'Failed to update dispute');
181
- }
182
- finally {
183
- setUpdatingDispute(false);
184
- setUpdateDispute(false);
185
- }
186
- });
187
- return (react_1.default.createElement(base_ui_1.Card, { className: `mx-1 my-4 md:mx-8 flex md:flex items-center justify-between rounded flex-col` },
188
- react_1.default.createElement("div", { key: (_a = clientInvoice === null || clientInvoice === void 0 ? void 0 : clientInvoice.invoice) === null || _a === void 0 ? void 0 : _a.id, className: "bg-[#0F172A] text-white mb-1 p-4 relative top-0 left-0 w-full rounded flex flex-row justify-between" },
189
- react_1.default.createElement("b", null,
190
- "Dispute Invoice #", (_b = clientInvoice === null || clientInvoice === void 0 ? void 0 : clientInvoice.invoice) === null || _b === void 0 ? void 0 :
191
- _b.id),
192
- react_1.default.createElement("p", null,
193
- "Dispute Date: ", clientInvoice === null || clientInvoice === void 0 ? void 0 :
194
- clientInvoice.disputeDate),
195
- react_1.default.createElement("p", null,
196
- "Invoice Date: ", (_c = clientInvoice === null || clientInvoice === void 0 ? void 0 : clientInvoice.invoice) === null || _c === void 0 ? void 0 :
197
- _c.dateGenerated),
198
- react_1.default.createElement("p", null,
199
- "Expert Name: ",
200
- ((_d = clientInvoice === null || clientInvoice === void 0 ? void 0 : clientInvoice.freelancer) === null || _d === void 0 ? void 0 : _d.firstName) + " " + ((_e = clientInvoice === null || clientInvoice === void 0 ? void 0 : clientInvoice.freelancer) === null || _e === void 0 ? void 0 : _e.lastName))),
201
- react_1.default.createElement("div", { className: "w-[95%] m-2" },
202
- react_1.default.createElement("div", { className: "flex flex-row justify-start items-center gap-4 mr-auto mb-2" },
203
- react_1.default.createElement("div", null,
204
- (clientInvoice === null || clientInvoice === void 0 ? void 0 : clientInvoice.status) === 'InProgress' &&
205
- react_1.default.createElement(base_ui_1.Tag, { color: "warning", label: "In Progress", variant: "subtle" }),
206
- (clientInvoice === null || clientInvoice === void 0 ? void 0 : clientInvoice.status) === 'Resolved' &&
207
- react_1.default.createElement(base_ui_1.Tag, { color: "success", label: "Resolved", variant: "subtle" }),
208
- (clientInvoice === null || clientInvoice === void 0 ? void 0 : clientInvoice.status) === 'UnderReview' &&
209
- react_1.default.createElement(base_ui_1.Tag, { color: "info", label: "Under Review", variant: "subtle" })),
210
- react_1.default.createElement("div", { className: "flex items-center" },
211
- "Last updated: ",
212
- (0, dayjs_1.default)(clientInvoice === null || clientInvoice === void 0 ? void 0 : clientInvoice.disputeUpdatedDate).format("YYYY-MM-DD"))),
213
- react_1.default.createElement(core_1.Table, { className: classes.table, "aria-labelledby": "tableTitle", size: 'medium', "aria-label": "enhanced table" },
214
- react_1.default.createElement(core_1.TableHead, { className: classes.tableHead },
215
- react_1.default.createElement(core_1.TableRow, null,
216
- headCells.map((headcell) => {
217
- return react_1.default.createElement(core_1.TableCell, { key: headcell.id, className: classes.tableCell }, headcell.label);
218
- }),
219
- react_1.default.createElement(core_1.TableCell, { className: classes.tableCell }))), projects === null || projects === void 0 ? void 0 :
220
- projects.map((row) => {
221
- var _a;
222
- return (react_1.default.createElement(react_1.default.Fragment, { key: row.projectId },
223
- react_1.default.createElement(core_1.TableRow, null,
224
- react_1.default.createElement(core_1.TableCell, { align: "left", className: classes.tableCell }, ((_a = row.project) === null || _a === void 0 ? void 0 : _a.name) || "N/A"),
225
- react_1.default.createElement(core_1.TableCell, { align: "left", className: classes.tableCell }, row.disputeType),
226
- react_1.default.createElement(core_1.TableCell, { align: "left", className: classes.tableCell }, row.disputeHours),
227
- react_1.default.createElement(core_1.TableCell, { align: "left", className: classes.tableCell }, row.disputeAmount),
228
- react_1.default.createElement(core_1.TableCell, { align: "left", className: classes.tableCell }, row.disputeReasonCode),
229
- react_1.default.createElement(core_1.TableCell, { align: "left", className: classes.tableCell },
230
- react_1.default.createElement(core_1.IconButton, { onClick: () => setExpandRow(expandRow === row.projectId ? null : row.projectId) }, expandRow === row.projectId ? react_1.default.createElement(base_icons_1.IconChevronUp, null) : react_1.default.createElement(base_icons_1.IconChevronDown, null)))),
231
- expandRow === row.projectId && (react_1.default.createElement(core_1.TableRow, { key: `expanded-${row.projectId}` },
232
- react_1.default.createElement(core_1.TableCell, { colSpan: headCells.length + 1, style: { paddingBottom: 0, paddingTop: 0 } },
233
- react_1.default.createElement(core_1.Collapse, { in: true, timeout: "auto", unmountOnExit: true },
234
- react_1.default.createElement(react_1.default.Fragment, null,
235
- react_1.default.createElement(base_ui_1.Card, { className: "bg-[#F8F9FA] m-2 flex items-start justify-between rounded flex-col w-full p-6" },
236
- react_1.default.createElement("b", { className: "mb-2 flex flex-row justify-start items-center" },
237
- "Explanation:",
238
- editingRowId === row.projectId ? (react_1.default.createElement(react_1.default.Fragment, null,
239
- react_1.default.createElement(base_ui_2.Input, { label: "", type: "text", className: "w-96 mx-4 overflow-ellipsis", value: editedExplanation, onChange: (e) => setEditedExplanation(e.target.value), placeholder: "Please provide an explanation", isInvalid: !editedExplanation, isInvalidText: "Please provide an explanation" }),
240
- react_1.default.createElement(base_ui_2.Button, { label: "Save", size: "md", color: "success", className: "ml-2 mt-1", onClick: () => { handleEditExplanation(row.projectId); } }))) : (react_1.default.createElement(base_ui_2.Button, { label: "Edit", iconLeft: react_1.default.createElement(base_icons_1.IconPencil, { size: "sm" }), onClick: () => {
241
- setEditedExplanation(row.clientExplanation);
242
- setEditingRowId(row.projectId);
243
- }, size: "sm", color: "info", className: "ml-6" }))),
244
- react_1.default.createElement("p", null, row.clientExplanation),
245
- react_1.default.createElement("b", { className: "flex flex-row flex-wrap justify-start items-center mt-2" },
246
- "Supporting Documents:",
247
- react_1.default.createElement("input", { id: "upload-file", type: "file", multiple: true, accept: ".pdf,.doc,.docx,.jpeg,.png,.gif,.csv", style: { display: 'none' }, ref: fileInputRef, onChange: handleUpload }),
248
- react_1.default.createElement(base_ui_2.Button, { label: "Add File", iconLeft: react_1.default.createElement(base_icons_1.IconPlus, { size: "sm" }), onClick: () => { var _a; return (_a = fileInputRef.current) === null || _a === void 0 ? void 0 : _a.click(); }, size: "sm", color: "info", className: "mx-2" }),
249
- (Array.isArray(row.clientDocumentLinks)
250
- ? row.clientDocumentLinks
251
- : []).map((f, index) => (react_1.default.createElement("div", { key: index, className: "mx-2" },
252
- react_1.default.createElement(base_ui_1.Tag, { variant: "subtle", color: "success", label: f, iconRight: react_1.default.createElement(base_icons_1.IconXCircle, null), onClick: () => {
253
- const updatedProjects = projects.map((p) => p.projectId === row.projectId
254
- ? Object.assign(Object.assign({}, p), { clientDocumentLinks: p.clientDocumentLinks.filter((file) => file !== f) }) : p);
255
- setProjects(updatedProjects);
256
- } })))))))))))));
257
- })),
258
- updatedDispute && react_1.default.createElement("div", { className: "flex justify-end ml-auto mt-2" },
259
- react_1.default.createElement(base_ui_2.Button, { label: "Update Dispute", color: "primary", isLoading: updatingDispute, onClick: () => handleSubmitDispute() })),
260
- (clientInvoice === null || clientInvoice === void 0 ? void 0 : clientInvoice.chatEnabled) && react_1.default.createElement("div", { className: "flex flex-row w-full my-2 justify-start" },
261
- react_1.default.createElement(DiscussionThread_1.DiscussionThread, { disputeId: (_f = projects[0]) === null || _f === void 0 ? void 0 : _f.id, currentUser: user, initialThreads: chatMessages, createDisputeChatMessage: createDisputeChatMessage })))));
9
+ const InvoiceHeader_1 = require("./InvoiceHeader");
10
+ const InvoiceDetails_1 = require("./InvoiceDetails");
11
+ const DisputeSection_1 = require("./DisputeSection");
12
+ const DiscussionSection_1 = require("./DiscussionSection");
13
+ const DecisionSection_1 = require("./DecisionSection");
14
+ const InvoiceCard = ({ clientInvoice, createDisputeChatMessage, user, chatMessages, updateClientInvoiceMutation, refetchInvoiceDisputes, documentUploadUrl, uploadExpertClientDisputeFiles, }) => {
15
+ var _a, _b, _c;
16
+ return (react_1.default.createElement(base_ui_1.Card, { className: "w-full bg-white rounded-lg shadow-sm overflow-hidden" },
17
+ react_1.default.createElement(InvoiceHeader_1.InvoiceHeader, { invoice: clientInvoice }),
18
+ react_1.default.createElement("div", { className: "p-6 space-y-6" },
19
+ react_1.default.createElement(InvoiceDetails_1.InvoiceDetails, { invoice: clientInvoice }),
20
+ react_1.default.createElement(DisputeSection_1.DisputeSection, { dispute: clientInvoice, onUpdateDispute: updateClientInvoiceMutation, onRefetch: refetchInvoiceDisputes, documentUploadUrl: documentUploadUrl, uploadExpertClientDisputeFiles: uploadExpertClientDisputeFiles }),
21
+ (clientInvoice === null || clientInvoice === void 0 ? void 0 : clientInvoice.chatEnabled) && (react_1.default.createElement(DiscussionSection_1.DiscussionSection, { disputeId: (_b = (_a = clientInvoice === null || clientInvoice === void 0 ? void 0 : clientInvoice.disputeProjects) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.id, currentUser: user, messages: chatMessages, onCreateMessage: createDisputeChatMessage })),
22
+ ((_c = user === null || user === void 0 ? void 0 : user.app_metadata) === null || _c === void 0 ? void 0 : _c.userTypeId) === 2 &&
23
+ react_1.default.createElement(DecisionSection_1.DecisionSection, { dispute: clientInvoice, onUpdateDispute: updateClientInvoiceMutation, onRefetch: refetchInvoiceDisputes }))));
262
24
  };
263
25
  exports.InvoiceCard = InvoiceCard;
@@ -0,0 +1,5 @@
1
+ interface InvoiceDetailsProps {
2
+ invoice: any;
3
+ }
4
+ export declare const InvoiceDetails: ({ invoice }: InvoiceDetailsProps) => JSX.Element;
5
+ export {};
@@ -0,0 +1,34 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.InvoiceDetails = void 0;
7
+ const react_1 = __importDefault(require("react"));
8
+ const base_ui_1 = require("@paro.io/base-ui");
9
+ const dayjs_1 = __importDefault(require("dayjs"));
10
+ const DisputeProjectCard_1 = require("./DisputeProjectCard");
11
+ const InvoiceDetails = ({ invoice }) => {
12
+ var _a;
13
+ const getStatusColor = (status) => {
14
+ switch (status) {
15
+ case 'InProgress':
16
+ return 'warning';
17
+ case 'UnderReview':
18
+ return 'info';
19
+ case 'Resolved':
20
+ return 'success';
21
+ default:
22
+ return 'default';
23
+ }
24
+ };
25
+ return (react_1.default.createElement("div", { className: "space-y-6" },
26
+ react_1.default.createElement("div", { className: "flex items-center space-x-4" },
27
+ react_1.default.createElement(base_ui_1.Tag, { label: invoice === null || invoice === void 0 ? void 0 : invoice.status, variant: "subtle", color: getStatusColor(invoice === null || invoice === void 0 ? void 0 : invoice.status) }),
28
+ react_1.default.createElement("span", { className: "text-xs text-[#666666]" },
29
+ "Last updated: ",
30
+ (0, dayjs_1.default)(invoice === null || invoice === void 0 ? void 0 : invoice.disputeUpdatedDate).format('MM-DD-YYYY'))),
31
+ ((_a = invoice === null || invoice === void 0 ? void 0 : invoice.disputeProjects) === null || _a === void 0 ? void 0 : _a.length) > 0 && (react_1.default.createElement("div", { className: "space-y-4" },
32
+ react_1.default.createElement("div", { className: "grid grid-cols-1 gap-4" }, invoice.disputeProjects.map((project) => (react_1.default.createElement(DisputeProjectCard_1.DisputeProjectCard, { key: project.id, project: project, client: invoice === null || invoice === void 0 ? void 0 : invoice.client, disputeDate: invoice === null || invoice === void 0 ? void 0 : invoice.disputeDate, disputeUpdatedDate: invoice === null || invoice === void 0 ? void 0 : invoice.disputeUpdatedDate }))))))));
33
+ };
34
+ exports.InvoiceDetails = InvoiceDetails;
@@ -0,0 +1,5 @@
1
+ interface InvoiceHeaderProps {
2
+ invoice: any;
3
+ }
4
+ export declare const InvoiceHeader: ({ invoice }: InvoiceHeaderProps) => JSX.Element;
5
+ export {};
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.InvoiceHeader = void 0;
7
+ const react_1 = __importDefault(require("react"));
8
+ const dayjs_1 = __importDefault(require("dayjs"));
9
+ const InvoiceHeader = ({ invoice }) => {
10
+ var _a, _b, _c, _d;
11
+ return (react_1.default.createElement("div", { className: "bg-[#0F172A] text-white px-6 py-4 rounded-t-lg flex items-center justify-between font-arial" },
12
+ react_1.default.createElement("div", { className: "flex items-center space-x-8" },
13
+ react_1.default.createElement("div", null,
14
+ react_1.default.createElement("span", { className: "text-sm font-bold" }, "Dispute Invoice #"),
15
+ react_1.default.createElement("span", { className: "ml-2" }, (_a = invoice === null || invoice === void 0 ? void 0 : invoice.invoice) === null || _a === void 0 ? void 0 : _a.id)),
16
+ react_1.default.createElement("div", null,
17
+ react_1.default.createElement("span", { className: "text-sm font-bold" }, "Dispute Date: "),
18
+ react_1.default.createElement("span", { className: "ml-2" }, (0, dayjs_1.default)(invoice === null || invoice === void 0 ? void 0 : invoice.disputeDate).format('MM-DD-YYYY'))),
19
+ react_1.default.createElement("div", null,
20
+ react_1.default.createElement("span", { className: "text-sm font-bold" }, "Invoice Date: "),
21
+ react_1.default.createElement("span", { className: "ml-2" }, (0, dayjs_1.default)((_b = invoice === null || invoice === void 0 ? void 0 : invoice.invoice) === null || _b === void 0 ? void 0 : _b.dateGenerated).format('MM-DD-YYYY')))),
22
+ react_1.default.createElement("div", null,
23
+ react_1.default.createElement("span", { className: "text-sm font-bold" }, "Expert Name: "),
24
+ react_1.default.createElement("span", { className: "ml-2" }, (_c = invoice === null || invoice === void 0 ? void 0 : invoice.freelancer) === null || _c === void 0 ? void 0 :
25
+ _c.firstName,
26
+ " ", (_d = invoice === null || invoice === void 0 ? void 0 : invoice.freelancer) === null || _d === void 0 ? void 0 :
27
+ _d.lastName))));
28
+ };
29
+ exports.InvoiceHeader = InvoiceHeader;
package/lib/index.d.ts CHANGED
@@ -12,3 +12,4 @@ export { sharedUtils } from './components/shared/utils';
12
12
  export { ActiveProjectCard } from './components/ProjectCard/ActiveProjectCard';
13
13
  export { InvoiceCard } from './components/Invoices/InvoiceCard';
14
14
  export { DiscussionThread } from './components/DiscussionThread';
15
+ export { fileUploader } from './components/FileUploader';
package/lib/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DiscussionThread = exports.InvoiceCard = exports.ActiveProjectCard = exports.sharedUtils = exports.ServiceLinesTemplate = exports.HeaderNavBar = exports.DocumentCenter = exports.ProfileCompletedPercentage = exports.ExpertProfileHeader = exports.OrganizationChart = exports.FirmEmployeeSection = exports.ClientReferenceSection = exports.Reviews = exports.ReviewsTab = void 0;
3
+ exports.fileUploader = exports.DiscussionThread = exports.InvoiceCard = exports.ActiveProjectCard = exports.sharedUtils = exports.ServiceLinesTemplate = exports.HeaderNavBar = exports.DocumentCenter = exports.ProfileCompletedPercentage = exports.ExpertProfileHeader = exports.OrganizationChart = exports.FirmEmployeeSection = exports.ClientReferenceSection = exports.Reviews = exports.ReviewsTab = void 0;
4
4
  var ReviewsTab_1 = require("./components/ReviewsTab");
5
5
  Object.defineProperty(exports, "ReviewsTab", { enumerable: true, get: function () { return ReviewsTab_1.ReviewsTab; } });
6
6
  var Reviews_1 = require("./components/Reviews");
@@ -29,3 +29,5 @@ var InvoiceCard_1 = require("./components/Invoices/InvoiceCard");
29
29
  Object.defineProperty(exports, "InvoiceCard", { enumerable: true, get: function () { return InvoiceCard_1.InvoiceCard; } });
30
30
  var DiscussionThread_1 = require("./components/DiscussionThread");
31
31
  Object.defineProperty(exports, "DiscussionThread", { enumerable: true, get: function () { return DiscussionThread_1.DiscussionThread; } });
32
+ var FileUploader_1 = require("./components/FileUploader");
33
+ Object.defineProperty(exports, "fileUploader", { enumerable: true, get: function () { return FileUploader_1.fileUploader; } });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@paro.io/expert-shared-components",
3
- "version": "1.11.3",
3
+ "version": "1.11.5",
4
4
  "description": "",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {