@paro.io/expert-shared-components 1.14.53 → 1.14.55
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.
- package/lib/components/TaxAxis/TaxAxisApi.d.ts +2 -0
- package/lib/components/TaxAxis/TaxAxisShell.js +138 -13
- package/lib/tax-axis/components/clientReport/ExecutiveSummary.d.ts +4 -1
- package/lib/tax-axis/components/clientReport/ExecutiveSummary.js +10 -6
- package/lib/tax-axis/components/clientReport/RecommendedStrategies.d.ts +6 -1
- package/lib/tax-axis/components/clientReport/RecommendedStrategies.js +26 -24
- package/lib/tax-axis/components/clientReport/StrategyCard.d.ts +1 -1
- package/lib/tax-axis/components/clientReport/StrategyCard.js +39 -23
- package/lib/tax-axis/components/clientReport/TaxAxisClientReport.d.ts +3 -1
- package/lib/tax-axis/components/clientReport/TaxAxisClientReport.js +4 -4
- package/lib/tax-axis/components/dashboard/DashboardSummary.d.ts +6 -1
- package/lib/tax-axis/components/dashboard/DashboardSummary.js +14 -4
- package/lib/tax-axis/components/dashboard/StrategyDetailPanel.d.ts +1 -1
- package/lib/tax-axis/components/dashboard/StrategyDetailPanel.js +123 -91
- package/lib/tax-axis/components/dashboard/TaxAxisDashboard.d.ts +3 -2
- package/lib/tax-axis/components/dashboard/TaxAxisDashboard.js +67 -19
- package/lib/tax-axis/components/documents/TaxAxisDocuments.d.ts +2 -0
- package/lib/tax-axis/components/documents/TaxAxisDocuments.js +46 -10
- package/lib/tax-axis/components/preparerWorkpaper/TaxAxisPreparerWorkpaper.d.ts +21 -1
- package/lib/tax-axis/components/preparerWorkpaper/TaxAxisPreparerWorkpaper.js +10 -1
- package/lib/tax-axis/components/processing/TaxAxisProcessing.d.ts +3 -1
- package/lib/tax-axis/components/processing/TaxAxisProcessing.js +77 -31
- package/lib/tax-axis/lib/adapters/useEngineOutput.d.ts +7 -0
- package/lib/tax-axis/lib/adapters/useEngineOutput.js +10 -4
- package/lib/tax-axis/lib/types/index.d.ts +10 -1
- package/package.json +1 -1
|
@@ -32,6 +32,8 @@ export type TaxAxisDocumentRecord = {
|
|
|
32
32
|
s3Key: string;
|
|
33
33
|
parseError?: string | null;
|
|
34
34
|
parseStageResults?: unknown[] | null;
|
|
35
|
+
parsedData?: Record<string, unknown> | null;
|
|
36
|
+
reviewedData?: Record<string, unknown> | null;
|
|
35
37
|
createdAt?: string;
|
|
36
38
|
updatedAt?: string;
|
|
37
39
|
};
|
|
@@ -53,6 +53,48 @@ function sleep(ms) {
|
|
|
53
53
|
setTimeout(resolve, ms);
|
|
54
54
|
});
|
|
55
55
|
}
|
|
56
|
+
const INSUFFICIENT_DATA_MESSAGE = "Based on what we parsed from your documents, we don't have enough data to identify any strategies. " +
|
|
57
|
+
"This is by design. TaxAxis never estimates missing inputs. Add more documents below to get started.";
|
|
58
|
+
function resolveErrorMessage(stage, reason, timedOut) {
|
|
59
|
+
if (timedOut) {
|
|
60
|
+
return 'Analysis is taking longer than expected. This can happen with complex documents. Please try again — if it keeps happening, try uploading fewer documents first.';
|
|
61
|
+
}
|
|
62
|
+
if (stage === 'FAILED') {
|
|
63
|
+
return 'All documents failed to parse. Please check that you uploaded the correct file types (PDF, Excel, or Word) and try again.';
|
|
64
|
+
}
|
|
65
|
+
if (!reason) {
|
|
66
|
+
return 'Analysis could not be completed. Please review your uploaded documents and try again.';
|
|
67
|
+
}
|
|
68
|
+
// INSUFFICIENT_DATA covers: no reports generated, all strategies not recommended,
|
|
69
|
+
// and G1/G2 schema failures caused by too few extracted fields.
|
|
70
|
+
if (reason.includes('INSUFFICIENT_DATA')) {
|
|
71
|
+
return INSUFFICIENT_DATA_MESSAGE;
|
|
72
|
+
}
|
|
73
|
+
if (reason.includes('NO_REPORTS_GENERATED')) {
|
|
74
|
+
return INSUFFICIENT_DATA_MESSAGE;
|
|
75
|
+
}
|
|
76
|
+
if (reason.includes('ALL_STRATEGIES_NOT_RECOMMENDED')) {
|
|
77
|
+
return INSUFFICIENT_DATA_MESSAGE;
|
|
78
|
+
}
|
|
79
|
+
if (reason.includes('REPORTS_NEED_REVIEW')) {
|
|
80
|
+
return 'Some strategies require CPA review before they can be shown. The analysis completed — your CPA will see these flagged items in the workpaper.';
|
|
81
|
+
}
|
|
82
|
+
if (reason.includes('EVAL_GATE_FAIL')) {
|
|
83
|
+
// Parse gate IDs out of the reason string for a more specific message
|
|
84
|
+
const gateMatch = reason.match(/G\d+_[A-Z_]+/g);
|
|
85
|
+
if ((gateMatch === null || gateMatch === void 0 ? void 0 : gateMatch.includes('G1_SCHEMA')) || (gateMatch === null || gateMatch === void 0 ? void 0 : gateMatch.includes('G2_COVERAGE'))) {
|
|
86
|
+
return 'The analysis output failed an internal consistency check. This is usually caused by documents that could not be fully parsed. Please re-upload your documents and try again.';
|
|
87
|
+
}
|
|
88
|
+
if (gateMatch === null || gateMatch === void 0 ? void 0 : gateMatch.includes('G4_MATH')) {
|
|
89
|
+
return 'A calculation error was detected in the analysis. Please try again — if the issue persists, contact support.';
|
|
90
|
+
}
|
|
91
|
+
if ((gateMatch === null || gateMatch === void 0 ? void 0 : gateMatch.includes('G5_BUSINESS_RULES')) || (gateMatch === null || gateMatch === void 0 ? void 0 : gateMatch.includes('G6_COMPLIANCE'))) {
|
|
92
|
+
return 'The analysis produced results that did not pass compliance checks. Please review your uploaded documents for accuracy and try again.';
|
|
93
|
+
}
|
|
94
|
+
return 'The analysis did not pass quality checks. Please review your uploaded documents and try again.';
|
|
95
|
+
}
|
|
96
|
+
return 'Analysis could not be completed. Please review your uploaded documents and try again.';
|
|
97
|
+
}
|
|
56
98
|
function toInt(value) {
|
|
57
99
|
const parsed = Number(String(value || '0').replace(/[^\d.-]/g, ''));
|
|
58
100
|
return Number.isFinite(parsed) ? parsed : 0;
|
|
@@ -99,13 +141,47 @@ const TaxAxisShell = ({ taxAxisApi, userContext = 'expert', initialSessionId, in
|
|
|
99
141
|
const [sessionId, setSessionId] = (0, react_1.useState)(initialSessionId || null);
|
|
100
142
|
const [isProspectFlow, setIsProspectFlow] = (0, react_1.useState)(false);
|
|
101
143
|
const [llmResult, setLlmResult] = (0, react_1.useState)(null);
|
|
144
|
+
const [parsedDocuments, setParsedDocuments] = (0, react_1.useState)([]);
|
|
102
145
|
const [isBusy, setIsBusy] = (0, react_1.useState)(false);
|
|
103
146
|
const [error, setError] = (0, react_1.useState)(null);
|
|
104
147
|
const [busyMessage, setBusyMessage] = (0, react_1.useState)('Syncing Tax Axis session...');
|
|
148
|
+
const [reportReady, setReportReady] = (0, react_1.useState)(false);
|
|
149
|
+
const isPollingRef = react_1.default.useRef(false);
|
|
105
150
|
// Derive live strategies from engineOutput so CLIENT_REPORT and PREPARER_WORKPAPER
|
|
106
151
|
// render real engine data instead of the static STRATEGIES catalog.
|
|
107
152
|
const engineOutput = (0, react_1.useMemo)(() => { var _a; return (_a = llmResult === null || llmResult === void 0 ? void 0 : llmResult.engineOutput) !== null && _a !== void 0 ? _a : null; }, [llmResult]);
|
|
108
153
|
const adapted = (0, useEngineOutput_1.useEngineOutput)(engineOutput);
|
|
154
|
+
const fetchAndSetParsedDocuments = (0, react_1.useCallback)((sid) => __awaiter(void 0, void 0, void 0, function* () {
|
|
155
|
+
try {
|
|
156
|
+
const docs = yield taxAxisApi.getDocuments(sid);
|
|
157
|
+
const extractionDocs = docs
|
|
158
|
+
.filter((doc) => doc.status === 'PARSED' && doc.parsedData)
|
|
159
|
+
.map((doc) => {
|
|
160
|
+
var _a;
|
|
161
|
+
const data = (_a = doc.parsedData) !== null && _a !== void 0 ? _a : {};
|
|
162
|
+
const fields = Object.entries(data)
|
|
163
|
+
.filter(([, v]) => v !== null && v !== undefined)
|
|
164
|
+
.map(([key, value]) => ({
|
|
165
|
+
field: key.replace(/_/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase()),
|
|
166
|
+
line: '',
|
|
167
|
+
value: typeof value === 'object' ? JSON.stringify(value) : String(value),
|
|
168
|
+
status: 'pass',
|
|
169
|
+
strategies: [],
|
|
170
|
+
}));
|
|
171
|
+
return {
|
|
172
|
+
docId: doc.documentId,
|
|
173
|
+
docName: doc.fileName || doc.documentType || 'Document',
|
|
174
|
+
code: (doc.documentType || 'DOC').toUpperCase().replace(/_/g, ' ').slice(0, 6),
|
|
175
|
+
flagCount: 0,
|
|
176
|
+
fields,
|
|
177
|
+
};
|
|
178
|
+
});
|
|
179
|
+
setParsedDocuments(extractionDocs);
|
|
180
|
+
}
|
|
181
|
+
catch (_a) {
|
|
182
|
+
// non-blocking — dashboard works without parsed document detail
|
|
183
|
+
}
|
|
184
|
+
}), [taxAxisApi]);
|
|
109
185
|
// On mount: if a sessionId was injected (e.g. returning to an existing session),
|
|
110
186
|
// reload the last GENERATION llmRun so the report screens get live data.
|
|
111
187
|
(0, react_1.useEffect)(() => {
|
|
@@ -116,9 +192,11 @@ const TaxAxisShell = ({ taxAxisApi, userContext = 'expert', initialSessionId, in
|
|
|
116
192
|
if (run === null || run === void 0 ? void 0 : run.outputPayload) {
|
|
117
193
|
setLlmResult(run.outputPayload);
|
|
118
194
|
setStep('DASHBOARD');
|
|
195
|
+
fetchAndSetParsedDocuments(initialSessionId);
|
|
119
196
|
}
|
|
120
197
|
})
|
|
121
198
|
.catch(() => { });
|
|
199
|
+
// fetchAndSetParsedDocuments intentionally excluded: only runs on mount for initial session restore
|
|
122
200
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
123
201
|
}, [initialSessionId]);
|
|
124
202
|
const updateSessionId = (0, react_1.useCallback)((nextSessionId) => {
|
|
@@ -151,6 +229,7 @@ const TaxAxisShell = ({ taxAxisApi, userContext = 'expert', initialSessionId, in
|
|
|
151
229
|
setProfile(null);
|
|
152
230
|
setIsProspectFlow(false);
|
|
153
231
|
setError(null);
|
|
232
|
+
setReportReady(false);
|
|
154
233
|
updateSessionId(null);
|
|
155
234
|
}, [updateSessionId]);
|
|
156
235
|
const handleProspect = (0, react_1.useCallback)((nextProfile) => {
|
|
@@ -221,6 +300,48 @@ const TaxAxisShell = ({ taxAxisApi, userContext = 'expert', initialSessionId, in
|
|
|
221
300
|
const handleDeleteDocument = (0, react_1.useCallback)((documentId) => __awaiter(void 0, void 0, void 0, function* () {
|
|
222
301
|
yield taxAxisApi.deleteDocument(documentId);
|
|
223
302
|
}), [taxAxisApi]);
|
|
303
|
+
const pollForResult = (0, react_1.useCallback)((sid) => __awaiter(void 0, void 0, void 0, function* () {
|
|
304
|
+
const POLL_INTERVAL_MS = 4000;
|
|
305
|
+
const MAX_ATTEMPTS = 4500; // 300 minutes max
|
|
306
|
+
let attempts = 0;
|
|
307
|
+
isPollingRef.current = true;
|
|
308
|
+
const poll = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
309
|
+
var _a;
|
|
310
|
+
attempts++;
|
|
311
|
+
try {
|
|
312
|
+
const session = yield taxAxisApi.getSession(sid);
|
|
313
|
+
if ((session === null || session === void 0 ? void 0 : session.stage) === 'REPORT_READY') {
|
|
314
|
+
if (taxAxisApi.getLlmRun) {
|
|
315
|
+
const run = yield taxAxisApi.getLlmRun(sid, 'GENERATION');
|
|
316
|
+
if (run === null || run === void 0 ? void 0 : run.outputPayload) {
|
|
317
|
+
setLlmResult(run.outputPayload);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
yield fetchAndSetParsedDocuments(sid);
|
|
321
|
+
isPollingRef.current = false;
|
|
322
|
+
// Signal the processing screen to animate to 100% — it calls onComplete when done
|
|
323
|
+
setReportReady(true);
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
const terminalFailure = (session === null || session === void 0 ? void 0 : session.stage) === 'EVAL_FAILED' ||
|
|
327
|
+
(session === null || session === void 0 ? void 0 : session.stage) === 'FAILED' ||
|
|
328
|
+
(session === null || session === void 0 ? void 0 : session.stage) === 'ERROR';
|
|
329
|
+
if (terminalFailure || attempts >= MAX_ATTEMPTS) {
|
|
330
|
+
isPollingRef.current = false;
|
|
331
|
+
const reason = ((_a = session === null || session === void 0 ? void 0 : session.summaryPayload) === null || _a === void 0 ? void 0 : _a.evalFailedReason) || '';
|
|
332
|
+
setError(resolveErrorMessage(session === null || session === void 0 ? void 0 : session.stage, reason, attempts >= MAX_ATTEMPTS));
|
|
333
|
+
setReportReady(false);
|
|
334
|
+
setStep('DOCUMENT_UPLOAD');
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
catch (_b) {
|
|
339
|
+
// network blip — keep polling
|
|
340
|
+
}
|
|
341
|
+
setTimeout(poll, POLL_INTERVAL_MS);
|
|
342
|
+
});
|
|
343
|
+
setTimeout(poll, POLL_INTERVAL_MS);
|
|
344
|
+
}), [taxAxisApi]);
|
|
224
345
|
const handleAnalyzeDocuments = (0, react_1.useCallback)(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
225
346
|
if (!profile)
|
|
226
347
|
return;
|
|
@@ -236,11 +357,12 @@ const TaxAxisShell = ({ taxAxisApi, userContext = 'expert', initialSessionId, in
|
|
|
236
357
|
setBusyMessage('Verifying all documents are parsed...');
|
|
237
358
|
yield taxAxisApi.analyzeDocuments(ensuredSessionId);
|
|
238
359
|
setBusyMessage('Generating strategy results...');
|
|
360
|
+
yield taxAxisApi.runLlm(ensuredSessionId);
|
|
361
|
+
// runLlm now returns { queued, sessionId } — navigate to processing
|
|
362
|
+
// screen immediately and poll for REPORT_READY in the background.
|
|
239
363
|
setStep('PROCESSING');
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
setLlmResult(llmResponse.outputPayload);
|
|
243
|
-
}
|
|
364
|
+
setIsBusy(false);
|
|
365
|
+
pollForResult(ensuredSessionId);
|
|
244
366
|
}
|
|
245
367
|
catch (requestError) {
|
|
246
368
|
const msg = requestError instanceof Error
|
|
@@ -249,10 +371,8 @@ const TaxAxisShell = ({ taxAxisApi, userContext = 'expert', initialSessionId, in
|
|
|
249
371
|
setError(msg);
|
|
250
372
|
setStep('DOCUMENT_UPLOAD');
|
|
251
373
|
setIsBusy(false);
|
|
252
|
-
return;
|
|
253
374
|
}
|
|
254
|
-
|
|
255
|
-
}), [createSessionIfNeeded, profile, taxAxisApi]);
|
|
375
|
+
}), [createSessionIfNeeded, pollForResult, profile, taxAxisApi]);
|
|
256
376
|
const handleSendReport = (0, react_1.useCallback)(() => __awaiter(void 0, void 0, void 0, function* () {
|
|
257
377
|
if (!sessionId) {
|
|
258
378
|
setError('No session id available for sending report.');
|
|
@@ -274,6 +394,7 @@ const TaxAxisShell = ({ taxAxisApi, userContext = 'expert', initialSessionId, in
|
|
|
274
394
|
}
|
|
275
395
|
}), [sessionId, taxAxisApi]);
|
|
276
396
|
const currentView = (0, react_1.useMemo)(() => {
|
|
397
|
+
var _a, _b, _c, _d;
|
|
277
398
|
if (step === 'SESSION_SETUP') {
|
|
278
399
|
return (react_1.default.createElement(ShellContainer, null,
|
|
279
400
|
react_1.default.createElement(TaxAxisIntake_1.TaxAxisIntake, { userContext: userContext, initialProfile: initialProfile, onProspect: handleProspect, onFullAnalysis: handleFullAnalysis })));
|
|
@@ -288,21 +409,23 @@ const TaxAxisShell = ({ taxAxisApi, userContext = 'expert', initialSessionId, in
|
|
|
288
409
|
react_1.default.createElement(TaxAxisProspectReport_1.TaxAxisProspectReport, { profile: profile, userContext: userContext, onUpgrade: () => setStep('DOCUMENT_UPLOAD'), onPresent: () => setStep('PRESENTATION'), onReset: handleReset })));
|
|
289
410
|
case 'DOCUMENT_UPLOAD':
|
|
290
411
|
return (react_1.default.createElement(ShellContainer, null,
|
|
291
|
-
react_1.default.createElement(TaxAxisDocuments_1.TaxAxisDocuments, { profile: profile, entityType: entityTypeKey(profile.entity), userContext: userContext, onUploadDocument: handleUploadDocument, onDeleteDocument: handleDeleteDocument, fetchUploadedDocuments: () => __awaiter(void 0, void 0, void 0, function* () {
|
|
412
|
+
react_1.default.createElement(TaxAxisDocuments_1.TaxAxisDocuments, { key: sessionId || 'new', profile: profile, entityType: entityTypeKey(profile.entity), userContext: userContext, onUploadDocument: handleUploadDocument, onDeleteDocument: handleDeleteDocument, fetchUploadedDocuments: () => __awaiter(void 0, void 0, void 0, function* () {
|
|
292
413
|
const ensuredSessionId = sessionId || (profile ? yield createSessionIfNeeded(profile) : null);
|
|
293
414
|
if (!ensuredSessionId) {
|
|
294
415
|
return [];
|
|
295
416
|
}
|
|
296
417
|
const docs = yield taxAxisApi.getDocuments(ensuredSessionId);
|
|
297
418
|
return docs.map((doc) => {
|
|
298
|
-
var _a;
|
|
419
|
+
var _a, _b;
|
|
299
420
|
return ({
|
|
421
|
+
documentId: doc.documentId,
|
|
300
422
|
fileName: doc.fileName,
|
|
301
423
|
status: doc.status,
|
|
302
424
|
documentType: doc.documentType,
|
|
303
425
|
parseError: doc.parseError,
|
|
304
426
|
updatedAt: doc.updatedAt,
|
|
305
427
|
parsedData: (_a = doc.parsedData) !== null && _a !== void 0 ? _a : null,
|
|
428
|
+
reviewedData: (_b = doc.reviewedData) !== null && _b !== void 0 ? _b : null,
|
|
306
429
|
});
|
|
307
430
|
});
|
|
308
431
|
}), jobId: sessionId || undefined, onSaveReviewedField: (documentId, reviewedData) => __awaiter(void 0, void 0, void 0, function* () {
|
|
@@ -310,19 +433,19 @@ const TaxAxisShell = ({ taxAxisApi, userContext = 'expert', initialSessionId, in
|
|
|
310
433
|
}), onContinue: handleAnalyzeDocuments, onBack: () => isProspectFlow ? setStep('PROSPECT_REPORT') : setStep('SESSION_SETUP') })));
|
|
311
434
|
case 'PROCESSING':
|
|
312
435
|
return (react_1.default.createElement(ShellContainer, null,
|
|
313
|
-
react_1.default.createElement(TaxAxisProcessing_1.TaxAxisProcessing, { profile: profile, userContext: userContext, onComplete: () => setStep('DASHBOARD') })));
|
|
436
|
+
react_1.default.createElement(TaxAxisProcessing_1.TaxAxisProcessing, { profile: profile, userContext: userContext, reportReady: reportReady, onComplete: () => setStep('DASHBOARD') })));
|
|
314
437
|
case 'PARSED_REVIEW':
|
|
315
438
|
return (react_1.default.createElement(ShellContainer, null,
|
|
316
439
|
react_1.default.createElement(TaxAxisExtractionReview_1.TaxAxisExtractionReview, { userContext: userContext, onBack: () => setStep('DASHBOARD'), onConfirmAndUnlock: () => setStep('DASHBOARD') })));
|
|
317
440
|
case 'DASHBOARD':
|
|
318
441
|
return (react_1.default.createElement(ShellContainer, null,
|
|
319
|
-
react_1.default.createElement(TaxAxisDashboard_1.TaxAxisDashboard, { profile: profile, userContext: userContext, llmResult: llmResult, onDownloadClient: () => setStep('CLIENT_REPORT'), onDownloadPreparer: () => setStep('PREPARER_WORKPAPER'), onPresent: () => setStep('PRESENTATION'), onSend: handleSendReport, onReviewData: () => setStep('PARSED_REVIEW'), onReset: handleReset })));
|
|
442
|
+
react_1.default.createElement(TaxAxisDashboard_1.TaxAxisDashboard, { profile: profile, userContext: userContext, llmResult: llmResult, parsedDocuments: parsedDocuments, onDownloadClient: () => setStep('CLIENT_REPORT'), onDownloadPreparer: () => setStep('PREPARER_WORKPAPER'), onPresent: () => setStep('PRESENTATION'), onSend: handleSendReport, onReviewData: () => setStep('PARSED_REVIEW'), onReset: handleReset })));
|
|
320
443
|
case 'CLIENT_REPORT':
|
|
321
444
|
return (react_1.default.createElement(ShellContainer, { fullWidth: true },
|
|
322
|
-
react_1.default.createElement(TaxAxisClientReport_1.TaxAxisClientReport, { profile: profile, userContext: userContext, onBack: () => setStep('DASHBOARD'), onNavigatePreparer: () => setStep('PREPARER_WORKPAPER'), liveStrategies: adapted === null || adapted === void 0 ? void 0 : adapted.strategies, liveComputedMap: adapted === null || adapted === void 0 ? void 0 : adapted.computedMap })));
|
|
445
|
+
react_1.default.createElement(TaxAxisClientReport_1.TaxAxisClientReport, { profile: profile, userContext: userContext, onBack: () => setStep('DASHBOARD'), onNavigatePreparer: () => setStep('PREPARER_WORKPAPER'), liveStrategies: adapted === null || adapted === void 0 ? void 0 : adapted.strategies, liveComputedMap: adapted === null || adapted === void 0 ? void 0 : adapted.computedMap, engineRawOutput: (_a = llmResult === null || llmResult === void 0 ? void 0 : llmResult.rawOutput) !== null && _a !== void 0 ? _a : llmResult === null || llmResult === void 0 ? void 0 : llmResult.engineOutput })));
|
|
323
446
|
case 'PREPARER_WORKPAPER':
|
|
324
447
|
return (react_1.default.createElement(ShellContainer, { fullWidth: true },
|
|
325
|
-
react_1.default.createElement(TaxAxisPreparerWorkpaper_1.TaxAxisPreparerWorkpaper, { profile: profile, userContext: userContext, onBack: () => setStep('DASHBOARD'), onToggleToClient: () => setStep('CLIENT_REPORT'), liveStrategies: adapted === null || adapted === void 0 ? void 0 : adapted.strategies, liveComputedMap: adapted === null || adapted === void 0 ? void 0 : adapted.computedMap })));
|
|
448
|
+
react_1.default.createElement(TaxAxisPreparerWorkpaper_1.TaxAxisPreparerWorkpaper, { profile: profile, userContext: userContext, onBack: () => setStep('DASHBOARD'), onToggleToClient: () => setStep('CLIENT_REPORT'), liveStrategies: adapted === null || adapted === void 0 ? void 0 : adapted.strategies, liveComputedMap: adapted === null || adapted === void 0 ? void 0 : adapted.computedMap, cpaWorkflow: (_b = llmResult === null || llmResult === void 0 ? void 0 : llmResult.rawOutput) === null || _b === void 0 ? void 0 : _b.cpa_workflow, riskDisclosures: (_c = llmResult === null || llmResult === void 0 ? void 0 : llmResult.rawOutput) === null || _c === void 0 ? void 0 : _c.risk_disclosures, businessProfile: (_d = llmResult === null || llmResult === void 0 ? void 0 : llmResult.rawOutput) === null || _d === void 0 ? void 0 : _d.business_profile })));
|
|
326
449
|
case 'PRESENTATION':
|
|
327
450
|
return (react_1.default.createElement(ShellContainer, { fullWidth: true },
|
|
328
451
|
react_1.default.createElement(TaxAxisPresentationMode_1.TaxAxisPresentationMode, { profile: profile, userContext: userContext, onBack: () => setStep(isProspectFlow ? 'PROSPECT_REPORT' : 'DASHBOARD') })));
|
|
@@ -344,6 +467,8 @@ const TaxAxisShell = ({ taxAxisApi, userContext = 'expert', initialSessionId, in
|
|
|
344
467
|
handleSendReport,
|
|
345
468
|
llmResult,
|
|
346
469
|
adapted,
|
|
470
|
+
reportReady,
|
|
471
|
+
parsedDocuments,
|
|
347
472
|
]);
|
|
348
473
|
return (react_1.default.createElement(react_1.default.Fragment, null,
|
|
349
474
|
error && (react_1.default.createElement("div", { className: 'fixed right-4 top-4 z-[200] max-w-sm rounded-lg border border-red-500/30 bg-tax-axis-surface px-4 py-3 text-xs text-red-200 shadow-lg' },
|
|
@@ -10,6 +10,9 @@ interface ExecutiveSummaryProps {
|
|
|
10
10
|
nowCount: number;
|
|
11
11
|
top3: Strategy[];
|
|
12
12
|
palette: Palette;
|
|
13
|
+
effectiveTaxRate?: number;
|
|
14
|
+
confidenceTier?: string;
|
|
15
|
+
dataYears?: number;
|
|
13
16
|
}
|
|
14
|
-
export declare function ExecutiveSummary({ profile, eligible, computed, totalLo, totalHi, nowCount, top3, palette }: ExecutiveSummaryProps): React.JSX.Element;
|
|
17
|
+
export declare function ExecutiveSummary({ profile, eligible, computed, totalLo, totalHi, nowCount, top3, palette, effectiveTaxRate, confidenceTier, dataYears }: ExecutiveSummaryProps): React.JSX.Element;
|
|
15
18
|
export {};
|
|
@@ -8,16 +8,17 @@ const react_1 = __importDefault(require("react"));
|
|
|
8
8
|
const compute_1 = require("../../lib/compute");
|
|
9
9
|
const SectionOpener_1 = require("./SectionOpener");
|
|
10
10
|
const ETRChart_1 = require("./ETRChart");
|
|
11
|
-
function ExecutiveSummary({ profile, eligible, computed, totalLo, totalHi, nowCount, top3, palette }) {
|
|
11
|
+
function ExecutiveSummary({ profile, eligible, computed, totalLo, totalHi, nowCount, top3, palette, effectiveTaxRate, confidenceTier, dataYears }) {
|
|
12
12
|
const bizName = profile.bizName || "Client";
|
|
13
13
|
const rev = parseInt((profile.revenue || "0").replace(/,/g, "")) || 500000;
|
|
14
|
+
const dataYearsLabel = dataYears ? `${dataYears} year${dataYears > 1 ? "s" : ""}` : (profile.taxDataYears || "1 year");
|
|
14
15
|
return (react_1.default.createElement(react_1.default.Fragment, null,
|
|
15
16
|
react_1.default.createElement(SectionOpener_1.SectionOpener, { number: "01", eyebrow: "EXECUTIVE SUMMARY", headline: "Where " + bizName + "'s tax dollars are going \u2014 and where they don't have to.", bullets: [
|
|
16
17
|
eligible.length + " strategies apply to your current profile, with combined estimated savings of $" + totalLo + "K\u2013$" + totalHi + "K annually.",
|
|
17
18
|
nowCount > 0
|
|
18
19
|
? nowCount + " can be initiated this week without any structural changes to your business."
|
|
19
20
|
: "Implementation timeline spans the current tax year with no structural changes required.",
|
|
20
|
-
"Analysis is based on " +
|
|
21
|
+
"Analysis is based on " + dataYearsLabel + " of financial data and current law as of April 2026, including OBBBA provisions.",
|
|
21
22
|
], palette: palette }),
|
|
22
23
|
react_1.default.createElement("div", { style: { marginBottom: 28 } },
|
|
23
24
|
react_1.default.createElement("div", { style: { background: palette.gray50, border: "1px solid " + palette.gray200, borderRadius: 10, padding: "24px 28px", marginBottom: 20 } },
|
|
@@ -46,15 +47,18 @@ function ExecutiveSummary({ profile, eligible, computed, totalLo, totalHi, nowCo
|
|
|
46
47
|
nowCount > 0 ? "Of these and the broader eligible set, " + nowCount + " can be initiated this week with no structural changes to your business." : "",
|
|
47
48
|
" Savings estimates reflect your current revenue of ",
|
|
48
49
|
"$" + Math.round(rev / 1000) + "K",
|
|
49
|
-
",
|
|
50
|
-
parseFloat(profile.federalRate || "24") + parseFloat(profile.stateRate || "4.95"),
|
|
50
|
+
", an effective tax rate of ",
|
|
51
|
+
effectiveTaxRate != null ? Math.round((effectiveTaxRate <= 1 ? effectiveTaxRate * 100 : effectiveTaxRate) * 10) / 10 : (parseFloat(profile.federalRate || "24") + parseFloat(profile.stateRate || "4.95")),
|
|
51
52
|
"%, and ",
|
|
52
|
-
|
|
53
|
+
dataYearsLabel,
|
|
53
54
|
" of financial data."),
|
|
54
55
|
(() => {
|
|
55
56
|
const fedRate = parseFloat(profile.federalRate || "24");
|
|
56
57
|
const stateRate = parseFloat(profile.stateRate || "4.95");
|
|
57
|
-
|
|
58
|
+
// If engine provided effective_tax_rate (as a decimal 0-1), convert to percentage
|
|
59
|
+
const currentRate = effectiveTaxRate != null
|
|
60
|
+
? (effectiveTaxRate <= 1 ? effectiveTaxRate * 100 : effectiveTaxRate)
|
|
61
|
+
: (fedRate + stateRate);
|
|
58
62
|
const net = (0, compute_1.parseNum)(profile.netIncome) || (rev * 0.20);
|
|
59
63
|
const aggregateMidSavings = eligible.reduce((a, s) => {
|
|
60
64
|
var _a, _b;
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import type { ClientProfile, Strategy, ComputedMap } from "../../lib/types";
|
|
3
3
|
import type { Palette } from "./palette";
|
|
4
|
+
interface InteractionWarning {
|
|
5
|
+
strategy_pair: [string, string];
|
|
6
|
+
warning_text: string;
|
|
7
|
+
}
|
|
4
8
|
interface RecommendedStrategiesProps {
|
|
5
9
|
profile: ClientProfile;
|
|
6
10
|
eligible: Strategy[];
|
|
@@ -8,6 +12,7 @@ interface RecommendedStrategiesProps {
|
|
|
8
12
|
top3: Strategy[];
|
|
9
13
|
hasOBBBA: boolean;
|
|
10
14
|
palette: Palette;
|
|
15
|
+
interactionWarnings?: InteractionWarning[];
|
|
11
16
|
}
|
|
12
|
-
export declare function RecommendedStrategies({ profile, eligible, computed, top3, hasOBBBA, palette }: RecommendedStrategiesProps): React.JSX.Element;
|
|
17
|
+
export declare function RecommendedStrategies({ profile, eligible, computed, top3, hasOBBBA, palette, interactionWarnings }: RecommendedStrategiesProps): React.JSX.Element;
|
|
13
18
|
export {};
|
|
@@ -5,47 +5,49 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.RecommendedStrategies = RecommendedStrategies;
|
|
7
7
|
const react_1 = __importDefault(require("react"));
|
|
8
|
-
const data_1 = require("../../lib/data");
|
|
9
8
|
const SectionOpener_1 = require("./SectionOpener");
|
|
10
9
|
const SavingsStackChart_1 = require("./SavingsStackChart");
|
|
11
10
|
const StrategyCard_1 = require("./StrategyCard");
|
|
12
|
-
function RecommendedStrategies({ profile, eligible, computed, top3, hasOBBBA, palette }) {
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
const
|
|
17
|
-
|
|
11
|
+
function RecommendedStrategies({ profile, eligible, computed, top3, hasOBBBA, palette, interactionWarnings = [] }) {
|
|
12
|
+
// Build a map from strategy code → strategy index in top3 (for interaction display)
|
|
13
|
+
const top3Codes = new Set(top3.map(s => s.code));
|
|
14
|
+
// Interaction warnings that involve at least one top-3 strategy
|
|
15
|
+
const activeWarnings = interactionWarnings.filter((w) => w.strategy_pair.some((id) => top3Codes.has(id)));
|
|
16
|
+
// For each top-3 strategy, find if it participates in any active interaction
|
|
17
|
+
const interactionMap = new Map(); // code → index of the other strategy in top3
|
|
18
|
+
for (const w of activeWarnings) {
|
|
19
|
+
const [a, b] = w.strategy_pair;
|
|
20
|
+
const idxA = top3.findIndex(s => s.code === a);
|
|
21
|
+
const idxB = top3.findIndex(s => s.code === b);
|
|
22
|
+
if (idxA >= 0 && idxB >= 0) {
|
|
23
|
+
interactionMap.set(a, idxB + 1);
|
|
24
|
+
interactionMap.set(b, idxA + 1);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
18
27
|
const stSavings = (s) => {
|
|
19
28
|
var _a, _b;
|
|
20
29
|
const c = computed.get(s.rank);
|
|
21
30
|
const lo = Math.round(((_a = c === null || c === void 0 ? void 0 : c.lo) !== null && _a !== void 0 ? _a : s.lo) / 100) * 100;
|
|
22
31
|
const hi = Math.round(((_b = c === null || c === void 0 ? void 0 : c.hi) !== null && _b !== void 0 ? _b : s.hi) / 100) * 100;
|
|
23
|
-
return "$" + (lo / 1000).toFixed(1) + "K
|
|
32
|
+
return "$" + (lo / 1000).toFixed(1) + "K–$" + (hi / 1000).toFixed(1) + "K";
|
|
24
33
|
};
|
|
25
34
|
return (react_1.default.createElement(react_1.default.Fragment, null,
|
|
26
35
|
react_1.default.createElement(SectionOpener_1.SectionOpener, { number: "02", eyebrow: "RECOMMENDED STRATEGIES", headline: "Three priorities to discuss first.", bullets: [
|
|
27
|
-
|
|
28
|
-
"Each strategy
|
|
36
|
+
top3.length + " strategies highlighted below are ranked by combined impact score — weighting dollar value, audit posture, and implementation effort.",
|
|
37
|
+
"Each strategy is anchored to a specific IRC section and explains the math, the audit posture, and the implementation path.",
|
|
29
38
|
hasOBBBA
|
|
30
|
-
? "One or more recommendations rely on OBBBA provisions enacted July 2025 with limited IRS guidance
|
|
39
|
+
? "One or more recommendations rely on OBBBA provisions enacted July 2025 with limited IRS guidance — flagged where applicable."
|
|
31
40
|
: "All recommendations rely on settled IRS guidance, published rulings, or controlling case law.",
|
|
32
41
|
], palette: palette }),
|
|
33
42
|
react_1.default.createElement(SavingsStackChart_1.SavingsStackChart, { eligible: eligible, computed: computed, palette: palette }),
|
|
34
43
|
react_1.default.createElement("div", { style: { marginBottom: 28 } }, top3.map((s, i) => {
|
|
44
|
+
var _a, _b;
|
|
35
45
|
const rank = i + 1;
|
|
36
|
-
const
|
|
37
|
-
const
|
|
38
|
-
return (react_1.default.createElement(StrategyCard_1.StrategyCard, { key: s.rank, strategy: s, rank: rank, profile: profile, computed: computed, palette: palette, nextStepText:
|
|
46
|
+
const interactsIdx = (_a = interactionMap.get(s.code)) !== null && _a !== void 0 ? _a : 0;
|
|
47
|
+
const interactsWith = interactsIdx > 0 ? (_b = top3[interactsIdx - 1]) === null || _b === void 0 ? void 0 : _b.rank : undefined;
|
|
48
|
+
return (react_1.default.createElement(StrategyCard_1.StrategyCard, { key: s.rank, strategy: s, rank: rank, profile: profile, computed: computed, palette: palette, nextStepText: s.action || "Discuss with your CPA to implement this strategy.", interactsWith: interactsWith, interactsIdx: interactsIdx, stSavings: stSavings(s) }));
|
|
39
49
|
})),
|
|
40
|
-
|
|
50
|
+
activeWarnings.length > 0 && (react_1.default.createElement("div", { style: { background: palette.orangePale, borderLeft: "3px solid " + palette.orange, borderRadius: "0 8px 8px 0", padding: "16px 20px", marginBottom: 24 } },
|
|
41
51
|
react_1.default.createElement("div", { style: { fontSize: 13, fontWeight: 700, color: palette.gray800, fontFamily: palette.head, marginBottom: 6 } }, "Strategy Interactions"),
|
|
42
|
-
|
|
43
|
-
const idxA = top3.findIndex(t => t.rank === a) + 1;
|
|
44
|
-
const idxB = top3.findIndex(t => t.rank === b) + 1;
|
|
45
|
-
return (react_1.default.createElement("p", { key: pi, style: { fontSize: 12, color: palette.gray700, lineHeight: 1.7, fontFamily: palette.body, marginBottom: 4 } }, "Strategies #" + idxA + " and #" + idxB + " affect each other. Your preparer has accounted for this \u2014 the combined savings may be less than the sum of individual estimates."));
|
|
46
|
-
})))));
|
|
47
|
-
}
|
|
48
|
-
// Inline fallback for next-steps text when NARRATIVE_FALLBACK is needed
|
|
49
|
-
function NARRATIVE_FALLBACK_NEXT(s) {
|
|
50
|
-
return "Discuss with your preparer to implement " + s.name + ".";
|
|
52
|
+
activeWarnings.map((w, i) => (react_1.default.createElement("p", { key: i, style: { fontSize: 12, color: palette.gray700, lineHeight: 1.7, fontFamily: palette.body, marginBottom: 4 } }, w.warning_text)))))));
|
|
51
53
|
}
|
|
@@ -12,5 +12,5 @@ interface StrategyCardProps {
|
|
|
12
12
|
interactsIdx: number;
|
|
13
13
|
stSavings: string;
|
|
14
14
|
}
|
|
15
|
-
export declare function StrategyCard({ strategy: s, rank, profile, computed, palette, nextStepText, interactsWith, interactsIdx, stSavings, }: StrategyCardProps): React.JSX.Element;
|
|
15
|
+
export declare function StrategyCard({ strategy: s, rank, profile: _profile, computed, palette, nextStepText, interactsWith, interactsIdx, stSavings, }: StrategyCardProps): React.JSX.Element;
|
|
16
16
|
export {};
|
|
@@ -5,11 +5,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.StrategyCard = StrategyCard;
|
|
7
7
|
const react_1 = __importDefault(require("react"));
|
|
8
|
-
const data_1 = require("../../lib/data");
|
|
9
8
|
const Sidebar_1 = require("./Sidebar");
|
|
10
|
-
|
|
11
|
-
const FootnoteBlock_1 = require("./FootnoteBlock");
|
|
12
|
-
function StrategyCard({ strategy: s, rank, profile, computed, palette, nextStepText, interactsWith, interactsIdx, stSavings, }) {
|
|
9
|
+
function StrategyCard({ strategy: s, rank, profile: _profile, computed, palette, nextStepText, interactsWith, interactsIdx, stSavings, }) {
|
|
13
10
|
const card = {
|
|
14
11
|
background: palette.white,
|
|
15
12
|
border: "1px solid " + palette.gray200,
|
|
@@ -19,14 +16,13 @@ function StrategyCard({ strategy: s, rank, profile, computed, palette, nextStepT
|
|
|
19
16
|
boxShadow: "0 1px 3px rgba(0,0,0,0.04)",
|
|
20
17
|
};
|
|
21
18
|
const pct = s.score;
|
|
22
|
-
const narr = data_1.STRATEGY_NARRATIVE[s.rank];
|
|
23
19
|
const c = computed.get(s.rank);
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
const
|
|
20
|
+
// Live text — client brief is the plain-language "why it applies to you" sentence
|
|
21
|
+
const clientFacingWhy = s.clientBrief || "";
|
|
22
|
+
// Full CPA engagement recommendation covers both why + breakdown
|
|
23
|
+
const cpaEngagement = s.abstract || "";
|
|
24
|
+
// Next step: prefer live engine value, fall back to passed-in prop, then generic
|
|
25
|
+
const nextText = s.action || nextStepText || "Work with your CPA to implement this strategy.";
|
|
30
26
|
return (react_1.default.createElement("div", { className: "strategy-card", style: Object.assign(Object.assign({}, card), { padding: 0, overflow: "hidden", marginBottom: 20 }) },
|
|
31
27
|
react_1.default.createElement("div", { style: { padding: "24px 28px 20px" } },
|
|
32
28
|
react_1.default.createElement("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "flex-start", marginBottom: 16 } },
|
|
@@ -34,7 +30,9 @@ function StrategyCard({ strategy: s, rank, profile, computed, palette, nextStepT
|
|
|
34
30
|
react_1.default.createElement("div", { style: { width: 36, height: 36, borderRadius: "50%", background: "linear-gradient(135deg," + palette.teal + ",#1A6B6C)", display: "flex", alignItems: "center", justifyContent: "center", fontSize: 15, fontWeight: 700, color: palette.white, fontFamily: palette.body, flexShrink: 0 } }, rank),
|
|
35
31
|
react_1.default.createElement("div", null,
|
|
36
32
|
react_1.default.createElement("div", { style: { fontSize: 18, fontWeight: 700, color: palette.gray900, fontFamily: palette.head } }, s.name),
|
|
37
|
-
react_1.default.createElement("div", { style: { fontSize: 12, color: palette.gray500, fontFamily: palette.mono, marginTop: 3 } },
|
|
33
|
+
react_1.default.createElement("div", { style: { fontSize: 12, color: palette.gray500, fontFamily: palette.mono, marginTop: 3 } },
|
|
34
|
+
s.code + " · " + s.timeline,
|
|
35
|
+
s.quickWin && (react_1.default.createElement("span", { style: { marginLeft: 8, background: palette.tealPale, color: palette.teal, padding: "1px 6px", borderRadius: 4, fontSize: 10, fontWeight: 700 } }, "\u26A1 Quick Win"))))),
|
|
38
36
|
react_1.default.createElement("div", { style: { textAlign: "right", flexShrink: 0 } },
|
|
39
37
|
react_1.default.createElement("div", { style: { fontSize: 20, fontWeight: 800, color: palette.teal, fontFamily: palette.head } }, stSavings),
|
|
40
38
|
react_1.default.createElement("div", { style: { fontSize: 10, color: palette.gray400, fontFamily: palette.body } }, "est. annual savings"))),
|
|
@@ -45,19 +43,37 @@ function StrategyCard({ strategy: s, rank, profile, computed, palette, nextStepT
|
|
|
45
43
|
pct,
|
|
46
44
|
"/100")),
|
|
47
45
|
react_1.default.createElement(Sidebar_1.Sidebar, { ircCode: s.code, palette: palette }),
|
|
48
|
-
react_1.default.createElement("div", { style: { marginBottom: 12 } },
|
|
49
|
-
react_1.default.createElement("div", { style: { fontSize: 11, fontWeight: 700, textTransform: "uppercase", letterSpacing: "0.1em", color: palette.gray400, marginBottom: 8, fontFamily: palette.mono } }, "WHY THIS
|
|
50
|
-
react_1.default.createElement("p", { style: { fontSize: 13, color: palette.gray700, lineHeight: 1.75, fontFamily: palette.body } },
|
|
51
|
-
react_1.default.createElement("div", { style: { marginBottom: 12 } },
|
|
52
|
-
react_1.default.createElement("div", { style: { fontSize: 11, fontWeight: 700, textTransform: "uppercase", letterSpacing: "0.1em", color: palette.gray400, marginBottom: 8, fontFamily: palette.mono } }, "
|
|
53
|
-
react_1.default.createElement("
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
46
|
+
clientFacingWhy && (react_1.default.createElement("div", { style: { marginBottom: 12 } },
|
|
47
|
+
react_1.default.createElement("div", { style: { fontSize: 11, fontWeight: 700, textTransform: "uppercase", letterSpacing: "0.1em", color: palette.gray400, marginBottom: 8, fontFamily: palette.mono } }, "WHY THIS APPLIES TO YOU"),
|
|
48
|
+
react_1.default.createElement("p", { style: { fontSize: 13, color: palette.gray700, lineHeight: 1.75, fontFamily: palette.body } }, clientFacingWhy))),
|
|
49
|
+
s.sourceDocuments && s.sourceDocuments.length > 0 && (react_1.default.createElement("div", { style: { marginBottom: 12 } },
|
|
50
|
+
react_1.default.createElement("div", { style: { fontSize: 11, fontWeight: 700, textTransform: "uppercase", letterSpacing: "0.1em", color: palette.gray400, marginBottom: 8, fontFamily: palette.mono } }, "DATA USED IN THIS ANALYSIS"),
|
|
51
|
+
react_1.default.createElement("div", { style: { border: "1px solid " + palette.gray200, borderRadius: 8, overflow: "hidden" } }, s.sourceDocuments.map((ref, i) => (react_1.default.createElement("div", { key: i, style: {
|
|
52
|
+
display: "flex",
|
|
53
|
+
justifyContent: "space-between",
|
|
54
|
+
alignItems: "center",
|
|
55
|
+
padding: "8px 14px",
|
|
56
|
+
background: i % 2 === 0 ? palette.gray50 : palette.white,
|
|
57
|
+
borderTop: i > 0 ? "1px solid " + palette.gray100 : "none",
|
|
58
|
+
gap: 12,
|
|
59
|
+
} },
|
|
60
|
+
react_1.default.createElement("div", { style: { flex: 1 } },
|
|
61
|
+
react_1.default.createElement("div", { style: { fontSize: 12, fontWeight: 600, color: palette.gray800, fontFamily: palette.body } }, ref.field_label),
|
|
62
|
+
react_1.default.createElement("div", { style: { fontSize: 10, color: palette.gray400, fontFamily: palette.mono, marginTop: 1 } }, ref.source_hint)),
|
|
63
|
+
react_1.default.createElement("div", { style: { fontSize: 13, fontWeight: 700, color: palette.teal, fontFamily: palette.mono, flexShrink: 0 } }, ref.value_display))))))),
|
|
64
|
+
cpaEngagement && (react_1.default.createElement("div", { style: { marginBottom: 12 } },
|
|
65
|
+
react_1.default.createElement("div", { style: { fontSize: 11, fontWeight: 700, textTransform: "uppercase", letterSpacing: "0.1em", color: palette.gray400, marginBottom: 8, fontFamily: palette.mono } }, "CPA ANALYSIS"),
|
|
66
|
+
react_1.default.createElement("p", { style: { fontSize: 13, color: palette.gray700, lineHeight: 1.75, fontFamily: palette.body, whiteSpace: "pre-line" } }, cpaEngagement))),
|
|
67
|
+
s.specialistNote && (react_1.default.createElement("div", { style: { marginBottom: 14, borderLeft: "3px solid " + palette.orange, paddingLeft: 16 } },
|
|
68
|
+
react_1.default.createElement("div", { style: { fontSize: 11, fontWeight: 700, textTransform: "uppercase", letterSpacing: "0.1em", color: palette.orange, marginBottom: 6, fontFamily: palette.mono } }, "WHY YOU NEED A SPECIALIST"),
|
|
69
|
+
react_1.default.createElement("p", { style: { fontSize: 13, color: palette.gray700, lineHeight: 1.75, fontFamily: palette.body } }, s.specialistNote))),
|
|
70
|
+
s.authority && (react_1.default.createElement("div", { style: { marginBottom: 14, padding: "6px 10px", background: palette.gray50, borderRadius: 6, fontSize: 11, fontFamily: palette.mono, color: palette.gray500 } },
|
|
71
|
+
"Authority: ",
|
|
72
|
+
s.authority,
|
|
73
|
+
s.forms ? " · Forms: " + s.forms : "")),
|
|
57
74
|
react_1.default.createElement("div", { className: "card-footer" },
|
|
58
75
|
react_1.default.createElement("div", { style: { background: palette.tealPale, borderRadius: 8, padding: "12px 16px" } },
|
|
59
76
|
react_1.default.createElement("div", { style: { fontSize: 11, fontWeight: 700, textTransform: "uppercase", letterSpacing: "0.1em", color: palette.green, marginBottom: 6, fontFamily: palette.mono } }, "WHAT WE'D DO NEXT"),
|
|
60
|
-
react_1.default.createElement("p", { style: { fontSize: 13, color: palette.gray700, lineHeight: 1.7, fontFamily: palette.body } },
|
|
61
|
-
react_1.default.createElement(FootnoteBlock_1.FootnoteBlock, { assigned: fnCounter.assigned, palette: palette }))),
|
|
77
|
+
react_1.default.createElement("p", { style: { fontSize: 13, color: palette.gray700, lineHeight: 1.7, fontFamily: palette.body } }, nextText)))),
|
|
62
78
|
interactsWith !== undefined && interactsIdx > 0 && (react_1.default.createElement("div", { style: { padding: "10px 28px", background: palette.orangePale, borderTop: "1px solid rgba(251,154,29,0.2)", fontSize: 12, color: palette.gray700, fontFamily: palette.body } }, "Note: This strategy interacts with Strategy #" + interactsIdx + ". Combined savings may differ from the sum of individual estimates."))));
|
|
63
79
|
}
|
|
@@ -9,5 +9,7 @@ export interface TaxAxisClientReportProps extends TaxAxisScreenProps {
|
|
|
9
9
|
liveStrategies?: Strategy[];
|
|
10
10
|
/** When provided, bypasses local computeAllStrategies. Must accompany liveStrategies. */
|
|
11
11
|
liveComputedMap?: ComputedMap;
|
|
12
|
+
/** Live engine output fields — passed through to sub-components */
|
|
13
|
+
engineRawOutput?: Record<string, any>;
|
|
12
14
|
}
|
|
13
|
-
export declare function TaxAxisClientReport({ profile, onBack, onNavigatePreparer, liveStrategies, liveComputedMap }: TaxAxisClientReportProps): React.JSX.Element;
|
|
15
|
+
export declare function TaxAxisClientReport({ profile, onBack, onNavigatePreparer, liveStrategies, liveComputedMap, engineRawOutput }: TaxAxisClientReportProps): React.JSX.Element;
|
|
@@ -34,8 +34,8 @@ const ExecutiveSummary_1 = require("./ExecutiveSummary");
|
|
|
34
34
|
const RecommendedStrategies_1 = require("./RecommendedStrategies");
|
|
35
35
|
const ImplementationRoadmap_1 = require("./ImplementationRoadmap");
|
|
36
36
|
const Methodology_1 = require("./Methodology");
|
|
37
|
-
function TaxAxisClientReport({ profile, onBack, onNavigatePreparer, liveStrategies, liveComputedMap }) {
|
|
38
|
-
var _a, _b, _c;
|
|
37
|
+
function TaxAxisClientReport({ profile, onBack, onNavigatePreparer, liveStrategies, liveComputedMap, engineRawOutput }) {
|
|
38
|
+
var _a, _b, _c, _d, _e, _f;
|
|
39
39
|
const [view, setView] = (0, react_1.useState)("client");
|
|
40
40
|
const staticEligible = (0, react_1.useMemo)(() => (0, compute_1.filterEligibleStrategies)(profile), [profile]);
|
|
41
41
|
const staticComputed = (0, react_1.useMemo)(() => (0, compute_1.computeAllStrategies)(profile), [profile]);
|
|
@@ -110,11 +110,11 @@ function TaxAxisClientReport({ profile, onBack, onNavigatePreparer, liveStrategi
|
|
|
110
110
|
react_1.default.createElement("div", { style: { maxWidth: 680, margin: "0 auto", padding: "32px 20px 60px" } },
|
|
111
111
|
react_1.default.createElement(ClientReportCover_1.ClientReportCover, { profile: profile, palette: palette_1.P }),
|
|
112
112
|
react_1.default.createElement(ClientReportTOC_1.ClientReportTOC, { bizName: bizName, palette: palette_1.P }),
|
|
113
|
-
react_1.default.createElement(ExecutiveSummary_1.ExecutiveSummary, { profile: profile, eligible: eligible, computed: computed, totalLo: totalLo, totalHi: totalHi, nowCount: nowCount, top3: top3, palette: palette_1.P }),
|
|
113
|
+
react_1.default.createElement(ExecutiveSummary_1.ExecutiveSummary, { profile: profile, eligible: eligible, computed: computed, totalLo: totalLo, totalHi: totalHi, nowCount: nowCount, top3: top3, palette: palette_1.P, effectiveTaxRate: (_d = engineRawOutput === null || engineRawOutput === void 0 ? void 0 : engineRawOutput.business_profile) === null || _d === void 0 ? void 0 : _d.effective_tax_rate, confidenceTier: (_e = engineRawOutput === null || engineRawOutput === void 0 ? void 0 : engineRawOutput.business_profile) === null || _e === void 0 ? void 0 : _e.confidence_tier, dataYears: (_f = engineRawOutput === null || engineRawOutput === void 0 ? void 0 : engineRawOutput.business_profile) === null || _f === void 0 ? void 0 : _f.data_years }),
|
|
114
114
|
react_1.default.createElement("div", { style: { background: palette_1.P.gray50, borderLeft: "3px solid " + palette_1.P.gray300, borderRadius: "0 8px 8px 0", padding: "14px 20px", marginBottom: 20, fontSize: 11, color: palette_1.P.gray500, lineHeight: 1.7, fontFamily: palette_1.P.body } },
|
|
115
115
|
"This summary was prepared using TaxAxis, Paro's AI-powered tax analysis engine. Savings estimates are based on financial data provided for tax year " + profile.year + " (" + (profile.taxDataYears || "1 year") + " of data). All figures are estimates. Your tax preparer has reviewed these recommendations.",
|
|
116
116
|
hasOBBBA && " Some strategies reference provisions from the One Big Beautiful Bill Act (OBBBA), signed July 4, 2025, with limited IRS guidance."),
|
|
117
|
-
react_1.default.createElement(RecommendedStrategies_1.RecommendedStrategies, { profile: profile, eligible: eligible, computed: computed, top3: top3, hasOBBBA: hasOBBBA, palette: palette_1.P }),
|
|
117
|
+
react_1.default.createElement(RecommendedStrategies_1.RecommendedStrategies, { profile: profile, eligible: eligible, computed: computed, top3: top3, hasOBBBA: hasOBBBA, palette: palette_1.P, interactionWarnings: engineRawOutput === null || engineRawOutput === void 0 ? void 0 : engineRawOutput.strategy_interaction_warnings }),
|
|
118
118
|
react_1.default.createElement(ImplementationRoadmap_1.ImplementationRoadmap, { eligible: eligible, computed: computed, top3: top3, nowCount: nowCount, nowMidK: nowMidK, bucketDefs: bucketDefs, bucketTotals: bucketTotals, activeBuckets: activeBuckets, palette: palette_1.P }),
|
|
119
119
|
react_1.default.createElement(Methodology_1.Methodology, { profile: profile, eligible: eligible, palette: palette_1.P }),
|
|
120
120
|
react_1.default.createElement("div", { style: { padding: "16px 20px", marginBottom: 16, fontSize: 10, color: palette_1.P.gray400, lineHeight: 1.7, fontFamily: palette_1.P.body } }, "This summary was generated by TaxAxis, an AI-powered analysis tool by Paro. Recommendations are based on financial data provided and current tax law as of April 2026, including OBBBA provisions. These are estimates, not guarantees. Consult your tax preparer before implementing any strategy. This is not legal or financial advice."),
|
|
@@ -6,6 +6,11 @@ interface DashboardSummaryProps {
|
|
|
6
6
|
computed: ComputedMap;
|
|
7
7
|
dataConfirmed: boolean;
|
|
8
8
|
reviewUnreviewed: number;
|
|
9
|
+
liveSavingsMin?: number;
|
|
10
|
+
liveSavingsMax?: number;
|
|
11
|
+
liveStrategyCount?: number;
|
|
12
|
+
confidenceTier?: string;
|
|
13
|
+
dataYears?: number;
|
|
9
14
|
}
|
|
10
|
-
export declare function DashboardSummary({ profile, dashEligible, computed, dataConfirmed, reviewUnreviewed, }: DashboardSummaryProps): React.JSX.Element;
|
|
15
|
+
export declare function DashboardSummary({ profile, dashEligible, computed, dataConfirmed, reviewUnreviewed, liveSavingsMin, liveSavingsMax, liveStrategyCount, confidenceTier, dataYears, }: DashboardSummaryProps): React.JSX.Element;
|
|
11
16
|
export {};
|