@paro.io/expert-shared-components 1.14.57 → 1.14.60

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 (80) hide show
  1. package/lib/README.md +2 -0
  2. package/lib/components/DocumentCenter/MultiFileUploadSection.js +121 -220
  3. package/lib/components/TaxAxis/TaxAxisApi.d.ts +2 -0
  4. package/lib/components/TaxAxis/TaxAxisShell.d.ts +1 -1
  5. package/lib/components/TaxAxis/TaxAxisShell.js +104 -5
  6. package/lib/components/TaxAxis/types.d.ts +5 -0
  7. package/lib/components/shared/UploadClient.d.ts +1 -2
  8. package/lib/components/shared/UploadClient.js +2 -6
  9. package/lib/index.d.ts +13 -2
  10. package/lib/index.js +27 -3
  11. package/lib/package.json +68 -0
  12. package/lib/tax-axis/components/clientReport/ExecutiveSummary.d.ts +1 -4
  13. package/lib/tax-axis/components/clientReport/ExecutiveSummary.js +6 -10
  14. package/lib/tax-axis/components/clientReport/Methodology.js +2 -2
  15. package/lib/tax-axis/components/clientReport/RecommendedStrategies.d.ts +1 -6
  16. package/lib/tax-axis/components/clientReport/RecommendedStrategies.js +24 -26
  17. package/lib/tax-axis/components/clientReport/StrategyCard.d.ts +1 -1
  18. package/lib/tax-axis/components/clientReport/StrategyCard.js +23 -39
  19. package/lib/tax-axis/components/clientReport/TaxAxisClientReport.d.ts +2 -8
  20. package/lib/tax-axis/components/clientReport/TaxAxisClientReport.js +7 -9
  21. package/lib/tax-axis/components/dashboard/DashboardActions.js +4 -5
  22. package/lib/tax-axis/components/dashboard/DashboardSummary.d.ts +1 -6
  23. package/lib/tax-axis/components/dashboard/DashboardSummary.js +4 -14
  24. package/lib/tax-axis/components/dashboard/StrategyDetailPanel.d.ts +1 -1
  25. package/lib/tax-axis/components/dashboard/StrategyDetailPanel.js +91 -120
  26. package/lib/tax-axis/components/dashboard/TaxAxisDashboard.d.ts +2 -59
  27. package/lib/tax-axis/components/dashboard/TaxAxisDashboard.js +36 -412
  28. package/lib/tax-axis/components/documents/DocumentCard.d.ts +3 -7
  29. package/lib/tax-axis/components/documents/DocumentCard.js +12 -65
  30. package/lib/tax-axis/components/documents/DocumentTier.d.ts +2 -5
  31. package/lib/tax-axis/components/documents/DocumentTier.js +2 -2
  32. package/lib/tax-axis/components/documents/TaxAxisDocuments.d.ts +1 -25
  33. package/lib/tax-axis/components/documents/TaxAxisDocuments.js +52 -267
  34. package/lib/tax-axis/components/documents/qbo/QboAvailableReportsModal.d.ts +13 -0
  35. package/lib/tax-axis/components/documents/qbo/QboAvailableReportsModal.js +180 -0
  36. package/lib/tax-axis/components/documents/qbo/QboClientSelectorModal.d.ts +10 -0
  37. package/lib/tax-axis/components/documents/qbo/QboClientSelectorModal.js +155 -0
  38. package/lib/tax-axis/components/documents/qbo/QboConnectBanner.d.ts +9 -0
  39. package/lib/tax-axis/components/documents/qbo/QboConnectBanner.js +55 -0
  40. package/lib/tax-axis/components/documents/qbo/QboDocumentMappingModal.d.ts +10 -0
  41. package/lib/tax-axis/components/documents/qbo/QboDocumentMappingModal.js +202 -0
  42. package/lib/tax-axis/components/documents/qbo/QboImportingModal.d.ts +8 -0
  43. package/lib/tax-axis/components/documents/qbo/QboImportingModal.js +75 -0
  44. package/lib/tax-axis/components/documents/qbo/QboPermissionsModal.d.ts +8 -0
  45. package/lib/tax-axis/components/documents/qbo/QboPermissionsModal.js +126 -0
  46. package/lib/tax-axis/components/documents/qbo/index.d.ts +8 -0
  47. package/lib/tax-axis/components/documents/qbo/index.js +17 -0
  48. package/lib/tax-axis/components/documents/qbo/qboConstants.d.ts +24 -0
  49. package/lib/tax-axis/components/documents/qbo/qboConstants.js +71 -0
  50. package/lib/tax-axis/components/documents/qbo/types.d.ts +43 -0
  51. package/lib/tax-axis/components/documents/qbo/types.js +3 -0
  52. package/lib/tax-axis/components/documents/qbo/useQboFlow.d.ts +19 -0
  53. package/lib/tax-axis/components/documents/qbo/useQboFlow.js +207 -0
  54. package/lib/tax-axis/components/extractionReview/TaxAxisExtractionReview.js +17 -17
  55. package/lib/tax-axis/components/intake/ClientParametersSection.js +29 -13
  56. package/lib/tax-axis/components/intake/IntakeCtaCards.d.ts +2 -1
  57. package/lib/tax-axis/components/intake/IntakeCtaCards.js +13 -6
  58. package/lib/tax-axis/components/intake/TaxAxisIntake.js +44 -5
  59. package/lib/tax-axis/components/intake/intakeSchema.d.ts +3 -0
  60. package/lib/tax-axis/components/intake/intakeSchema.js +4 -2
  61. package/lib/tax-axis/components/preparerWorkpaper/TaxAxisPreparerWorkpaper.d.ts +2 -26
  62. package/lib/tax-axis/components/preparerWorkpaper/TaxAxisPreparerWorkpaper.js +4 -15
  63. package/lib/tax-axis/components/processing/TaxAxisProcessing.d.ts +1 -3
  64. package/lib/tax-axis/components/processing/TaxAxisProcessing.js +31 -102
  65. package/lib/tax-axis/components/prospectReport/ProspectPrintView.js +2 -0
  66. package/lib/tax-axis/components/prospectReport/ProspectStrategyCard.d.ts +8 -1
  67. package/lib/tax-axis/components/prospectReport/ProspectStrategyCard.js +5 -5
  68. package/lib/tax-axis/components/prospectReport/TaxAxisProspectReport.d.ts +27 -1
  69. package/lib/tax-axis/components/prospectReport/TaxAxisProspectReport.js +43 -25
  70. package/lib/tax-axis/index.d.ts +0 -4
  71. package/lib/tax-axis/index.js +1 -6
  72. package/lib/tax-axis/lib/adapters/useEngineOutput.d.ts +13 -138
  73. package/lib/tax-axis/lib/adapters/useEngineOutput.js +7 -156
  74. package/lib/tax-axis/lib/data/documents.d.ts +2 -3
  75. package/lib/tax-axis/lib/data/documents.js +25 -225
  76. package/lib/tax-axis/lib/data/strategies.js +9 -9
  77. package/lib/tax-axis/lib/documentFieldCatalog.d.ts +12 -7
  78. package/lib/tax-axis/lib/documentFieldCatalog.js +8 -805
  79. package/lib/tax-axis/lib/types/index.d.ts +1 -13
  80. package/package.json +1 -1
@@ -6,6 +6,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.DocumentCard = DocumentCard;
7
7
  const react_1 = __importDefault(require("react"));
8
8
  const TaxAxisBadge_1 = require("../shared/TaxAxisBadge");
9
+ // Inline style values per status — mock T hex values for SVG strokes and
10
+ // rgba borders that don't have direct Tailwind token equivalents.
9
11
  const STATUS_STYLES = {
10
12
  empty: {
11
13
  iconBg: "#171B44",
@@ -17,28 +19,16 @@ const STATUS_STYLES = {
17
19
  iconBorder: "rgba(251,154,29,0.25)",
18
20
  cardBorder: "rgba(251,154,29,0.25)",
19
21
  },
20
- parsing: {
21
- iconBg: "rgba(36,131,132,0.08)",
22
- iconBorder: "rgba(36,131,132,0.25)",
23
- cardBorder: "rgba(36,131,132,0.25)",
24
- },
25
- failed: {
26
- iconBg: "rgba(197,48,48,0.08)",
27
- iconBorder: "rgba(197,48,48,0.25)",
28
- cardBorder: "rgba(197,48,48,0.25)",
29
- },
30
22
  valid: {
31
23
  iconBg: "rgba(15,110,86,0.08)",
32
24
  iconBorder: "rgba(15,110,86,0.25)",
33
25
  cardBorder: "rgba(15,110,86,0.25)",
34
26
  },
35
27
  };
36
- function DocumentCard({ doc, tierBorderColor, tierBadgeColor, tierBadgeText, helpOverride, fieldCount, onUpload, onClear, onRemove, onReview, }) {
37
- var _a, _b, _c;
28
+ function DocumentCard({ doc, tierBorderColor, tierBadgeColor, tierBadgeText, helpOverride, onUpload, onClear, }) {
29
+ var _a;
38
30
  const ss = STATUS_STYLES[doc.status];
39
31
  const leftBorder = doc.status === "valid" ? "rgba(15,110,86,0.6)" : tierBorderColor;
40
- const fileInputId = `tax-axis-upload-${doc.id}`;
41
- const reuploadInputId = `tax-axis-reupload-${doc.id}`;
42
32
  return (react_1.default.createElement("div", { className: "bg-tax-axis-surface overflow-hidden", style: {
43
33
  border: `1px solid ${ss.cardBorder}`,
44
34
  borderLeft: `3px solid ${leftBorder}`,
@@ -52,63 +42,20 @@ function DocumentCard({ doc, tierBorderColor, tierBadgeColor, tierBadgeText, hel
52
42
  } }, doc.status === "validating" ? (react_1.default.createElement("div", { className: "w-3.5 h-3.5 rounded-full animate-spin", style: {
53
43
  border: "2px solid transparent",
54
44
  borderTopColor: "#FB9A1D",
55
- } })) : doc.status === "parsing" ? (react_1.default.createElement("div", { className: "w-3.5 h-3.5 rounded-full animate-spin", style: {
56
- border: "2px solid transparent",
57
- borderTopColor: "#248384",
58
- } })) : doc.status === "failed" ? (react_1.default.createElement("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none" },
59
- react_1.default.createElement("path", { d: "M4 4l6 6M10 4L4 10", stroke: "#C53030", strokeWidth: "2", strokeLinecap: "round" }))) : doc.status === "valid" ? (react_1.default.createElement("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none" },
45
+ } })) : doc.status === "valid" ? (react_1.default.createElement("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none" },
60
46
  react_1.default.createElement("path", { d: "M2.5 7l3.5 3.5 5.5-6", stroke: "#0F6E56", strokeWidth: "2", strokeLinecap: "round" }))) : (react_1.default.createElement("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none" },
61
47
  react_1.default.createElement("circle", { cx: "7", cy: "7", r: "5", stroke: "#9498B8", strokeWidth: "1.5", strokeDasharray: "3 3" })))),
62
48
  react_1.default.createElement("div", { className: "flex-1 min-w-0" },
63
49
  react_1.default.createElement("div", { className: "flex items-center gap-1.5" },
64
50
  react_1.default.createElement("span", { className: "text-[13px] font-medium text-white font-tax-axis-body" }, doc.name),
65
- doc.status === "empty" && (react_1.default.createElement(TaxAxisBadge_1.TaxAxisBadge, { color: tierBadgeColor, size: "xs" }, tierBadgeText)),
66
- doc.status === "failed" && (react_1.default.createElement(TaxAxisBadge_1.TaxAxisBadge, { color: "red", size: "xs" }, "FAILED"))),
67
- react_1.default.createElement("div", { className: "text-[11px] text-tax-axis-text-3 font-tax-axis-body mt-0.5" }, doc.status === "valid" && doc.fileName
68
- ? `${doc.fileName}${fieldCount ? ` \u00b7 ${fieldCount} fields extracted` : ""}`
69
- : doc.status === "failed" && doc.parseError
70
- ? doc.parseError
71
- : ((_a = doc.fileName) !== null && _a !== void 0 ? _a : (helpOverride !== null && helpOverride !== void 0 ? helpOverride : doc.help)))),
51
+ doc.status === "empty" && (react_1.default.createElement(TaxAxisBadge_1.TaxAxisBadge, { color: tierBadgeColor, size: "xs" }, tierBadgeText))),
52
+ react_1.default.createElement("div", { className: "text-[11px] text-tax-axis-text-3 font-tax-axis-body mt-0.5" }, (_a = doc.fileName) !== null && _a !== void 0 ? _a : (helpOverride !== null && helpOverride !== void 0 ? helpOverride : doc.help))),
72
53
  react_1.default.createElement("div", { className: "flex items-center gap-2 flex-shrink-0" },
73
54
  doc.status === "valid" && (react_1.default.createElement(react_1.default.Fragment, null,
74
- react_1.default.createElement("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none" },
75
- react_1.default.createElement("path", { d: "M2.5 7l3.5 3.5 5.5-6", stroke: "#0F6E56", strokeWidth: "2", strokeLinecap: "round" })),
76
- onReview && (react_1.default.createElement("button", { onClick: onReview, className: "rounded-md px-3 py-1 text-[11px] font-semibold font-tax-axis-mono cursor-pointer flex items-center gap-1.5", style: {
77
- background: "rgba(99,102,241,0.12)",
78
- border: "1px solid rgba(99,102,241,0.30)",
79
- color: "#a5b4fc",
80
- } },
81
- react_1.default.createElement("svg", { width: "12", height: "12", viewBox: "0 0 12 12", fill: "none" },
82
- react_1.default.createElement("rect", { x: "2", y: "1.5", width: "8", height: "9", rx: "1.5", stroke: "#a5b4fc", strokeWidth: "1.2" }),
83
- react_1.default.createElement("path", { d: "M4 4.5h4M4 6.5h4M4 8.5h2", stroke: "#a5b4fc", strokeWidth: "0.9", strokeLinecap: "round" })),
84
- "Review")),
55
+ react_1.default.createElement("span", { className: "text-[10px] font-semibold font-tax-axis-mono", style: { color: "#0F6E56" } }, "Done"),
85
56
  react_1.default.createElement("button", { onClick: onClear, className: "bg-transparent border-none p-1 text-tax-axis-text-4 text-sm cursor-pointer" }, "\u00D7"))),
86
- (doc.status === "parsing" || doc.status === "validating") && (react_1.default.createElement("span", { className: "text-[10px] font-semibold font-tax-axis-mono", style: { color: doc.status === "parsing" ? "#248384" : "#FB9A1D" } }, doc.status === "parsing" ? "Parsing…" : "Uploading…")),
87
- doc.status === "failed" && (react_1.default.createElement(react_1.default.Fragment, null,
88
- react_1.default.createElement("input", { id: reuploadInputId, type: "file", className: "hidden", accept: (_b = doc.accept) === null || _b === void 0 ? void 0 : _b.join(","), onChange: (event) => {
89
- var _a;
90
- const file = (_a = event.target.files) === null || _a === void 0 ? void 0 : _a[0];
91
- if (file) {
92
- onUpload(file);
93
- }
94
- event.currentTarget.value = "";
95
- } }),
96
- react_1.default.createElement("label", { htmlFor: reuploadInputId, className: "rounded-md px-2.5 py-1 text-[10px] font-semibold text-tax-axis-teal-light font-tax-axis-mono cursor-pointer", style: {
97
- background: "rgba(36,131,132,0.10)",
98
- border: "1px solid rgba(36,131,132,0.2)",
99
- } }, "Re-upload"),
100
- react_1.default.createElement("button", { onClick: onRemove || onClear, className: "bg-transparent border-none p-1 text-tax-axis-text-4 text-sm cursor-pointer", title: "Remove document" }, "\u00D7"))),
101
- doc.status === "empty" && (react_1.default.createElement(react_1.default.Fragment, null,
102
- react_1.default.createElement("input", { id: fileInputId, type: "file", className: "hidden", accept: (_c = doc.accept) === null || _c === void 0 ? void 0 : _c.join(","), onChange: (event) => {
103
- var _a;
104
- const file = (_a = event.target.files) === null || _a === void 0 ? void 0 : _a[0];
105
- if (file) {
106
- onUpload(file);
107
- }
108
- event.currentTarget.value = "";
109
- } }),
110
- react_1.default.createElement("label", { htmlFor: fileInputId, className: "rounded-md px-3.5 py-1.5 text-[11px] font-semibold text-tax-axis-teal-light font-tax-axis-mono cursor-pointer", style: {
111
- background: "rgba(36,131,132,0.10)",
112
- border: "1px solid rgba(36,131,132,0.2)",
113
- } }, "Upload")))))));
57
+ doc.status === "empty" && (react_1.default.createElement("button", { onClick: onUpload, className: "rounded-md px-3.5 py-1.5 text-[11px] font-semibold text-tax-axis-teal-light font-tax-axis-mono cursor-pointer", style: {
58
+ background: "rgba(36,131,132,0.10)",
59
+ border: "1px solid rgba(36,131,132,0.2)",
60
+ } }, "Upload"))))));
114
61
  }
@@ -14,11 +14,8 @@ interface DocumentTierProps {
14
14
  tier: TierDef;
15
15
  docs: DocState[];
16
16
  helpOverrides: Record<string, string>;
17
- fieldCounts?: Record<string, number>;
18
- onUpload: (idx: number, file: File) => void;
17
+ onUpload: (idx: number) => void;
19
18
  onClear: (idx: number) => void;
20
- onRemove?: (idx: number) => void;
21
- onReview?: (docId: string) => void;
22
19
  }
23
- export declare function DocumentTier({ tier, docs, helpOverrides, fieldCounts, onUpload, onClear, onRemove, onReview, }: DocumentTierProps): React.JSX.Element | null;
20
+ export declare function DocumentTier({ tier, docs, helpOverrides, onUpload, onClear, }: DocumentTierProps): React.JSX.Element | null;
24
21
  export {};
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.DocumentTier = DocumentTier;
7
7
  const react_1 = __importDefault(require("react"));
8
8
  const DocumentCard_1 = require("./DocumentCard");
9
- function DocumentTier({ tier, docs, helpOverrides, fieldCounts, onUpload, onClear, onRemove, onReview, }) {
9
+ function DocumentTier({ tier, docs, helpOverrides, onUpload, onClear, }) {
10
10
  const tierDocs = tier.ids
11
11
  .map((id) => {
12
12
  const idx = docs.findIndex((d) => d.id === id);
@@ -19,5 +19,5 @@ function DocumentTier({ tier, docs, helpOverrides, fieldCounts, onUpload, onClea
19
19
  react_1.default.createElement("div", { className: "flex items-center gap-2 mb-2" },
20
20
  react_1.default.createElement("span", { className: "text-[10px] font-bold uppercase tracking-widest font-tax-axis-mono", style: { color: tier.labelColor } }, tier.label),
21
21
  react_1.default.createElement("span", { className: "text-[10px] text-tax-axis-text-4 font-tax-axis-body" }, tier.sublabel)),
22
- react_1.default.createElement("div", { className: "grid gap-1.5" }, tierDocs.map(({ doc, idx }) => (react_1.default.createElement(DocumentCard_1.DocumentCard, { key: doc.id, doc: doc, tierBorderColor: tier.borderColor, tierBadgeColor: tier.badgeColor, tierBadgeText: tier.badgeText, helpOverride: helpOverrides[doc.id], fieldCount: fieldCounts === null || fieldCounts === void 0 ? void 0 : fieldCounts[doc.id], onUpload: (file) => onUpload(idx, file), onClear: () => onClear(idx), onRemove: onRemove ? () => onRemove(idx) : undefined, onReview: onReview ? () => onReview(doc.id) : undefined }))))));
22
+ react_1.default.createElement("div", { className: "grid gap-1.5" }, tierDocs.map(({ doc, idx }) => (react_1.default.createElement(DocumentCard_1.DocumentCard, { key: doc.id, doc: doc, tierBorderColor: tier.borderColor, tierBadgeColor: tier.badgeColor, tierBadgeText: tier.badgeText, helpOverride: helpOverrides[doc.id], onUpload: () => onUpload(idx), onClear: () => onClear(idx) }))))));
23
23
  }
@@ -1,32 +1,8 @@
1
1
  import React from "react";
2
2
  import { ClientProfile, TaxAxisScreenProps } from "../../lib/types";
3
- import { TaxAxisEntityTypeKey } from "../../lib/data/documents";
4
- import { DocState } from "./DocumentCard";
5
3
  export interface TaxAxisDocumentsProps extends TaxAxisScreenProps {
6
4
  profile: ClientProfile;
7
- entityType?: TaxAxisEntityTypeKey | string | null;
8
5
  onContinue: () => void;
9
6
  onBack: () => void;
10
- onUploadDocument?: (doc: DocState, file: File) => Promise<void | {
11
- documentId?: string;
12
- fileName?: string;
13
- status?: string;
14
- documentType?: string;
15
- parseError?: string | null;
16
- }>;
17
- onDeleteDocument?: (documentId: string) => Promise<void>;
18
- fetchUploadedDocuments?: () => Promise<Array<{
19
- documentId?: string;
20
- fileName: string;
21
- status: string;
22
- documentType?: string | null;
23
- parseError?: string | null;
24
- updatedAt?: string | null;
25
- parsedData?: Record<string, unknown> | null;
26
- reviewedData?: Record<string, unknown> | null;
27
- }>>;
28
- parsedFieldCounts?: Record<string, number>;
29
- jobId?: string;
30
- onSaveReviewedField?: (documentId: string, reviewedData: Record<string, unknown>) => Promise<void>;
31
7
  }
32
- export declare function TaxAxisDocuments({ profile, entityType, onContinue, onBack, onUploadDocument, onDeleteDocument, fetchUploadedDocuments, parsedFieldCounts, jobId, onSaveReviewedField, userContext: _userContext, }: TaxAxisDocumentsProps): React.JSX.Element;
8
+ export declare function TaxAxisDocuments({ profile, onContinue, onBack, userContext: _userContext, }: TaxAxisDocumentsProps): React.JSX.Element;
@@ -22,22 +22,34 @@ var __importStar = (this && this.__importStar) || function (mod) {
22
22
  __setModuleDefault(result, mod);
23
23
  return result;
24
24
  };
25
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
26
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
27
- return new (P || (P = Promise))(function (resolve, reject) {
28
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
29
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
30
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
31
- step((generator = generator.apply(thisArg, _arguments || [])).next());
32
- });
33
- };
34
25
  Object.defineProperty(exports, "__esModule", { value: true });
35
26
  exports.TaxAxisDocuments = TaxAxisDocuments;
36
27
  const react_1 = __importStar(require("react"));
37
28
  const documents_1 = require("../../lib/data/documents");
38
29
  const TaxAxisButton_1 = require("../shared/TaxAxisButton");
39
30
  const DocumentTier_1 = require("./DocumentTier");
40
- const DocumentReviewModal_1 = require("./DocumentReviewModal");
31
+ // Stub filenames assigned when the user clicks "Upload" (mock App.jsx:1237)
32
+ const STUB_FILENAMES = {
33
+ "1120s": "2025_1120S.pdf",
34
+ "state-return": "2025_State_Return.pdf",
35
+ "payroll": "2025_Payroll.xlsx",
36
+ "pnl": "QBO_PnL_2025.pdf",
37
+ "balance": "QBO_BS_2025.pdf",
38
+ "cashflow": "QBO_CashFlow_2025.pdf",
39
+ "fixed-assets": "Fixed_Assets.xlsx",
40
+ "prior-returns": "Prior_Returns.pdf",
41
+ };
42
+ // Short help text shown below each doc name when not yet uploaded
43
+ // (mock App.jsx:1384). Overrides the longer DOC_SPECS_BASE.help.
44
+ const HELP_OVERRIDES = {
45
+ "1120s": "Improves accuracy of every strategy",
46
+ "payroll": "Critical for Entity Structure and QBI calculations",
47
+ "fixed-assets": "Unlocks Cost Segregation (potential $15K\u201385K)",
48
+ "state-return": "If client operates in income-tax states",
49
+ "cashflow": "Helps with income deferral and timing strategies",
50
+ "prior-returns": "Improves confidence intervals (more years = tighter estimates)",
51
+ };
52
+ // Tier groupings from mock (App.jsx:1379-1383)
41
53
  const TIER_DEFS = [
42
54
  {
43
55
  key: "required",
@@ -47,7 +59,7 @@ const TIER_DEFS = [
47
59
  labelColor: "#C53030",
48
60
  badgeColor: "red",
49
61
  badgeText: "REQUIRED",
50
- ids: [],
62
+ ids: ["pnl", "balance"],
51
63
  },
52
64
  {
53
65
  key: "recommended",
@@ -57,7 +69,7 @@ const TIER_DEFS = [
57
69
  labelColor: "#FB9A1D",
58
70
  badgeColor: "orange",
59
71
  badgeText: "RECOMMENDED",
60
- ids: [],
72
+ ids: ["1120s", "payroll", "fixed-assets"],
61
73
  },
62
74
  {
63
75
  key: "conditional",
@@ -67,247 +79,37 @@ const TIER_DEFS = [
67
79
  labelColor: "#C8CCE5",
68
80
  badgeColor: "neutral",
69
81
  badgeText: "CONDITIONAL",
70
- ids: [],
82
+ ids: ["state-return", "cashflow", "prior-returns"],
71
83
  },
72
84
  ];
73
- function TaxAxisDocuments({ profile, entityType, onContinue, onBack, onUploadDocument, onDeleteDocument, fetchUploadedDocuments, parsedFieldCounts, jobId = "stub-job-id", onSaveReviewedField, userContext: _userContext = "expert", }) {
74
- var _a, _b, _c, _d, _e;
75
- const docSpecs = (0, react_1.useMemo)(() => (0, documents_1.getDocSpecs)(entityType !== null && entityType !== void 0 ? entityType : undefined), [entityType]);
76
- // Build tier defs dynamically from the doc spec list so they're always in sync.
77
- const tierDefs = (0, react_1.useMemo)(() => {
78
- return TIER_DEFS.map((def) => (Object.assign(Object.assign({}, def), { ids: docSpecs
79
- .filter((s) => s.tier === def.key)
80
- .map((s) => s.id) })));
81
- }, [docSpecs]);
82
- const [docs, setDocs] = (0, react_1.useState)(() => docSpecs.map((s) => (Object.assign(Object.assign({}, s), { status: "empty", fileName: null, parseError: null }))));
83
- const [uploadedDocIds, setUploadedDocIds] = (0, react_1.useState)({});
84
- const [continueError, setContinueError] = (0, react_1.useState)(null);
85
- const [reviewDocId, setReviewDocId] = (0, react_1.useState)(null);
86
- // parsedData keyed by documentType (e.g. "profit_loss") — populated from poll results
87
- const [parsedDataByDocType, setParsedDataByDocType] = (0, react_1.useState)({});
88
- // reviewedData keyed by documentType — populated after CPA saves corrections; takes priority over parsedData in modal
89
- const [reviewedDataByDocType, setReviewedDataByDocType] = (0, react_1.useState)({});
90
- // On mount, restore card state from any already-uploaded documents in this session.
91
- // This runs whenever the user re-enters the DOCUMENT_UPLOAD step (e.g. after an
92
- // eval failure) so previously uploaded files appear as valid/failed instead of empty.
93
- (0, react_1.useEffect)(() => {
94
- if (!fetchUploadedDocuments)
95
- return;
96
- fetchUploadedDocuments().then((uploaded) => {
97
- if (!uploaded.length)
98
- return;
99
- const parsedByType = {};
100
- const reviewedByType = {};
101
- const nextDocIds = {};
102
- setDocs((prev) => prev.map((docState, idx) => {
103
- // Match by documentType (canonical key) — find the most recently updated upload
104
- const candidates = uploaded.filter((u) => String(u.documentType || '').toLowerCase() === (docState.documentType || docState.id).toLowerCase());
105
- const match = candidates.sort((a, b) => new Date(String(b.updatedAt || 0)).getTime() - new Date(String(a.updatedAt || 0)).getTime())[0];
106
- if (!match)
107
- return docState;
108
- // Populate uploadedDocIds for delete/review actions
109
- if (match.documentId) {
110
- nextDocIds[idx] = match.documentId;
111
- }
112
- const apiStatus = String(match.status || '').toUpperCase();
113
- const nextStatus = apiStatus === 'PARSED' ? 'valid' :
114
- apiStatus === 'FAILED' ? 'failed' :
115
- apiStatus === 'PARSING' || apiStatus === 'UPLOADED' ? 'parsing' :
116
- docState.status;
117
- return Object.assign(Object.assign({}, docState), { status: nextStatus, fileName: match.fileName || docState.fileName, parseError: match.parseError || null });
118
- }));
119
- // Populate document ID map in one batch
120
- setUploadedDocIds((prev) => (Object.assign(Object.assign({}, prev), nextDocIds)));
121
- // Populate review modal data
122
- for (const doc of uploaded) {
123
- if (!doc.documentType)
124
- continue;
125
- if (doc.parsedData)
126
- parsedByType[doc.documentType] = doc.parsedData;
127
- if (doc.reviewedData && Object.keys(doc.reviewedData).length > 0) {
128
- reviewedByType[doc.documentType] = doc.reviewedData;
129
- }
130
- }
131
- if (Object.keys(parsedByType).length > 0)
132
- setParsedDataByDocType(parsedByType);
133
- if (Object.keys(reviewedByType).length > 0)
134
- setReviewedDataByDocType(reviewedByType);
135
- }).catch(() => { });
136
- // eslint-disable-next-line react-hooks/exhaustive-deps
137
- }, []);
138
- const reviewDoc = reviewDocId
139
- ? (_a = docs.find((d) => d.id === reviewDocId)) !== null && _a !== void 0 ? _a : null
140
- : null;
141
- // Prefer reviewedData (CPA-corrected) over parsedData (raw LLM extraction)
142
- const reviewParsedData = reviewDoc
143
- ? ((_e = (_d = (_c = (_b = reviewedDataByDocType[reviewDoc.documentType || reviewDoc.id]) !== null && _b !== void 0 ? _b : reviewedDataByDocType[reviewDoc.id]) !== null && _c !== void 0 ? _c : parsedDataByDocType[reviewDoc.documentType || reviewDoc.id]) !== null && _d !== void 0 ? _d : parsedDataByDocType[reviewDoc.id]) !== null && _e !== void 0 ? _e : null)
144
- : null;
145
- const pollForParseStatus = (idx, fileName, documentType) => __awaiter(this, void 0, void 0, function* () {
146
- if (!fetchUploadedDocuments)
147
- return;
148
- const maxAttempts = 80;
149
- const pollIntervalMs = 1500;
150
- for (let attempt = 0; attempt < maxAttempts; attempt += 1) {
151
- try {
152
- const uploaded = yield fetchUploadedDocuments();
153
- // Match by documentType first (exact lowercase key), fall back to fileName.
154
- const typeMatch = uploaded.filter((doc) => doc.fileName === fileName &&
155
- String(doc.documentType || "").toLowerCase() === documentType.toLowerCase());
156
- const nameMatch = uploaded.filter((doc) => doc.fileName === fileName);
157
- const candidates = typeMatch.length > 0 ? typeMatch : nameMatch;
158
- const match = candidates
159
- .slice()
160
- .sort((a, b) => new Date(String(b.updatedAt || "")).getTime() -
161
- new Date(String(a.updatedAt || "")).getTime())[0];
162
- if (match) {
163
- const status = String(match.status || "").toUpperCase();
164
- if (status === "FAILED") {
165
- setDocs((prev) => prev.map((d, i) => i === idx
166
- ? Object.assign(Object.assign({}, d), { status: "failed", fileName, parseError: match.parseError || "Parsing failed. Try a different file." }) : d));
167
- return;
168
- }
169
- if (status === "PARSED") {
170
- setDocs((prev) => prev.map((d, i) => i === idx
171
- ? Object.assign(Object.assign({}, d), { status: "valid", fileName, parseError: null }) : d));
172
- if (match.parsedData && documentType) {
173
- setParsedDataByDocType((prev) => (Object.assign(Object.assign({}, prev), { [documentType]: match.parsedData })));
174
- }
175
- return;
176
- }
177
- if (status === "PARSING" || status === "UPLOADED") {
178
- setDocs((prev) => prev.map((d, i) => i === idx
179
- ? Object.assign(Object.assign({}, d), { status: "parsing", fileName }) : d));
180
- }
181
- }
182
- }
183
- catch (_pollError) {
184
- // keep polling
185
- }
186
- yield new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
187
- }
188
- setDocs((prev) => prev.map((d, i) => i === idx ? Object.assign(Object.assign({}, d), { status: "failed", fileName, parseError: "Parsing timed out. Please try again." }) : d));
189
- });
190
- const handleUpload = (idx, file) => __awaiter(this, void 0, void 0, function* () {
191
- const selectedDoc = docs[idx];
192
- if (!selectedDoc)
193
- return;
194
- setContinueError(null);
85
+ function TaxAxisDocuments({ profile, onContinue, onBack, userContext: _userContext = "expert", }) {
86
+ const docSpecs = (0, react_1.useMemo)(() => (0, documents_1.getDocSpecs)(profile), [profile]);
87
+ const [docs, setDocs] = (0, react_1.useState)(() => docSpecs.map((s) => (Object.assign(Object.assign({}, s), { status: "empty", fileName: null }))));
88
+ // Stub upload: empty -> validating -> valid after a short delay
89
+ // (mock App.jsx:1238-1241)
90
+ const handleUpload = (idx) => {
195
91
  setDocs((prev) => prev.map((d, i) => i === idx
196
- ? Object.assign(Object.assign({}, d), { status: "validating", fileName: file.name, parseError: null }) : d));
197
- try {
198
- const uploadResult = onUploadDocument
199
- ? yield onUploadDocument(selectedDoc, file)
200
- : undefined;
201
- const nextFileName = file.name;
202
- // Use the canonical documentType from the catalog entry.
203
- const documentType = selectedDoc.documentType;
204
- const immediateStatus = String((uploadResult === null || uploadResult === void 0 ? void 0 : uploadResult.status) || "").toUpperCase();
205
- if (uploadResult === null || uploadResult === void 0 ? void 0 : uploadResult.documentId) {
206
- setUploadedDocIds((prev) => (Object.assign(Object.assign({}, prev), { [idx]: uploadResult.documentId })));
207
- }
208
- if (immediateStatus === "PARSED") {
209
- setDocs((prev) => prev.map((d, i) => i === idx
210
- ? Object.assign(Object.assign({}, d), { status: "valid", fileName: (uploadResult === null || uploadResult === void 0 ? void 0 : uploadResult.fileName) || nextFileName, parseError: null }) : d));
211
- // parsedData is not on the upload mutation response — fetch it now via one poll call.
212
- if (fetchUploadedDocuments && documentType) {
213
- try {
214
- const uploaded = yield fetchUploadedDocuments();
215
- const match = uploaded.find((d) => String(d.documentType || "").toLowerCase() === documentType.toLowerCase());
216
- if (match === null || match === void 0 ? void 0 : match.parsedData) {
217
- setParsedDataByDocType((prev) => (Object.assign(Object.assign({}, prev), { [documentType]: match.parsedData })));
218
- }
219
- }
220
- catch (_a) {
221
- // non-fatal — user can still open modal, will show empty state
222
- }
223
- }
224
- return;
225
- }
226
- if (immediateStatus === "FAILED") {
227
- setDocs((prev) => prev.map((d, i) => i === idx
228
- ? Object.assign(Object.assign({}, d), { status: "failed", fileName: (uploadResult === null || uploadResult === void 0 ? void 0 : uploadResult.fileName) || nextFileName, parseError: (uploadResult === null || uploadResult === void 0 ? void 0 : uploadResult.parseError) || "Parsing failed. Please upload a correct document." }) : d));
229
- return;
230
- }
231
- setDocs((prev) => prev.map((d, i) => i === idx ? Object.assign(Object.assign({}, d), { status: "parsing", fileName: nextFileName }) : d));
232
- yield pollForParseStatus(idx, nextFileName, documentType);
233
- }
234
- catch (uploadError) {
235
- const errorMessage = uploadError instanceof Error
236
- ? uploadError.message
237
- : "Upload failed. Please try again.";
238
- setDocs((prev) => prev.map((d, i) => i === idx
239
- ? Object.assign(Object.assign({}, d), { status: "failed", parseError: errorMessage }) : d));
240
- }
241
- });
92
+ ? Object.assign(Object.assign({}, d), { status: "validating", fileName: STUB_FILENAMES[d.id] || "document.pdf" }) : d));
93
+ setTimeout(() => setDocs((prev) => prev.map((d, i) => i === idx ? Object.assign(Object.assign({}, d), { status: "valid" }) : d)), 500 + Math.random() * 400);
94
+ };
242
95
  const handleClear = (idx) => {
243
96
  setDocs((prev) => prev.map((d, i) => i === idx
244
- ? Object.assign(Object.assign({}, d), { status: "empty", fileName: null, parseError: null }) : d));
245
- setContinueError(null);
97
+ ? Object.assign(Object.assign({}, d), { status: "empty", fileName: null }) : d));
246
98
  };
247
- const handleRemove = (idx) => __awaiter(this, void 0, void 0, function* () {
248
- const docId = uploadedDocIds[idx];
249
- if (docId && onDeleteDocument) {
250
- try {
251
- yield onDeleteDocument(docId);
252
- }
253
- catch (_deleteError) {
254
- // clear from UI even if backend delete fails
255
- }
256
- }
257
- setUploadedDocIds((prev) => {
258
- const next = Object.assign({}, prev);
259
- delete next[idx];
260
- return next;
261
- });
262
- handleClear(idx);
263
- });
264
- const handleContinue = () => __awaiter(this, void 0, void 0, function* () {
265
- const failedDocs = docs.filter((d) => d.status === "failed");
266
- if (failedDocs.length > 0) {
267
- const names = failedDocs.map((d) => d.name).join(", ");
268
- setContinueError(`Cannot continue — ${failedDocs.length === 1 ? "this document has" : "these documents have"} errors: ${names}. Please re-upload correct files or remove them.`);
269
- return;
270
- }
271
- setContinueError(null);
272
- // For every parsed doc that was not manually reviewed, persist parsedData.fields
273
- // as reviewedData so the engine always has a reviewedData record to read from.
274
- if (onSaveReviewedField) {
275
- const validDocs = docs.filter((d) => d.status === "valid");
276
- yield Promise.all(validDocs.map((doc) => __awaiter(this, void 0, void 0, function* () {
277
- var _a;
278
- const idx = docs.indexOf(doc);
279
- const documentId = uploadedDocIds[idx];
280
- if (!documentId)
281
- return;
282
- const parsed = parsedDataByDocType[doc.documentType || doc.id];
283
- if (!parsed)
284
- return;
285
- const fields = ((_a = parsed.fields) !== null && _a !== void 0 ? _a : parsed);
286
- // Convert all scalar values to strings for reviewedData
287
- const reviewedFields = {};
288
- for (const [k, v] of Object.entries(fields)) {
289
- if (v !== null && v !== undefined && typeof v !== "object") {
290
- reviewedFields[k] = v;
291
- }
292
- }
293
- if (Object.keys(reviewedFields).length > 0) {
294
- try {
295
- yield onSaveReviewedField(documentId, reviewedFields);
296
- }
297
- catch ( /* non-fatal */_b) { /* non-fatal */ }
298
- }
299
- })));
300
- }
301
- onContinue();
302
- });
303
99
  const validCount = docs.filter((d) => d.status === "valid").length;
304
- const failedCount = docs.filter((d) => d.status === "failed").length;
305
- const busyCount = docs.filter((d) => d.status === "validating" || d.status === "parsing").length;
306
- const coveragePct = validCount === 0 ? 32
307
- : validCount === 1 ? 50
308
- : validCount === 2 ? 60
309
- : validCount <= 4 ? 75
310
- : validCount <= 6 ? 90
100
+ const requiredCount = docs.filter((d) => d.required === true).length;
101
+ const requiredValid = docs.filter((d) => d.required === true && d.status === "valid").length;
102
+ // Coverage heuristic from mock (App.jsx:1361)
103
+ const coveragePct = validCount === 0
104
+ ? 32
105
+ : validCount === 1
106
+ ? 50
107
+ : validCount === 2
108
+ ? 60
109
+ : validCount <= 4
110
+ ? 75
111
+ : validCount <= 6
112
+ ? 90
311
113
  : 100;
312
114
  const strategiesCovered = Math.round((25 * coveragePct) / 100);
313
115
  return (react_1.default.createElement("div", { className: "max-w-[580px] mx-auto" },
@@ -346,27 +148,10 @@ function TaxAxisDocuments({ profile, entityType, onContinue, onBack, onUploadDoc
346
148
  coveragePct,
347
149
  "% coverage"),
348
150
  validCount < docs.length && (react_1.default.createElement("span", { className: "text-[10px] text-tax-axis-text-4 font-tax-axis-body" }, "Upload more documents to increase strategy coverage")))),
349
- continueError && (react_1.default.createElement("div", { className: "py-3 px-4 mb-4 text-xs leading-relaxed font-tax-axis-body rounded-lg", style: {
350
- background: "rgba(197,48,48,0.08)",
351
- border: "1px solid rgba(197,48,48,0.3)",
352
- color: "#FEB2B2",
353
- } }, continueError)),
354
- tierDefs.map((tier) => (react_1.default.createElement(DocumentTier_1.DocumentTier, { key: tier.key, tier: tier, docs: docs, helpOverrides: {}, fieldCounts: parsedFieldCounts, onUpload: handleUpload, onClear: handleClear, onRemove: handleRemove, onReview: (docId) => setReviewDocId(docId) }))),
151
+ TIER_DEFS.map((tier) => (react_1.default.createElement(DocumentTier_1.DocumentTier, { key: tier.key, tier: tier, docs: docs, helpOverrides: HELP_OVERRIDES, onUpload: handleUpload, onClear: handleClear }))),
355
152
  react_1.default.createElement("div", { className: "flex gap-3 mt-6" },
356
153
  react_1.default.createElement(TaxAxisButton_1.TaxAxisButton, { variant: "secondary", onClick: onBack }, "Back"),
357
- react_1.default.createElement(TaxAxisButton_1.TaxAxisButton, { onClick: handleContinue, className: "flex-1", disabled: busyCount > 0 }, busyCount > 0
358
- ? "Processing..."
359
- : failedCount > 0
360
- ? `Continue (${failedCount} failed)`
361
- : "Continue")),
362
- reviewDoc && (react_1.default.createElement(DocumentReviewModal_1.DocumentReviewModal, { documentId: reviewDoc.id, documentName: reviewDoc.name, fileName: reviewDoc.fileName || "", jobId: jobId, parsedData: reviewParsedData, onSaveReviewedData: onSaveReviewedField
363
- ? (fields) => __awaiter(this, void 0, void 0, function* () {
364
- const docId = uploadedDocIds[docs.indexOf(reviewDoc)];
365
- if (docId)
366
- yield onSaveReviewedField(docId, fields);
367
- // Store locally so reopening the modal shows corrected values
368
- const docTypeKey = reviewDoc.documentType || reviewDoc.id;
369
- setReviewedDataByDocType((prev) => (Object.assign(Object.assign({}, prev), { [docTypeKey]: fields })));
370
- })
371
- : undefined, onClose: () => setReviewDocId(null) }))));
154
+ react_1.default.createElement(TaxAxisButton_1.TaxAxisButton, { onClick: onContinue, disabled: requiredCount > requiredValid, className: "flex-1" }, requiredCount > requiredValid
155
+ ? `Upload ${requiredCount - requiredValid} more required doc${requiredCount - requiredValid > 1 ? "s" : ""}`
156
+ : "Run Analysis"))));
372
157
  }
@@ -0,0 +1,13 @@
1
+ import React from "react";
2
+ import type { QboReportOption } from "./types";
3
+ interface QboAvailableReportsModalProps {
4
+ open: boolean;
5
+ companyName: string;
6
+ reports?: QboReportOption[];
7
+ selectedYear?: number;
8
+ onYearChange?: (year: number) => void;
9
+ onCancel: () => void;
10
+ onConfirm: (selectedReportIds: string[]) => void;
11
+ }
12
+ export declare function QboAvailableReportsModal({ open, companyName, reports, selectedYear, onYearChange, onCancel, onConfirm, }: QboAvailableReportsModalProps): React.JSX.Element | null;
13
+ export {};