@paro.io/expert-shared-components 1.14.56 → 1.14.59

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 (40) hide show
  1. package/lib/components/TaxAxis/TaxAxisApi.d.ts +1 -0
  2. package/lib/components/TaxAxis/TaxAxisShell.d.ts +1 -1
  3. package/lib/components/TaxAxis/TaxAxisShell.js +52 -2
  4. package/lib/components/TaxAxis/types.d.ts +5 -0
  5. package/lib/tax-axis/components/clientReport/Methodology.js +2 -2
  6. package/lib/tax-axis/components/dashboard/DashboardActions.js +4 -4
  7. package/lib/tax-axis/components/dashboard/DashboardSummary.js +5 -6
  8. package/lib/tax-axis/components/dashboard/StrategyDetailPanel.js +2 -4
  9. package/lib/tax-axis/components/dashboard/TaxAxisDashboard.js +162 -57
  10. package/lib/tax-axis/components/documents/DocumentCard.d.ts +1 -0
  11. package/lib/tax-axis/components/documents/DocumentCard.js +19 -3
  12. package/lib/tax-axis/components/documents/TaxAxisDocuments.d.ts +12 -1
  13. package/lib/tax-axis/components/documents/TaxAxisDocuments.js +110 -48
  14. package/lib/tax-axis/components/documents/qbo/QboAvailableReportsModal.d.ts +13 -0
  15. package/lib/tax-axis/components/documents/qbo/QboAvailableReportsModal.js +180 -0
  16. package/lib/tax-axis/components/documents/qbo/QboClientSelectorModal.d.ts +10 -0
  17. package/lib/tax-axis/components/documents/qbo/QboClientSelectorModal.js +155 -0
  18. package/lib/tax-axis/components/documents/qbo/QboConnectBanner.d.ts +9 -0
  19. package/lib/tax-axis/components/documents/qbo/QboConnectBanner.js +55 -0
  20. package/lib/tax-axis/components/documents/qbo/QboDocumentMappingModal.d.ts +10 -0
  21. package/lib/tax-axis/components/documents/qbo/QboDocumentMappingModal.js +202 -0
  22. package/lib/tax-axis/components/documents/qbo/QboImportingModal.d.ts +8 -0
  23. package/lib/tax-axis/components/documents/qbo/QboImportingModal.js +75 -0
  24. package/lib/tax-axis/components/documents/qbo/QboPermissionsModal.d.ts +8 -0
  25. package/lib/tax-axis/components/documents/qbo/QboPermissionsModal.js +126 -0
  26. package/lib/tax-axis/components/documents/qbo/index.d.ts +8 -0
  27. package/lib/tax-axis/components/documents/qbo/index.js +17 -0
  28. package/lib/tax-axis/components/documents/qbo/qboConstants.d.ts +24 -0
  29. package/lib/tax-axis/components/documents/qbo/qboConstants.js +71 -0
  30. package/lib/tax-axis/components/documents/qbo/types.d.ts +43 -0
  31. package/lib/tax-axis/components/documents/qbo/types.js +3 -0
  32. package/lib/tax-axis/components/documents/qbo/useQboFlow.d.ts +19 -0
  33. package/lib/tax-axis/components/documents/qbo/useQboFlow.js +207 -0
  34. package/lib/tax-axis/components/intake/ClientParametersSection.js +1 -1
  35. package/lib/tax-axis/components/intake/CpaIntakeQuestionsSection.js +3 -3
  36. package/lib/tax-axis/components/intake/IntakeCtaCards.js +1 -1
  37. package/lib/tax-axis/components/intake/RefineAnalysisSection.js +7 -7
  38. package/lib/tax-axis/components/intake/TaxAxisIntake.js +2 -2
  39. package/lib/tax-axis/components/processing/TaxAxisProcessing.js +30 -5
  40. package/package.json +1 -1
@@ -54,4 +54,5 @@ export type TaxAxisApi = {
54
54
  updateReport: (reportId: string, reviewedReport: Record<string, unknown>) => Promise<any>;
55
55
  generatePdf: (sessionId: string) => Promise<any>;
56
56
  getArtifacts: (sessionId: string) => Promise<any[]>;
57
+ importQboReport?: (sessionId: string, expertId: string, year: number, reportType: string) => Promise<any>;
57
58
  };
@@ -1,3 +1,3 @@
1
1
  import type { TaxAxisShellProps } from './types';
2
- export declare const TaxAxisShell: ({ taxAxisApi, userContext, initialSessionId, initialProfile, onSessionChange, documentUploadUrl, uploadBucketName, sessionDefaults, }: TaxAxisShellProps) => JSX.Element;
2
+ export declare const TaxAxisShell: ({ taxAxisApi, userContext, initialSessionId, initialProfile, onSessionChange, documentUploadUrl, uploadBucketName, sessionDefaults, qboAuthorizeUrl, expertId, qboConnected, qboCompanyName, }: TaxAxisShellProps) => JSX.Element;
3
3
  export default TaxAxisShell;
@@ -135,7 +135,7 @@ function ShellContainer({ children, fullWidth = false, }) {
135
135
  return (react_1.default.createElement("div", { className: 'min-h-screen bg-tax-axis-navy text-white font-tax-axis-body' },
136
136
  react_1.default.createElement("div", { className: 'max-w-[960px] mx-auto px-5 py-7' }, children)));
137
137
  }
