@paro.io/expert-shared-components 1.14.54 → 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.
Files changed (26) hide show
  1. package/lib/components/TaxAxis/TaxAxisApi.d.ts +2 -0
  2. package/lib/components/TaxAxis/TaxAxisShell.js +102 -13
  3. package/lib/tax-axis/components/clientReport/ExecutiveSummary.d.ts +4 -1
  4. package/lib/tax-axis/components/clientReport/ExecutiveSummary.js +10 -6
  5. package/lib/tax-axis/components/clientReport/RecommendedStrategies.d.ts +6 -1
  6. package/lib/tax-axis/components/clientReport/RecommendedStrategies.js +26 -24
  7. package/lib/tax-axis/components/clientReport/StrategyCard.d.ts +1 -1
  8. package/lib/tax-axis/components/clientReport/StrategyCard.js +39 -23
  9. package/lib/tax-axis/components/clientReport/TaxAxisClientReport.d.ts +3 -1
  10. package/lib/tax-axis/components/clientReport/TaxAxisClientReport.js +4 -4
  11. package/lib/tax-axis/components/dashboard/DashboardSummary.d.ts +6 -1
  12. package/lib/tax-axis/components/dashboard/DashboardSummary.js +14 -4
  13. package/lib/tax-axis/components/dashboard/StrategyDetailPanel.d.ts +1 -1
  14. package/lib/tax-axis/components/dashboard/StrategyDetailPanel.js +123 -91
  15. package/lib/tax-axis/components/dashboard/TaxAxisDashboard.d.ts +3 -2
  16. package/lib/tax-axis/components/dashboard/TaxAxisDashboard.js +67 -19
  17. package/lib/tax-axis/components/documents/TaxAxisDocuments.d.ts +2 -0
  18. package/lib/tax-axis/components/documents/TaxAxisDocuments.js +46 -10
  19. package/lib/tax-axis/components/preparerWorkpaper/TaxAxisPreparerWorkpaper.d.ts +21 -1
  20. package/lib/tax-axis/components/preparerWorkpaper/TaxAxisPreparerWorkpaper.js +10 -1
  21. package/lib/tax-axis/components/processing/TaxAxisProcessing.d.ts +3 -1
  22. package/lib/tax-axis/components/processing/TaxAxisProcessing.js +77 -31
  23. package/lib/tax-axis/lib/adapters/useEngineOutput.d.ts +7 -0
  24. package/lib/tax-axis/lib/adapters/useEngineOutput.js +8 -3
  25. package/lib/tax-axis/lib/types/index.d.ts +10 -1
  26. 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,14 +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);
105
149
  const isPollingRef = react_1.default.useRef(false);
106
150
  // Derive live strategies from engineOutput so CLIENT_REPORT and PREPARER_WORKPAPER
107
151
  // render real engine data instead of the static STRATEGIES catalog.
108
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]);
109
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]);
110
185
  // On mount: if a sessionId was injected (e.g. returning to an existing session),
111
186
  // reload the last GENERATION llmRun so the report screens get live data.
112
187
  (0, react_1.useEffect)(() => {
@@ -117,9 +192,11 @@ const TaxAxisShell = ({ taxAxisApi, userContext = 'expert', initialSessionId, in
117
192
  if (run === null || run === void 0 ? void 0 : run.outputPayload) {
118
193
  setLlmResult(run.outputPayload);
119
194
  setStep('DASHBOARD');
195
+ fetchAndSetParsedDocuments(initialSessionId);
120
196
  }
121
197
  })
122
198
  .catch(() => { });
199
+ // fetchAndSetParsedDocuments intentionally excluded: only runs on mount for initial session restore
123
200
  // eslint-disable-next-line react-hooks/exhaustive-deps
124
201
  }, [initialSessionId]);
