@paro.io/expert-shared-components 1.14.64 → 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
- // Stub fetch: simulate loading delay
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));
@@ -20,4 +20,4 @@ export interface TaxAxisDocumentsProps extends TaxAxisScreenProps {
20
20
  onQboConnected?: (companyName: string) => void;
21
21
  onQboDisconnected?: () => void;
22
22
  }
23
- export declare function TaxAxisDocuments({ profile, onContinue, onBack, userContext: _userContext, }: TaxAxisDocumentsProps): React.JSX.Element;
23
+ export declare function TaxAxisDocuments({ profile, onContinue, onBack, onUploadDocument, onDeleteDocument, fetchUploadedDocuments, jobId, userContext: _userContext, }: TaxAxisDocumentsProps): React.JSX.Element;
@@ -22,6 +22,15 @@ var __importStar = (this && this.__importStar) || function (mod) {
22
22
  __setModuleDefault(result, mod);
23
23
  return result;
24
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
+ };
25
34
  Object.defineProperty(exports, "__esModule", { value: true });
26
35
  exports.TaxAxisDocuments = TaxAxisDocuments;
27
36
  const react_1 = __importStar(require("react"));
@@ -29,28 +38,22 @@ const documents_1 = require("../../lib/data/documents");
29
38
  const TaxAxisButton_1 = require("../shared/TaxAxisButton");
30
39
  const DocumentTier_1 = require("./DocumentTier");
31
40
  const DocumentReviewModal_1 = require("./DocumentReviewModal");
32
- // Stub filenames assigned when the user clicks "Upload" (mock App.jsx:1237)
33
- const STUB_FILENAMES = {
34
- "1120s": "2025_1120S.pdf",
35
- "state-return": "2025_State_Return.pdf",
36
- "payroll": "2025_Payroll.xlsx",
37
- "pnl": "QBO_PnL_2025.pdf",
38
- "balance": "QBO_BS_2025.pdf",
39
- "cashflow": "QBO_CashFlow_2025.pdf",
40
- "fixed-assets": "Fixed_Assets.xlsx",
41
- "prior-returns": "Prior_Returns.pdf",
42
- };
43
- // Stub field counts per doc (mirrors STUB_SECTIONS in DocumentReviewModal)
44
- const STUB_FIELD_COUNTS = {
45
- pnl: 13,
46
- balance: 9,
47
- "1120s": 3,
48
- payroll: 3,
49
- "state-return": 2,
50
- cashflow: 1,
51
- "fixed-assets": 1,
52
- "prior-returns": 1,
53
- };
41
+ // Stub filenames from original mock (App.jsx:1237) — retained for reference.
42
+ // const STUB_FILENAMES: Record<string, string> = {
43
+ // "1120s": "2025_1120S.pdf",
44
+ // "state-return": "2025_State_Return.pdf",
45
+ // "payroll": "2025_Payroll.xlsx",
46
+ // "pnl": "QBO_PnL_2025.pdf",
47
+ // "balance": "QBO_BS_2025.pdf",
48
+ // "cashflow": "QBO_CashFlow_2025.pdf",
49
+ // "fixed-assets": "Fixed_Assets.xlsx",
50
+ // "prior-returns": "Prior_Returns.pdf",
51
+ // };
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
+ // };
54
57
  // Short help text shown below each doc name when not yet uploaded
55
58
  // (mock App.jsx:1384). Overrides the longer DOC_SPECS_BASE.help.
56
59
  const HELP_OVERRIDES = {
@@ -94,7 +97,7 @@ const TIER_DEFS = [
94
97
  ids: ["state-return", "cashflow", "prior-returns"],
95
98
  },
96
99
  ];