138
- const TaxAxisShell = ({ taxAxisApi, userContext = 'expert', initialSessionId, initialProfile, onSessionChange, documentUploadUrl, uploadBucketName, sessionDefaults, }) => {
138
+ const TaxAxisShell = ({ taxAxisApi, userContext = 'expert', initialSessionId, initialProfile, onSessionChange, documentUploadUrl, uploadBucketName, sessionDefaults, qboAuthorizeUrl, expertId, qboConnected, qboCompanyName, }) => {
139
139
  const [step, setStep] = (0, react_1.useState)('SESSION_SETUP');
140
140
  const [profile, setProfile] = (0, react_1.useState)(initialProfile ? Object.assign({}, initialProfile) : null);
141
141
  const [sessionId, setSessionId] = (0, react_1.useState)(initialSessionId || null);
@@ -147,6 +147,12 @@ const TaxAxisShell = ({ taxAxisApi, userContext = 'expert', initialSessionId, in
147
147
  const [busyMessage, setBusyMessage] = (0, react_1.useState)('Syncing Tax Axis session...');
148
148
  const [reportReady, setReportReady] = (0, react_1.useState)(false);
149
149
  const isPollingRef = react_1.default.useRef(false);
150
+ // QBO state driven by EPS props — not localStorage
151
+ const [qboConnectedState, setQboConnectedState] = (0, react_1.useState)(!!qboConnected);
152
+ const [qboCompanyNameState, setQboCompanyNameState] = (0, react_1.useState)(qboCompanyName !== null && qboCompanyName !== void 0 ? qboCompanyName : null);
153
+ // Sync if parent re-fetches EPS data
154
+ react_1.default.useEffect(() => { setQboConnectedState(!!qboConnected); }, [qboConnected]);
155
+ react_1.default.useEffect(() => { setQboCompanyNameState(qboCompanyName !== null && qboCompanyName !== void 0 ? qboCompanyName : null); }, [qboCompanyName]);
150
156
  // Derive live strategies from engineOutput so CLIENT_REPORT and PREPARER_WORKPAPER
151
157
  // render real engine data instead of the static STRATEGIES catalog.
152
158
  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]);
@@ -300,6 +306,17 @@ const TaxAxisShell = ({ taxAxisApi, userContext = 'expert', initialSessionId, in
300
306
  const handleDeleteDocument = (0, react_1.useCallback)((documentId) => __awaiter(void 0, void 0, void 0, function* () {
301
307
  yield taxAxisApi.deleteDocument(documentId);
302
308
  }), [taxAxisApi]);
309
+ const handleImportQboReport = (0, react_1.useCallback)((sid, _realmId, reportType, _accountingMethod, year) => __awaiter(void 0, void 0, void 0, function* () {
310
+ var _a;
311
+ if (!taxAxisApi.importQboReport) {
312
+ throw new Error('QBO import is not available.');
313
+ }
314
+ if (!expertId) {
315
+ throw new Error('Expert ID is required for QBO import.');
316
+ }
317
+ const taxYear = (_a = year !== null && year !== void 0 ? year : Number(profile === null || profile === void 0 ? void 0 : profile.year)) !== null && _a !== void 0 ? _a : new Date().getFullYear();
318
+ return taxAxisApi.importQboReport(sid, expertId, taxYear, reportType);
319
+ }), [taxAxisApi, expertId, profile]);
303
320
  const pollForResult = (0, react_1.useCallback)((sid) => __awaiter(void 0, void 0, void 0, function* () {
304
321
  const POLL_INTERVAL_MS = 4000;
305
322
  const MAX_ATTEMPTS = 4500; // 300 minutes max
@@ -409,6 +426,28 @@ const TaxAxisShell = ({ taxAxisApi, userContext = 'expert', initialSessionId, in
409
426
  react_1.default.createElement(TaxAxisProspectReport_1.TaxAxisProspectReport, { profile: profile, userContext: userContext, onUpgrade: () => setStep('DOCUMENT_UPLOAD'), onPresent: () => setStep('PRESENTATION'), onReset: handleReset })));
410
427
  case 'DOCUMENT_UPLOAD':
411
428
  return (react_1.default.createElement(ShellContainer, null,
429
+ react_1.default.createElement("div", { className: "flex items-center justify-between rounded-lg px-4 py-3 mb-5", style: {
430
+ background: '#1A1F3A',
431
+ border: '1px solid #2E3160',
432
+ } },
433
+ react_1.default.createElement("div", { className: "flex items-center gap-3 min-w-0" },
434
+ react_1.default.createElement("span", { className: "text-[14px] font-semibold text-white font-tax-axis-body truncate max-w-[160px] sm:max-w-none" }, profile.bizName || 'Untitled Client'),
435
+ profile.entity && (react_1.default.createElement("span", { className: "text-[10px] font-semibold font-tax-axis-mono flex-shrink-0", style: {
436
+ padding: '2px 8px',
437
+ borderRadius: 4,
438
+ background: 'rgba(36,131,132,0.10)',
439
+ border: '1px solid rgba(36,131,132,0.25)',
440
+ color: '#A1E5E6',
441
+ letterSpacing: '0.03em',
442
+ } }, profile.entity)),
443
+ profile.year && (react_1.default.createElement("span", { className: "hidden sm:inline text-[12px] text-tax-axis-text-3 font-tax-axis-body flex-shrink-0" },
444
+ "TY ",
445
+ profile.year))),
446
+ react_1.default.createElement("button", { onClick: () => isProspectFlow ? setStep('PROSPECT_REPORT') : setStep('SESSION_SETUP'), className: "text-[11px] font-semibold font-tax-axis-mono cursor-pointer flex-shrink-0 rounded-md px-3 py-1", style: {
447
+ background: 'rgba(99,102,241,0.12)',
448
+ border: '1px solid rgba(99,102,241,0.30)',
449
+ color: '#a5b4fc',
450
+ } }, "Edit")),
412
451
  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* () {
413
452
  const ensuredSessionId = sessionId || (profile ? yield createSessionIfNeeded(profile) : null);
414
453
  if (!ensuredSessionId) {
@@ -430,7 +469,13 @@ const TaxAxisShell = ({ taxAxisApi, userContext = 'expert', initialSessionId, in
430
469
  });
431
470
  }), jobId: sessionId || undefined, onSaveReviewedField: (documentId, reviewedData) => __awaiter(void 0, void 0, void 0, function* () {
432
471
  yield taxAxisApi.saveReviewedData(documentId, reviewedData);
433
- }), onContinue: handleAnalyzeDocuments, onBack: () => isProspectFlow ? setStep('PROSPECT_REPORT') : setStep('SESSION_SETUP') })));
472
+ }), onContinue: handleAnalyzeDocuments, onBack: () => isProspectFlow ? setStep('PROSPECT_REPORT') : setStep('SESSION_SETUP'), qboConnected: qboConnectedState, qboCompanyName: qboCompanyNameState, qboAuthorizeUrl: qboAuthorizeUrl, qboClientConfirmed: qboConnectedState, onImportQboReport: taxAxisApi.importQboReport ? handleImportQboReport : undefined, onQboConnected: (companyName) => {
473
+ setQboConnectedState(true);
474
+ setQboCompanyNameState(companyName);
475
+ }, onQboDisconnected: () => {
476
+ setQboConnectedState(false);
477
+ setQboCompanyNameState(null);
478
+ } })));
434
479
  case 'PROCESSING':
435
480
  return (react_1.default.createElement(ShellContainer, null,
436
481
  react_1.default.createElement(TaxAxisProcessing_1.TaxAxisProcessing, { profile: profile, userContext: userContext, reportReady: reportReady, onComplete: () => setStep('DASHBOARD') })));
@@ -469,6 +514,11 @@ const TaxAxisShell = ({ taxAxisApi, userContext = 'expert', initialSessionId, in
469
514
  adapted,
470
515
  reportReady,
471
516
  parsedDocuments,
517
+ qboConnectedState,
518
+ qboCompanyNameState,
519
+ qboAuthorizeUrl,
520
+ expertId,
521
+ handleImportQboReport,
472
522
  ]);
