@taskforcehq/taskforce 0.3.303 → 0.3.305
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/README.md +140 -247
- package/dist/Taskforce.module.css +102 -5
- package/dist/TaskforceCore.js +635 -238
- package/dist/TaskforceCore.test.js +2243 -495
- package/dist/annotatedAttachments/service.d.ts +1 -1
- package/dist/annotatedAttachments/types.d.ts +1 -1
- package/dist/compat/workspaceSyncCompat.js +6 -0
- package/dist/components/features/AgentsModule.js +1 -1
- package/dist/components/features/AgentsModule.test.js +1 -1
- package/dist/components/features/DocumentIndex.d.ts +1 -1
- package/dist/components/features/DocumentIndex.js +1 -1
- package/dist/components/features/DocumentViewer.d.ts +1 -1
- package/dist/components/features/DocumentViewer.js +32 -9
- package/dist/components/features/DocumentWorkspace.d.ts +5 -22
- package/dist/components/features/DocumentWorkspace.js +12 -155
- package/dist/components/features/DocumentWorkspace.test.js +193 -6
- package/dist/components/features/TaskSettings.js +87 -41
- package/dist/components/features/TaskSettings.test.js +180 -40
- package/dist/components/features/documentWorkspaceModel.d.ts +24 -0
- package/dist/components/features/documentWorkspaceModel.js +57 -0
- package/dist/components/features/useDocumentWorkspaceController.d.ts +34 -0
- package/dist/components/features/useDocumentWorkspaceController.js +113 -0
- package/dist/components/task/TaskActionHeader.d.ts +2 -1
- package/dist/components/task/TaskActionHeader.js +3 -3
- package/dist/components/task/TaskActionHeader.test.js +8 -0
- package/dist/components/task/TaskCard.js +16 -1
- package/dist/components/task/TaskCard.test.js +31 -0
- package/dist/components/task/TaskForm.d.ts +9 -8
- package/dist/components/task/TaskForm.js +374 -219
- package/dist/components/task/TaskForm.test.js +482 -361
- package/dist/components/task/TaskKanban.d.ts +1 -1
- package/dist/components/task/TaskKanban.js +33 -161
- package/dist/components/task/TaskStatusActions.d.ts +6 -1
- package/dist/components/task/TaskStatusActions.js +8 -3
- package/dist/components/task/TaskStatusActions.test.js +9 -0
- package/dist/components/task/taskKanbanDropRules.d.ts +58 -0
- package/dist/components/task/taskKanbanDropRules.js +187 -0
- package/dist/components/task/taskKanbanDropRules.test.d.ts +1 -0
- package/dist/components/task/taskKanbanDropRules.test.js +114 -0
- package/dist/components/ui/Modal.d.ts +1 -1
- package/dist/components/ui/Modal.js +1 -0
- package/dist/components/views/AppShellHeader.js +5 -1
- package/dist/components/views/AppShellHeader.test.js +4 -0
- package/dist/components/views/PlanComparisonPage.d.ts +3 -2
- package/dist/components/views/PlanComparisonPage.js +9 -10
- package/dist/components/views/PlanComparisonPage.test.js +55 -41
- package/dist/components/views/PlansPage.d.ts +5 -1
- package/dist/components/views/PlansPage.js +150 -81
- package/dist/components/views/PlansPage.test.js +367 -41
- package/dist/components/views/StandaloneLayout.js +135 -54
- package/dist/components/views/WidgetView.js +11 -2
- package/dist/components/views/panels/FilterToolbar.test.js +6 -4
- package/dist/components/views/standalone/modals/AccountHubModal.d.ts +2 -14
- package/dist/components/views/standalone/modals/AccountHubModal.js +12 -14
- package/dist/components/views/standalone/modals/AccountHubModal.test.js +24 -1
- package/dist/components/views/standalone/modals/SyncStatusModal.js +1 -1
- package/dist/config/envSchema.js +1 -1
- package/dist/core/AiProfileService.d.ts +6 -1
- package/dist/core/AiProfileService.js +161 -16
- package/dist/core/AiProfileService.test.js +3 -1
- package/dist/core/AiProfiles.test.js +200 -0
- package/dist/core/AuthTokenService.test.d.ts +1 -0
- package/dist/core/AuthTokenService.test.js +78 -0
- package/dist/core/DeletedTaskLifecycleService.d.ts +2 -1
- package/dist/core/EntitlementsPolicy.test.js +75 -13
- package/dist/core/PlanEntitlementService.d.ts +9 -1
- package/dist/core/PlanEntitlementService.js +144 -19
- package/dist/core/PlanVersionPolicy.test.js +19 -26
- package/dist/core/SignupMonetizationSettings.test.js +75 -15
- package/dist/core/SystemAdmin.test.js +212 -56
- package/dist/core/TaskActivityService.d.ts +4 -1
- package/dist/core/TaskActivityService.js +45 -0
- package/dist/core/TaskAuditTimeline.test.js +52 -1
- package/dist/core/TaskTaxonomyValidation.test.js +53 -0
- package/dist/core/Taskforce.d.ts +18 -11
- package/dist/core/Taskforce.js +93 -12
- package/dist/core/WorkspacePlanMode.test.js +73 -8
- package/dist/core/shared.d.ts +2 -0
- package/dist/core/shared.js +11 -0
- package/dist/documentReviews/service.d.ts +1 -1
- package/dist/documentReviews/types.d.ts +1 -1
- package/dist/hooks/auth/useTaskforceAuthBootstrap.d.ts +48 -0
- package/dist/hooks/auth/useTaskforceAuthBootstrap.js +219 -0
- package/dist/hooks/sync/auth.d.ts +37 -0
- package/dist/hooks/sync/auth.js +63 -0
- package/dist/hooks/sync/bootstrap.d.ts +34 -0
- package/dist/hooks/sync/bootstrap.js +52 -0
- package/dist/hooks/sync/orchestratorShared.d.ts +36 -0
- package/dist/hooks/sync/orchestratorShared.js +209 -0
- package/dist/hooks/sync/orchestratorShared.test.d.ts +1 -0
- package/dist/hooks/sync/orchestratorShared.test.js +49 -0
- package/dist/hooks/sync/realtime.d.ts +25 -0
- package/dist/hooks/sync/realtime.js +60 -0
- package/dist/hooks/sync/recovery.d.ts +69 -0
- package/dist/hooks/sync/recovery.js +118 -0
- package/dist/hooks/sync/transfers.d.ts +123 -0
- package/dist/hooks/sync/transfers.js +435 -0
- package/dist/hooks/sync/useDataVersionRefresh.d.ts +16 -0
- package/dist/hooks/sync/useDataVersionRefresh.js +73 -0
- package/dist/hooks/ui/useResourceExport.d.ts +3 -1
- package/dist/hooks/ui/useResourceExport.js +5 -2
- package/dist/hooks/ui/useWorkflowTemplates.js +11 -6
- package/dist/hooks/useRealtimeSync.js +2 -1
- package/dist/hooks/useSyncOrchestrator.aiProfiles.test.js +8 -0
- package/dist/hooks/useSyncOrchestrator.context-assets.test.d.ts +1 -0
- package/dist/hooks/useSyncOrchestrator.context-assets.test.js +228 -0
- package/dist/hooks/useSyncOrchestrator.d.ts +12 -8
- package/dist/hooks/useSyncOrchestrator.js +388 -879
- package/dist/hooks/useSyncOrchestrator.retry-closure.test.js +641 -6
- package/dist/hooks/useTaskMutations.d.ts +2 -1
- package/dist/hooks/useTaskMutations.js +57 -7
- package/dist/hooks/useTaskMutations.test.js +176 -0
- package/dist/hooks/useTaskforce.d.ts +13 -4
- package/dist/hooks/useTaskforce.js +302 -817
- package/dist/hooks/useTaskforce.sync-behavior.test.js +78 -0
- package/dist/hooks/useWorkspaceSyncController.d.ts +4 -1
- package/dist/hooks/useWorkspaceSyncController.js +33 -11
- package/dist/hooks/useWorkspaceSyncController.test.js +1 -0
- package/dist/hooks/workspace/useTaskforceApiRouting.test.js +17 -0
- package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.d.ts +166 -0
- package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.js +434 -0
- package/dist/hooks/workspace/workspaceLocalState.d.ts +19 -0
- package/dist/hooks/workspace/workspaceLocalState.js +100 -0
- package/dist/hooks/workspace/workspaceLocalState.test.d.ts +1 -0
- package/dist/hooks/workspace/workspaceLocalState.test.js +59 -0
- package/dist/localization/locales/en-US.js +2 -2
- package/dist/mcp/adminWorkspaceRegistrar.d.ts +2 -0
- package/dist/mcp/adminWorkspaceRegistrar.js +154 -0
- package/dist/mcp/canonicalAssetHelpers.d.ts +70 -0
- package/dist/mcp/canonicalAssetHelpers.js +259 -0
- package/dist/mcp/clientIntegrations.d.ts +12 -1
- package/dist/mcp/clientIntegrations.js +161 -6
- package/dist/mcp/clientIntegrations.test.js +76 -4
- package/dist/mcp/collaborationRegistrar.d.ts +2 -0
- package/dist/mcp/collaborationRegistrar.js +71 -0
- package/dist/mcp/documentAssetRegistrar.d.ts +2 -0
- package/dist/mcp/documentAssetRegistrar.js +346 -0
- package/dist/mcp/documentHelpers.d.ts +100 -0
- package/dist/mcp/documentHelpers.js +161 -0
- package/dist/mcp/runtime.js +2455 -3925
- package/dist/mcp/runtime.test.js +240 -8
- package/dist/mcp/taskAttachmentHelpers.d.ts +41 -0
- package/dist/mcp/taskAttachmentHelpers.js +60 -0
- package/dist/mcp/taskAttachmentTypes.d.ts +37 -0
- package/dist/mcp/taskAttachmentTypes.js +1 -0
- package/dist/mcp/taskPlanningRegistrar.d.ts +2 -0
- package/dist/mcp/taskPlanningRegistrar.js +418 -0
- package/dist/mcp/toolCatalog.d.ts +35 -0
- package/dist/mcp/toolCatalog.js +3 -0
- package/dist/plugins/vite.js +1 -1
- package/dist/runtime/appGateDefinitions.d.ts +1 -1
- package/dist/runtime/appGateDefinitions.js +1 -1
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.js +46 -3
- package/dist/server/index.test.js +12 -1
- package/dist/server/routes/admin.d.ts +1 -5
- package/dist/server/routes/admin.js +151 -80
- package/dist/server/routes/annotatedAttachments.d.ts +1 -1
- package/dist/server/routes/annotatedAttachments.js +1 -1
- package/dist/server/routes/auth.d.ts +1 -4
- package/dist/server/routes/auth.js +59 -64
- package/dist/server/routes/authSupport.d.ts +30 -0
- package/dist/server/routes/authSupport.js +81 -0
- package/dist/server/routes/billing.d.ts +1 -1
- package/dist/server/routes/billing.js +122 -255
- package/dist/server/routes/billing.test.js +257 -86
- package/dist/server/routes/documentReviews.d.ts +1 -1
- package/dist/server/routes/documentReviews.js +1 -1
- package/dist/server/routes/documents.d.ts +4 -2
- package/dist/server/routes/documents.js +73 -7
- package/dist/server/routes/primitives.d.ts +11 -0
- package/dist/server/routes/primitives.js +73 -0
- package/dist/server/routes/resources.d.ts +1 -1
- package/dist/server/routes/resources.js +1 -1
- package/dist/server/routes/shared.d.ts +31 -1
- package/dist/server/routes/shared.js +26 -0
- package/dist/server/routes/sync.d.ts +1 -1
- package/dist/server/routes/sync.integration.test.js +20 -0
- package/dist/server/routes/sync.js +111 -238
- package/dist/server/routes/tasks.d.ts +1 -1
- package/dist/server/routes/tasks.js +50 -8
- package/dist/server/routes/workspaces.d.ts +1 -1
- package/dist/server/routes/workspaces.js +1 -1
- package/dist/server/routes.d.ts +2 -9
- package/dist/server/routes.js +84 -181
- package/dist/server/routes.test.js +912 -32
- package/dist/shared/taskActor.d.ts +10 -0
- package/dist/shared/taskActor.js +1 -0
- package/dist/sync/collaborationSyncPayload.js +4 -0
- package/dist/sync/collaborationSyncPayload.test.js +8 -0
- package/dist/sync/collaborationSyncState.d.ts +3 -1
- package/dist/sync/collaborationSyncState.js +19 -0
- package/dist/sync/contentSyncState.js +28 -17
- package/dist/sync/syncApplyHandlers.d.ts +87 -1
- package/dist/sync/syncApplyHandlers.js +370 -8
- package/dist/sync/syncApplyHandlers.test.d.ts +1 -0
- package/dist/sync/syncApplyHandlers.test.js +379 -0
- package/dist/sync/syncService.d.ts +9 -1
- package/dist/sync/syncService.js +44 -2
- package/dist/sync/syncService.test.js +62 -0
- package/dist/sync/workspacePullFeed.d.ts +1 -0
- package/dist/sync/workspacePullFeed.js +66 -59
- package/dist/sync/workspacePullFeed.test.js +72 -4
- package/dist/sync/workspaceSyncModel.d.ts +25 -1
- package/dist/sync/workspaceSyncModel.js +77 -3
- package/dist/sync/workspaceSyncModel.test.js +153 -0
- package/dist/sync/workspaceSyncState.d.ts +2 -0
- package/dist/sync/workspaceSyncState.js +1 -0
- package/dist/types.d.ts +2 -10
- package/dist/ui/assets/AgentsModule-N2MnQBZk.js +1 -0
- package/dist/ui/assets/{AnnotatedAttachmentWorkspace-C_TmXZwA.js → AnnotatedAttachmentWorkspace-BG6P_GVW.js} +1 -1
- package/dist/ui/assets/DocumentWorkspace-B3nV_Gc5.css +1 -0
- package/dist/ui/assets/DocumentWorkspace-DRbDMwh8.js +252 -0
- package/dist/ui/assets/{InitiativesModule-4-Rh2K2n.js → InitiativesModule-Dhm7M8e7.js} +1 -1
- package/dist/ui/assets/{PlansPage-BlVC_lRq.css → PlansPage-C2dd2n1X.css} +1 -1
- package/dist/ui/assets/PlansPage-Cq0zXj3I.js +1 -0
- package/dist/ui/assets/TaskSettings-ln0aF7xc.js +9 -0
- package/dist/ui/assets/{WorkflowsModule-CGk9s3NJ.js → WorkflowsModule-BcS4afRn.js} +1 -1
- package/dist/ui/assets/index-C2-OXZV7.css +1 -0
- package/dist/ui/assets/index-DcSI5F86.js +6 -0
- package/dist/ui/assets/{vendor-icons-pWocEipT.js → vendor-icons-BSUaRwF8.js} +1 -1
- package/dist/ui/index.html +4 -4
- package/dist/utils/commercialLifecyclePresentation.d.ts +19 -0
- package/dist/utils/commercialLifecyclePresentation.js +196 -0
- package/dist/utils/taskActivity.js +4 -0
- package/dist/utils/taskActivity.test.js +25 -1
- package/package.json +10 -1
- package/scripts/check-cloud-mcp.mjs +83 -0
- package/scripts/playwright-auth.sh +9 -1
- package/scripts/run-billing-performance-smoke.sh +24 -0
- package/dist/ui/assets/AgentsModule-CpsTmrIW.js +0 -1
- package/dist/ui/assets/DocumentWorkspace-C3GyzrWm.js +0 -250
- package/dist/ui/assets/DocumentWorkspace-C_8T8oz-.css +0 -1
- package/dist/ui/assets/PlansPage-BP7AYOqr.js +0 -1
- package/dist/ui/assets/TaskSettings-BOC5F6Ag.js +0 -8
- package/dist/ui/assets/index-CLIMgR6g.js +0 -6
- package/dist/ui/assets/index-DneETxcu.css +0 -1
|
@@ -7,18 +7,15 @@ import { PlanComparisonPage } from './PlanComparisonPage';
|
|
|
7
7
|
import { AppShellHeader } from './AppShellHeader';
|
|
8
8
|
import { resolveTaskforceCloudEnvironmentFromBaseUrl } from '../../config/cloudEnvironment';
|
|
9
9
|
import { ACCOUNT_PROFILE_SUMMARY_PATH, invalidateAccountProfileSummaryCache, loadAccountProfileSummaryCached } from '../../utils/accountProfileSummaryCache';
|
|
10
|
+
import { isCommercialEntitlementActive, resolveCommercialLifecyclePresentation } from '../../utils/commercialLifecyclePresentation';
|
|
10
11
|
const TRANSIENT_CHECKOUT_QUERY_CLEAR_DELAY_MS = 1600;
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
const BILLING_CONFLICT_MESSAGES = {
|
|
19
|
-
CHECKOUT_SESSION_EXPIRED: 'Your previous checkout expired. Choose a plan to continue.',
|
|
20
|
-
CHECKOUT_SESSION_SUPERSEDED: 'A newer checkout replaced your previous checkout. Choose a plan to continue.'
|
|
21
|
-
};
|
|
12
|
+
const CHECKOUT_CONFIRM_RETRY_DELAYS_MS = [0, 500, 1000, 2000];
|
|
13
|
+
function shouldRetryCheckoutConfirmation(statusCode, errorCode) {
|
|
14
|
+
if (statusCode === 401) {
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
17
|
+
return String(errorCode || '').trim().toUpperCase() === 'CHECKOUT_SESSION_INCOMPLETE';
|
|
18
|
+
}
|
|
22
19
|
function parseInterval(raw) {
|
|
23
20
|
return raw === 'year' ? 'year' : 'month';
|
|
24
21
|
}
|
|
@@ -38,13 +35,14 @@ function buildEmbeddedPlansPath(params = {}) {
|
|
|
38
35
|
}
|
|
39
36
|
return `/?${query.toString()}`;
|
|
40
37
|
}
|
|
41
|
-
export function PlansPage({ isAuthenticated, authUserId, runtimeMode, authSessionResolved, currentTheme = 'dark', projectName, currentWorkspaceId, apiBaseUrl, connectedEnvironmentSource, resolveCloudAuthUrl, embedded = false, shellOwnsScroll = false }) {
|
|
38
|
+
export function PlansPage({ isAuthenticated, authUserId, runtimeMode, authSessionResolved, currentTheme = 'dark', projectName, currentWorkspaceId, apiBaseUrl, connectedEnvironmentSource, resolveCloudAuthUrl, embedded = false, shellOwnsScroll = false, onAccountProfileSummaryChange, onContinueToTaskforce, continueBusy = false }) {
|
|
42
39
|
const navigate = useNavigate();
|
|
43
40
|
const location = useLocation();
|
|
44
41
|
const [accountProfileSummary, setAccountProfileSummary] = useState(null);
|
|
45
42
|
const [actionBusy, setActionBusy] = useState(false);
|
|
46
43
|
const [feedback, setFeedback] = useState(null);
|
|
47
44
|
const checkoutStartRan = useRef(false);
|
|
45
|
+
const checkoutSuccessHandledRef = useRef(null);
|
|
48
46
|
const transientQueryClearTimeoutRef = useRef(null);
|
|
49
47
|
const query = useMemo(() => new URLSearchParams(location.search), [location.search]);
|
|
50
48
|
const checkoutState = String(query.get('checkout') || '').trim().toLowerCase();
|
|
@@ -89,32 +87,9 @@ export function PlansPage({ isAuthenticated, authUserId, runtimeMode, authSessio
|
|
|
89
87
|
].map((value) => String(value || '').trim()).filter((value, index, all) => value && all.indexOf(value) === index);
|
|
90
88
|
return endpoints;
|
|
91
89
|
}, [normalizedApiBaseUrl]);
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}, [accountProfileSummary?.entitlementState]);
|
|
96
|
-
const gateMessage = useMemo(() => {
|
|
97
|
-
if (hasResolvedActiveEntitlement)
|
|
98
|
-
return null;
|
|
99
|
-
const billingConflictCode = String(accountProfileSummary?.billingConflictCode || '').trim().toUpperCase();
|
|
100
|
-
if (billingConflictCode === 'CHECKOUT_SESSION_EXPIRED')
|
|
101
|
-
return BILLING_CONFLICT_MESSAGES.CHECKOUT_SESSION_EXPIRED;
|
|
102
|
-
if (billingConflictCode === 'CHECKOUT_SESSION_SUPERSEDED')
|
|
103
|
-
return BILLING_CONFLICT_MESSAGES.CHECKOUT_SESSION_SUPERSEDED;
|
|
104
|
-
const summaryMessage = String(accountProfileSummary?.message || '').trim();
|
|
105
|
-
if (summaryMessage)
|
|
106
|
-
return summaryMessage;
|
|
107
|
-
const effectiveGateState = String(accountProfileSummary?.gate || gateState || '').trim().toLowerCase();
|
|
108
|
-
if (effectiveGateState === 'plan_selection_required')
|
|
109
|
-
return GATE_MESSAGES.planSelectionRequired;
|
|
110
|
-
if (effectiveGateState === 'checkout_pending')
|
|
111
|
-
return GATE_MESSAGES.checkoutPending;
|
|
112
|
-
if (effectiveGateState === 'misconfigured')
|
|
113
|
-
return GATE_MESSAGES.misconfigured;
|
|
114
|
-
if (effectiveGateState === 'missing_entitlement')
|
|
115
|
-
return GATE_MESSAGES.missingEntitlement;
|
|
116
|
-
return null;
|
|
117
|
-
}, [accountProfileSummary?.billingConflictCode, accountProfileSummary?.gate, accountProfileSummary?.message, gateState, hasResolvedActiveEntitlement]);
|
|
90
|
+
const lifecyclePresentation = useMemo(() => resolveCommercialLifecyclePresentation(accountProfileSummary, gateState), [accountProfileSummary, gateState]);
|
|
91
|
+
const hasResolvedActiveEntitlement = useMemo(() => lifecyclePresentation.allowReturnToApp, [lifecyclePresentation.allowReturnToApp]);
|
|
92
|
+
const gateMessage = useMemo(() => lifecyclePresentation.message, [lifecyclePresentation.message]);
|
|
118
93
|
const clearTransientQueryState = useCallback(() => {
|
|
119
94
|
const next = new URLSearchParams(location.search);
|
|
120
95
|
next.delete('checkout');
|
|
@@ -148,25 +123,24 @@ export function PlansPage({ isAuthenticated, authUserId, runtimeMode, authSessio
|
|
|
148
123
|
force: options?.force === true
|
|
149
124
|
});
|
|
150
125
|
setAccountProfileSummary(payload);
|
|
126
|
+
onAccountProfileSummaryChange?.(payload);
|
|
151
127
|
return payload;
|
|
152
128
|
}
|
|
153
129
|
catch {
|
|
154
130
|
// best effort only
|
|
155
131
|
return null;
|
|
156
132
|
}
|
|
157
|
-
}, [authUserId, buildCloudBillingUrl, isAuthenticated]);
|
|
133
|
+
}, [authUserId, buildCloudBillingUrl, isAuthenticated, onAccountProfileSummaryChange]);
|
|
158
134
|
const resolveRecoveryMessage = useCallback((summary, fallback) => {
|
|
159
|
-
const
|
|
160
|
-
if (
|
|
161
|
-
return
|
|
162
|
-
if (billingConflictCode === 'CHECKOUT_SESSION_SUPERSEDED')
|
|
163
|
-
return BILLING_CONFLICT_MESSAGES.CHECKOUT_SESSION_SUPERSEDED;
|
|
164
|
-
const summaryMessage = String(summary?.message || '').trim();
|
|
165
|
-
if (summaryMessage)
|
|
166
|
-
return summaryMessage;
|
|
135
|
+
const message = resolveCommercialLifecyclePresentation(summary, gateState).message;
|
|
136
|
+
if (message)
|
|
137
|
+
return message;
|
|
167
138
|
return String(fallback || 'Unable to confirm checkout completion.').trim() || 'Unable to confirm checkout completion.';
|
|
139
|
+
}, [gateState]);
|
|
140
|
+
const shouldAutoContinueAfterActivation = useCallback((summary, fallbackGate) => {
|
|
141
|
+
return !resolveCommercialLifecyclePresentation(summary, fallbackGate).allowReturnToApp;
|
|
168
142
|
}, []);
|
|
169
|
-
const startCheckout = useCallback(async (planVersionId, interval) => {
|
|
143
|
+
const startCheckout = useCallback(async (planVersionId, interval, options) => {
|
|
170
144
|
if (!planVersionId) {
|
|
171
145
|
setFeedback({ type: 'error', message: 'Selected plan is missing a default plan version.' });
|
|
172
146
|
return;
|
|
@@ -189,13 +163,21 @@ export function PlansPage({ isAuthenticated, authUserId, runtimeMode, authSessio
|
|
|
189
163
|
if (checkoutState === 'free') {
|
|
190
164
|
setFeedback({ type: 'success', message: 'Plan selected. Your access is ready.' });
|
|
191
165
|
invalidateAccountProfileSummaryCache(buildCloudBillingUrl(ACCOUNT_PROFILE_SUMMARY_PATH), { identityKey: authUserId });
|
|
192
|
-
await loadAccountProfileSummary();
|
|
166
|
+
const summary = await loadAccountProfileSummary({ force: true });
|
|
167
|
+
const summaryState = String(summary?.entitlementState || '').trim().toLowerCase();
|
|
168
|
+
if (options?.autoContinueOnActivation === true && isCommercialEntitlementActive(summaryState)) {
|
|
169
|
+
await onContinueToTaskforce?.();
|
|
170
|
+
}
|
|
193
171
|
return;
|
|
194
172
|
}
|
|
195
173
|
if (checkoutState === 'updated') {
|
|
196
174
|
setFeedback({ type: 'success', message: String(payload?.message || 'Your subscription was updated successfully.') });
|
|
197
175
|
invalidateAccountProfileSummaryCache(buildCloudBillingUrl(ACCOUNT_PROFILE_SUMMARY_PATH), { identityKey: authUserId });
|
|
198
|
-
await loadAccountProfileSummary();
|
|
176
|
+
const summary = await loadAccountProfileSummary({ force: true });
|
|
177
|
+
const summaryState = String(summary?.entitlementState || '').trim().toLowerCase();
|
|
178
|
+
if (options?.autoContinueOnActivation === true && isCommercialEntitlementActive(summaryState)) {
|
|
179
|
+
await onContinueToTaskforce?.();
|
|
180
|
+
}
|
|
199
181
|
return;
|
|
200
182
|
}
|
|
201
183
|
if (checkoutState === 'already_current_plan') {
|
|
@@ -235,12 +217,13 @@ export function PlansPage({ isAuthenticated, authUserId, runtimeMode, authSessio
|
|
|
235
217
|
finally {
|
|
236
218
|
setActionBusy(false);
|
|
237
219
|
}
|
|
238
|
-
}, [authUserId, buildCloudBillingUrl, loadAccountProfileSummary, runtimeMode]);
|
|
239
|
-
const confirmCheckoutSession = useCallback(async (sessionId) => {
|
|
220
|
+
}, [authUserId, buildCloudBillingUrl, loadAccountProfileSummary, onContinueToTaskforce, runtimeMode]);
|
|
221
|
+
const confirmCheckoutSession = useCallback(async (sessionId, options) => {
|
|
240
222
|
if (!sessionId) {
|
|
241
223
|
return {
|
|
242
224
|
confirmed: false,
|
|
243
|
-
errorMessage: 'Checkout session is missing.'
|
|
225
|
+
errorMessage: 'Checkout session is missing.',
|
|
226
|
+
retryable: false
|
|
244
227
|
};
|
|
245
228
|
}
|
|
246
229
|
try {
|
|
@@ -252,22 +235,29 @@ export function PlansPage({ isAuthenticated, authUserId, runtimeMode, authSessio
|
|
|
252
235
|
});
|
|
253
236
|
const payload = await res.json().catch(() => ({}));
|
|
254
237
|
if (!res.ok) {
|
|
238
|
+
const errorCode = String(payload?.code || '').trim();
|
|
255
239
|
return {
|
|
256
240
|
confirmed: false,
|
|
257
|
-
errorMessage: String(payload?.error || 'Unable to confirm checkout completion.')
|
|
241
|
+
errorMessage: String(payload?.error || 'Unable to confirm checkout completion.'),
|
|
242
|
+
retryable: shouldRetryCheckoutConfirmation(res.status, errorCode)
|
|
258
243
|
};
|
|
259
244
|
}
|
|
260
245
|
invalidateAccountProfileSummaryCache(buildCloudBillingUrl(ACCOUNT_PROFILE_SUMMARY_PATH), { identityKey: authUserId });
|
|
261
|
-
await loadAccountProfileSummary();
|
|
246
|
+
const summary = await loadAccountProfileSummary({ force: true });
|
|
247
|
+
const summaryState = String(summary?.entitlementState || '').trim().toLowerCase();
|
|
248
|
+
if (options?.autoContinueOnActivation === true && isCommercialEntitlementActive(summaryState)) {
|
|
249
|
+
await onContinueToTaskforce?.();
|
|
250
|
+
}
|
|
262
251
|
return { confirmed: true };
|
|
263
252
|
}
|
|
264
253
|
catch {
|
|
265
254
|
return {
|
|
266
255
|
confirmed: false,
|
|
267
|
-
errorMessage: 'Unable to confirm checkout completion.'
|
|
256
|
+
errorMessage: 'Unable to confirm checkout completion.',
|
|
257
|
+
retryable: true
|
|
268
258
|
};
|
|
269
259
|
}
|
|
270
|
-
}, [authUserId, buildCloudBillingUrl, loadAccountProfileSummary]);
|
|
260
|
+
}, [authUserId, buildCloudBillingUrl, loadAccountProfileSummary, onContinueToTaskforce]);
|
|
271
261
|
const openBillingPortal = useCallback(async () => {
|
|
272
262
|
if (!isAuthenticated) {
|
|
273
263
|
navigate(`/login?mode=login&next=${encodeURIComponent(buildEmbeddedPlansPath())}`);
|
|
@@ -315,15 +305,84 @@ export function PlansPage({ isAuthenticated, authUserId, runtimeMode, authSessio
|
|
|
315
305
|
}
|
|
316
306
|
}, []);
|
|
317
307
|
useEffect(() => {
|
|
308
|
+
if (checkoutState !== 'success') {
|
|
309
|
+
checkoutSuccessHandledRef.current = null;
|
|
310
|
+
}
|
|
311
|
+
}, [checkoutState]);
|
|
312
|
+
useEffect(() => {
|
|
313
|
+
let cancelled = false;
|
|
314
|
+
const pendingRetryTimeouts = new Set();
|
|
315
|
+
const waitForRetry = async (delayMs) => {
|
|
316
|
+
if (delayMs <= 0)
|
|
317
|
+
return;
|
|
318
|
+
await new Promise((resolve) => {
|
|
319
|
+
const timeoutId = window.setTimeout(() => {
|
|
320
|
+
pendingRetryTimeouts.delete(timeoutId);
|
|
321
|
+
resolve();
|
|
322
|
+
}, delayMs);
|
|
323
|
+
pendingRetryTimeouts.add(timeoutId);
|
|
324
|
+
});
|
|
325
|
+
};
|
|
318
326
|
if (checkoutState === 'success') {
|
|
319
327
|
checkoutStartRan.current = true;
|
|
320
328
|
setFeedback({ type: 'success', message: 'Checkout completed successfully. Finalizing your plan...' });
|
|
329
|
+
if (!authSessionResolved) {
|
|
330
|
+
return () => {
|
|
331
|
+
cancelled = true;
|
|
332
|
+
pendingRetryTimeouts.forEach((timeoutId) => window.clearTimeout(timeoutId));
|
|
333
|
+
pendingRetryTimeouts.clear();
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
if (!isAuthenticated) {
|
|
337
|
+
setFeedback({ type: 'error', message: 'Sign in to finish activating your plan.' });
|
|
338
|
+
return () => {
|
|
339
|
+
cancelled = true;
|
|
340
|
+
pendingRetryTimeouts.forEach((timeoutId) => window.clearTimeout(timeoutId));
|
|
341
|
+
pendingRetryTimeouts.clear();
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
const successFlowKey = querySessionId || '__missing_session__';
|
|
345
|
+
if (checkoutSuccessHandledRef.current === successFlowKey) {
|
|
346
|
+
return () => {
|
|
347
|
+
cancelled = true;
|
|
348
|
+
pendingRetryTimeouts.forEach((timeoutId) => window.clearTimeout(timeoutId));
|
|
349
|
+
pendingRetryTimeouts.clear();
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
checkoutSuccessHandledRef.current = successFlowKey;
|
|
321
353
|
void (async () => {
|
|
322
|
-
const
|
|
323
|
-
|
|
324
|
-
|
|
354
|
+
const preConfirmationSummary = await loadAccountProfileSummary({ force: true });
|
|
355
|
+
if (cancelled)
|
|
356
|
+
return;
|
|
357
|
+
let confirmation = querySessionId
|
|
358
|
+
? { confirmed: false, errorMessage: 'Unable to confirm checkout completion.', retryable: true }
|
|
359
|
+
: { confirmed: false, errorMessage: 'Checkout session is missing.', retryable: false };
|
|
360
|
+
for (const delayMs of CHECKOUT_CONFIRM_RETRY_DELAYS_MS) {
|
|
361
|
+
await waitForRetry(delayMs);
|
|
362
|
+
if (cancelled)
|
|
363
|
+
return;
|
|
364
|
+
confirmation = querySessionId
|
|
365
|
+
? await confirmCheckoutSession(querySessionId, {
|
|
366
|
+
autoContinueOnActivation: shouldAutoContinueAfterActivation(preConfirmationSummary, gateState)
|
|
367
|
+
})
|
|
368
|
+
: { confirmed: false, errorMessage: 'Checkout session is missing.', retryable: false };
|
|
369
|
+
if (cancelled)
|
|
370
|
+
return;
|
|
371
|
+
if (confirmation.confirmed) {
|
|
372
|
+
setFeedback({ type: 'success', message: 'Checkout completed successfully.' });
|
|
373
|
+
scheduleTransientQueryClear();
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
if (!confirmation.retryable) {
|
|
377
|
+
break;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
if (cancelled)
|
|
381
|
+
return;
|
|
325
382
|
if (!confirmation.confirmed) {
|
|
326
383
|
const summary = await loadAccountProfileSummary({ force: true });
|
|
384
|
+
if (cancelled)
|
|
385
|
+
return;
|
|
327
386
|
setFeedback({
|
|
328
387
|
type: 'error',
|
|
329
388
|
message: resolveRecoveryMessage(summary, confirmation.errorMessage)
|
|
@@ -331,10 +390,12 @@ export function PlansPage({ isAuthenticated, authUserId, runtimeMode, authSessio
|
|
|
331
390
|
scheduleTransientQueryClear();
|
|
332
391
|
return;
|
|
333
392
|
}
|
|
334
|
-
setFeedback({ type: 'success', message: 'Checkout completed successfully.' });
|
|
335
|
-
scheduleTransientQueryClear();
|
|
336
393
|
})();
|
|
337
|
-
return
|
|
394
|
+
return () => {
|
|
395
|
+
cancelled = true;
|
|
396
|
+
pendingRetryTimeouts.forEach((timeoutId) => window.clearTimeout(timeoutId));
|
|
397
|
+
pendingRetryTimeouts.clear();
|
|
398
|
+
};
|
|
338
399
|
}
|
|
339
400
|
if (checkoutState === 'cancel') {
|
|
340
401
|
void (async () => {
|
|
@@ -349,29 +410,31 @@ export function PlansPage({ isAuthenticated, authUserId, runtimeMode, authSessio
|
|
|
349
410
|
}
|
|
350
411
|
if ((checkoutState === 'start' || checkoutState === 'free') && isAuthenticated && queryPlanVersionId && !checkoutStartRan.current) {
|
|
351
412
|
checkoutStartRan.current = true;
|
|
352
|
-
void
|
|
413
|
+
void (async () => {
|
|
414
|
+
const preCheckoutSummary = await loadAccountProfileSummary({ force: true });
|
|
415
|
+
await startCheckout(queryPlanVersionId, queryInterval, {
|
|
416
|
+
autoContinueOnActivation: shouldAutoContinueAfterActivation(preCheckoutSummary, gateState)
|
|
417
|
+
});
|
|
418
|
+
})();
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
}, [authSessionResolved, checkoutState, confirmCheckoutSession, gateState, isAuthenticated, loadAccountProfileSummary, queryInterval, queryPlanVersionId, querySessionId, resolveRecoveryMessage, scheduleTransientQueryClear, shouldAutoContinueAfterActivation, startCheckout]);
|
|
422
|
+
useEffect(() => {
|
|
423
|
+
if (checkoutState === 'success' || checkoutState === 'cancel' || checkoutState === 'start' || checkoutState === 'free') {
|
|
353
424
|
return;
|
|
354
425
|
}
|
|
355
426
|
if (gateMessage) {
|
|
356
|
-
setFeedback({ type:
|
|
427
|
+
setFeedback({ type: lifecyclePresentation.statusTone, message: gateMessage });
|
|
357
428
|
return;
|
|
358
429
|
}
|
|
359
430
|
setFeedback((current) => {
|
|
360
431
|
if (!current || current.type !== 'error')
|
|
361
432
|
return current;
|
|
362
|
-
if (current.message ===
|
|
363
|
-
return null;
|
|
364
|
-
if (current.message === GATE_MESSAGES.misconfigured)
|
|
365
|
-
return null;
|
|
366
|
-
if (current.message === GATE_MESSAGES.missingEntitlement)
|
|
367
|
-
return null;
|
|
368
|
-
if (current.message === BILLING_CONFLICT_MESSAGES.CHECKOUT_SESSION_EXPIRED)
|
|
369
|
-
return null;
|
|
370
|
-
if (current.message === BILLING_CONFLICT_MESSAGES.CHECKOUT_SESSION_SUPERSEDED)
|
|
433
|
+
if (current.message === lifecyclePresentation.message)
|
|
371
434
|
return null;
|
|
372
435
|
return current;
|
|
373
436
|
});
|
|
374
|
-
}, [checkoutState,
|
|
437
|
+
}, [checkoutState, gateMessage, lifecyclePresentation.message, lifecyclePresentation.statusTone]);
|
|
375
438
|
const handleSelectPlan = useCallback(async (intent) => {
|
|
376
439
|
if (!isAuthenticated) {
|
|
377
440
|
const nextCheckoutState = intent.pricingType === 'free' ? 'free' : 'start';
|
|
@@ -392,13 +455,18 @@ export function PlansPage({ isAuthenticated, authUserId, runtimeMode, authSessio
|
|
|
392
455
|
await openBillingPortal();
|
|
393
456
|
return;
|
|
394
457
|
}
|
|
458
|
+
const autoContinueOnActivation = shouldAutoContinueAfterActivation(accountProfileSummary, gateState);
|
|
395
459
|
if (intent.pricingType === 'free') {
|
|
396
|
-
await startCheckout(intent.planVersionId, intent.interval);
|
|
460
|
+
await startCheckout(intent.planVersionId, intent.interval, { autoContinueOnActivation });
|
|
397
461
|
return;
|
|
398
462
|
}
|
|
399
|
-
await startCheckout(intent.planVersionId, intent.interval);
|
|
400
|
-
}, [isAuthenticated, navigate, openBillingPortal, startCheckout]);
|
|
463
|
+
await startCheckout(intent.planVersionId, intent.interval, { autoContinueOnActivation });
|
|
464
|
+
}, [accountProfileSummary, gateState, isAuthenticated, navigate, openBillingPortal, shouldAutoContinueAfterActivation, startCheckout]);
|
|
401
465
|
const handleBack = useCallback(() => {
|
|
466
|
+
if (hasResolvedActiveEntitlement && onContinueToTaskforce) {
|
|
467
|
+
void onContinueToTaskforce();
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
402
470
|
if (embedded) {
|
|
403
471
|
closeEmbeddedPlansScreen();
|
|
404
472
|
return;
|
|
@@ -408,8 +476,9 @@ export function PlansPage({ isAuthenticated, authUserId, runtimeMode, authSessio
|
|
|
408
476
|
return;
|
|
409
477
|
}
|
|
410
478
|
navigate('/');
|
|
411
|
-
}, [closeEmbeddedPlansScreen, embedded, navigate]);
|
|
412
|
-
const
|
|
479
|
+
}, [closeEmbeddedPlansScreen, embedded, hasResolvedActiveEntitlement, navigate, onContinueToTaskforce]);
|
|
480
|
+
const continueAction = hasResolvedActiveEntitlement && onContinueToTaskforce ? (_jsx("button", { className: styles.navAction, onClick: () => { void onContinueToTaskforce(); }, disabled: continueBusy || actionBusy, children: "Take Me to Taskforce" })) : null;
|
|
481
|
+
const topActions = embedded ? (_jsxs(_Fragment, { children: [continueAction, isAuthenticated && accountProfileSummary?.stripeCustomerId && (_jsx("button", { className: styles.navAction, onClick: () => { void openBillingPortal(); }, disabled: actionBusy, children: "Manage billing" }))] })) : (_jsxs(_Fragment, { children: [_jsx("button", { className: styles.navAction, onClick: handleBack, disabled: continueBusy || (isAuthenticated && !hasResolvedActiveEntitlement), children: hasResolvedActiveEntitlement ? 'Take Me to Taskforce' : 'Back' }), isAuthenticated && accountProfileSummary?.stripeCustomerId && (_jsx("button", { className: styles.navAction, onClick: () => { void openBillingPortal(); }, disabled: actionBusy, children: "Manage billing" })), !isAuthenticated && (_jsxs("button", { className: styles.navAction, onClick: () => navigate(`/login?mode=login&next=${encodeURIComponent(buildEmbeddedPlansPath())}`), children: [_jsx(User, { size: 16, style: { marginRight: '8px', verticalAlign: 'text-bottom' } }), "Sign In"] }))] }));
|
|
413
482
|
const chrome = embedded ? null : (_jsx(AppShellHeader, { projectName: projectName, currentWorkspaceId: currentWorkspaceId, runtimeMode: runtimeMode === 'cloud' ? 'cloud' : 'local', theme: currentTheme, actions: topActions }));
|
|
414
|
-
return (_jsx(PlanComparisonPage, { chrome: chrome, heading: "Subscription Plans", subtitle: "Manage your subscription and explore available features. Your current plan is highlighted below.", isAuthenticated: isAuthenticated, authSessionResolved: authSessionResolved, currentPlanId: String(accountProfileSummary?.planId || queryPlanId || '').trim() || null, currentPlanVersionId: String(accountProfileSummary?.planVersionId || queryPlanVersionId || '').trim() || null, currentEntitlementState: String(accountProfileSummary?.entitlementState || '').trim() || null, canManageCurrentPlan: Boolean(accountProfileSummary?.stripeCustomerId), pricingEndpoint: pricingEndpoints[0], statusBanner: feedback, onBack: handleBack, backLabel: embedded ? 'Back to Board' : 'Back to App', topActions: embedded ? topActions : null, shellOwnsScroll: shellOwnsScroll, showPageBackButton: !embedded, fallbackPricingSource: fallbackPricingSource, onSelectPlan: handleSelectPlan }));
|
|
483
|
+
return (_jsx(PlanComparisonPage, { chrome: chrome, heading: "Subscription Plans", subtitle: "Manage your subscription and explore available features. Your current plan is highlighted below.", isAuthenticated: isAuthenticated, authSessionResolved: authSessionResolved, currentPlanId: String(accountProfileSummary?.planId || queryPlanId || '').trim() || null, currentPlanVersionId: String(accountProfileSummary?.planVersionId || queryPlanVersionId || '').trim() || null, currentEntitlementState: String(accountProfileSummary?.entitlementState || '').trim() || null, canManageCurrentPlan: Boolean(accountProfileSummary?.stripeCustomerId), pricingEndpoint: pricingEndpoints[0], statusBanner: feedback, onBack: handleBack, backLabel: hasResolvedActiveEntitlement ? 'Take Me to Taskforce' : (embedded ? 'Back to Board' : 'Back to App'), backDisabled: continueBusy || (isAuthenticated && !hasResolvedActiveEntitlement), topActions: embedded ? topActions : null, shellOwnsScroll: shellOwnsScroll, showPageBackButton: !embedded, fallbackPricingSource: fallbackPricingSource, onSelectPlan: handleSelectPlan }));
|
|
415
484
|
}
|