97
- function TaxAxisDocuments({ profile, onContinue, onBack, userContext: _userContext = "expert", }) {
100
+ function TaxAxisDocuments({ profile, onContinue, onBack, onUploadDocument, onDeleteDocument, fetchUploadedDocuments, jobId, userContext: _userContext = "expert", }) {
98
101
  var _a;
99
102
  const docSpecs = (0, react_1.useMemo)(() => (0, documents_1.getDocSpecs)(profile), [profile]);
100
103
  const [docs, setDocs] = (0, react_1.useState)(() => docSpecs.map((s) => (Object.assign(Object.assign({}, s), { status: "empty", fileName: null }))));
@@ -103,17 +106,108 @@ function TaxAxisDocuments({ profile, onContinue, onBack, userContext: _userConte
103
106
  const reviewDoc = reviewDocId
104
107
  ? (_a = docs.find((d) => d.id === reviewDocId)) !== null && _a !== void 0 ? _a : null
105
108
  : null;
106
- // Stub upload: empty -> validating -> valid after a short delay
107
- // (mock App.jsx:1238-1241)
109
+ // File picker refs
110
+ const fileInputRef = (0, react_1.useRef)(null);
111
+ const uploadTargetIdx = (0, react_1.useRef)(-1);
112
+ const documentIdMap = (0, react_1.useRef)({});
113
+ const [parsedDataMap, setParsedDataMap] = (0, react_1.useState)({});
114
+ // Load previously uploaded documents on mount
115
+ (0, react_1.useEffect)(() => {
116
+ if (!fetchUploadedDocuments)
117
+ return;
118
+ fetchUploadedDocuments()
119
+ .then((uploaded) => {
120
+ if (!uploaded || uploaded.length === 0)
121
+ return;
122
+ const newParsed = {};
123
+ setDocs((prev) => prev.map((d) => {
124
+ const match = uploaded.find((u) => u.documentType === d.id);
125
+ if (!match)
126
+ return d;
127
+ if (match.documentId)
128
+ documentIdMap.current[d.id] = match.documentId;
129
+ if (match.parsedData)
130
+ newParsed[d.id] = match.parsedData;
131
+ const status = match.status === 'PARSED' || match.status === 'valid'
132
+ ? 'valid'
133
+ : match.status === 'FAILED'
134
+ ? 'empty'
135
+ : 'validating';
136
+ return Object.assign(Object.assign({}, d), { status, fileName: match.fileName || d.fileName });
137
+ }));
138
+ if (Object.keys(newParsed).length > 0) {
139
+ setParsedDataMap((prev) => (Object.assign(Object.assign({}, prev), newParsed)));
140
+ }
141
+ })
142
+ .catch((err) => console.error('[TaxAxisDocuments] Failed to fetch uploaded documents:', err));
143
+ }, []); // eslint-disable-line react-hooks/exhaustive-deps
144
+ // Open native file picker for the target document slot
108
145
  const handleUpload = (idx) => {
109
- setDocs((prev) => prev.map((d, i) => i === idx
110
- ? Object.assign(Object.assign({}, d), { status: "validating", fileName: STUB_FILENAMES[d.id] || "document.pdf" }) : d));
111
- setTimeout(() => setDocs((prev) => prev.map((d, i) => i === idx ? Object.assign(Object.assign({}, d), { status: "valid" }) : d)), 500 + Math.random() * 400);
146
+ if (!onUploadDocument) {
147
+ console.warn('[TaxAxisDocuments] No onUploadDocument handler provided');
148
+ return;
149
+ }
150
+ uploadTargetIdx.current = idx;
151
+ if (fileInputRef.current) {
152
+ const doc = docs[idx];
153
+ fileInputRef.current.accept = '.pdf,.doc,.docx,.xlsx,.xls,.csv';
154
+ fileInputRef.current.value = '';
155
+ fileInputRef.current.click();
156
+ }
112
157
  };
158
+ // Handle file selection from the native picker
159
+ const handleFileSelected = (0, react_1.useCallback)((e) => __awaiter(this, void 0, void 0, function* () {
160
+ var _a;
161
+ const file = (_a = e.target.files) === null || _a === void 0 ? void 0 : _a[0];
162
+ const idx = uploadTargetIdx.current;
163
+ if (!file || idx < 0 || !onUploadDocument)
164
+ return;
165
+ const doc = docs[idx];
166
+ // Mark as validating immediately
167
+ setDocs((prev) => prev.map((d, i) => i === idx ? Object.assign(Object.assign({}, d), { status: 'validating', fileName: file.name }) : d));
168
+ try {
169
+ const result = yield onUploadDocument({ id: doc.id }, file);
170
+ if (result === null || result === void 0 ? void 0 : result.documentId) {
171
+ documentIdMap.current[doc.id] = result.documentId;
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
+ }
176
+ setDocs((prev) => prev.map((d, i) => i === idx ? Object.assign(Object.assign({}, d), { status: 'valid', fileName: file.name }) : d));
177
+ }
178
+ catch (err) {
179
+ console.error('[TaxAxisDocuments] Upload failed:', err);
180
+ setDocs((prev) => prev.map((d, i) => i === idx ? Object.assign(Object.assign({}, d), { status: 'empty', fileName: null }) : d));
181
+ }
182
+ }), [docs, onUploadDocument]);
113
183
  const handleClear = (idx) => {
184
+ const doc = docs[idx];
185
+ const backendDocId = documentIdMap.current[doc.id];
186
+ if (backendDocId && onDeleteDocument) {
187
+ onDeleteDocument(backendDocId).catch((err) => console.error('[TaxAxisDocuments] Delete failed:', err));
188
+ delete documentIdMap.current[doc.id];
189
+ }
190
+ setParsedDataMap((prev) => {
191
+ const next = Object.assign({}, prev);
192
+ delete next[doc.id];
193
+ return next;
194
+ });
114
195
  setDocs((prev) => prev.map((d, i) => i === idx
115
196
  ? Object.assign(Object.assign({}, d), { status: "empty", fileName: null }) : d));
116
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]);
117
211
  const validCount = docs.filter((d) => d.status === "valid").length;
118
212
  const requiredCount = docs.filter((d) => d.required === true).length;
119
213
  const requiredValid = docs.filter((d) => d.required === true && d.status === "valid").length;
@@ -166,11 +260,12 @@ function TaxAxisDocuments({ profile, onContinue, onBack, userContext: _userConte
166
260
  coveragePct,
167
261
  "% coverage"),
168
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")))),
169
- TIER_DEFS.map((tier) => (react_1.default.createElement(DocumentTier_1.DocumentTier, { key: tier.key, tier: tier, docs: docs, helpOverrides: HELP_OVERRIDES, fieldCounts: STUB_FIELD_COUNTS, onUpload: handleUpload, onClear: handleClear, onReview: (docId) => setReviewDocId(docId) }))),
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) }))),
170
264
  react_1.default.createElement("div", { className: "flex gap-3 mt-6" },
171
265
  react_1.default.createElement(TaxAxisButton_1.TaxAxisButton, { variant: "secondary", onClick: onBack }, "Back"),
172
266
  react_1.default.createElement(TaxAxisButton_1.TaxAxisButton, { onClick: onContinue, disabled: requiredCount > requiredValid, className: "flex-1" }, requiredCount > requiredValid
173
267
  ? `Upload ${requiredCount - requiredValid} more required doc${requiredCount - requiredValid > 1 ? "s" : ""}`
174
268
  : "Run Analysis")),
175
- reviewDoc && (react_1.default.createElement(DocumentReviewModal_1.DocumentReviewModal, { documentId: reviewDoc.id, documentName: reviewDoc.name, fileName: reviewDoc.fileName || "", jobId: "stub-job-id", onClose: () => setReviewDocId(null) }))));
269
+ react_1.default.createElement("input", { ref: fileInputRef, type: "file", style: { display: 'none' }, onChange: handleFileSelected }),
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 }))));
176
271
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@paro.io/expert-shared-components",
3
- "version": "1.14.64",
3
+ "version": "1.14.66",
4
4
  "description": "",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {