@paro.io/expert-shared-components 1.14.77 → 1.14.79

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.
@@ -1,3 +1,3 @@
1
1
  import type { TaxAxisShellProps } from './types';
2
- export declare const TaxAxisShell: ({ taxAxisApi, userContext, initialSessionId, initialProfile, onSessionChange, documentUploadUrl, uploadBucketName, sessionDefaults, qboAuthorizeUrl, expertId, qboConnected, qboCompanyName, }: TaxAxisShellProps) => JSX.Element;
2
+ export declare const TaxAxisShell: ({ taxAxisApi, userContext, initialSessionId, initialProfile, onSessionChange, documentUploadUrl, uploadBucketName, sessionDefaults, qboAuthorizeUrl, qboRedirectUri, onQboDisconnect, expertId, qboConnected, qboCompanyName, }: TaxAxisShellProps) => JSX.Element;
3
3
  export default TaxAxisShell;
@@ -148,7 +148,7 @@ function ShellContainer({ children, fullWidth = false, }) {
148
148
  return (react_1.default.createElement("div", { className: 'min-h-screen bg-tax-axis-navy text-white font-tax-axis-body' },
149
149
  react_1.default.createElement("div", { className: 'max-w-[960px] mx-auto px-5 py-7' }, children)));
150
150
  }
151
- const TaxAxisShell = ({ taxAxisApi, userContext = 'expert', initialSessionId, initialProfile, onSessionChange, documentUploadUrl, uploadBucketName, sessionDefaults, qboAuthorizeUrl, expertId, qboConnected, qboCompanyName, }) => {
151
+ const TaxAxisShell = ({ taxAxisApi, userContext = 'expert', initialSessionId, initialProfile, onSessionChange, documentUploadUrl, uploadBucketName, sessionDefaults, qboAuthorizeUrl, qboRedirectUri, onQboDisconnect, expertId, qboConnected, qboCompanyName, }) => {
152
152
  const [step, setStep] = (0, react_1.useState)('SESSION_SETUP');
153
153
  const [profile, setProfile] = (0, react_1.useState)(initialProfile ? Object.assign({}, initialProfile) : null);
154
154
  const [sessionId, setSessionId] = (0, react_1.useState)(initialSessionId || null);
@@ -556,13 +556,15 @@ const TaxAxisShell = ({ taxAxisApi, userContext = 'expert', initialSessionId, in
556
556
  });
557
557
  }), jobId: sessionId || undefined, onSaveReviewedField: (documentId, reviewedData) => __awaiter(void 0, void 0, void 0, function* () {
558
558
  yield taxAxisApi.saveReviewedData(documentId, reviewedData);
559
- }), 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) => {
559
+ }), onContinue: handleAnalyzeDocuments, onBack: () => isProspectFlow ? setStep('PROSPECT_REPORT') : setStep('SESSION_SETUP'), qboConnected: qboConnectedState, qboCompanyName: qboCompanyNameState, qboAuthorizeUrl: qboAuthorizeUrl, qboRedirectUri: qboRedirectUri, qboClientConfirmed: qboConnectedState, onImportQboReport: taxAxisApi.importQboReport ? handleImportQboReport : undefined, onQboConnected: (companyName) => {
560
560
  setQboConnectedState(true);
561
561
  setQboCompanyNameState(companyName);
562
- }, onQboDisconnected: () => {
562
+ }, onQboDisconnected: () => __awaiter(void 0, void 0, void 0, function* () {
563
+ if (onQboDisconnect)
564
+ yield onQboDisconnect();
563
565
  setQboConnectedState(false);
564
566
  setQboCompanyNameState(null);
565
- } })));
567
+ }) })));
566
568
  case 'PROCESSING':
567
569
  return (react_1.default.createElement(ShellContainer, null,
568
570
  react_1.default.createElement(TaxAxisProcessing_1.TaxAxisProcessing, { profile: profile, userContext: userContext, reportReady: reportReady, onComplete: () => setStep('DASHBOARD') })));
@@ -15,6 +15,8 @@ export type TaxAxisShellProps = {
15
15
  };
16
16
  qboAuthorizeUrl?: string;
17
17
  qboGetClientsUrl?: string;
18
+ qboRedirectUri?: string;
19
+ onQboDisconnect?: () => Promise<void>;
18
20
  expertId?: string;
19
21
  qboConnected?: boolean;
20
22
  qboCompanyName?: string | null;
@@ -31,6 +31,7 @@ export interface TaxAxisDocumentsProps extends TaxAxisScreenProps {
31
31
  qboConnected?: boolean;
32
32
  qboCompanyName?: string | null;
33
33
  qboAuthorizeUrl?: string;
34
+ qboRedirectUri?: string;
34
35
  onImportQboReport?: (sessionId: string, realmId: string, reportType: string, accountingMethod: string) => Promise<any>;
35
36
  onQboConnected?: (companyName: string) => void;
36
37
  onQboDisconnected?: () => void;
@@ -40,4 +41,4 @@ export interface TaxAxisDocumentsProps extends TaxAxisScreenProps {
40
41
  }[]) => void;
41
42
  qboClientConfirmed?: boolean;
42
43
  }
43
- export declare function TaxAxisDocuments({ profile, entityType, onContinue, onBack, onUploadDocument, onDeleteDocument, fetchUploadedDocuments, parsedFieldCounts, jobId, onSaveReviewedField, userContext: _userContext, qboConnected, qboCompanyName, qboAuthorizeUrl, onImportQboReport, onQboConnected, onQboDisconnected, onQboImport, qboClientConfirmed, }: TaxAxisDocumentsProps): React.JSX.Element;
44
+ export declare function TaxAxisDocuments({ profile, entityType, onContinue, onBack, onUploadDocument, onDeleteDocument, fetchUploadedDocuments, parsedFieldCounts, jobId, onSaveReviewedField, userContext: _userContext, qboConnected, qboCompanyName, qboAuthorizeUrl, qboRedirectUri, onImportQboReport, onQboConnected, onQboDisconnected, onQboImport, qboClientConfirmed: _qboClientConfirmed, }: TaxAxisDocumentsProps): React.JSX.Element;
@@ -77,7 +77,7 @@ const TIER_DEFS = [
77
77
  ids: [],
78
78
  },
79
79
  ];
80
- function TaxAxisDocuments({ profile, entityType, onContinue, onBack, onUploadDocument, onDeleteDocument, fetchUploadedDocuments, parsedFieldCounts, jobId = "stub-job-id", onSaveReviewedField, userContext: _userContext = "expert", qboConnected = false, qboCompanyName, qboAuthorizeUrl, onImportQboReport, onQboConnected, onQboDisconnected, onQboImport, qboClientConfirmed, }) {
80
+ function TaxAxisDocuments({ profile, entityType, onContinue, onBack, onUploadDocument, onDeleteDocument, fetchUploadedDocuments, parsedFieldCounts, jobId = "stub-job-id", onSaveReviewedField, userContext: _userContext = "expert", qboConnected = false, qboCompanyName, qboAuthorizeUrl, qboRedirectUri, onImportQboReport, onQboConnected, onQboDisconnected, onQboImport, qboClientConfirmed: _qboClientConfirmed, }) {
81
81
  var _a, _b, _c, _d, _e, _f, _g, _h;
82
82
  const docSpecs = (0, react_1.useMemo)(() => (0, documents_1.getDocSpecs)(entityType !== null && entityType !== void 0 ? entityType : undefined), [entityType]);
83
83
  // Build tier defs dynamically from the doc spec list so they're always in sync.
@@ -124,6 +124,7 @@ function TaxAxisDocuments({ profile, entityType, onContinue, onBack, onUploadDoc
124
124
  qboConnected,
125
125
  qboCompanyName,
126
126
  qboAuthorizeUrl,
127
+ qboRedirectUri,
127
128
  onImportQboReport,
128
129
  fetchUploadedDocuments,
129
130
  sessionId: jobId,
@@ -134,7 +135,7 @@ function TaxAxisDocuments({ profile, entityType, onContinue, onBack, onUploadDoc
134
135
  });
135
136
  const showQboBanner = !!qboAuthorizeUrl || qboConnected;
136
137
  // FIX B: gate document list behind QBO client confirmation or manual skip
137
- const [clientConfirmed, setClientConfirmed] = (0, react_1.useState)(!!(qboClientConfirmed || qboConnected || !showQboBanner));
138
+ const [clientConfirmed, setClientConfirmed] = (0, react_1.useState)(true);
138
139
  // Sync when qboConnected prop updates (e.g. localStorage restore in parent)
139
140
  (0, react_1.useEffect)(() => {
140
141
  if (qboConnected)
@@ -29,6 +29,8 @@ export interface QboFlowProps {
29
29
  qboCompanyName?: string | null;
30
30
  qboAuthorizeUrl?: string;
31
31
  qboGetClientsUrl?: string;
32
+ /** Override the OAuth redirect URI (e.g. /tax-axis/quickbooks for in-context connect) */
33
+ qboRedirectUri?: string;
32
34
  /** Selected tax year from the session profile — pre-fills the year dropdown */
33
35
  profileYear?: number;
34
36
  onImportQboReport?: (sessionId: string, realmId: string, reportType: string, accountingMethod: string, year?: number) => Promise<any>;
@@ -1,5 +1,5 @@
1
1
  import type { QboCompany, QboModalStep, QboFlowProps } from "./types";
2
- export declare function useQboFlow({ qboConnected, qboCompanyName, qboAuthorizeUrl, onImportQboReport, fetchUploadedDocuments, sessionId, profileYear, onQboConnected, onQboDisconnected, onQboImportComplete, }: QboFlowProps): {
2
+ export declare function useQboFlow({ qboConnected, qboCompanyName, qboAuthorizeUrl, qboRedirectUri, onImportQboReport, fetchUploadedDocuments, sessionId, profileYear, onQboConnected, onQboDisconnected, onQboImportComplete, }: QboFlowProps): {
3
3
  modalStep: QboModalStep;
4
4
  selectedCompany: QboCompany | null;
5
5
  selectedReports: string[];
@@ -10,10 +10,10 @@ export declare function useQboFlow({ qboConnected, qboCompanyName, qboAuthorizeU
10
10
  companies: QboCompany[];
11
11
  openPermissions: () => void;
12
12
  openImport: () => void;
13
- handlePermissionsContinue: () => void;
13
+ handlePermissionsContinue: () => Promise<void>;
14
14
  handleCompanySelected: (company: QboCompany) => void;
15
15
  handleReportsConfirmed: (reportIds: string[]) => Promise<void>;
16
16
  handleMappingConfirm: () => Promise<void>;
17
- handleDisconnect: () => void;
17
+ handleDisconnect: () => Promise<void>;
18
18
  closeModal: () => void;
19
19
  };
@@ -21,14 +21,13 @@ function buildCompany(companyName) {
21
21
  .join("");
22
22
  return { id: "connected", name, initials, color: "#2CA01C" };
23
23
  }
24
- function useQboFlow({ qboConnected, qboCompanyName, qboAuthorizeUrl, onImportQboReport, fetchUploadedDocuments, sessionId, profileYear, onQboConnected, onQboDisconnected, onQboImportComplete, }) {
24
+ function useQboFlow({ qboConnected, qboCompanyName, qboAuthorizeUrl, qboRedirectUri, onImportQboReport, fetchUploadedDocuments, sessionId, profileYear, onQboConnected, onQboDisconnected, onQboImportComplete, }) {
25
25
  const [modalStep, setModalStep] = (0, react_1.useState)("closed");
26
26
  const [selectedCompany, setSelectedCompany] = (0, react_1.useState)(qboConnected ? buildCompany(qboCompanyName) : null);
27
27
  const [selectedReports, setSelectedReports] = (0, react_1.useState)([]);
28
28
  const [selectedYear, setSelectedYear] = (0, react_1.useState)(profileYear !== null && profileYear !== void 0 ? profileYear : new Date().getFullYear());
29
29
  const [importStepIndex, setImportStepIndex] = (0, react_1.useState)(0);
30
30
  const [error, setError] = (0, react_1.useState)(null);
31
- const pollTimerRef = (0, react_1.useRef)(null);
32
31
  // Keep selectedYear in sync if profileYear prop changes
33
32
  const prevProfileYear = (0, react_1.useRef)(profileYear);
34
33
  if (profileYear !== prevProfileYear.current) {
@@ -41,13 +40,6 @@ function useQboFlow({ qboConnected, qboCompanyName, qboAuthorizeUrl, onImportQbo
41
40
  if (qboConnected)
42
41
  setSelectedCompany(buildCompany(qboCompanyName));
43
42
  }, [qboConnected, qboCompanyName]);
44
- // Cleanup poll timer on unmount
45
- (0, react_1.useEffect)(() => {
46
- return () => {
47
- if (pollTimerRef.current)
48
- clearInterval(pollTimerRef.current);
49
- };
50
- }, []);
51
43
  // ── Already connected: skip straight to report selection ──
52
44
  const openImport = (0, react_1.useCallback)(() => {
53
45
  setError(null);
@@ -59,40 +51,41 @@ function useQboFlow({ qboConnected, qboCompanyName, qboAuthorizeUrl, onImportQbo
59
51
  setError(null);
60
52
  setModalStep("permissions");
61
53
  }, []);
62
- // ── After permissions: start OAuth popup ──
63
- const handlePermissionsContinue = (0, react_1.useCallback)(() => {
64
- const isDemoMode = typeof window !== "undefined" &&
65
- (!qboAuthorizeUrl || window.location.hostname === "localhost");
66
- if (isDemoMode) {
67
- setSelectedCompany(buildCompany(qboCompanyName));
68
- setModalStep("client_selector");
54
+ // ── After permissions: POST to authorize endpoint to get Intuit OAuth URL, then open popup ──
55
+ const handlePermissionsContinue = (0, react_1.useCallback)(() => __awaiter(this, void 0, void 0, function* () {
56
+ if (!qboAuthorizeUrl) {
57
+ setError("QuickBooks authorization URL is not configured.");
69
58
  return;
70
59
  }
71
60
  setModalStep("oauth_pending");
72
- const authorizeUrl = qboAuthorizeUrl;
73
- const redirectUri = `${window.location.origin}/integrations-hub/callback`;
74
- const authUrl = authorizeUrl.includes("?")
75
- ? `${authorizeUrl}&redirectUri=${encodeURIComponent(redirectUri)}`
76
- : `${authorizeUrl}?redirectUri=${encodeURIComponent(redirectUri)}`;
77
- const popup = window.open(authUrl, "qbo_oauth", "width=600,height=700,scrollbars=yes");
78
- // Poll for popup close — parent will re-fetch EPS and update qboConnected prop
79
- let elapsed = 0;
80
- pollTimerRef.current = setInterval(() => {
81
- elapsed += 500;
82
- if (popup && popup.closed) {
83
- if (pollTimerRef.current)
84
- clearInterval(pollTimerRef.current);
85
- // Parent prop update will trigger the useEffect above to set selectedCompany
86
- setModalStep("client_selector");
61
+ try {
62
+ const redirectUri = qboRedirectUri !== null && qboRedirectUri !== void 0 ? qboRedirectUri : `${window.location.origin}/integrations-hub/callback`;
63
+ const res = yield fetch(qboAuthorizeUrl, {
64
+ method: "POST",
65
+ headers: { "Content-Type": "application/json" },
66
+ body: JSON.stringify({ redirectUri, environment: "sandbox" }),
67
+ });
68
+ if (!res.ok) {
69
+ throw new Error(`Authorization request failed: ${res.status}`);
87
70
  }
88
- if (elapsed >= 120000) {
89
- if (pollTimerRef.current)
90
- clearInterval(pollTimerRef.current);
91
- setError("QuickBooks authorization timed out. Please try again.");
92
- setModalStep("closed");
71
+ const data = yield res.json();
72
+ if (!data.authUri) {
73
+ throw new Error("No authorization URL returned.");
93
74
  }
94
- }, 500);
95
- }, [qboAuthorizeUrl, qboCompanyName]);
75
+ // Persist session context so the callback page can resume the correct session
76
+ if (sessionId) {
77
+ localStorage.setItem("taxaxis_pending_session", JSON.stringify({ sessionId, ts: Date.now() }));
78
+ }
79
+ localStorage.setItem("current_integration", "quickbooks");
80
+ // Full-page redirect — same as integrations hub. Intuit will redirect back
81
+ // to the registered redirectUri (/tax-axis/quickbooks) when complete.
82
+ window.location.href = data.authUri;
83
+ }
84
+ catch (err) {
85
+ setError(err.message || "Failed to start QuickBooks authorization.");
86
+ setModalStep("closed");
87
+ }
88
+ }), [qboAuthorizeUrl, qboRedirectUri, sessionId]);
96
89
  // ── Company selected ──
97
90
  const handleCompanySelected = (0, react_1.useCallback)((company) => {
98
91
  setSelectedCompany(company);
@@ -173,14 +166,16 @@ function useQboFlow({ qboConnected, qboCompanyName, qboAuthorizeUrl, onImportQbo
173
166
  const handleMappingConfirm = (0, react_1.useCallback)(() => __awaiter(this, void 0, void 0, function* () {
174
167
  yield handleReportsConfirmed(selectedReports);
175
168
  }), [handleReportsConfirmed, selectedReports]);
176
- const handleDisconnect = (0, react_1.useCallback)(() => {
169
+ const handleDisconnect = (0, react_1.useCallback)(() => __awaiter(this, void 0, void 0, function* () {
170
+ if (!window.confirm("Are you sure you want to disconnect QuickBooks? This will stop data imports for this account."))
171
+ return;
177
172
  setSelectedCompany(null);
178
173
  setSelectedReports([]);
179
174
  setImportStepIndex(0);
180
175
  setModalStep("closed");
181
176
  if (onQboDisconnected)
182
- onQboDisconnected();
183
- }, [onQboDisconnected]);
177
+ yield onQboDisconnected();
178
+ }), [onQboDisconnected]);
184
179
  const closeModal = (0, react_1.useCallback)(() => {
185
180
  setModalStep("closed");
186
181
  setError(null);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@paro.io/expert-shared-components",
3
- "version": "1.14.77",
3
+ "version": "1.14.79",
4
4
  "description": "",
5
5
  "main": "lib/index.js",
6
6
  "scripts": {