125
202
  const updateSessionId = (0, react_1.useCallback)((nextSessionId) => {
@@ -152,6 +229,7 @@ const TaxAxisShell = ({ taxAxisApi, userContext = 'expert', initialSessionId, in
152
229
  setProfile(null);
153
230
  setIsProspectFlow(false);
154
231
  setError(null);
232
+ setReportReady(false);
155
233
  updateSessionId(null);
156
234
  }, [updateSessionId]);
157
235
  const handleProspect = (0, react_1.useCallback)((nextProfile) => {
@@ -228,6 +306,7 @@ const TaxAxisShell = ({ taxAxisApi, userContext = 'expert', initialSessionId, in
228
306
  let attempts = 0;
229
307
  isPollingRef.current = true;
230
308
  const poll = () => __awaiter(void 0, void 0, void 0, function* () {
309
+ var _a;
231
310
  attempts++;
232
311
  try {
233
312
  const session = yield taxAxisApi.getSession(sid);
@@ -238,17 +317,25 @@ const TaxAxisShell = ({ taxAxisApi, userContext = 'expert', initialSessionId, in
238
317
  setLlmResult(run.outputPayload);
239
318
  }
240
319
  }
320
+ yield fetchAndSetParsedDocuments(sid);
241
321
  isPollingRef.current = false;
242
- setStep('DASHBOARD');
322
+ // Signal the processing screen to animate to 100% — it calls onComplete when done
323
+ setReportReady(true);
243
324
  return;
244
325
  }
245
- if ((session === null || session === void 0 ? void 0 : session.stage) === 'ERROR' || attempts >= MAX_ATTEMPTS) {
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) {
246
330
  isPollingRef.current = false;
247
- setError('Strategy generation timed out. Please try again.');
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');
248
335
  return;
249
336
  }
250
337
  }
251
- catch (_a) {
338
+ catch (_b) {
252
339
  // network blip — keep polling
253
340
  }
254
341
  setTimeout(poll, POLL_INTERVAL_MS);
@@ -307,6 +394,7 @@ const TaxAxisShell = ({ taxAxisApi, userContext = 'expert', initialSessionId, in
307
394
  }
308
395
  }), [sessionId, taxAxisApi]);
309
396
  const currentView = (0, react_1.useMemo)(() => {
397
+ var _a, _b, _c, _d;
310
398
  if (step === 'SESSION_SETUP') {
311
399
  return (react_1.default.createElement(ShellContainer, null,
312
400
  react_1.default.createElement(TaxAxisIntake_1.TaxAxisIntake, { userContext: userContext, initialProfile: initialProfile, onProspect: handleProspect, onFullAnalysis: handleFullAnalysis })));
@@ -321,21 +409,23 @@ const TaxAxisShell = ({ taxAxisApi, userContext = 'expert', initialSessionId, in
321
409
  react_1.default.createElement(TaxAxisProspectReport_1.TaxAxisProspectReport, { profile: profile, userContext: userContext, onUpgrade: () => setStep('DOCUMENT_UPLOAD'), onPresent: () => setStep('PRESENTATION'), onReset: handleReset })));
322
410
  case 'DOCUMENT_UPLOAD':
323
411
  return (react_1.default.createElement(ShellContainer, null,
324
- 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* () {
325
413
  const ensuredSessionId = sessionId || (profile ? yield createSessionIfNeeded(profile) : null);
326
414
  if (!ensuredSessionId) {
327
415
  return [];
328
416
  }
329
417
  const docs = yield taxAxisApi.getDocuments(ensuredSessionId);
330
418
  return docs.map((doc) => {
331
- var _a;
419
+ var _a, _b;
332
420
  return ({
421
+ documentId: doc.documentId,
333
422
  fileName: doc.fileName,
334
423
  status: doc.status,
335
424
  documentType: doc.documentType,
336
425
  parseError: doc.parseError,
337
426
  updatedAt: doc.updatedAt,
338
427
  parsedData: (_a = doc.parsedData) !== null && _a !== void 0 ? _a : null,
428
+ reviewedData: (_b = doc.reviewedData) !== null && _b !== void 0 ? _b : null,
339
429
  });
340
430
  });
341
431
  }), jobId: sessionId || undefined, onSaveReviewedField: (documentId, reviewedData) => __awaiter(void 0, void 0, void 0, function* () {
@@ -343,22 +433,19 @@ const TaxAxisShell = ({ taxAxisApi, userContext = 'expert', initialSessionId, in
343
433
  }), onContinue: handleAnalyzeDocuments, onBack: () => isProspectFlow ? setStep('PROSPECT_REPORT') : setStep('SESSION_SETUP') })));
344
434
  case 'PROCESSING':
345
435
  return (react_1.default.createElement(ShellContainer, null,
346
- react_1.default.createElement(TaxAxisProcessing_1.TaxAxisProcessing, { profile: profile, userContext: userContext, onComplete: () => {
347
- if (!isPollingRef.current)
348
- setStep('DASHBOARD');
349
- } })));
436
+ react_1.default.createElement(TaxAxisProcessing_1.TaxAxisProcessing, { profile: profile, userContext: userContext, reportReady: reportReady, onComplete: () => setStep('DASHBOARD') })));
350
437
  case 'PARSED_REVIEW':
351
438
  return (react_1.default.createElement(ShellContainer, null,
352
439
  react_1.default.createElement(TaxAxisExtractionReview_1.TaxAxisExtractionReview, { userContext: userContext, onBack: () => setStep('DASHBOARD'), onConfirmAndUnlock: () => setStep('DASHBOARD') })));
353
440
  case 'DASHBOARD':
354
441
  return (react_1.default.createElement(ShellContainer, null,
355
- 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 })));
356
443
  case 'CLIENT_REPORT':
357
444
  return (react_1.default.createElement(ShellContainer, { fullWidth: true },
358
- 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 })));
359
446
  case 'PREPARER_WORKPAPER':
360
447
  return (react_1.default.createElement(ShellContainer, { fullWidth: true },
361
- 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 })));
362
449
  case 'PRESENTATION':