473
523
  return (react_1.default.createElement(react_1.default.Fragment, null,
474
524
  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' },
@@ -13,4 +13,9 @@ export type TaxAxisShellProps = {
13
13
  freelancerId: number;
14
14
  clientId?: number | null;
15
15
  };
16
+ qboAuthorizeUrl?: string;
17
+ qboGetClientsUrl?: string;
18
+ expertId?: string;
19
+ qboConnected?: boolean;
20
+ qboCompanyName?: string | null;
16
21
  };
@@ -10,13 +10,13 @@ const SectionOpener_1 = require("./SectionOpener");
10
10
  function Methodology({ profile, eligible, palette }) {
11
11
  return (react_1.default.createElement(react_1.default.Fragment, null,
12
12
  react_1.default.createElement(SectionOpener_1.SectionOpener, { number: "04", eyebrow: "METHODOLOGY", headline: "How TaxAxis arrives at these numbers.", bullets: [
13
- "25 federal strategies evaluated against your profile via deterministic, rule-based computation at temperature zero \u2014 every recommendation traces to a specific IRC section, revenue ruling, or OBBBA provision.",
13
+ "25 federal strategies evaluated against your profile through a two-stage AI and deterministic pipeline \u2014 every recommendation traces to a specific IRC section, revenue ruling, or OBBBA provision.",
14
14
  "Confidence bands reflect data quality: \u00B130% on single-year data, narrowing to \u00B115% with multi-year history.",
15
15
  "Aggregate savings on the cover chart are adjusted by an interaction-discount factor reflecting strategy stacking, marginal-rate exhaustion, and \u00A76694 reasonableness.",
16
16
  ], palette: palette }),
17
17
  react_1.default.createElement("div", { style: { marginBottom: 28 } },
18
18
  react_1.default.createElement("div", { style: { fontSize: 10, fontWeight: 700, textTransform: "uppercase", letterSpacing: "0.12em", color: palette.gray400, fontFamily: palette.mono, marginBottom: 8 } }, "EVAL PIPELINE"),
19
- react_1.default.createElement("div", { style: { fontSize: 13, color: palette.gray700, lineHeight: 1.8, fontFamily: palette.body } }, "TaxAxis evaluates 25 federal tax strategies against each client profile. The computation is entirely deterministic and rule-based, operating at temperature zero \u2014 no generative model is involved at any stage. Each strategy passes through a three-phase pipeline: an eligibility filter removes strategies that do not apply to the entity type, industry, or fact pattern, then the computation engine calculates savings ranges against IRC-cited rules using the client's actual financial data. Every recommendation is reviewed by a qualified tax professional before the report is issued.")),
19
+ react_1.default.createElement("div", { style: { fontSize: 13, color: palette.gray700, lineHeight: 1.8, fontFamily: palette.body } }, "TaxAxis evaluates 25 federal tax strategies against each client profile through a two-stage pipeline. An AI model (Claude Sonnet 4.5 via AWS Bedrock) reads your financial documents, identifies applicable strategies, and drafts narrative content. A deterministic post-processing engine then recomputes savings ranges against IRC-cited rules, corrects confidence bands based on data quality, and validates every output through an 8-gate compliance check \u2014 covering schema integrity, strategy coverage, profile consistency, mathematical accuracy, business rules, \u00A76694 compliance, narrative quality, and entity eligibility. Every recommendation is reviewed by a qualified tax professional before the report is issued.")),
20
20
  react_1.default.createElement("div", { style: { marginBottom: 28 } },
21
21
  react_1.default.createElement("div", { style: { fontSize: 10, fontWeight: 700, textTransform: "uppercase", letterSpacing: "0.12em", color: palette.gray400, fontFamily: palette.mono, marginBottom: 8 } }, "CONFIDENCE BANDS"),
22
22
  react_1.default.createElement("div", { style: { fontSize: 13, color: palette.gray700, lineHeight: 1.8, fontFamily: palette.body, marginBottom: 12 } }, "Savings ranges reflect data quality. Clients who provide multiple years of financial history receive tighter bands of \u00B115%, because multi-year data reveals trends and reduces estimation variance. Single-year data widens the bands to \u00B130%. Within each band, the low estimate uses conservative inputs at the minimum applicable rate, while the high estimate uses full applicable inputs at the client's marginal rate."),
@@ -70,10 +70,10 @@ function DashboardActions({ profile, dashEligible, computed, onDownloadPreparer,
70
70
  "across ",
71
71
  dashEligible.length,
72
72
  " strategies \u2014 use this to close the engagement or expand scope."),
73
- react_1.default.createElement("div", { className: "flex gap-2.5 flex-wrap" },
74
- react_1.default.createElement(TaxAxisButton_1.TaxAxisButton, { variant: "orange", onClick: onSend }, "Send Report to Client"),
75
- react_1.default.createElement(TaxAxisButton_1.TaxAxisButton, { onClick: onPresent }, "Present to Client"),
76
- react_1.default.createElement(TaxAxisButton_1.TaxAxisButton, { variant: "secondary" }, "Generate Engagement Letter")))),
73
+ react_1.default.createElement("div", { className: "flex flex-col sm:flex-row gap-2.5 flex-wrap" },
74
+ react_1.default.createElement(TaxAxisButton_1.TaxAxisButton, { variant: "orange", onClick: onSend, className: "w-full sm:w-auto" }, "Send Report to Client"),
75
+ react_1.default.createElement(TaxAxisButton_1.TaxAxisButton, { onClick: onPresent, className: "w-full sm:w-auto" }, "Present to Client"),
76
+ react_1.default.createElement(TaxAxisButton_1.TaxAxisButton, { variant: "secondary", className: "w-full sm:w-auto" }, "Generate Engagement Letter")))),
77
77
  react_1.default.createElement("div", { className: "flex gap-4 mt-4 pt-3.5", style: { borderTop: "1px solid rgba(36,131,132,0.15)" } }, [
78
78
  { v: "3x", l: "Faster than Manual" },
79
79
  { v: String(dashEligible.length), l: "Strategies Found" },
@@ -23,10 +23,9 @@ function DashboardSummary({ profile, dashEligible, computed, dataConfirmed, revi
23
23
  const fmtHero = (k) => k >= 1000 ? "$" + (k / 1000).toFixed(1) + "M" : "$" + k + "K";
24
24
  const riskVal = parseInt(profile.riskTolerance) || 3;
25
25
  const riskLabels = ["", "Very Conservative", "Conservative", "Moderate", "Aggressive", "Very Aggressive"];
26
- return (react_1.default.createElement("div", { className: "relative overflow-hidden rounded-2xl mb-5", style: {
26
+ return (react_1.default.createElement("div", { className: "relative overflow-hidden rounded-2xl mb-5 p-4 sm:p-8", style: {
27
27
  background: "radial-gradient(ellipse at 30% 0%, rgba(36,131,132,0.08) 0%, transparent 60%), #0E1132",
28
28
  border: "1px solid rgba(36,131,132,0.12)",
29
- padding: "32px 32px 28px",
30
29
  boxShadow: "0 4px 20px rgba(6,8,33,0.3)",
31
30
  } },
32
31
  react_1.default.createElement("div", { className: "absolute inset-0 opacity-[0.02]", style: {
@@ -79,13 +78,13 @@ function DashboardSummary({ profile, dashEligible, computed, dataConfirmed, revi
79
78
  profile.cpaName && (react_1.default.createElement("div", { className: "text-xs text-tax-axis-text-3 mt-1" },
80
79
  "Preparer: ",
81
80
  profile.cpaName)),
82
- react_1.default.createElement("div", { className: "flex gap-6 mt-[22px] pt-[18px]", style: { borderTop: "1px solid rgba(36,131,132,0.12)" } }, [
83
- { v: String(scoreUp), u: "/100", l: "Avg. Confidence", warn: false, ok: false },
81
+ react_1.default.createElement("div", { className: "grid grid-cols-2 gap-4 sm:flex sm:gap-6 mt-[22px] pt-[18px]", style: { borderTop: "1px solid rgba(36,131,132,0.12)" } }, [
82
+ { v: confidenceTier ? confidenceTier.replace(/-DATA$/, "").replace(/-/g, " ") : "—", u: "", l: "Confidence Tier", warn: false, ok: !!confidenceTier },
84
83
  { v: String(strategyCount), u: "", l: "Strategies Identified", warn: false, ok: false },
85
84
  {
86
- v: dataConfirmed ? "Done" : String(reviewUnreviewed),
85
+ v: dataConfirmed ? "Verified" : String(reviewUnreviewed),
87
86
  u: "",
88
- l: dataConfirmed ? "Data Confirmed" : "Need Review",
87
+ l: "Data Quality",
89
88
  warn: !dataConfirmed && reviewUnreviewed > 0,
90
89
  ok: dataConfirmed,
91
90
  },
@@ -103,13 +103,11 @@ function StrategyDetailPanel({ s, profile: _profile, computed, onClose }) {
103
103
  const { savingsSection, cpaSection } = splitEngagementText(s.abstract || "");
104
104
  return (react_1.default.createElement(react_1.default.Fragment, null,
105
105
  react_1.default.createElement("div", { onClick: onClose, className: "fixed inset-0 z-[200]", style: { background: "rgba(6,8,33,0.7)", backdropFilter: "blur(4px)" } }),
106
- react_1.default.createElement("div", { className: "fixed top-0 right-0 bottom-0 z-[201] flex flex-col", style: {
107
- width: 520,
108
- maxWidth: "calc(100vw - 24px)",
106
+ react_1.default.createElement("div", { className: "fixed bottom-0 left-0 right-0 md:top-0 md:left-auto md:right-0 md:bottom-0 z-[201] flex flex-col w-full md:w-[520px] max-h-[85vh] md:max-h-none overflow-y-auto border-t border-tax-axis-border md:border-t-0 md:border-l", style: {
109
107
  background: "#0E1132",
110
- borderLeft: "1px solid rgba(36,131,132,0.12)",
111
108
  boxShadow: "-8px 0 40px rgba(6,8,33,0.6)",
112
109
  } },
110
+ react_1.default.createElement("div", { className: "w-10 h-1 rounded-full bg-tax-axis-text-4 mx-auto my-2 md:hidden" }),
113
111
  react_1.default.createElement("div", { className: "px-6 py-5 flex-shrink-0", style: { borderBottom: "1px solid rgba(36,131,132,0.12)" } },
114
112
  react_1.default.createElement("div", { className: "flex justify-between items-start mb-3" },
115
113
  react_1.default.createElement("div", null,
@@ -26,12 +26,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
26
26
  exports.TaxAxisDashboard = TaxAxisDashboard;
27
27
  const react_1 = __importStar(require("react"));
28
28
  const data_1 = require("../../lib/data");
29
+ const strategyNarrative_1 = require("../../lib/data/strategyNarrative");
29
30
  const compute_1 = require("../../lib/compute");
30
31
  const useEngineOutput_1 = require("../../lib/adapters/useEngineOutput");
31
32
  const TaxAxisButton_1 = require("../shared/TaxAxisButton");
32
33
  const TaxAxisBadge_1 = require("../shared/TaxAxisBadge");
33
34
  const DashboardSummary_1 = require("./DashboardSummary");
34
- const DashboardTopBar_1 = require("./DashboardTopBar");
35
35
  const DashboardActions_1 = require("./DashboardActions");
36
36
  const StrategyTile_1 = require("./StrategyTile");
37
37
  const StrategyDetailPanel_1 = require("./StrategyDetailPanel");
@@ -97,18 +97,109 @@ function buildRoadmapBucketLookup(llm) {
97
97
  }
98
98
  return map;
99
99
  }
100
- function mapLlmToStrategies(llm) {
100
+ // LLM sometimes emits these literal placeholder strings as why_it_applies / next_step
101
+ // content. When detected, we fall back to STRATEGY_NARRATIVE static content.
102
+ const PLACEHOLDER_WHY = "This strategy may apply based on your financial profile.";
103
+ const PLACEHOLDER_NEXT = "Discuss with your CPA to evaluate and implement this strategy.";
104
+ function isMissingOrPlaceholder(value, knownPlaceholder) {
105
+ if (!value)
106
+ return true;
107
+ const trimmed = value.trim();
108
+ if (trimmed === "")
109
+ return true;
110
+ if (trimmed === knownPlaceholder)
111
+ return true;
112
+ return false;
113
+ }
114
+ // Parse "S10" -> 10. Returns null if strategyId doesn't match the SN pattern.
115
+ function parseStrategyNumber(strategyId) {
116
+ if (!strategyId)
117
+ return null;
118
+ const match = strategyId.match(/^S(\d+)$/);
119
+ if (!match)
120
+ return null;
121
+ return parseInt(match[1], 10);
122
+ }
123
+ // Build a strategy_id -> client_summary entry lookup using strategy_analysis as the join table.
124
+ function buildClientSummaryLookup(llm) {
125
+ var _a, _b, _c, _d;
126
+ const lookup = new Map();
127
+ const engineOutput = (_a = llm.engineOutput) !== null && _a !== void 0 ? _a : llm.rawOutput;
128
+ if (!engineOutput)
129
+ return lookup;
130
+ const strategyAnalysis = (_b = engineOutput.strategy_analysis) !== null && _b !== void 0 ? _b : [];
131
+ const clientSummaryStrategies = (_d = (_c = engineOutput.client_summary) === null || _c === void 0 ? void 0 : _c.strategies) !== null && _d !== void 0 ? _d : [];
132
+ // strategy_name -> client_summary entry
133
+ const byName = new Map();
134
+ for (const cs of clientSummaryStrategies) {
135
+ if (cs.strategy_name) {
136
+ byName.set(cs.strategy_name, cs);
137
+ }
138
+ }
139
+ // strategy_id -> client_summary entry (via strategy_analysis name lookup)
140
+ for (const sa of strategyAnalysis) {
141
+ if (sa.strategy_id && sa.strategy_name) {
142
+ const cs = byName.get(sa.strategy_name);
143
+ if (cs) {
144
+ lookup.set(sa.strategy_id, cs);
145
+ }
146
+ }
147
+ }
148
+ return lookup;
149
+ }
150
+ function mapLlmToStrategies(llm, profile) {
101
151
  const nameLookup = buildEngineNameLookup(llm);
102
152
  const bucketLookup = buildRoadmapBucketLookup(llm);
153
+ const clientSummaryLookup = buildClientSummaryLookup(llm);
103
154
  return llm.strategies
104
155
  .filter((s) => s.applicable)
105
156
  .map((s, idx) => {
106
- var _a, _b, _c, _d, _e;
157
+ var _a, _b, _c, _d, _e, _f, _g;
107
158
  const id = (_a = s.strategyId) !== null && _a !== void 0 ? _a : s.strategyType;
108
- // Prefer the real strategy_name from engineOutput, fall back to humanized strategyType
109
159
  const name = (_b = nameLookup.get(id)) !== null && _b !== void 0 ? _b : humanizeStrategyType(s.strategyType);
110
160
  const timelineBucket = (_c = bucketLookup.get(id)) !== null && _c !== void 0 ? _c : (s.priority === "HIGH" ? "now" : "90d");
111
161
  const weightedScore = (_d = s.weightedScore) !== null && _d !== void 0 ? _d : (s.priority === "HIGH" ? 85 : s.priority === "MEDIUM" ? 70 : 55);
162
+ // Lookup the client_summary entry for this strategy (top-3 strategies only)
163
+ const csEntry = clientSummaryLookup.get(id);
164
+ const llmWhy = csEntry === null || csEntry === void 0 ? void 0 : csEntry.why_it_applies;
165
+ const llmNext = csEntry === null || csEntry === void 0 ? void 0 : csEntry.next_step;
166
+ // Strategy number for STRATEGY_NARRATIVE static lookup (e.g. "S10" -> 10)
167
+ const strategyNumber = parseStrategyNumber(id);
168
+ const staticNarrative = strategyNumber !== null ? strategyNarrative_1.STRATEGY_NARRATIVE[strategyNumber] : undefined;
169
+ // clientBrief: prefer LLM why_it_applies, fall back to static, then neutral default
170
+ let clientBrief;
171
+ if (!isMissingOrPlaceholder(llmWhy, PLACEHOLDER_WHY)) {
172
+ clientBrief = llmWhy;
173
+ }
174
+ else if (staticNarrative === null || staticNarrative === void 0 ? void 0 : staticNarrative.whyMatters) {
175
+ clientBrief = staticNarrative.whyMatters(profile);
176
+ }
177
+ else {
178
+ clientBrief = "Your preparer will review this strategy and include the analysis in your engagement report.";
179
+ }
180
+ // action: prefer LLM next_step, fall back to static nextSteps, then implementationSteps[0]
181
+ let action;
182
+ if (!isMissingOrPlaceholder(llmNext, PLACEHOLDER_NEXT)) {
183
+ action = llmNext;
184
+ }
185
+ else if (staticNarrative === null || staticNarrative === void 0 ? void 0 : staticNarrative.nextSteps) {
186
+ action = staticNarrative.nextSteps;
187
+ }
188
+ else if ((_e = s.implementationSteps) === null || _e === void 0 ? void 0 : _e[0]) {
189
+ action = s.implementationSteps[0];
190
+ }
191
+ else {
192
+ action = "Work with your CPA to implement this strategy.";
193
+ }
194
+ // Pull calculation_trace fields. Defensive about shape.
195
+ const ct = ((_f = s.calculationTrace) !== null && _f !== void 0 ? _f : {});
196
+ const sourceDocuments = Array.isArray(ct.source_documents) ? ct.source_documents : undefined;
197
+ const specialistNote = typeof ct.specialist_note === "string" && ct.specialist_note.trim() !== ""
198
+ ? ct.specialist_note
199
+ : ((_g = staticNarrative === null || staticNarrative === void 0 ? void 0 : staticNarrative.whySpecialist) !== null && _g !== void 0 ? _g : undefined);
200
+ const positionStrength = typeof ct.position_strength === "string" ? ct.position_strength : undefined;
201
+ const authority = typeof ct.irs_cite === "string" ? ct.irs_cite : undefined;
202
+ const formsFromTrace = Array.isArray(ct.forms_required) ? ct.forms_required.join(", ") : undefined;
112
203
  return {
113
204
  rank: idx + 1,
114
205
  code: id,
@@ -121,13 +212,18 @@ function mapLlmToStrategies(llm) {
121
212
  hi: s.estimatedSavings.max,
122
213
  timeline: timelineBucket === "now" ? "Act now" : timelineBucket === "30d" ? "Within 30 days" : "Within 90 days",
123
214
  timelineBucket,
124
- clientBrief: s.summary,
125
- action: (_e = s.implementationSteps[0]) !== null && _e !== void 0 ? _e : s.summary,
126
- forms: s.requiredForms.join(", "),
215
+ clientBrief,
216
+ action,
217
+ forms: formsFromTrace !== null && formsFromTrace !== void 0 ? formsFromTrace : s.requiredForms.join(", "),
127
218
  abstract: s.summary,
128
219
  sources: [],
129
220
  trace: [],
130
221
  cost: undefined,
222
+ sourceDocuments,
223
+ specialistNote,
224
+ positionStrength,
225
+ authority,
226
+ quickWin: s.quickWin,
131
227
  };
132
228
  });
133
229
  }
@@ -147,7 +243,7 @@ const BUCKETS = [
147
243
  { key: "90d", label: "Within 90 Days", desc: "Structural changes — new accounts or plan setup" },
148
244
  ];
149
245
  function TaxAxisDashboard({ profile, llmResult, parsedDocuments, onDownloadClient, onDownloadPreparer, onPresent, onSend, onReset, onReviewData, onUploadMore, userContext: _userContext = "expert", }) {
150
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v;
246
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u;
151
247
  const hasLlm = !!((_a = llmResult === null || llmResult === void 0 ? void 0 : llmResult.strategies) === null || _a === void 0 ? void 0 : _a.length);
152
248
  // All live fields from rawOutput — nothing hardcoded
153
249
  const rawOutput = (_c = (_b = llmResult === null || llmResult === void 0 ? void 0 : llmResult.rawOutput) !== null && _b !== void 0 ? _b : llmResult === null || llmResult === void 0 ? void 0 : llmResult.engineOutput) !== null && _c !== void 0 ? _c : null;
@@ -177,8 +273,8 @@ function TaxAxisDashboard({ profile, llmResult, parsedDocuments, onDownloadClien
177
273
  if ((_a = adapted === null || adapted === void 0 ? void 0 : adapted.strategies) === null || _a === void 0 ? void 0 : _a.length)
178
274
  return adapted.strategies;
179
275
  // Fallback to thin mapping for old payload shapes without engineOutput.
180
- return mapLlmToStrategies(llmResult);
181
- }, [hasLlm, adapted, llmResult]);
276
+ return mapLlmToStrategies(llmResult, profile);
277
+ }, [hasLlm, adapted, llmResult, profile]);
182
278
  const llmComputed = (0, react_1.useMemo)(() => {
183
279
  var _a;
184
280
  if (!hasLlm)
@@ -243,29 +339,25 @@ function TaxAxisDashboard({ profile, llmResult, parsedDocuments, onDownloadClien
243
339
  return map;
244
340
  }, [uncalculated]);
245
341
  const [uncalcExpanded, setUncalcExpanded] = (0, react_1.useState)(false);
342
+ const [excludedExpanded, setExcludedExpanded] = (0, react_1.useState)(false);
343
+ const [riskExpanded, setRiskExpanded] = (0, react_1.useState)(false);
246
344
  return (react_1.default.createElement("div", null,
247
- hasLlm && (llmResult === null || llmResult === void 0 ? void 0 : llmResult.meta) && (react_1.default.createElement("div", { className: "rounded-lg mb-3 px-4 py-2.5 flex items-center justify-between text-[11px] font-tax-axis-mono", style: {
248
- background: "rgba(36,131,132,0.06)",
249
- border: "1px solid rgba(36,131,132,0.15)",
250
- color: "#9498B8",
251
- } },
252
- react_1.default.createElement("span", null,
253
- "Source: ",
254
- react_1.default.createElement("strong", { className: "text-tax-axis-teal-light" }, llmResult.meta.provider),
255
- " · ",
256
- llmResult.meta.parsedDocumentCount,
257
- " documents parsed",
258
- " · ",
259
- ((_v = llmResult.meta.detectedDocumentTypes) === null || _v === void 0 ? void 0 : _v.length) || 0,
260
- " document types detected"),
261
- react_1.default.createElement("span", { className: "text-tax-axis-text-4" }, (() => {
262
- var _a, _b;
263
- const tokens = (_b = (_a = llmResult.meta.totalTokens) !== null && _a !== void 0 ? _a : llmResult.meta.tokenCount) !== null && _b !== void 0 ? _b : 0;
264
- return tokens > 0 ? `${tokens} tokens` : "";
265
- })()))),
345
+ react_1.default.createElement("style", null, `
346
+ @media print {
347
+ * { print-color-adjust: exact; -webkit-print-color-adjust: exact; }
348
+ .MuiDrawer-root, .MuiDrawer-paper, .MuiDrawer-permanent,
349
+ .MuiAppBar-root { display: none !important; }
350
+ .tax-axis-no-print { display: none !important; }
351
+ body { background: white !important; }
352
+ .min-h-screen.bg-tax-axis-navy { background: white !important; }
353
+ .flex.flex-col.w-full { margin-top: 0 !important; margin-left: 0 !important; }
354
+ main.w-full { width: 100% !important; margin: 0 !important; padding: 0 !important; }
355
+ .rounded-\\[14px\\] { page-break-inside: avoid; break-inside: avoid; }
356
+ .rounded-2xl { page-break-inside: avoid; break-inside: avoid; }
357
+ }
358
+ `),
266
359
  react_1.default.createElement(DashboardSummary_1.DashboardSummary, { profile: profile, dashEligible: dashEligible, computed: computed, dataConfirmed: dataConfirmed, reviewUnreviewed: reviewStatus.unreviewed, liveSavingsMin: hasLlm ? llmResult.summary.estimatedSavingsMin : undefined, liveSavingsMax: hasLlm ? llmResult.summary.estimatedSavingsMax : undefined, liveStrategyCount: hasLlm ? llmResult.summary.applicableCount : undefined, confidenceTier: businessProfile === null || businessProfile === void 0 ? void 0 : businessProfile.confidence_tier, dataYears: businessProfile === null || businessProfile === void 0 ? void 0 : businessProfile.data_years }),
267
- react_1.default.createElement(DashboardTopBar_1.DashboardTopBar, { topTab: topTab, setTopTab: setTopTab, dataConfirmed: dataConfirmed, reviewUnreviewed: reviewStatus.unreviewed }),
268
- topTab === "extraction" && (react_1.default.createElement("div", null,
360
+ false && (react_1.default.createElement("div", null,
269
361
  dataQualityFlags.length > 0 && (react_1.default.createElement("div", { className: "rounded-[14px] mb-4", style: { background: "rgba(251,154,29,0.04)", border: "1px solid rgba(251,154,29,0.2)", padding: "16px 20px" } },
270
362
  react_1.default.createElement("div", { className: "text-[11px] font-bold text-white uppercase tracking-widest font-tax-axis-mono mb-2.5 flex items-center gap-2" },
271
363
  react_1.default.createElement("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none" },
@@ -318,8 +410,8 @@ function TaxAxisDashboard({ profile, llmResult, parsedDocuments, onDownloadClien
318
410
  border: "1px solid rgba(15,110,86,0.25)",
319
411
  color: "#0F6E56",
320
412
  } }, "View Report"))))),
321
- topTab === "report" && dataConfirmed && (react_1.default.createElement("div", null,
322
- react_1.default.createElement("div", { className: "flex gap-0 mb-5", style: { borderBottom: "1px solid rgba(36,131,132,0.12)" } }, [["report", "Preparer Report"], ["client", "Client Summary"]].map(([id, lbl]) => (react_1.default.createElement("button", { key: id, onClick: () => setReportTab(id), className: "bg-transparent border-0 font-tax-axis-body mr-6", style: {
413
+ (react_1.default.createElement("div", null,
414
+ react_1.default.createElement("div", { className: "flex gap-0 mb-5 tax-axis-no-print", style: { borderBottom: "1px solid rgba(36,131,132,0.12)" } }, [["report", "Preparer Report"], ["client", "Client Summary"]].map(([id, lbl]) => (react_1.default.createElement("button", { key: id, onClick: () => setReportTab(id), className: "bg-transparent border-0 font-tax-axis-body mr-6", style: {
323
415
  padding: "10px 0",
324
416
  fontSize: 14,
325
417
  fontWeight: reportTab === id ? 600 : 400,
@@ -369,20 +461,14 @@ function TaxAxisDashboard({ profile, llmResult, parsedDocuments, onDownloadClien
369
461
  const clo = (_d = (_c = computed.get(s.rank)) === null || _c === void 0 ? void 0 : _c.lo) !== null && _d !== void 0 ? _d : s.lo;
370
462
  const w = (chi / maxSavings) * 100;
371
463
  return (react_1.default.createElement("div", { key: s.rank, className: "flex items-center gap-3 cursor-pointer", style: { marginBottom: chartExpanded ? 14 : 10 }, onClick: () => setSelected(s) },
372
- react_1.default.createElement("span", { className: "text-tax-axis-text font-tax-axis-body flex-shrink-0 overflow-hidden text-ellipsis whitespace-nowrap", style: {
373
- fontSize: chartExpanded ? 13 : 12,
374
- width: chartExpanded ? 180 : 140,
375
- } }, s.name),
464
+ react_1.default.createElement("span", { className: `text-tax-axis-text font-tax-axis-body flex-shrink-0 overflow-hidden text-ellipsis whitespace-nowrap ${chartExpanded ? "w-[100px] sm:w-[180px] text-[13px]" : "w-[100px] sm:w-[140px] text-[12px]"}` }, s.name),
376
465
  react_1.default.createElement("div", { className: "flex-1 bg-tax-axis-surface-2 rounded-sm overflow-hidden", style: { height: chartExpanded ? 10 : 6 } },
377
466
  react_1.default.createElement("div", { className: "h-full rounded-sm", style: {
378
467
  width: `${w}%`,
379
468
  background: "linear-gradient(90deg, #248384, #A1E5E6)",
380
469
  transition: "width 0.8s cubic-bezier(.16,1,.3,1)",
381
470
  } })),
382
- react_1.default.createElement("span", { className: "font-semibold text-tax-axis-teal-light font-tax-axis-mono flex-shrink-0 text-right", style: {
383
- fontSize: chartExpanded ? 13 : 12,
384
- width: chartExpanded ? 100 : 80,
385
- } },
471
+ react_1.default.createElement("span", { className: `font-semibold text-tax-axis-teal-light font-tax-axis-mono flex-shrink-0 text-right ${chartExpanded ? "w-[60px] sm:w-[100px] text-[13px]" : "w-[60px] sm:w-[80px] text-[12px]"}` },
386
472
  fmtK(clo),
387
473
  "\u2013",
388
474
  fmtK(chi))));
@@ -400,7 +486,7 @@ function TaxAxisDashboard({ profile, llmResult, parsedDocuments, onDownloadClien
400
486
  dashEligible.length,
401
487
  " Strategies"),
402
488
  react_1.default.createElement("span", { className: "font-normal normal-case tracking-normal text-tax-axis-text-2 text-xs font-tax-axis-body" }, "Click to explore")),
403
- react_1.default.createElement("div", { className: "grid grid-cols-2 gap-2.5 mb-4" },
489
+ react_1.default.createElement("div", { className: "grid grid-cols-1 sm:grid-cols-2 gap-2.5 mb-4" },
404
490
  calculable.map((s, i) => {
405
491
  var _a, _b;
406
492
  const c = computed.get(s.rank);
@@ -469,29 +555,46 @@ function TaxAxisDashboard({ profile, llmResult, parsedDocuments, onDownloadClien
469
555
  react_1.default.createElement("div", { className: "flex items-center gap-2.5" },
470
556
  react_1.default.createElement("span", { className: "text-[11px] font-tax-axis-mono text-tax-axis-text-2" }, a.savings),
471
557
  react_1.default.createElement(TaxAxisBadge_1.TaxAxisBadge, { color: a.priority === "MEDIUM" ? "orange" : "neutral", size: "xs" }, a.priority))))))),
472
- engagementRecs.length > 0 && (react_1.default.createElement("div", { className: "rounded-[14px] mb-4 overflow-hidden", style: { background: "#0E1132", border: "1px solid rgba(36,131,132,0.12)" } },
473
- react_1.default.createElement("div", { className: "px-5 py-3.5", style: { borderBottom: "1px solid rgba(36,131,132,0.12)" } },
474
- react_1.default.createElement("span", { className: "text-[13px] font-bold text-white uppercase tracking-widest font-tax-axis-head" }, "CPA Engagement Notes")),
475
- engagementRecs.map((rec, i) => (react_1.default.createElement("div", { key: i, className: "px-5 py-3.5 text-[12px] text-tax-axis-text leading-[1.7] font-tax-axis-body", style: { borderTop: i > 0 ? "1px solid rgba(36,131,132,0.08)" : "none" } }, rec))))),
558
+ engagementRecs.length > 0 && (react_1.default.createElement("div", { className: "mb-4" },
559
+ react_1.default.createElement("div", { className: "text-[13px] font-bold text-white uppercase tracking-widest font-tax-axis-head mb-3" }, "CPA Engagement Notes"),
560
+ react_1.default.createElement("div", { className: "flex flex-col gap-3" }, engagementRecs.map((rec, i) => {
561
+ const colonIdx = rec.indexOf(":");
562
+ const hasLabel = colonIdx > 0 && colonIdx < 60;
563
+ const label = hasLabel ? rec.slice(0, colonIdx).trim() : null;
564
+ const body = hasLabel ? rec.slice(colonIdx + 1).trim() : rec;
565
+ return (react_1.default.createElement("div", { key: i, className: "rounded-[10px] p-4", style: {
566
+ background: "#0E1132",
567
+ border: "1px solid rgba(36,131,132,0.12)",
568
+ borderLeft: "3px solid rgba(36,131,132,0.4)",
569
+ } },
570
+ label && (react_1.default.createElement("div", { className: "text-xs font-semibold tracking-wide text-tax-axis-teal-light font-tax-axis-mono mb-1.5 uppercase" }, label)),
571
+ react_1.default.createElement("div", { className: "text-[12px] text-tax-axis-text leading-[1.7] font-tax-axis-body" }, body)));
572
+ })))),
476
573
  nexusFlags.length > 0 && (react_1.default.createElement("div", { className: "rounded-[14px] mb-4", style: { background: "#0E1132", border: "1px solid rgba(36,131,132,0.12)", padding: "16px 20px" } },
477
574
  react_1.default.createElement("div", { className: "text-[11px] font-bold text-white uppercase tracking-widest font-tax-axis-mono mb-2" }, "State Nexus"),
478
575
  nexusFlags.map((f, i) => (react_1.default.createElement("div", { key: i, className: "text-[12px] text-tax-axis-text-2 font-tax-axis-body py-1.5", style: { borderTop: i > 0 ? "1px solid rgba(36,131,132,0.08)" : "none" } },
479
576
  react_1.default.createElement("span", { className: "font-semibold text-tax-axis-teal-light mr-2" }, f.state),
480
577
  f.flag_text))))),
481
- riskDisclosures.length > 0 && (react_1.default.createElement("div", { className: "rounded-[14px] mb-4", style: { background: "rgba(251,154,29,0.04)", border: "1px solid rgba(251,154,29,0.15)", padding: "16px 20px" } },
482
- react_1.default.createElement("div", { className: "text-[11px] font-bold text-white uppercase tracking-widest font-tax-axis-mono mb-2.5" }, "Risk Disclosures"),
483
- riskDisclosures.map((r, i) => (react_1.default.createElement("div", { key: i, className: "text-[11px] text-tax-axis-text-2 font-tax-axis-body py-1.5", style: { borderTop: i > 0 ? "1px solid rgba(251,154,29,0.08)" : "none" } }, r))))),
578
+ riskDisclosures.length > 0 && (react_1.default.createElement("div", { className: "rounded-[14px] mb-4 overflow-hidden", style: { background: "rgba(251,154,29,0.04)", border: "1px solid rgba(251,154,29,0.15)" } },
579
+ react_1.default.createElement("div", { className: "px-5 py-3.5 flex items-center justify-between cursor-pointer", onClick: () => setRiskExpanded(p => !p) },
580
+ react_1.default.createElement("span", { className: "text-[13px] text-tax-axis-text-2 font-tax-axis-body" }, "Risk disclosures & IRC \u00A76694 notices"),
581
+ react_1.default.createElement("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none", style: { transform: riskExpanded ? "rotate(180deg)" : "none", transition: "transform .2s" } },
582
+ react_1.default.createElement("path", { d: "M3 4.5l3 3 3-3", stroke: "#9498B8", strokeWidth: "1.5", strokeLinecap: "round" }))),
583
+ riskExpanded && (react_1.default.createElement("div", { style: { borderTop: "1px solid rgba(251,154,29,0.12)", padding: "12px 20px" } }, riskDisclosures.map((r, i) => (react_1.default.createElement("div", { key: i, className: "text-[11px] text-tax-axis-text-2 font-tax-axis-body py-1.5", style: { borderTop: i > 0 ? "1px solid rgba(251,154,29,0.08)" : "none" } }, r))))))),
484
584
  excludedStrategies.length > 0 && (react_1.default.createElement("div", { className: "rounded-[14px] mb-4 overflow-hidden", style: { background: "#0E1132", border: "1px solid rgba(148,152,184,0.15)" } },
485
- react_1.default.createElement("div", { className: "px-5 py-3.5", style: { borderBottom: "1px solid rgba(148,152,184,0.1)" } },
486
- react_1.default.createElement("span", { className: "text-[13px] font-bold text-white uppercase tracking-widest font-tax-axis-head" },
585
+ react_1.default.createElement("div", { className: "px-5 py-3.5 flex items-center justify-between cursor-pointer", onClick: () => setExcludedExpanded(p => !p) },
586
+ react_1.default.createElement("span", { className: "text-[13px] text-tax-axis-text-2 font-tax-axis-body" },
487
587
  excludedStrategies.length,
488
- " Strategies Not Applicable")),
489
- excludedStrategies.map((ex, i) => (react_1.default.createElement("div", { key: i, className: "px-5 py-2.5 flex items-start gap-3", style: { borderTop: i > 0 ? "1px solid rgba(148,152,184,0.08)" : "none" } },
588
+ " strategies screened \u2014 not applicable to this client"),
589
+ react_1.default.createElement("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none", style: { transform: excludedExpanded ? "rotate(180deg)" : "none", transition: "transform .2s" } },
590
+ react_1.default.createElement("path", { d: "M3 4.5l3 3 3-3", stroke: "#9498B8", strokeWidth: "1.5", strokeLinecap: "round" }))),
591
+ excludedExpanded && (react_1.default.createElement("div", { style: { borderTop: "1px solid rgba(148,152,184,0.1)" } }, excludedStrategies.map((ex, i) => (react_1.default.createElement("div", { key: i, className: "px-5 py-2.5 flex items-start gap-3", style: { borderTop: i > 0 ? "1px solid rgba(148,152,184,0.08)" : "none" } },
490
592
  react_1.default.createElement("span", { className: "text-[10px] font-semibold font-tax-axis-mono text-tax-axis-text-3 bg-tax-axis-surface px-2 py-0.5 rounded flex-shrink-0 mt-0.5" }, ex.strategy_id),
491
593
  react_1.default.createElement("div", { className: "flex-1" },
492
594
  react_1.default.createElement("div", { className: "text-[11px] text-tax-axis-text-2 font-tax-axis-body" }, ex.reason),
493
- ex.irs_cite && (react_1.default.createElement("div", { className: "text-[10px] text-tax-axis-text-3 font-tax-axis-mono mt-0.5" }, ex.irs_cite)))))))),
494
- react_1.default.createElement(TaxAxisButton_1.TaxAxisButton, { onClick: onDownloadPreparer, className: "w-full" }, "Download Full Preparer Report"))) : (
595
+ ex.irs_cite && (react_1.default.createElement("div", { className: "text-[10px] text-tax-axis-text-3 font-tax-axis-mono mt-0.5" }, ex.irs_cite)))))))))),
596
+ react_1.default.createElement("div", { className: "tax-axis-no-print" },
597
+ react_1.default.createElement(TaxAxisButton_1.TaxAxisButton, { onClick: onDownloadPreparer, className: "w-full" }, "Download Full Preparer Report")))) : (
495
598
  /* ═══ CLIENT SUMMARY — Timeline View ═══ */
496
599
  react_1.default.createElement("div", null,
497
600
  clientSummaryOpening ? (react_1.default.createElement("div", { className: "rounded-[10px] mb-5 text-[12px] text-tax-axis-text leading-[1.7] font-tax-axis-body", style: { background: "rgba(36,131,132,0.04)", border: "1px solid rgba(36,131,132,0.1)", padding: "14px 16px" } }, clientSummaryOpening)) : (react_1.default.createElement("p", { className: "text-sm text-tax-axis-text leading-[1.7] mb-6 font-tax-axis-body" },
@@ -557,8 +660,10 @@ function TaxAxisDashboard({ profile, llmResult, parsedDocuments, onDownloadClien
557
660
  s.cost && s.cost !== "$0" && (react_1.default.createElement("span", { className: "text-[11px] font-semibold font-tax-axis-mono", style: { color: "#FB9A1D" } }, s.cost)))));
558
661
  })));
559
662
  }),
560
- react_1.default.createElement(TaxAxisButton_1.TaxAxisButton, { onClick: onDownloadClient, className: "w-full mt-1.5" }, "Download Client Summary"),
663
+ react_1.default.createElement("div", { className: "tax-axis-no-print" },
664
+ react_1.default.createElement(TaxAxisButton_1.TaxAxisButton, { onClick: onDownloadClient, className: "w-full mt-1.5" }, "Download Client Summary")),
561
665
  clientSummaryClosing && (react_1.default.createElement("div", { className: "mt-4 text-[11px] text-tax-axis-text-3 leading-[1.65] font-tax-axis-body", style: { borderTop: "1px solid rgba(36,131,132,0.08)", paddingTop: 14 } }, clientSummaryClosing)))),
562
- react_1.default.createElement(DashboardActions_1.DashboardActions, { profile: profile, dashEligible: dashEligible, computed: computed, onDownloadPreparer: onDownloadPreparer, onPresent: onPresent, onSend: onSend, onReset: onReset }))),
666
+ react_1.default.createElement("div", { className: "tax-axis-no-print" },
667
+ react_1.default.createElement(DashboardActions_1.DashboardActions, { profile: profile, dashEligible: dashEligible, computed: computed, onDownloadPreparer: onDownloadPreparer, onPresent: onPresent, onSend: onSend, onReset: onReset })))),
563
668
  enrichedSelected && (react_1.default.createElement(StrategyDetailPanel_1.StrategyDetailPanel, { s: enrichedSelected, profile: profile, computed: computed, onClose: () => setSelected(null) }))));
564
669
  }
@@ -5,6 +5,7 @@ export interface DocState extends DocSpec {
5
5
  status: DocStatus;
6
6
  fileName: string | null;
7
7
  parseError?: string | null;
8
+ qboSource?: boolean;
8
9
  }
9
10
  interface DocumentCardProps {
10
11
  doc: DocState;