@paro.io/expert-shared-components 1.14.63 → 1.14.65

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.
@@ -113,6 +113,36 @@ function entityTypeKey(entity) {
113
113
  return undefined;
114
114
  return (_a = ENTITY_DISPLAY_TO_KEY[entity]) !== null && _a !== void 0 ? _a : entity;
115
115
  }
116
+ const GENERATING_STAGES = [
117
+ 'Analyzing entity structure…',
118
+ 'Evaluating deduction strategies…',
119
+ 'Modeling tax scenarios…',
120
+ 'Scoring strategy opportunities…',
121
+ 'Generating prospect insights…',
122
+ ];
123
+ function GeneratingOverlay() {
124
+ const [elapsed, setElapsed] = (0, react_1.useState)(0);
125
+ const [mounted, setMounted] = (0, react_1.useState)(false);
126
+ (0, react_1.useEffect)(() => { setMounted(true); }, []);
127
+ (0, react_1.useEffect)(() => {
128
+ const id = setInterval(() => setElapsed((s) => s + 1), 1000);
129
+ return () => clearInterval(id);
130
+ }, []);
131
+ const stageIdx = Math.min(Math.floor(elapsed / 9), GENERATING_STAGES.length - 1);
132
+ return (react_1.default.createElement("div", { className: 'fixed inset-0 z-[180] bg-black/25' },
133
+ react_1.default.createElement("div", { className: 'absolute left-1/2 top-1/3 -translate-x-1/2 rounded-xl bg-tax-axis-surface px-8 py-6 text-center shadow-lg border border-tax-axis-border', style: { maxWidth: 360, width: '100%' } },
134
+ react_1.default.createElement("div", { className: 'w-full h-1.5 rounded-full bg-tax-axis-border mb-4 overflow-hidden' },
135
+ react_1.default.createElement("div", { className: 'h-full rounded-full', style: {
136
+ background: 'linear-gradient(90deg, #248384, #2dd4bf)',
137
+ width: mounted ? '95%' : '0%',
138
+ transition: 'width 45s ease-out',
139
+ } })),
140
+ react_1.default.createElement("div", { className: 'text-sm font-semibold text-white mb-1 font-tax-axis-body' }, "Generating Prospect Report"),
141
+ react_1.default.createElement("div", { className: 'text-xs text-tax-axis-text-3 font-tax-axis-body mb-2', style: { minHeight: 18 } }, GENERATING_STAGES[stageIdx]),
142
+ react_1.default.createElement("div", { className: 'text-[10px] text-tax-axis-text-4 font-tax-axis-mono' },
143
+ elapsed,
144
+ "s elapsed"))));
145
+ }
116
146
  function buildSessionInput(profile) {
117
147
  const taxYear = Number(profile.year) || new Date().getFullYear();
118
148
  return {
@@ -574,11 +604,7 @@ const TaxAxisShell = ({ taxAxisApi, userContext = 'expert', initialSessionId, in
574
604
  react_1.default.createElement("div", { className: 'flex items-center gap-2' },
575
605
  react_1.default.createElement("div", { className: 'w-3 h-3 rounded-full animate-spin flex-shrink-0', style: { border: '2px solid transparent', borderTopColor: '#248384' } }),
576
606
  busyMessage)))),
577
- generating && (react_1.default.createElement("div", { className: 'fixed inset-0 z-[180] bg-black/25' },
578
- react_1.default.createElement("div", { className: 'absolute left-1/2 top-1/3 -translate-x-1/2 rounded-xl bg-tax-axis-surface px-8 py-6 text-center shadow-lg border border-tax-axis-border', style: { maxWidth: 360 } },
579
- react_1.default.createElement("div", { className: 'w-6 h-6 rounded-full animate-spin mx-auto mb-3', style: { border: '2.5px solid transparent', borderTopColor: '#248384' } }),
580
- react_1.default.createElement("div", { className: 'text-sm font-semibold text-white mb-1 font-tax-axis-body' }, "Generating Prospect Report"),
581
- react_1.default.createElement("div", { className: 'text-xs text-tax-axis-text-3 font-tax-axis-body' }, "Analyzing strategies and generating insights...")))),
607
+ generating && react_1.default.createElement(GeneratingOverlay, null),
582
608
  currentView));
