@paro.io/expert-shared-components 1.14.65 → 1.14.66
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.
|
@@ -7,5 +7,6 @@ export interface DocumentReviewModalProps {
|
|
|
7
7
|
fileName: string;
|
|
8
8
|
jobId: string;
|
|
9
9
|
onClose: () => void;
|
|
10
|
+
parsedData?: Record<string, unknown> | null;
|
|
10
11
|
}
|
|
11
|
-
export declare function DocumentReviewModal({ documentId, documentName, fileName, jobId: _jobId, onClose, }: DocumentReviewModalProps): React.JSX.Element;
|
|
12
|
+
export declare function DocumentReviewModal({ documentId, documentName, fileName, jobId: _jobId, onClose, parsedData, }: DocumentReviewModalProps): React.JSX.Element;
|
|
@@ -143,23 +143,59 @@ function getStubSections(documentId) {
|
|
|
143
143
|
},
|
|
144
144
|
];
|
|
145
145
|
}
|
|
146
|
+
function humanizeFieldKey(key) {
|
|
147
|
+
return key.replace(/_/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase());
|
|
148
|
+
}
|
|
149
|
+
function formatFieldValue(value) {
|
|
150
|
+
if (typeof value === 'number') {
|
|
151
|
+
return Math.abs(value) >= 1
|
|
152
|
+
? `$${value.toLocaleString('en-US', { maximumFractionDigits: 0 })}`
|
|
153
|
+
: String(value);
|
|
154
|
+
}
|
|
155
|
+
return String(value !== null && value !== void 0 ? value : '');
|
|
156
|
+
}
|
|
157
|
+
function parsedDataToSections(data) {
|
|
158
|
+
const fields = data.fields;
|
|
159
|
+
if (!fields || typeof fields !== 'object')
|
|
160
|
+
return [];
|
|
161
|
+
const entries = Object.entries(fields)
|
|
162
|
+
.filter(([key]) => !key.startsWith('_'));
|
|
163
|
+
if (entries.length === 0)
|
|
164
|
+
return [];
|
|
165
|
+
return [{
|
|
166
|
+
head: humanizeFieldKey(String(data.documentType || 'Extracted Fields')),
|
|
167
|
+
fields: entries.map(([key, value]) => ({
|
|
168
|
+
key,
|
|
169
|
+
label: humanizeFieldKey(key),
|
|
170
|
+
value: formatFieldValue(value),
|
|
171
|
+
sourceRef: 'Extracted',
|
|
172
|
+
confidence: 0.92,
|
|
173
|
+
})),
|
|
174
|
+
}];
|
|
175
|
+
}
|
|
146
176
|
function getTotalFieldCount(sections) {
|
|
147
177
|
return sections.reduce((sum, s) => sum + s.fields.length, 0);
|
|
148
178
|
}
|
|
149
179
|
// ── Component ──
|
|
150
|
-
function DocumentReviewModal({ documentId, documentName, fileName, jobId: _jobId, onClose, }) {
|
|
180
|
+
function DocumentReviewModal({ documentId, documentName, fileName, jobId: _jobId, onClose, parsedData, }) {
|
|
151
181
|
const [loading, setLoading] = (0, react_1.useState)(true);
|
|
152
182
|
const [sections, setSections] = (0, react_1.useState)([]);
|
|
153
183
|
const [saveStatus, setSaveStatus] = (0, react_1.useState)("idle");
|
|
154
184
|
const debounceTimers = (0, react_1.useRef)({});
|
|
155
|
-
//
|
|
185
|
+
// Load sections from real parsedData when available, fall back to stubs
|
|
156
186
|
(0, react_1.useEffect)(() => {
|
|
187
|
+
if (parsedData && typeof parsedData === 'object' && parsedData.fields) {
|
|
188
|
+
setSections(parsedDataToSections(parsedData));
|
|
189
|
+
setLoading(false);
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
// Fallback: stub data with simulated delay
|
|
157
193
|
const t = setTimeout(() => {
|
|
158
194
|
setSections(getStubSections(documentId));
|
|
159
195
|
setLoading(false);
|
|
160
196
|
}, 400);
|
|
161
197
|
return () => clearTimeout(t);
|
|
162
|
-
}, [documentId]);
|
|
198
|
+
}, [documentId, parsedData]);
|
|
163
199
|
const handleFieldChange = (0, react_1.useCallback)((sectionIdx, fieldIdx, newValue) => {
|
|
164
200
|
setSections((prev) => prev.map((sec, si) => si === sectionIdx
|
|
165
201
|
? Object.assign(Object.assign({}, sec), { fields: sec.fields.map((f, fi) => fi === fieldIdx ? Object.assign(Object.assign({}, f), { value: newValue }) : f) }) : sec));
|
|
@@ -49,17 +49,11 @@ const DocumentReviewModal_1 = require("./DocumentReviewModal");
|
|
|
49
49
|
// "fixed-assets": "Fixed_Assets.xlsx",
|
|
50
50
|
// "prior-returns": "Prior_Returns.pdf",
|
|
51
51
|
// };
|
|
52
|
-
// Stub field counts
|
|
53
|
-
const STUB_FIELD_COUNTS = {
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
payroll: 3,
|
|
58
|
-
"state-return": 2,
|
|
59
|
-
cashflow: 1,
|
|
60
|
-
"fixed-assets": 1,
|
|
61
|
-
"prior-returns": 1,
|
|
62
|
-
};
|
|
52
|
+
// Stub field counts from original mock — retained for reference.
|
|
53
|
+
// const STUB_FIELD_COUNTS: Record<string, number> = {
|
|
54
|
+
// pnl: 13, balance: 9, "1120s": 3, payroll: 3,
|
|
55
|
+
// "state-return": 2, cashflow: 1, "fixed-assets": 1, "prior-returns": 1,
|
|
56
|
+
// };
|
|
63
57
|
// Short help text shown below each doc name when not yet uploaded
|
|
64
58
|
// (mock App.jsx:1384). Overrides the longer DOC_SPECS_BASE.help.
|
|
65
59
|
const HELP_OVERRIDES = {
|
|
@@ -116,6 +110,7 @@ function TaxAxisDocuments({ profile, onContinue, onBack, onUploadDocument, onDel
|
|
|
116
110
|
const fileInputRef = (0, react_1.useRef)(null);
|
|
117
111
|
const uploadTargetIdx = (0, react_1.useRef)(-1);
|
|
118
112
|
const documentIdMap = (0, react_1.useRef)({});
|
|
113
|
+
const [parsedDataMap, setParsedDataMap] = (0, react_1.useState)({});
|
|
119
114
|
// Load previously uploaded documents on mount
|
|
120
115
|
(0, react_1.useEffect)(() => {
|
|
121
116
|
if (!fetchUploadedDocuments)
|
|
@@ -124,12 +119,15 @@ function TaxAxisDocuments({ profile, onContinue, onBack, onUploadDocument, onDel
|
|
|
124
119
|
.then((uploaded) => {
|
|
125
120
|
if (!uploaded || uploaded.length === 0)
|
|
126
121
|
return;
|
|
122
|
+
const newParsed = {};
|
|
127
123
|
setDocs((prev) => prev.map((d) => {
|
|
128
124
|
const match = uploaded.find((u) => u.documentType === d.id);
|
|
129
125
|
if (!match)
|
|
130
126
|
return d;
|
|
131
127
|
if (match.documentId)
|
|
132
128
|
documentIdMap.current[d.id] = match.documentId;
|
|
129
|
+
if (match.parsedData)
|
|
130
|
+
newParsed[d.id] = match.parsedData;
|
|
133
131
|
const status = match.status === 'PARSED' || match.status === 'valid'
|
|
134
132
|
? 'valid'
|
|
135
133
|
: match.status === 'FAILED'
|
|
@@ -137,12 +135,14 @@ function TaxAxisDocuments({ profile, onContinue, onBack, onUploadDocument, onDel
|
|
|
137
135
|
: 'validating';
|
|
138
136
|
return Object.assign(Object.assign({}, d), { status, fileName: match.fileName || d.fileName });
|
|
139
137
|
}));
|
|
138
|
+
if (Object.keys(newParsed).length > 0) {
|
|
139
|
+
setParsedDataMap((prev) => (Object.assign(Object.assign({}, prev), newParsed)));
|
|
140
|
+
}
|
|
140
141
|
})
|
|
141
142
|
.catch((err) => console.error('[TaxAxisDocuments] Failed to fetch uploaded documents:', err));
|
|
142
143
|
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
|
143
144
|
// Open native file picker for the target document slot
|
|
144
145
|
const handleUpload = (idx) => {
|
|
145
|
-
var _a;
|
|
146
146
|
if (!onUploadDocument) {
|
|
147
147
|
console.warn('[TaxAxisDocuments] No onUploadDocument handler provided');
|
|
148
148
|
return;
|
|
@@ -150,7 +150,7 @@ function TaxAxisDocuments({ profile, onContinue, onBack, onUploadDocument, onDel
|
|
|
150
150
|
uploadTargetIdx.current = idx;
|
|
151
151
|
if (fileInputRef.current) {
|
|
152
152
|
const doc = docs[idx];
|
|
153
|
-
fileInputRef.current.accept =
|
|
153
|
+
fileInputRef.current.accept = '.pdf,.doc,.docx,.xlsx,.xls,.csv';
|
|
154
154
|
fileInputRef.current.value = '';
|
|
155
155
|
fileInputRef.current.click();
|
|
156
156
|
}
|
|
@@ -170,6 +170,9 @@ function TaxAxisDocuments({ profile, onContinue, onBack, onUploadDocument, onDel
|
|
|
170
170
|
if (result === null || result === void 0 ? void 0 : result.documentId) {
|
|
171
171
|
documentIdMap.current[doc.id] = result.documentId;
|
|
172
172
|
}
|
|
173
|
+
if (result === null || result === void 0 ? void 0 : result.parsedData) {
|
|
174
|
+
setParsedDataMap((prev) => (Object.assign(Object.assign({}, prev), { [doc.id]: result.parsedData })));
|
|
175
|
+
}
|
|
173
176
|
setDocs((prev) => prev.map((d, i) => i === idx ? Object.assign(Object.assign({}, d), { status: 'valid', fileName: file.name }) : d));
|
|
174
177
|
}
|
|
175
178
|
catch (err) {
|
|
@@ -184,9 +187,27 @@ function TaxAxisDocuments({ profile, onContinue, onBack, onUploadDocument, onDel
|
|
|
184
187
|
onDeleteDocument(backendDocId).catch((err) => console.error('[TaxAxisDocuments] Delete failed:', err));
|
|
185
188
|
delete documentIdMap.current[doc.id];
|
|
186
189
|
}
|
|
190
|
+
setParsedDataMap((prev) => {
|
|
191
|
+
const next = Object.assign({}, prev);
|
|
192
|
+
delete next[doc.id];
|
|
193
|
+
return next;
|
|
194
|
+
});
|
|
187
195
|
setDocs((prev) => prev.map((d, i) => i === idx
|
|
188
196
|
? Object.assign(Object.assign({}, d), { status: "empty", fileName: null }) : d));
|
|
189
197
|
};
|
|
198
|
+
// Compute real field counts from parsed data
|
|
199
|
+
const fieldCounts = (0, react_1.useMemo)(() => {
|
|
200
|
+
const counts = {};
|
|
201
|
+
for (const doc of docs) {
|
|
202
|
+
const pd = parsedDataMap[doc.id];
|
|
203
|
+
const fields = pd === null || pd === void 0 ? void 0 : pd.fields;
|
|
204
|
+
if (fields && typeof fields === 'object') {
|
|
205
|
+
counts[doc.id] = Object.keys(fields)
|
|
206
|
+
.filter((k) => !k.startsWith('_')).length;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return counts;
|
|
210
|
+
}, [docs, parsedDataMap]);
|
|
190
211
|
const validCount = docs.filter((d) => d.status === "valid").length;
|
|
191
212
|
const requiredCount = docs.filter((d) => d.required === true).length;
|
|
192
213
|
const requiredValid = docs.filter((d) => d.required === true && d.status === "valid").length;
|
|
@@ -239,12 +260,12 @@ function TaxAxisDocuments({ profile, onContinue, onBack, onUploadDocument, onDel
|
|
|
239
260
|
coveragePct,
|
|
240
261
|
"% coverage"),
|
|
241
262
|
validCount < docs.length && (react_1.default.createElement("span", { className: "text-[10px] text-tax-axis-text-4 font-tax-axis-body" }, "Upload more documents to increase strategy coverage")))),
|
|
242
|
-
TIER_DEFS.map((tier) => (react_1.default.createElement(DocumentTier_1.DocumentTier, { key: tier.key, tier: tier, docs: docs, helpOverrides: HELP_OVERRIDES, fieldCounts:
|
|
263
|
+
TIER_DEFS.map((tier) => (react_1.default.createElement(DocumentTier_1.DocumentTier, { key: tier.key, tier: tier, docs: docs, helpOverrides: HELP_OVERRIDES, fieldCounts: fieldCounts, onUpload: handleUpload, onClear: handleClear, onReview: (docId) => setReviewDocId(docId) }))),
|
|
243
264
|
react_1.default.createElement("div", { className: "flex gap-3 mt-6" },
|
|
244
265
|
react_1.default.createElement(TaxAxisButton_1.TaxAxisButton, { variant: "secondary", onClick: onBack }, "Back"),
|
|
245
266
|
react_1.default.createElement(TaxAxisButton_1.TaxAxisButton, { onClick: onContinue, disabled: requiredCount > requiredValid, className: "flex-1" }, requiredCount > requiredValid
|
|
246
267
|
? `Upload ${requiredCount - requiredValid} more required doc${requiredCount - requiredValid > 1 ? "s" : ""}`
|
|
247
268
|
: "Run Analysis")),
|
|
248
269
|
react_1.default.createElement("input", { ref: fileInputRef, type: "file", style: { display: 'none' }, onChange: handleFileSelected }),
|
|
249
|
-
reviewDoc && (react_1.default.createElement(DocumentReviewModal_1.DocumentReviewModal, { documentId: documentIdMap.current[reviewDoc.id] || reviewDoc.id, documentName: reviewDoc.name, fileName: reviewDoc.fileName || "", jobId: jobId || "", onClose: () => setReviewDocId(null) }))));
|
|
270
|
+
reviewDoc && (react_1.default.createElement(DocumentReviewModal_1.DocumentReviewModal, { documentId: documentIdMap.current[reviewDoc.id] || reviewDoc.id, documentName: reviewDoc.name, fileName: reviewDoc.fileName || "", jobId: jobId || "", onClose: () => setReviewDocId(null), parsedData: parsedDataMap[reviewDoc.id] || null }))));
|
|
250
271
|
}
|