363
450
  return (react_1.default.createElement(ShellContainer, { fullWidth: true },
364
451
  react_1.default.createElement(TaxAxisPresentationMode_1.TaxAxisPresentationMode, { profile: profile, userContext: userContext, onBack: () => setStep(isProspectFlow ? 'PROSPECT_REPORT' : 'DASHBOARD') })));
@@ -380,6 +467,8 @@ const TaxAxisShell = ({ taxAxisApi, userContext = 'expert', initialSessionId, in
380
467
  handleSendReport,
381
468
  llmResult,
382
469
  adapted,
470
+ reportReady,
471
+ parsedDocuments,
383
472
  ]);
384
473
  return (react_1.default.createElement(react_1.default.Fragment, null,
385
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 " + (profile.taxDataYears || "1 year") + " of financial data and current law as of April 2026, including OBBBA provisions.",
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
- ", a combined federal/state marginal rate of ",
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
- profile.taxDataYears || "1 year",
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
- const currentRate = fedRate + stateRate;
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
- const top3Ranks = new Set(top3.map(s => s.rank));
14
- const interactingPairs = data_1.INTERACTION_PAIRS.filter(([a, b]) => top3Ranks.has(a) && top3Ranks.has(b));
15
- const hasInteractions = interactingPairs.length > 0;
16
- const interactionMap = {};
17
- interactingPairs.forEach(([a, b]) => { interactionMap[a] = b; interactionMap[b] = a; });
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\u2013$" + (hi / 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
- "The " + top3.length + " strategies highlighted below are ranked by combined impact score \u2014 weighting dollar value, audit posture, and implementation effort.",
28
- "Each strategy below is anchored to a specific IRC section and explains the math, the audit posture, and the implementation path.",
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 \u2014 flagged where applicable."
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 interactsWith = interactionMap[s.rank];
37
- const interactsIdx = interactsWith ? top3.findIndex(t => t.rank === interactsWith) + 1 : 0;
38
- return (react_1.default.createElement(StrategyCard_1.StrategyCard, { key: s.rank, strategy: s, rank: rank, profile: profile, computed: computed, palette: palette, nextStepText: data_1.NEXT_STEPS[s.rank] || NARRATIVE_FALLBACK_NEXT(s), interactsWith: interactsWith, interactsIdx: interactsIdx, stSavings: stSavings(s) }));
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
- hasInteractions && (react_1.default.createElement("div", { style: { background: palette.orangePale, borderLeft: "3px solid " + palette.orange, borderRadius: "0 8px 8px 0", padding: "16px 20px", marginBottom: 24 } },
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
- interactingPairs.map(([a, b], pi) => {
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
- const applyFootnotes_1 = require("./applyFootnotes");
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
- const whyText = narr ? narr.whyMatters(profile) : data_1.NARRATIVE_FALLBACK.whyMatters(s);
25
- const breakdownText = narr ? narr.breakdown(profile, c) : data_1.NARRATIVE_FALLBACK.breakdown(s, c);
26
- const specialistText = (narr === null || narr === void 0 ? void 0 : narr.whySpecialist) || data_1.NARRATIVE_FALLBACK.whySpecialist(s);
27
- const nextText = narr ? narr.nextSteps : nextStepText;
28
- const footnotes = data_1.FOOTNOTE_DATA[s.rank] || [];
29
- const fnCounter = { next: 0, assigned: [], cardKey: s.rank };
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 } }, s.code + " \u00B7 " + s.timeline))),
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 MATTERS"),
50
- react_1.default.createElement("p", { style: { fontSize: 13, color: palette.gray700, lineHeight: 1.75, fontFamily: palette.body } }, (0, applyFootnotes_1.applyFootnotes)(whyText, footnotes, fnCounter, palette))),
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 } }, "HOW SAVINGS BREAK DOWN"),
53
- react_1.default.createElement("p", { style: { fontSize: 13, color: palette.gray700, lineHeight: 1.75, fontFamily: palette.body } }, (0, applyFootnotes_1.applyFootnotes)(breakdownText, footnotes, fnCounter, palette))),
54
- react_1.default.createElement("div", { style: { marginBottom: 12, borderLeft: "3px solid " + palette.orange, padding: "0 0 0 16px" } },
55
- react_1.default.createElement("div", { style: { fontSize: 11, fontWeight: 700, textTransform: "uppercase", letterSpacing: "0.1em", color: palette.orange, marginBottom: 8, fontFamily: palette.mono } }, "WHY YOU NEED A SPECIALIST"),
56
- react_1.default.createElement("p", { style: { fontSize: 13, color: palette.gray700, lineHeight: 1.75, fontFamily: palette.body } }, (0, applyFootnotes_1.applyFootnotes)(specialistText, footnotes, fnCounter, palette))),
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 } }, (0, applyFootnotes_1.applyFootnotes)(nextText, footnotes, fnCounter, palette))),
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 {};
@@ -6,10 +6,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.DashboardSummary = DashboardSummary;
7
7
  const react_1 = __importDefault(require("react"));
8
8
  const useCountUp_1 = require("./useCountUp");
9
- function DashboardSummary({ profile, dashEligible, computed, dataConfirmed, reviewUnreviewed, }) {
9
+ function DashboardSummary({ profile, dashEligible, computed, dataConfirmed, reviewUnreviewed, liveSavingsMin, liveSavingsMax, liveStrategyCount, confidenceTier, dataYears, }) {
10
10
  var _a;
11
- const totalLo = dashEligible.reduce((a, s) => { var _a, _b; return a + ((_b = (_a = computed.get(s.rank)) === null || _a === void 0 ? void 0 : _a.lo) !== null && _b !== void 0 ? _b : s.lo); }, 0);
12
- const totalHi = dashEligible.reduce((a, s) => { var _a, _b; return a + ((_b = (_a = computed.get(s.rank)) === null || _a === void 0 ? void 0 : _a.hi) !== null && _b !== void 0 ? _b : s.hi); }, 0);
11
+ const computedLo = dashEligible.reduce((a, s) => { var _a, _b; return a + ((_b = (_a = computed.get(s.rank)) === null || _a === void 0 ? void 0 : _a.lo) !== null && _b !== void 0 ? _b : s.lo); }, 0);
12
+ const computedHi = dashEligible.reduce((a, s) => { var _a, _b; return a + ((_b = (_a = computed.get(s.rank)) === null || _a === void 0 ? void 0 : _a.hi) !== null && _b !== void 0 ? _b : s.hi); }, 0);
13
+ const totalLo = liveSavingsMin !== null && liveSavingsMin !== void 0 ? liveSavingsMin : computedLo;
14
+ const totalHi = liveSavingsMax !== null && liveSavingsMax !== void 0 ? liveSavingsMax : computedHi;
15
+ const strategyCount = liveStrategyCount !== null && liveStrategyCount !== void 0 ? liveStrategyCount : dashEligible.length;
13
16
  const bizName = profile.bizName || "Client";
14
17
  const stateCount = ((_a = profile.states) === null || _a === void 0 ? void 0 : _a.length) || 1;
15
18
  const scoreAvg = Math.round(dashEligible.reduce((a, s) => a + s.score, 0) / (dashEligible.length || 1));
@@ -78,7 +81,7 @@ function DashboardSummary({ profile, dashEligible, computed, dataConfirmed, revi
78
81
  profile.cpaName)),
79
82
  react_1.default.createElement("div", { className: "flex gap-6 mt-[22px] pt-[18px]", style: { borderTop: "1px solid rgba(36,131,132,0.12)" } }, [
80
83
  { v: String(scoreUp), u: "/100", l: "Avg. Confidence", warn: false, ok: false },
81
- { v: String(dashEligible.length), u: "", l: "Strategies Identified", warn: false, ok: false },
84
+ { v: String(strategyCount), u: "", l: "Strategies Identified", warn: false, ok: false },
82
85
  {
83
86
  v: dataConfirmed ? "Done" : String(reviewUnreviewed),
84
87
  u: "",
@@ -86,6 +89,13 @@ function DashboardSummary({ profile, dashEligible, computed, dataConfirmed, revi
86
89
  warn: !dataConfirmed && reviewUnreviewed > 0,
87
90
  ok: dataConfirmed,
88
91
  },
92
+ ...(confidenceTier ? [{
93
+ v: dataYears ? String(dataYears) : "1",
94
+ u: "yr",
95
+ l: confidenceTier.replace(/-DATA$/, "").replace(/-/g, " ").replace(/YR/, "yr"),
96
+ warn: (dataYears !== null && dataYears !== void 0 ? dataYears : 1) < 2,
97
+ ok: (dataYears !== null && dataYears !== void 0 ? dataYears : 1) >= 3,
98
+ }] : []),
89
99
  ].map(({ v, u, l, warn, ok }) => (react_1.default.createElement("div", { key: l },
90
100
  react_1.default.createElement("div", { className: "font-tax-axis-mono font-bold leading-none", style: {
91
101
  fontSize: 24,
@@ -6,5 +6,5 @@ interface StrategyDetailPanelProps {
6
6
  computed: ComputedMap;
7
7
  onClose: () => void;
8
8
  }
9
- export declare function StrategyDetailPanel({ s, profile, computed, onClose }: StrategyDetailPanelProps): React.JSX.Element;
9
+ export declare function StrategyDetailPanel({ s, profile: _profile, computed, onClose }: StrategyDetailPanelProps): React.JSX.Element;
10
10
  export {};