583
609
  };
584
610
  exports.TaxAxisShell = TaxAxisShell;
@@ -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,17 +38,17 @@ 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
- };
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
+ // };
43
52
  // Stub field counts per doc (mirrors STUB_SECTIONS in DocumentReviewModal)
44
53
  const STUB_FIELD_COUNTS = {
45
54
  pnl: 13,
@@ -94,7 +103,7 @@ const TIER_DEFS = [
94
103
  ids: ["state-return", "cashflow", "prior-returns"],
95
104
  },
96
105
  ];
97
- function TaxAxisDocuments({ profile, onContinue, onBack, userContext: _userContext = "expert", }) {
106
+ function TaxAxisDocuments({ profile, onContinue, onBack, onUploadDocument, onDeleteDocument, fetchUploadedDocuments, jobId, userContext: _userContext = "expert", }) {
98
107
  var _a;
99
108
  const docSpecs = (0, react_1.useMemo)(() => (0, documents_1.getDocSpecs)(profile), [profile]);
100
109
  const [docs, setDocs] = (0, react_1.useState)(() => docSpecs.map((s) => (Object.assign(Object.assign({}, s), { status: "empty", fileName: null }))));
@@ -103,14 +112,78 @@ function TaxAxisDocuments({ profile, onContinue, onBack, userContext: _userConte
103
112
  const reviewDoc = reviewDocId
104
113
  ? (_a = docs.find((d) => d.id === reviewDocId)) !== null && _a !== void 0 ? _a : null
105
114
  : null;
106
- // Stub upload: empty -> validating -> valid after a short delay
107
- // (mock App.jsx:1238-1241)
115
+ // File picker refs
116
+ const fileInputRef = (0, react_1.useRef)(null);
117
+ const uploadTargetIdx = (0, react_1.useRef)(-1);
118
+ const documentIdMap = (0, react_1.useRef)({});
119
+ // Load previously uploaded documents on mount
120
+ (0, react_1.useEffect)(() => {
121
+ if (!fetchUploadedDocuments)
122
+ return;
123
+ fetchUploadedDocuments()
124
+ .then((uploaded) => {
125
+ if (!uploaded || uploaded.length === 0)
126
+ return;
127
+ setDocs((prev) => prev.map((d) => {
128
+ const match = uploaded.find((u) => u.documentType === d.id);
129
+ if (!match)
130
+ return d;
131
+ if (match.documentId)
132
+ documentIdMap.current[d.id] = match.documentId;
133
+ const status = match.status === 'PARSED' || match.status === 'valid'
134
+ ? 'valid'
135
+ : match.status === 'FAILED'
136
+ ? 'empty'
137
+ : 'validating';
138
+ return Object.assign(Object.assign({}, d), { status, fileName: match.fileName || d.fileName });
139
+ }));
140
+ })
141
+ .catch((err) => console.error('[TaxAxisDocuments] Failed to fetch uploaded documents:', err));
142
+ }, []); // eslint-disable-line react-hooks/exhaustive-deps
143
+ // Open native file picker for the target document slot
108
144
  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);
145
+ var _a;
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 = ((_a = doc.accept) === null || _a === void 0 ? void 0 : _a.join(',')) || '.pdf,.docx,.xlsx,.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
+ setDocs((prev) => prev.map((d, i) => i === idx ? Object.assign(Object.assign({}, d), { status: 'valid', fileName: file.name }) : d));
174
+ }
175
+ catch (err) {
176
+ console.error('[TaxAxisDocuments] Upload failed:', err);
177
+ setDocs((prev) => prev.map((d, i) => i === idx ? Object.assign(Object.assign({}, d), { status: 'empty', fileName: null }) : d));
178
+ }
179
+ }), [docs, onUploadDocument]);
113
180
  const handleClear = (idx) => {
181
+ const doc = docs[idx];
182
+ const backendDocId = documentIdMap.current[doc.id];
183
+ if (backendDocId && onDeleteDocument) {
184
+ onDeleteDocument(backendDocId).catch((err) => console.error('[TaxAxisDocuments] Delete failed:', err));
185
+ delete documentIdMap.current[doc.id];
186
+ }
114
187
  setDocs((prev) => prev.map((d, i) => i === idx
115
188
  ? Object.assign(Object.assign({}, d), { status: "empty", fileName: null }) : d));
116
189
  };
