@paro.io/expert-shared-components 1.14.70 → 1.14.71
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/components/TaxAxis/TaxAxisApi.d.ts +0 -8
- package/lib/components/TaxAxis/TaxAxisShell.js +13 -29
- package/lib/tax-axis/components/clientReport/ExecutiveSummary.js +10 -6
- package/lib/tax-axis/components/clientReport/ImplementationRoadmap.d.ts +2 -1
- package/lib/tax-axis/components/clientReport/ImplementationRoadmap.js +11 -8
- package/lib/tax-axis/components/clientReport/ImplementationTimelineChart.d.ts +2 -1
- package/lib/tax-axis/components/clientReport/ImplementationTimelineChart.js +9 -3
- package/lib/tax-axis/components/clientReport/QuarterlyCashChart.d.ts +2 -1
- package/lib/tax-axis/components/clientReport/QuarterlyCashChart.js +9 -3
- package/lib/tax-axis/components/clientReport/TaxAxisClientReport.js +9 -2
- package/lib/tax-axis/components/dashboard/StrategyDetailPanel.js +3 -1
- package/lib/tax-axis/components/dashboard/TaxAxisDashboard.js +5 -2
- package/lib/tax-axis/components/intake/ClientParametersSection.js +14 -4
- package/lib/tax-axis/components/intake/IntakeCtaCards.d.ts +2 -8
- package/lib/tax-axis/components/intake/IntakeCtaCards.js +7 -70
- package/lib/tax-axis/components/intake/TaxAxisIntake.js +1 -1
- package/lib/tax-axis/components/intake/intakeSchema.js +1 -1
- package/lib/tax-axis/components/preparerWorkpaper/EngagementHeader.js +6 -2
- package/lib/tax-axis/components/preparerWorkpaper/TaxAxisPreparerWorkpaper.js +3 -3
- package/lib/tax-axis/components/processing/TaxAxisProcessing.d.ts +1 -5
- package/lib/tax-axis/components/processing/TaxAxisProcessing.js +11 -27
- package/lib/tax-axis/components/prospectReport/ProspectNextSteps.js +1 -3
- package/lib/tax-axis/components/prospectReport/ProspectStrategyCard.d.ts +1 -7
- package/lib/tax-axis/components/prospectReport/ProspectStrategyCard.js +6 -8
- package/lib/tax-axis/components/prospectReport/TaxAxisProspectReport.d.ts +1 -33
- package/lib/tax-axis/components/prospectReport/TaxAxisProspectReport.js +56 -57
- package/lib/tax-axis/lib/adapters/useEngineOutput.d.ts +42 -58
- package/lib/tax-axis/lib/adapters/useEngineOutput.js +99 -35
- package/lib/tax-axis/lib/data/nextSteps.js +13 -13
- package/lib/tax-axis/lib/data/sourceDescriptions.js +6 -0
- package/lib/tax-axis/lib/documentFieldCatalog.d.ts +10 -5
- package/lib/tax-axis/lib/documentFieldCatalog.js +329 -115
- package/lib/tax-axis/lib/types/index.d.ts +2 -0
- package/package.json +1 -1
- package/lib/tax-axis/components/prospectReport/ProspectReportSkeleton.d.ts +0 -2
- package/lib/tax-axis/components/prospectReport/ProspectReportSkeleton.js +0 -78
|
@@ -31,21 +31,8 @@ const CRAWL_TICK_MS = 300; // interval between crawl ticks
|
|
|
31
31
|
const CRAWL_STEP = 0.27; // % added each tick → 0.27 / 0.3s = 0.9%/s
|
|
32
32
|
const FINISH_MS = 400; // ms to animate from 99% → 100% once ready
|
|
33
33
|
const STAGE_ADVANCE_MS = 3200; // how often the displayed stage advances
|
|
34
|
-
function buildStages(profile
|
|
34
|
+
function buildStages(profile) {
|
|
35
35
|
var _a;
|
|
36
|
-
const entity = (profile === null || profile === void 0 ? void 0 : profile.entity) || "your business";
|
|
37
|
-
const stateCount = ((_a = profile === null || profile === void 0 ? void 0 : profile.states) === null || _a === void 0 ? void 0 : _a.length) || 1;
|
|
38
|
-
const bizName = (profile === null || profile === void 0 ? void 0 : profile.bizName) || "your business";
|
|
39
|
-
if (mode === 'prospect') {
|
|
40
|
-
return [
|
|
41
|
-
`Classifying ${entity} profile`,
|
|
42
|
-
"Injecting IRS 2026 + OBBBA parameters",
|
|
43
|
-
`Screening 25 strategies across ${stateCount} state${stateCount > 1 ? "s" : ""}`,
|
|
44
|
-
"Computing deterministic tax math",
|
|
45
|
-
"Generating strategy narratives",
|
|
46
|
-
`Assembling prospect report for ${bizName}`,
|
|
47
|
-
];
|
|
48
|
-
}
|
|
49
36
|
if (!profile) {
|
|
50
37
|
return [
|
|
51
38
|
"Parsing source documents",
|
|
@@ -55,6 +42,9 @@ function buildStages(profile, mode) {
|
|
|
55
42
|
"Generating IRS citations",
|
|
56
43
|
];
|
|
57
44
|
}
|
|
45
|
+
const entity = profile.entity || "your business";
|
|
46
|
+
const stateCount = ((_a = profile.states) === null || _a === void 0 ? void 0 : _a.length) || 1;
|
|
47
|
+
const bizName = profile.bizName || "your business";
|
|
58
48
|
const base = [
|
|
59
49
|
"Parsing source documents",
|
|
60
50
|
"Injecting IRS 2026 parameters",
|
|
@@ -70,16 +60,10 @@ function buildStages(profile, mode) {
|
|
|
70
60
|
base.push("Screening 30 eligible strategies", "Computing deterministic tax math", "Cross-validating source documents", "Generating IRS citations", `Assembling TaxAxis report for ${bizName}`);
|
|
71
61
|
return base;
|
|
72
62
|
}
|
|
73
|
-
function TaxAxisProcessing({ onComplete, profile, reportReady = false,
|
|
74
|
-
const stages = (0, react_1.useMemo)(() => buildStages(profile
|
|
63
|
+
function TaxAxisProcessing({ onComplete, profile, reportReady = false, userContext: _userContext = "expert", }) {
|
|
64
|
+
const stages = (0, react_1.useMemo)(() => buildStages(profile), [profile]);
|
|
75
65
|
const [progress, setProgress] = (0, react_1.useState)(0);
|
|
76
66
|
const [stageIdx, setStageIdx] = (0, react_1.useState)(0);
|
|
77
|
-
const effectiveCrawlStep = crawlStep !== null && crawlStep !== void 0 ? crawlStep : CRAWL_STEP;
|
|
78
|
-
// Stable refs so effects don't restart when props change after mount
|
|
79
|
-
const crawlStepRef = (0, react_1.useRef)(effectiveCrawlStep);
|
|
80
|
-
const onCompleteRef = (0, react_1.useRef)(onComplete);
|
|
81
|
-
(0, react_1.useEffect)(() => { crawlStepRef.current = effectiveCrawlStep; }, [effectiveCrawlStep]);
|
|
82
|
-
(0, react_1.useEffect)(() => { onCompleteRef.current = onComplete; }, [onComplete]);
|
|
83
67
|
const finishingRef = (0, react_1.useRef)(false);
|
|
84
68
|
const completedRef = (0, react_1.useRef)(false);
|
|
85
69
|
// Track current progress in a ref so the finish animation can read it synchronously
|
|
@@ -113,7 +97,7 @@ function TaxAxisProcessing({ onComplete, profile, reportReady = false, crawlStep
|
|
|
113
97
|
progressRef.current = v;
|
|
114
98
|
setProgress(v);
|
|
115
99
|
};
|
|
116
|
-
// ── Crawl: 0 →
|
|
100
|
+
// ── Crawl: 0 → 80% — skipped entirely if reportReady already true on mount ──
|
|
117
101
|
(0, react_1.useEffect)(() => {
|
|
118
102
|
if (reportReady || finishingRef.current)
|
|
119
103
|
return;
|
|
@@ -122,14 +106,14 @@ function TaxAxisProcessing({ onComplete, profile, reportReady = false, crawlStep
|
|
|
122
106
|
clearInterval(iv);
|
|
123
107
|
return;
|
|
124
108
|
}
|
|
125
|
-
const next = Math.min(progressRef.current +
|
|
109
|
+
const next = Math.min(progressRef.current + CRAWL_STEP, CRAWL_CEILING);
|
|
126
110
|
setProgressSync(next);
|
|
127
111
|
if (next >= CRAWL_CEILING)
|
|
128
112
|
clearInterval(iv);
|
|
129
113
|
}, CRAWL_TICK_MS);
|
|
130
114
|
return () => clearInterval(iv);
|
|
131
|
-
//
|
|
132
|
-
}, []);
|
|
115
|
+
// reportReady intentionally in deps: if it arrives before mount effect runs, skip crawl
|
|
116
|
+
}, [reportReady]);
|
|
133
117
|
// ── Stage labels: advance every STAGE_ADVANCE_MS ─────────────────
|
|
134
118
|
(0, react_1.useEffect)(() => {
|
|
135
119
|
const iv = setInterval(() => {
|
|
@@ -158,7 +142,7 @@ function TaxAxisProcessing({ onComplete, profile, reportReady = false, crawlStep
|
|
|
158
142
|
setProgressSync(100);
|
|
159
143
|
if (!completedRef.current) {
|
|
160
144
|
completedRef.current = true;
|
|
161
|
-
setTimeout(
|
|
145
|
+
setTimeout(onComplete, 150);
|
|
162
146
|
}
|
|
163
147
|
}
|
|
164
148
|
};
|
|
@@ -25,9 +25,7 @@ function ProspectNextSteps({ sectionNum, bizName, onUpgrade, onPresent, onPrint,
|
|
|
25
25
|
bizName,
|
|
26
26
|
"'s documents to run the full TaxAxis analysis \u2014 computed savings with calculation traces, IRS citations, and a client-ready report."),
|
|
27
27
|
react_1.default.createElement("div", { style: { display: "flex", gap: 10, flexWrap: "wrap" } },
|
|
28
|
-
react_1.default.createElement(GlowBtn, { onClick:
|
|
29
|
-
onUpgrade();
|
|
30
|
-
} } }, "Upload Documents"),
|
|
28
|
+
react_1.default.createElement(GlowBtn, { onClick: onUpgrade }, "Upload Documents"),
|
|
31
29
|
react_1.default.createElement(GlowBtn, { onClick: onPresent, style: { fontSize: 13 } }, "Present to Client"),
|
|
32
30
|
react_1.default.createElement(GlowBtn, { secondary: true, onClick: onPrint, style: { fontSize: 13 } }, "Download PDF")),
|
|
33
31
|
react_1.default.createElement("div", { style: { fontSize: 11, color: theme_1.T.text4, marginTop: 12, fontFamily: theme_1.T.body } }, "Typical turnaround: same day. No obligation until you review the results.")))),
|
|
@@ -1,16 +1,10 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import type { Strategy, ClientProfile, ComputedMap } from "../../lib/types";
|
|
3
|
-
export interface LlmStrategyNarrative {
|
|
4
|
-
whyItMatters: string;
|
|
5
|
-
whatWeDo: string;
|
|
6
|
-
}
|
|
7
3
|
interface ProspectStrategyCardProps {
|
|
8
4
|
strategy: Strategy;
|
|
9
5
|
index: number;
|
|
10
6
|
profile: ClientProfile;
|
|
11
7
|
computed: ComputedMap;
|
|
12
|
-
/** LLM-generated narrative — when present, overrides all static/computed text */
|
|
13
|
-
llmNarrative?: LlmStrategyNarrative;
|
|
14
8
|
}
|
|
15
|
-
export declare function ProspectStrategyCard({ strategy: s, index: i, profile, computed
|
|
9
|
+
export declare function ProspectStrategyCard({ strategy: s, index: i, profile, computed }: ProspectStrategyCardProps): React.JSX.Element;
|
|
16
10
|
export {};
|
|
@@ -8,19 +8,17 @@ const react_1 = __importDefault(require("react"));
|
|
|
8
8
|
const strategyNarrative_1 = require("../../lib/data/strategyNarrative");
|
|
9
9
|
const strategyProspect_1 = require("../../lib/data/strategyProspect");
|
|
10
10
|
const theme_1 = require("./theme");
|
|
11
|
-
function ProspectStrategyCard({ strategy: s, index: i, profile, computed
|
|
12
|
-
var _a, _b
|
|
11
|
+
function ProspectStrategyCard({ strategy: s, index: i, profile, computed }) {
|
|
12
|
+
var _a, _b;
|
|
13
13
|
const p = strategyProspect_1.STRATEGY_PROSPECT[s.rank];
|
|
14
14
|
const narr = strategyNarrative_1.STRATEGY_NARRATIVE[s.rank];
|
|
15
15
|
const c = computed.get(s.rank);
|
|
16
|
-
const
|
|
17
|
-
const scaledHi = Math.round(((_b = c === null || c === void 0 ? void 0 : c.hi) !== null && _b !== void 0 ? _b : s.hi) / 1000);
|
|
18
|
-
// When LLM narrative is provided, use it for whyItMatters and whatWeDo.
|
|
19
|
-
// HOW SAVINGS BREAK DOWN and WHY YOU NEED A SPECIALIST remain deterministic.
|
|
20
|
-
const whyText = (_c = llmNarrative === null || llmNarrative === void 0 ? void 0 : llmNarrative.whyItMatters) !== null && _c !== void 0 ? _c : (narr ? narr.whyMatters(profile) : strategyNarrative_1.NARRATIVE_FALLBACK.whyMatters(s));
|
|
16
|
+
const whyText = narr ? narr.whyMatters(profile) : strategyNarrative_1.NARRATIVE_FALLBACK.whyMatters(s);
|
|
21
17
|
const breakdownText = narr ? narr.breakdown(profile, c) : strategyNarrative_1.NARRATIVE_FALLBACK.breakdown(s, c);
|
|
22
18
|
const specialistText = (narr === null || narr === void 0 ? void 0 : narr.whySpecialist) || strategyNarrative_1.NARRATIVE_FALLBACK.whySpecialist(s);
|
|
23
|
-
const nextText =
|
|
19
|
+
const nextText = narr ? narr.nextSteps : strategyNarrative_1.NARRATIVE_FALLBACK.nextSteps(s);
|
|
20
|
+
const scaledLo = Math.round(((_a = c === null || c === void 0 ? void 0 : c.lo) !== null && _a !== void 0 ? _a : s.lo) / 1000);
|
|
21
|
+
const scaledHi = Math.round(((_b = c === null || c === void 0 ? void 0 : c.hi) !== null && _b !== void 0 ? _b : s.hi) / 1000);
|
|
24
22
|
return (react_1.default.createElement("div", { style: { background: theme_1.T.surface, border: `1px solid ${theme_1.T.border}`, borderRadius: 14, padding: "24px 28px", boxShadow: theme_1.T.shadow } },
|
|
25
23
|
react_1.default.createElement("div", { style: { display: "flex", alignItems: "flex-start", justifyContent: "space-between", marginBottom: 16 } },
|
|
26
24
|
react_1.default.createElement("div", { style: { display: "flex", alignItems: "center", gap: 14 } },
|
|
@@ -1,41 +1,9 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import type { ClientProfile, TaxAxisScreenProps } from "../../lib/types";
|
|
3
|
-
import type { LlmStrategyNarrative } from "./ProspectStrategyCard";
|
|
4
|
-
/** LLM output payload shape returned by generateProspectReport mutation */
|
|
5
|
-
export interface LlmOutputPayload {
|
|
6
|
-
execSummary: string;
|
|
7
|
-
strategyNarratives: Record<string, LlmStrategyNarrative>;
|
|
8
|
-
additionalStrategiesBlurb: string;
|
|
9
|
-
}
|
|
10
|
-
/** Backend-computed math payload */
|
|
11
|
-
export interface BackendComputePayload {
|
|
12
|
-
savingsLo: number;
|
|
13
|
-
savingsHi: number;
|
|
14
|
-
eligibleCount: number;
|
|
15
|
-
highPriorityCount: number;
|
|
16
|
-
quickWinCount: number;
|
|
17
|
-
gapBlock: {
|
|
18
|
-
currentTax: number;
|
|
19
|
-
optimizedTax: number;
|
|
20
|
-
realizedSavings: number;
|
|
21
|
-
realizedPctOfCurrent: number;
|
|
22
|
-
fedRatePct: number;
|
|
23
|
-
stateRatePct: number;
|
|
24
|
-
};
|
|
25
|
-
barData: Array<{
|
|
26
|
-
name: string;
|
|
27
|
-
mid: number;
|
|
28
|
-
pct: number;
|
|
29
|
-
}>;
|
|
30
|
-
}
|
|
31
3
|
export interface TaxAxisProspectReportProps extends TaxAxisScreenProps {
|
|
32
4
|
profile: ClientProfile;
|
|
33
|
-
/** When provided (from generateProspectReport mutation), all math comes from backend */
|
|
34
|
-
backendComputePayload?: BackendComputePayload | null;
|
|
35
|
-
/** LLM-generated narrative text from the mutation */
|
|
36
|
-
outputPayload?: LlmOutputPayload | null;
|
|
37
5
|
onUpgrade?: () => void;
|
|
38
6
|
onPresent?: () => void;
|
|
39
7
|
onReset?: () => void;
|
|
40
8
|
}
|
|
41
|
-
export declare function TaxAxisProspectReport({ profile,
|
|
9
|
+
export declare function TaxAxisProspectReport({ profile, onUpgrade, onPresent, onReset, }: TaxAxisProspectReportProps): React.JSX.Element;
|
|
@@ -35,48 +35,47 @@ const SampleAnalysisPreview_1 = require("./SampleAnalysisPreview");
|
|
|
35
35
|
const ProspectDocuments_1 = require("./ProspectDocuments");
|
|
36
36
|
const ProspectNextSteps_1 = require("./ProspectNextSteps");
|
|
37
37
|
const ProspectPrintView_1 = require("./ProspectPrintView");
|
|
38
|
-
function TaxAxisProspectReport({ profile,
|
|
39
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
38
|
+
function TaxAxisProspectReport({ profile, onUpgrade, onPresent, onReset, }) {
|
|
40
39
|
const bizName = profile.bizName || "Client";
|
|
41
40
|
const [confirmReset, setConfirmReset] = (0, react_1.useState)(false);
|
|
42
41
|
const [printMode, setPrintMode] = (0, react_1.useState)(false);
|
|
43
|
-
// ═══ ELIGIBLE STRATEGIES
|
|
42
|
+
// ═══ ELIGIBLE STRATEGIES ═══
|
|
44
43
|
const eligible = (0, react_1.useMemo)(() => (0, compute_1.filterEligibleStrategies)(profile), [profile]);
|
|
45
44
|
const high = eligible.filter(s => s.priority === "HIGH");
|
|
46
45
|
const quick = eligible.filter(s => s.priority === "QUICK WIN");
|
|
46
|
+
// Top 3 by score for the sales narrative
|
|
47
47
|
const top3 = (0, react_1.useMemo)(() => [...eligible].sort((a, b) => b.score - a.score).slice(0, 3), [eligible]);
|
|
48
48
|
const remaining = eligible.length - top3.length;
|
|
49
49
|
const remainingNames = eligible.filter(s => !top3.includes(s)).slice(0, 4).map(s => s.name);
|
|
50
|
-
// ═══ SAVINGS RANGE
|
|
50
|
+
// ═══ SAVINGS RANGE (dynamic computation) ═══
|
|
51
51
|
const computed = (0, react_1.useMemo)(() => (0, compute_1.computeAllStrategies)(profile), [profile]);
|
|
52
|
-
const displayLo =
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
: Math.round(eligible.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) / 1000);
|
|
58
|
-
// ═══ GAP BLOCK — backend-authoritative when available ═══
|
|
59
|
-
const fedRatePct = (_a = backendComputePayload === null || backendComputePayload === void 0 ? void 0 : backendComputePayload.gapBlock.fedRatePct) !== null && _a !== void 0 ? _a : (parseFloat((profile.federalRate || "24").replace("%", "")) || 24);
|
|
60
|
-
const stateRatePct = (_b = backendComputePayload === null || backendComputePayload === void 0 ? void 0 : backendComputePayload.gapBlock.stateRatePct) !== null && _b !== void 0 ? _b : (parseFloat(profile.stateRate || "4.95") || 4.95);
|
|
61
|
-
const currentTax = (_c = backendComputePayload === null || backendComputePayload === void 0 ? void 0 : backendComputePayload.gapBlock.currentTax) !== null && _c !== void 0 ? _c : Math.round(((0, compute_1.parseNum)(profile.netIncome) || Math.round((0, compute_1.parseNum)(profile.revenue) * 0.20))
|
|
62
|
-
* (fedRatePct + stateRatePct) / 100);
|
|
63
|
-
const realizedSavings = (_d = backendComputePayload === null || backendComputePayload === void 0 ? void 0 : backendComputePayload.gapBlock.realizedSavings) !== null && _d !== void 0 ? _d : Math.min(Math.round((displayLo + displayHi) / 2 * 1000 * 0.325), Math.round(currentTax * 0.6));
|
|
64
|
-
const optimizedTax = (_e = backendComputePayload === null || backendComputePayload === void 0 ? void 0 : backendComputePayload.gapBlock.optimizedTax) !== null && _e !== void 0 ? _e : (currentTax - realizedSavings);
|
|
65
|
-
const realizedPctOfCurrent = (_f = backendComputePayload === null || backendComputePayload === void 0 ? void 0 : backendComputePayload.gapBlock.realizedPctOfCurrent) !== null && _f !== void 0 ? _f : (currentTax > 0 ? Math.round((realizedSavings / currentTax) * 100) : 0);
|
|
66
|
-
// ═══ BAR CHART DATA ═══
|
|
67
|
-
const barData = (_g = backendComputePayload === null || backendComputePayload === void 0 ? void 0 : backendComputePayload.barData) !== null && _g !== void 0 ? _g : (() => {
|
|
68
|
-
const totalMid = eligible.reduce((a, s) => { var _a, _b; const c = computed.get(s.rank); return a + (((_a = c === null || c === void 0 ? void 0 : c.lo) !== null && _a !== void 0 ? _a : s.lo) + ((_b = c === null || c === void 0 ? void 0 : c.hi) !== null && _b !== void 0 ? _b : s.hi)) / 2; }, 0);
|
|
69
|
-
return top3.map(s => { var _a, _b; const c = computed.get(s.rank); const mid = Math.round((((_a = c === null || c === void 0 ? void 0 : c.lo) !== null && _a !== void 0 ? _a : s.lo) + ((_b = c === null || c === void 0 ? void 0 : c.hi) !== null && _b !== void 0 ? _b : s.hi)) / 2); return { name: s.name, mid, pct: totalMid > 0 ? Math.round(mid / totalMid * 100) : 0 }; });
|
|
70
|
-
})();
|
|
52
|
+
const displayLo = Math.round(eligible.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) / 1000);
|
|
53
|
+
const displayHi = Math.round(eligible.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) / 1000);
|
|
54
|
+
// Savings bar chart data — top 3 strategies with midpoint + percentage
|
|
55
|
+
const totalMid = eligible.reduce((a, s) => { var _a, _b; const c = computed.get(s.rank); return a + (((_a = c === null || c === void 0 ? void 0 : c.lo) !== null && _a !== void 0 ? _a : s.lo) + ((_b = c === null || c === void 0 ? void 0 : c.hi) !== null && _b !== void 0 ? _b : s.hi)) / 2; }, 0);
|
|
56
|
+
const barData = top3.map(s => { var _a, _b; const c = computed.get(s.rank); const mid = Math.round((((_a = c === null || c === void 0 ? void 0 : c.lo) !== null && _a !== void 0 ? _a : s.lo) + ((_b = c === null || c === void 0 ? void 0 : c.hi) !== null && _b !== void 0 ? _b : s.hi)) / 2); return { name: s.name, mid, pct: totalMid > 0 ? Math.round(mid / totalMid * 100) : 0 }; });
|
|
71
57
|
const maxMid = Math.max(...barData.map(b => b.mid), 1);
|
|
58
|
+
// ═══ GAP BLOCK MATH — Current vs Optimized tax position ═══
|
|
59
|
+
const rev = (0, compute_1.parseNum)(profile.revenue);
|
|
60
|
+
const netIncome = (0, compute_1.parseNum)(profile.netIncome) || Math.round(rev * 0.20);
|
|
61
|
+
const fedRatePct = parseFloat((profile.federalRate || "24").replace("%", "")) || 24;
|
|
62
|
+
const stateRatePct = parseFloat(profile.stateRate || "4.95") || 4.95;
|
|
63
|
+
const combinedRate = (fedRatePct + stateRatePct) / 100;
|
|
64
|
+
const currentTax = Math.round(netIncome * combinedRate);
|
|
65
|
+
const theoreticalMidK = (displayLo + displayHi) / 2;
|
|
66
|
+
const realizedSavings = Math.min(Math.round(theoreticalMidK * 1000 * 0.325), Math.round(currentTax * 0.6));
|
|
67
|
+
const optimizedTax = currentTax - realizedSavings;
|
|
68
|
+
const realizedPctOfCurrent = currentTax > 0 ? Math.round((realizedSavings / currentTax) * 100) : 0;
|
|
69
|
+
// Section numbering — shifts when Additional Strategies section is present
|
|
70
|
+
const sectionNums = remaining > 0
|
|
71
|
+
? { recommended: "02", additional: "03", sample: "04", documents: "05", nextSteps: "06" }
|
|
72
|
+
: { recommended: "02", additional: null, sample: "03", documents: "04", nextSteps: "05" };
|
|
72
73
|
// ═══ DOCUMENT GROUPING ═══
|
|
73
74
|
const allDocsNeeded = [...new Set(eligible.flatMap(s => { var _a; return ((_a = strategyProspect_1.STRATEGY_PROSPECT[s.rank]) === null || _a === void 0 ? void 0 : _a.docsNeeded) || []; }))];
|
|
74
75
|
const requiredDocs = ["Profit & Loss Statement (current year)", "Balance Sheet (current year)"];
|
|
75
76
|
const recommendedDocs = ["Federal Tax Return (most recent)", "W-2 / Officer Compensation records"];
|
|
76
77
|
const conditionalDocs = allDocsNeeded.filter(d => !requiredDocs.includes(d) && !recommendedDocs.includes(d)).slice(0, 5);
|
|
77
|
-
|
|
78
|
-
? { recommended: "02", additional: "03", sample: "04", documents: "05", nextSteps: "06" }
|
|
79
|
-
: { recommended: "02", additional: null, sample: "03", documents: "04", nextSteps: "05" };
|
|
78
|
+
// ═══ PRINT HANDLER ═══
|
|
80
79
|
const handlePrint = () => {
|
|
81
80
|
setPrintMode(true);
|
|
82
81
|
requestAnimationFrame(() => {
|
|
@@ -87,16 +86,18 @@ function TaxAxisProspectReport({ profile, backendComputePayload, outputPayload,
|
|
|
87
86
|
});
|
|
88
87
|
});
|
|
89
88
|
};
|
|
89
|
+
// ═══ PRINT MODE — Light theme layout ═══
|
|
90
90
|
if (printMode) {
|
|
91
91
|
return (react_1.default.createElement(ProspectPrintView_1.ProspectPrintView, { profile: profile, bizName: bizName, displayLo: displayLo, displayHi: displayHi, currentTax: currentTax, optimizedTax: optimizedTax, realizedSavings: realizedSavings, realizedPctOfCurrent: realizedPctOfCurrent, fedRatePct: fedRatePct, stateRatePct: stateRatePct, eligible: eligible, high: high, quick: quick, top3: top3, computed: computed, fmtTax: compute_1.fmtTax, fmtSavings: compute_1.fmtSavings, requiredDocs: requiredDocs, recommendedDocs: recommendedDocs, conditionalDocs: conditionalDocs }));
|
|
92
92
|
}
|
|
93
|
+
// ═══ INTERACTIVE MODE — Dark theme layout ═══
|
|
93
94
|
return (react_1.default.createElement("div", null,
|
|
94
95
|
react_1.default.createElement(ProspectCover_1.ProspectCover, { profile: profile, bizName: bizName, displayLo: displayLo, displayHi: displayHi }),
|
|
95
96
|
react_1.default.createElement(TaxPositionGap_1.TaxPositionGap, { bizName: bizName, currentTax: currentTax, optimizedTax: optimizedTax, realizedSavings: realizedSavings, realizedPctOfCurrent: realizedPctOfCurrent, fedRatePct: fedRatePct, stateRatePct: stateRatePct, eligibleCount: eligible.length, fmtTax: compute_1.fmtTax, fmtSavings: compute_1.fmtSavings }),
|
|
96
97
|
react_1.default.createElement("div", { style: { background: theme_1.T.surface, border: `1px solid ${theme_1.T.border}`, borderRadius: 14, padding: "24px 28px", marginBottom: 24, boxShadow: theme_1.T.shadow } },
|
|
97
98
|
react_1.default.createElement("div", { style: { fontSize: 10, fontWeight: 700, textTransform: "uppercase", letterSpacing: "0.15em", color: theme_1.T.text4, marginBottom: 16, fontFamily: theme_1.T.mono } }, "AT A GLANCE"),
|
|
98
99
|
react_1.default.createElement("div", { style: { display: "grid", gridTemplateColumns: "1fr 1fr 1fr 1fr", gap: 16 } }, [
|
|
99
|
-
{ v: `$${displayLo}K
|
|
100
|
+
{ v: `$${displayLo}K\u2013$${displayHi}K`, l: "Est. Savings" },
|
|
100
101
|
{ v: String(eligible.length), l: "Strategies Found" },
|
|
101
102
|
{ v: String(high.length), l: "High Impact" },
|
|
102
103
|
{ v: String(quick.length), l: "Quick Wins" },
|
|
@@ -108,30 +109,29 @@ function TaxAxisProspectReport({ profile, backendComputePayload, outputPayload,
|
|
|
108
109
|
react_1.default.createElement("span", { style: { fontSize: 11, fontWeight: 700, color: theme_1.T.accent, fontFamily: theme_1.T.mono } }, "01"),
|
|
109
110
|
react_1.default.createElement("span", { style: { fontSize: 11, fontWeight: 700, textTransform: "uppercase", letterSpacing: "0.12em", color: theme_1.T.text4, fontFamily: theme_1.T.body } }, "EXECUTIVE SUMMARY")),
|
|
110
111
|
react_1.default.createElement("div", { style: { height: 2, background: theme_1.T.accent, width: 48, marginBottom: 16 } }),
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
" These are profile-based estimates \u2014 uploading financial documents will refine savings calculations and unlock all 25 strategies.")))),
|
|
112
|
+
react_1.default.createElement("div", { style: { fontSize: 14, color: theme_1.T.text2, fontFamily: theme_1.T.body, lineHeight: 1.8, marginBottom: 12 } },
|
|
113
|
+
"We evaluated ",
|
|
114
|
+
bizName,
|
|
115
|
+
"'s tax position against 25 federal strategies and identified ",
|
|
116
|
+
react_1.default.createElement("strong", { style: { color: theme_1.T.white } },
|
|
117
|
+
eligible.length,
|
|
118
|
+
" applicable opportunities"),
|
|
119
|
+
" with combined estimated savings of ",
|
|
120
|
+
react_1.default.createElement("strong", { style: { color: theme_1.T.accentLt } },
|
|
121
|
+
"$",
|
|
122
|
+
displayLo,
|
|
123
|
+
"K\u2013$",
|
|
124
|
+
displayHi,
|
|
125
|
+
"K annually"),
|
|
126
|
+
". ",
|
|
127
|
+
high.length,
|
|
128
|
+
" strategies could have significant impact."),
|
|
129
|
+
react_1.default.createElement("div", { style: { fontSize: 14, color: theme_1.T.text2, fontFamily: theme_1.T.body, lineHeight: 1.8 } },
|
|
130
|
+
"The top ",
|
|
131
|
+
top3.length,
|
|
132
|
+
" strategies are detailed below.",
|
|
133
|
+
quick.length > 0 ? ` ${quick.length} can be implemented this week with no structural changes.` : "",
|
|
134
|
+
" These are profile-based estimates \u2014 uploading financial documents will refine savings calculations and unlock all 25 strategies.")),
|
|
135
135
|
react_1.default.createElement("div", { style: { background: theme_1.T.surface, border: `1px solid ${theme_1.T.border}`, borderRadius: 14, padding: "20px 24px", marginBottom: 24, boxShadow: theme_1.T.shadow } },
|
|
136
136
|
react_1.default.createElement("div", { style: { fontSize: 10, fontWeight: 700, color: theme_1.T.text4, textTransform: "uppercase", letterSpacing: "0.12em", marginBottom: 12, fontFamily: theme_1.T.mono } }, "SAVINGS DISTRIBUTION"),
|
|
137
137
|
barData.map((b, i) => (react_1.default.createElement("div", { key: i, style: { display: "flex", alignItems: "center", gap: 10, marginBottom: i < barData.length - 1 ? 8 : 0 } },
|
|
@@ -151,10 +151,7 @@ function TaxAxisProspectReport({ profile, backendComputePayload, outputPayload,
|
|
|
151
151
|
react_1.default.createElement("span", { style: { fontSize: 11, fontWeight: 700, color: theme_1.T.accent, fontFamily: theme_1.T.mono } }, "02"),
|
|
152
152
|
react_1.default.createElement("span", { style: { fontSize: 11, fontWeight: 700, textTransform: "uppercase", letterSpacing: "0.12em", color: theme_1.T.text4, fontFamily: theme_1.T.body } }, "RECOMMENDED STRATEGIES")),
|
|
153
153
|
react_1.default.createElement("div", { style: { height: 2, background: theme_1.T.accent, width: 48, marginBottom: 16 } }),
|
|
154
|
-
react_1.default.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 14 } }, top3.map((s, i) => {
|
|
155
|
-
var _a;
|
|
156
|
-
return (react_1.default.createElement(ProspectStrategyCard_1.ProspectStrategyCard, { key: s.rank, strategy: s, index: i, profile: profile, computed: computed, llmNarrative: (_a = outputPayload === null || outputPayload === void 0 ? void 0 : outputPayload.strategyNarratives) === null || _a === void 0 ? void 0 : _a[`S${s.rank}`] }));
|
|
157
|
-
}))),
|
|
154
|
+
react_1.default.createElement("div", { style: { display: "flex", flexDirection: "column", gap: 14 } }, top3.map((s, i) => (react_1.default.createElement(ProspectStrategyCard_1.ProspectStrategyCard, { key: s.rank, strategy: s, index: i, profile: profile, computed: computed }))))),
|
|
158
155
|
remaining > 0 && (react_1.default.createElement("div", { style: { marginBottom: 24 } },
|
|
159
156
|
react_1.default.createElement("div", { style: { display: "flex", alignItems: "baseline", gap: 10, marginBottom: 6 } },
|
|
160
157
|
react_1.default.createElement("span", { style: { fontSize: 11, fontWeight: 700, color: theme_1.T.accent, fontFamily: theme_1.T.mono } }, "03"),
|
|
@@ -165,8 +162,10 @@ function TaxAxisProspectReport({ profile, backendComputePayload, outputPayload,
|
|
|
165
162
|
"+ ",
|
|
166
163
|
remaining,
|
|
167
164
|
" more strategies identified"),
|
|
168
|
-
react_1.default.createElement("div", { style: { fontSize: 13, color: theme_1.T.text2, fontFamily: theme_1.T.body, lineHeight: 1.7, marginBottom: 10 } },
|
|
169
|
-
|
|
165
|
+
react_1.default.createElement("div", { style: { fontSize: 13, color: theme_1.T.text2, fontFamily: theme_1.T.body, lineHeight: 1.7, marginBottom: 10 } },
|
|
166
|
+
"Including: ",
|
|
167
|
+
remainingNames.join(", "),
|
|
168
|
+
remaining > 4 ? " and more" : ""),
|
|
170
169
|
react_1.default.createElement("div", { style: { fontSize: 12, color: theme_1.T.text3, fontFamily: theme_1.T.body, lineHeight: 1.6, borderLeft: `3px solid ${theme_1.T.accent}`, paddingLeft: 12 } }, "Full analysis with documents unlocks specific savings estimates for every eligible strategy.")))),
|
|
171
170
|
react_1.default.createElement(SampleAnalysisPreview_1.SampleAnalysisPreview, { sectionNum: sectionNums.sample, eligibleCount: eligible.length }),
|
|
172
171
|
react_1.default.createElement(ProspectDocuments_1.ProspectDocuments, { sectionNum: sectionNums.documents, requiredDocs: requiredDocs, recommendedDocs: recommendedDocs, conditionalDocs: conditionalDocs }),
|
|
@@ -1,65 +1,49 @@
|
|
|
1
1
|
import type { Strategy, ComputedMap } from "../types";
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
weighted_score?: number;
|
|
14
|
-
quick_win?: boolean;
|
|
15
|
-
timeline_bucket?: string;
|
|
16
|
-
calculation_trace?: {
|
|
17
|
-
irs_cite?: string;
|
|
18
|
-
forms_required?: string[];
|
|
19
|
-
source_documents?: Array<{
|
|
20
|
-
field_label: string;
|
|
21
|
-
value_display: string;
|
|
22
|
-
source_hint: string;
|
|
23
|
-
}>;
|
|
24
|
-
position_strength?: string;
|
|
25
|
-
specialist_note?: string;
|
|
26
|
-
};
|
|
27
|
-
why_it_applies?: string;
|
|
28
|
-
next_step?: string;
|
|
29
|
-
risk_disclosure?: string;
|
|
30
|
-
}
|
|
31
|
-
export interface EngineCpaWorkflow {
|
|
32
|
-
engagement_recommendations?: string[];
|
|
2
|
+
/** Raw engine output blob from the LLM run. */
|
|
3
|
+
export type EngineOutput = Record<string, unknown> | null;
|
|
4
|
+
/** Pre-computed formula engine results from outputPayload.meta.preComputed. */
|
|
5
|
+
export interface PreComputedResults {
|
|
6
|
+
results: Record<string, PreComputedStrategyResult>;
|
|
7
|
+
completedCount?: number;
|
|
8
|
+
excludedCount?: number;
|
|
9
|
+
failedCount?: number;
|
|
10
|
+
totalSavingsLow?: number;
|
|
11
|
+
totalSavingsHigh?: number;
|
|
12
|
+
computedAt?: string;
|
|
33
13
|
}
|
|
34
|
-
export interface
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
why_it_applies?: string;
|
|
45
|
-
next_step?: string;
|
|
46
|
-
}>;
|
|
47
|
-
};
|
|
48
|
-
nexus_flags?: Array<{
|
|
49
|
-
state: string;
|
|
50
|
-
nexus_type: string;
|
|
51
|
-
flag_text: string;
|
|
52
|
-
}>;
|
|
53
|
-
eligibility_screening?: {
|
|
54
|
-
excluded_strategies?: Array<{
|
|
55
|
-
strategy_id: string;
|
|
56
|
-
reason: string;
|
|
57
|
-
irs_cite: string;
|
|
58
|
-
}>;
|
|
59
|
-
};
|
|
14
|
+
export interface PreComputedStrategyResult {
|
|
15
|
+
strategy_id: string;
|
|
16
|
+
status: "COMPLETE" | "EXCLUDED" | "FAILED";
|
|
17
|
+
result_gross: number | null;
|
|
18
|
+
confidence_interval?: {
|
|
19
|
+
low: number;
|
|
20
|
+
high: number;
|
|
21
|
+
basis?: string;
|
|
22
|
+
} | null;
|
|
23
|
+
trace?: Record<string, unknown>;
|
|
60
24
|
}
|
|
25
|
+
/** Adapter result passed to client-report / preparer-workpaper components. */
|
|
61
26
|
export interface EngineOutputAdapterResult {
|
|
62
27
|
strategies: Strategy[];
|
|
63
28
|
computedMap: ComputedMap;
|
|
64
29
|
}
|
|
65
|
-
|
|
30
|
+
/** Single strategy analysis entry from the engine. */
|
|
31
|
+
export interface EngineStrategyAnalysis {
|
|
32
|
+
strategy_id: string;
|
|
33
|
+
strategy_name: string;
|
|
34
|
+
priority_tier: string;
|
|
35
|
+
weighted_score: number;
|
|
36
|
+
quick_win: boolean;
|
|
37
|
+
engagement_recommendation: string;
|
|
38
|
+
risk_disclosure?: string | null;
|
|
39
|
+
calculation_trace?: Record<string, unknown>;
|
|
40
|
+
_reconciledPreComputed?: boolean;
|
|
41
|
+
[key: string]: unknown;
|
|
42
|
+
}
|
|
43
|
+
/** CPA workflow section from the engine. */
|
|
44
|
+
export interface EngineCpaWorkflow {
|
|
45
|
+
workpaper_codes: string[] | null;
|
|
46
|
+
engagement_recommendations: string[];
|
|
47
|
+
[key: string]: unknown;
|
|
48
|
+
}
|
|
49
|
+
export declare function useEngineOutput(engineOutput: EngineOutput, preComputed?: PreComputedResults | null): EngineOutputAdapterResult | null;
|