@@ -172,5 +245,6 @@ function TaxAxisDocuments({ profile, onContinue, onBack, userContext: _userConte
172
245
  react_1.default.createElement(TaxAxisButton_1.TaxAxisButton, { onClick: onContinue, disabled: requiredCount > requiredValid, className: "flex-1" }, requiredCount > requiredValid
173
246
  ? `Upload ${requiredCount - requiredValid} more required doc${requiredCount - requiredValid > 1 ? "s" : ""}`
174
247
  : "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) }))));
248
+ 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) }))));
176
250
  }
@@ -88,26 +88,6 @@ function TaxAxisIntake({ userContext = "expert", onProspect, onFullAnalysis, ini
88
88
  });
89
89
  const profile = methods.watch();
90
90
  const missingFields = (0, react_1.useMemo)(() => getMissingFields(profile), [profile]);
91
- const [showDemo, setShowDemo] = (0, react_1.useState)(false);
92
- const konamiRef = (0, react_1.useRef)({ keys: '', timer: null });
93
- (0, react_1.useEffect)(() => {
94
- const handler = (e) => {
95
- var _a;
96
- const tag = (_a = e.target) === null || _a === void 0 ? void 0 : _a.tagName;
97
- if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT')
98
- return;
99
- const k = konamiRef.current;
100
- k.keys += e.key.toLowerCase();
101
- clearTimeout(k.timer);
102
- k.timer = setTimeout(() => { k.keys = ''; }, 3000);
103
- if (k.keys.includes('demo')) {
104
- setShowDemo(true);
105
- k.keys = '';
106
- }
107
- };
108
- window.addEventListener('keydown', handler);
109
- return () => window.removeEventListener('keydown', handler);
110
- }, []);
111
91
  const handleDemoToggle = () => {
112
92
  const current = methods.getValues();
113
93
  const isDemoFilled = current.bizName === DEMO_PROFILE.bizName;
@@ -127,9 +107,17 @@ function TaxAxisIntake({ userContext = "expert", onProspect, onFullAnalysis, ini
127
107
  };
128
108
  return (react_1.default.createElement(react_hook_form_1.FormProvider, Object.assign({}, methods),
129
109
  react_1.default.createElement("div", { className: "grid grid-cols-[1fr_1fr] gap-5" },
130
- react_1.default.createElement("div", { className: "relative flex flex-col gap-4" },
131
- showDemo && (react_1.default.createElement("button", { type: "button", onClick: handleDemoToggle, className: "absolute right-0 -top-1 text-[10px] text-tax-axis-text-4 opacity-40 hover:opacity-80 transition-opacity cursor-pointer bg-transparent border-none font-tax-axis-mono", style: { zIndex: 20 } }, "Demo")),
132
- react_1.default.createElement(ClientParametersSection_1.ClientParametersSection, { userContext: userContext }),
110
+ react_1.default.createElement("div", { className: "flex flex-col gap-4" },
111
+ react_1.default.createElement("div", { onClick: (e) => {
112
+ var _a;
113
+ if (e.detail === 3) {
114
+ const tag = (_a = e.target) === null || _a === void 0 ? void 0 : _a.tagName;
115
+ if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT')
116
+ return;
117
+ handleDemoToggle();
118
+ }
119
+ } },
120
+ react_1.default.createElement(ClientParametersSection_1.ClientParametersSection, { userContext: userContext })),
133
121
  react_1.default.createElement(RefineAnalysisSection_1.RefineAnalysisSection, { userContext: userContext }),
134
122
  react_1.default.createElement(CpaIntakeQuestionsSection_1.CpaIntakeQuestionsSection, { userContext: userContext }),
135
123
  react_1.default.createElement(IntakeCtaCards_1.IntakeCtaCards, { onProspect: handleProspect, onFullAnalysis: handleFull, userContext: userContext, missingFields: missingFields })),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@paro.io/expert-shared-components",
3
- "version": "1.14.63",
3
+ "version": "1.14.65",
4
4
  "description": "",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {