@taskforcehq/taskforce 0.3.302 → 0.3.304
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/dist/Taskforce.module.css +113 -5
- package/dist/TaskforceCore.js +560 -208
- package/dist/TaskforceCore.test.js +1833 -91
- package/dist/annotatedAttachments/service.d.ts +1 -1
- package/dist/annotatedAttachments/types.d.ts +1 -1
- package/dist/components/features/AgentsModule.js +1 -1
- package/dist/components/features/AgentsModule.test.js +1 -1
- package/dist/components/features/TaskSettings.js +58 -38
- package/dist/components/features/TaskSettings.test.js +139 -40
- 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 +2 -1
- package/dist/components/task/TaskForm.js +22 -4
- package/dist/components/task/TaskForm.test.js +46 -0
- package/dist/components/task/TaskKanban.d.ts +1 -1
- package/dist/components/task/TaskKanban.js +31 -160
- package/dist/components/task/TaskKanban.test.js +17 -0
- 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 +4 -2
- package/dist/components/views/PlanComparisonPage.test.js +4 -0
- package/dist/components/views/PlansPage.d.ts +7 -1
- package/dist/components/views/PlansPage.js +139 -53
- package/dist/components/views/PlansPage.test.d.ts +1 -0
- package/dist/components/views/PlansPage.test.js +157 -0
- package/dist/components/views/StandaloneLayout.d.ts +1 -0
- package/dist/components/views/StandaloneLayout.js +216 -186
- package/dist/components/views/WidgetView.js +11 -2
- package/dist/components/views/standalone/modals/AccountHubModal.d.ts +8 -3
- package/dist/components/views/standalone/modals/AccountHubModal.js +16 -2
- package/dist/components/views/standalone/modals/AccountHubModal.test.d.ts +1 -0
- package/dist/components/views/standalone/modals/AccountHubModal.test.js +26 -0
- package/dist/components/views/standalone/modals/SyncStatusModal.js +1 -1
- package/dist/core/DeletedTaskLifecycleService.d.ts +2 -1
- package/dist/core/EntitlementsPolicy.test.js +2 -2
- package/dist/core/GlobalSettingsService.js +78 -18
- package/dist/core/PlanEntitlementService.d.ts +31 -5
- package/dist/core/PlanEntitlementService.js +350 -222
- package/dist/core/SignupMonetizationSettings.test.js +208 -49
- package/dist/core/SystemAdmin.test.js +186 -96
- 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/Taskforce.d.ts +65 -20
- package/dist/core/Taskforce.js +261 -51
- package/dist/core/Taskforce.mcpAuth.test.js +0 -2
- package/dist/core/UserProfileAvatarDrafts.test.js +12 -3
- package/dist/core/WorkspacePlanMode.test.js +65 -0
- package/dist/core/types.d.ts +11 -12
- 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 +61 -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 +122 -0
- package/dist/hooks/sync/transfers.js +431 -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 +5 -4
- package/dist/hooks/useSyncOrchestrator.js +315 -860
- package/dist/hooks/useSyncOrchestrator.retry-closure.test.js +665 -8
- package/dist/hooks/useTaskMutations.js +45 -6
- package/dist/hooks/useTaskMutations.test.js +147 -0
- package/dist/hooks/useTaskforce.d.ts +11 -2
- package/dist/hooks/useTaskforce.js +290 -812
- package/dist/hooks/useWorkspaceSyncController.d.ts +1 -0
- package/dist/hooks/useWorkspaceSyncController.js +22 -10
- 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/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/documentHelpers.d.ts +100 -0
- package/dist/mcp/documentHelpers.js +161 -0
- package/dist/mcp/runtime.js +264 -505
- package/dist/mcp/runtime.test.js +353 -89
- 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/migrations/billingSchemaParity.test.js +3 -0
- package/dist/migrations/taskSchemaMigrations.js +15 -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.cookieProxy.test.js +1 -0
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.js +48 -3
- package/dist/server/index.test.js +14 -2
- package/dist/server/mockStripe.d.ts +1 -0
- package/dist/server/mockStripe.js +2 -0
- package/dist/server/routes/admin.js +91 -40
- package/dist/server/routes/auth.d.ts +1 -0
- package/dist/server/routes/auth.js +231 -55
- package/dist/server/routes/billing.js +473 -39
- package/dist/server/routes/billing.test.js +972 -37
- package/dist/server/routes/shared.d.ts +30 -0
- package/dist/server/routes/shared.js +31 -3
- package/dist/server/routes/sync.integration.test.js +180 -0
- package/dist/server/routes/sync.js +125 -242
- package/dist/server/routes/tasks.js +49 -7
- package/dist/server/routes.js +11 -22
- package/dist/server/routes.test.js +530 -81
- package/dist/shared/taskActor.d.ts +10 -0
- package/dist/shared/taskActor.js +1 -0
- package/dist/sync/cloudSyncApi.d.ts +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.d.ts +10 -0
- package/dist/sync/contentSyncState.js +277 -14
- 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 +377 -0
- package/dist/sync/syncService.d.ts +9 -1
- package/dist/sync/syncService.js +57 -10
- package/dist/sync/syncService.test.js +48 -0
- package/dist/sync/workspacePullFeed.d.ts +11 -0
- package/dist/sync/workspacePullFeed.js +123 -68
- package/dist/sync/workspacePullFeed.test.js +180 -0
- package/dist/sync/workspaceSyncModel.d.ts +22 -1
- package/dist/sync/workspaceSyncModel.js +65 -0
- package/dist/sync/workspaceSyncModel.test.js +119 -0
- package/dist/types.d.ts +2 -10
- package/dist/ui/assets/AgentsModule-BLWVbuKP.js +1 -0
- package/dist/ui/assets/{AnnotatedAttachmentWorkspace-Dvi02Y7q.js → AnnotatedAttachmentWorkspace-BlS-cqnl.js} +1 -1
- package/dist/ui/assets/{DocumentWorkspace-iys3zfca.js → DocumentWorkspace-BH_6DF6x.js} +1 -1
- package/dist/ui/assets/{InitiativesModule-DJvLJI3A.js → InitiativesModule-BjR6iBf9.js} +1 -1
- package/dist/ui/assets/{PlansPage-BlVC_lRq.css → PlansPage-C2dd2n1X.css} +1 -1
- package/dist/ui/assets/PlansPage-CSDQ4sLa.js +1 -0
- package/dist/ui/assets/TaskSettings-CEDy34Jo.js +9 -0
- package/dist/ui/assets/{WorkflowsModule-BguHeQaV.js → WorkflowsModule-DnFqdUME.js} +1 -1
- package/dist/ui/assets/index-DXUdtH1B.js +6 -0
- package/dist/ui/assets/index-Ii0B0rTO.css +1 -0
- package/dist/ui/assets/{vendor-icons-C8EED3Oh.js → vendor-icons-I7ZwG1z_.js} +1 -1
- package/dist/ui/index.html +4 -4
- package/dist/utils/accountProfileSummaryCache.d.ts +31 -0
- package/dist/utils/accountProfileSummaryCache.js +97 -0
- package/dist/utils/accountProfileSummaryCache.test.d.ts +1 -0
- package/dist/utils/accountProfileSummaryCache.test.js +63 -0
- package/dist/utils/taskActivity.js +4 -0
- package/dist/utils/taskActivity.test.js +25 -1
- package/package.json +5 -3
- package/scripts/playwright-auth.sh +4 -0
- package/dist/ui/assets/AgentsModule-JG5jc3he.js +0 -1
- package/dist/ui/assets/PlansPage-BMPlpFpw.js +0 -1
- package/dist/ui/assets/TaskSettings-Bqd4cCzW.js +0 -8
- package/dist/ui/assets/index-MXeWoebC.css +0 -1
- package/dist/ui/assets/index-YJ4k5QNa.js +0 -6
- package/dist/utils/billingStatusCache.d.ts +0 -21
- package/dist/utils/billingStatusCache.js +0 -70
|
@@ -270,7 +270,16 @@ export function WidgetView(props) {
|
|
|
270
270
|
handleNavigation(() => {
|
|
271
271
|
setActiveTab('settings');
|
|
272
272
|
});
|
|
273
|
-
}, title: "Settings", children: _jsx(Settings, { size: 18 }) }), _jsx("button", { className: "tf-control-icon", onClick: handleXClick, title: activeTab === 'tasks' ? "Close" : "Back to Tasks", children: activeTab === 'tasks' ? _jsx(X, { size: 20 }) : _jsx(ArrowLeft, { size: 20 }) })] })] }), _jsx(TopNoticeLayer, { notice: uiNotice, onDismiss: clearNotice }), activeTab === 'add' && (_jsx(TaskActionHeader, { editingTaskId: editingTaskId, loading: loading, autoSaveState: autoSaveState, title: title, currentTask: currentTask, currentTaskWorkstream: currentTaskWorkstream, currentTaskInitiative: currentTaskInitiative, workstreamInput: workstreamInput, onWorkstreamInputChange: setWorkstreamInput, onSetWorkstreamForCurrentTask: handleSetWorkstreamForCurrentTask, handleSubmit: handleSubmit, resetForm: resetForm, handleCopyId: handleCopyId, copiedId: copiedId, tasks: tasks, handleToggleInProgress: handleToggleInProgress, handleToggleReview: handleToggleReview, handleToggleComplete: handleToggleComplete, handleToggleCancel: handleToggleCancel, handleArchiveTask: handleArchiveTask,
|
|
273
|
+
}, title: "Settings", children: _jsx(Settings, { size: 18 }) }), _jsx("button", { className: "tf-control-icon", onClick: handleXClick, title: activeTab === 'tasks' ? "Close" : "Back to Tasks", children: activeTab === 'tasks' ? _jsx(X, { size: 20 }) : _jsx(ArrowLeft, { size: 20 }) })] })] }), _jsx(TopNoticeLayer, { notice: uiNotice, onDismiss: clearNotice }), activeTab === 'add' && (_jsx(TaskActionHeader, { editingTaskId: editingTaskId, loading: loading, autoSaveState: autoSaveState, title: title, currentTask: currentTask, currentTaskWorkstream: currentTaskWorkstream, currentTaskInitiative: currentTaskInitiative, workstreamInput: workstreamInput, onWorkstreamInputChange: setWorkstreamInput, onSetWorkstreamForCurrentTask: handleSetWorkstreamForCurrentTask, handleSubmit: handleSubmit, resetForm: resetForm, handleCopyId: handleCopyId, copiedId: copiedId, tasks: tasks, handleToggleInProgress: handleToggleInProgress, handleToggleReview: handleToggleReview, handleToggleComplete: handleToggleComplete, handleToggleCancel: handleToggleCancel, handleArchiveTask: handleArchiveTask, handleUnarchiveTask: (taskId) => {
|
|
274
|
+
if (currentTask?.isDeleted) {
|
|
275
|
+
const deletedRecord = deletedRecordByTaskId.get(taskId);
|
|
276
|
+
if (deletedRecord) {
|
|
277
|
+
void handleRestoreDeletedTask(deletedRecord.id);
|
|
278
|
+
}
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
handleUnarchive(taskId);
|
|
282
|
+
}, handleRestoreDeletedTask: (deletedRecordId) => {
|
|
274
283
|
void handleRestoreDeletedTask(deletedRecordId);
|
|
275
284
|
}, handlePermanentlyDeleteDeletedTask: (deletedRecordId) => {
|
|
276
285
|
void handlePermanentlyDeleteDeletedTask(deletedRecordId);
|
|
@@ -290,7 +299,7 @@ export function WidgetView(props) {
|
|
|
290
299
|
void handleDelete(taskId);
|
|
291
300
|
}, onDeleteAllDeleted: () => {
|
|
292
301
|
void handleEmptyDeletedTasks();
|
|
293
|
-
}, onFetchArchive: fetchArchive, onAddTaskToCategory: handleAddTaskToCategory, supplementalTasks: deletedScopeTasks })), activeTab === 'add' && (_jsx(TaskForm, { editingTaskId: editingTaskId, error: error, title: title, description: description, checklistItems: checklistItems, category: category, type: type, priority: priority, complexity: complexity, manualComplexityEnabled: manualComplexityEnabled, approach: approach, assignee: assignee, scheduledDate: scheduledDate, dueDate: dueDate, workstreamInput: workstreamInput, formTaxonomies: formTaxonomies, onTaxonomyChange: onTaxonomyChange, taxonomies: taxonomies, comments: comments, newCommentText: newCommentText, contextFiles: attachments, currentWorkspaceId: currentWorkspaceId, apiBaseUrl: "", descriptionFocused: descriptionFocused, showMarkdownHelp: showMarkdownHelp, showChecklist: showChecklist, checklistEnabled: checklistDropdownEnabled, showComments: showComments, categories: activeCategories, types: activeTypes, priorities: priorities, taxonomyDisplayLabels: taxonomyDisplayLabels, assigneeOptions: assigneeOptions, workstreams: props.workstreams, copiedId: copiedId, onTitleChange: setTitle, onDescriptionChange: setDescription, onChecklistItemsChange: setChecklistItems, onCategoryChange: setCategory, onTypeChange: setType, onPriorityChange: setPriority, onComplexityChange: setComplexity, onApproachChange: (val) => { setApproach(val); setFormTaxonomies(prev => ({ ...prev, approach: val })); }, onAssigneeChange: setAssignee, onScheduledDateChange: setScheduledDate, onDueDateChange: setDueDate, onWorkstreamInputChange: setWorkstreamInput, onNewCommentTextChange: setNewCommentText, onDescriptionFocusedChange: setDescriptionFocused, onShowMarkdownHelpChange: setShowMarkdownHelp, onShowChecklistChange: setShowChecklist, onShowCommentsChange: setShowComments, onOpenSettings: (section) => {
|
|
302
|
+
}, onFetchArchive: fetchArchive, onAddTaskToCategory: handleAddTaskToCategory, supplementalTasks: deletedScopeTasks })), activeTab === 'add' && (_jsx(TaskForm, { editingTaskId: editingTaskId, error: error, title: title, description: description, checklistItems: checklistItems, category: category, type: type, priority: priority, complexity: complexity, manualComplexityEnabled: manualComplexityEnabled, approach: approach, assignee: assignee, scheduledDate: scheduledDate, dueDate: dueDate, workstreamInput: workstreamInput, formTaxonomies: formTaxonomies, onTaxonomyChange: onTaxonomyChange, taxonomies: taxonomies, comments: comments, newCommentText: newCommentText, contextFiles: attachments, currentWorkspaceId: currentWorkspaceId, apiBaseUrl: "", descriptionFocused: descriptionFocused, showMarkdownHelp: showMarkdownHelp, showChecklist: showChecklist, checklistEnabled: checklistDropdownEnabled, showComments: showComments, categories: activeCategories, types: activeTypes, priorities: priorities, taxonomyDisplayLabels: taxonomyDisplayLabels, assigneeOptions: assigneeOptions, workstreams: props.workstreams, initiatives: props.initiatives, copiedId: copiedId, onTitleChange: setTitle, onDescriptionChange: setDescription, onChecklistItemsChange: setChecklistItems, onCategoryChange: setCategory, onTypeChange: setType, onPriorityChange: setPriority, onComplexityChange: setComplexity, onApproachChange: (val) => { setApproach(val); setFormTaxonomies(prev => ({ ...prev, approach: val })); }, onAssigneeChange: setAssignee, onScheduledDateChange: setScheduledDate, onDueDateChange: setDueDate, onWorkstreamInputChange: setWorkstreamInput, onNewCommentTextChange: setNewCommentText, onDescriptionFocusedChange: setDescriptionFocused, onShowMarkdownHelpChange: setShowMarkdownHelp, onShowChecklistChange: setShowChecklist, onShowCommentsChange: setShowComments, onOpenSettings: (section) => {
|
|
294
303
|
handleNavigation(() => {
|
|
295
304
|
setSettingsSection(section);
|
|
296
305
|
setActiveTab('settings');
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import type { ModalRuntimeMode, ModalThemeName } from './types';
|
|
2
|
-
interface
|
|
2
|
+
interface AccountProfileSummaryLike {
|
|
3
3
|
planId?: string | null;
|
|
4
|
+
planName?: string | null;
|
|
4
5
|
entitlementState?: string | null;
|
|
5
6
|
stripeStatus?: string | null;
|
|
6
7
|
effectiveUntil?: string | null;
|
|
7
8
|
stripeSubscriptionId?: string | null;
|
|
8
9
|
stripeCustomerId?: string | null;
|
|
10
|
+
gate?: string | null;
|
|
11
|
+
message?: string | null;
|
|
12
|
+
billingConflictCode?: 'CHECKOUT_SESSION_EXPIRED' | 'CHECKOUT_SESSION_SUPERSEDED' | null;
|
|
13
|
+
planSelectionRequired?: boolean;
|
|
9
14
|
}
|
|
10
15
|
interface AccountHubModalProps {
|
|
11
16
|
isOpen: boolean;
|
|
@@ -21,7 +26,7 @@ interface AccountHubModalProps {
|
|
|
21
26
|
billingNotice: string | null;
|
|
22
27
|
billingActionBusy: boolean;
|
|
23
28
|
billingIntervalChoice: 'month' | 'year';
|
|
24
|
-
|
|
29
|
+
accountProfileSummary: AccountProfileSummaryLike | null;
|
|
25
30
|
currentWorkspaceId: string;
|
|
26
31
|
canOpenTeamManagement: boolean;
|
|
27
32
|
canManageWorkspaceSync: boolean;
|
|
@@ -39,5 +44,5 @@ interface AccountHubModalProps {
|
|
|
39
44
|
onToggleWorkspaceSync: (enabled: boolean) => void;
|
|
40
45
|
onOpenHelp: () => void;
|
|
41
46
|
}
|
|
42
|
-
export declare function AccountHubModal({ isOpen, theme, runtimeMode, authRequiredForApi, isAuthenticated, hasAuthIdentity, authIdentityLabel, billingLoading, billingError, billingActionError, billingNotice, billingActionBusy, billingIntervalChoice,
|
|
47
|
+
export declare function AccountHubModal({ isOpen, theme, runtimeMode, authRequiredForApi, isAuthenticated, hasAuthIdentity, authIdentityLabel, billingLoading, billingError, billingActionError, billingNotice, billingActionBusy, billingIntervalChoice, accountProfileSummary, currentWorkspaceId, canOpenTeamManagement, canManageWorkspaceSync, workspaceCloudSyncEnabled, syncStatusLabel, workspaceSyncError, syncControlBusy, onClose, onOpenWorkspaceAudit, onBillingIntervalChange, onRefreshBilling, onUpdateInterval, onManageBilling, onStartCheckout, onToggleWorkspaceSync, onOpenHelp, }: AccountHubModalProps): import("react/jsx-runtime").JSX.Element;
|
|
43
48
|
export {};
|
|
@@ -5,7 +5,21 @@ import accountStyles from '../../StandaloneAccount.module.css';
|
|
|
5
5
|
import authStyles from '../../AuthShell.module.css';
|
|
6
6
|
import managementStyles from '../../StandaloneManagement.module.css';
|
|
7
7
|
import styles from '../../../../Taskforce.module.css';
|
|
8
|
-
export function AccountHubModal({ isOpen, theme, runtimeMode, authRequiredForApi, isAuthenticated, hasAuthIdentity, authIdentityLabel, billingLoading, billingError, billingActionError, billingNotice, billingActionBusy, billingIntervalChoice,
|
|
8
|
+
export function AccountHubModal({ isOpen, theme, runtimeMode, authRequiredForApi, isAuthenticated, hasAuthIdentity, authIdentityLabel, billingLoading, billingError, billingActionError, billingNotice, billingActionBusy, billingIntervalChoice, accountProfileSummary, currentWorkspaceId, canOpenTeamManagement, canManageWorkspaceSync, workspaceCloudSyncEnabled, syncStatusLabel, workspaceSyncError, syncControlBusy, onClose, onOpenWorkspaceAudit, onBillingIntervalChange, onRefreshBilling, onUpdateInterval, onManageBilling, onStartCheckout, onToggleWorkspaceSync, onOpenHelp, }) {
|
|
9
|
+
const billingGate = String(accountProfileSummary?.gate || '').trim().toLowerCase();
|
|
10
|
+
const billingConflictCode = String(accountProfileSummary?.billingConflictCode || '').trim().toUpperCase();
|
|
11
|
+
const billingRecoveryMessage = String(accountProfileSummary?.message || '').trim()
|
|
12
|
+
|| (billingConflictCode === 'CHECKOUT_SESSION_EXPIRED'
|
|
13
|
+
? 'Your latest checkout expired. Choose a plan to continue.'
|
|
14
|
+
: (billingConflictCode === 'CHECKOUT_SESSION_SUPERSEDED'
|
|
15
|
+
? 'A newer checkout is already in progress for this account. Open plans to continue.'
|
|
16
|
+
: ''));
|
|
17
|
+
const startCheckoutLabel = billingConflictCode === 'CHECKOUT_SESSION_EXPIRED'
|
|
18
|
+
|| accountProfileSummary?.planSelectionRequired === true
|
|
19
|
+
|| billingGate === 'plan_selection_required'
|
|
20
|
+
? 'Choose Plan'
|
|
21
|
+
: (billingGate === 'checkout_pending' ? 'Open Plans' : 'Start Checkout');
|
|
22
|
+
const resolvedPlanLabel = String(accountProfileSummary?.planName || accountProfileSummary?.planId || 'n/a').trim() || 'n/a';
|
|
9
23
|
return (_jsx(Modal, { isOpen: isOpen, onClose: onClose, title: "Account Hub", size: "md", theme: theme, draggable: true, children: _jsxs("div", { className: `${modalStyles.form} ${managementStyles.modalBody}`, children: [_jsx("p", { className: managementStyles.mutedText, children: "Manage sign in, profile, and workspace controls from one place." }), _jsxs("div", { className: accountStyles.accountHubCard, children: [_jsx("strong", { children: "Authentication" }), _jsx("p", { className: managementStyles.sectionText, children: runtimeMode === 'cloud'
|
|
10
24
|
? (authRequiredForApi
|
|
11
25
|
? (isAuthenticated ? 'Signed in. API access is enabled.' : 'Cloud mode requires sign in before app usage.')
|
|
@@ -14,7 +28,7 @@ export function AccountHubModal({ isOpen, theme, runtimeMode, authRequiredForApi
|
|
|
14
28
|
? (hasAuthIdentity
|
|
15
29
|
? _jsxs(_Fragment, { children: ["Signed in as ", _jsx("span", { className: accountStyles.accountIdentityEmail, children: authIdentityLabel })] })
|
|
16
30
|
: 'Signed in')
|
|
17
|
-
: 'Signed in as: not signed in' }), runtimeMode === 'cloud' && authRequiredForApi && !isAuthenticated && (_jsx("p", { className: managementStyles.sectionTextTop, children: "Use the `/login` screen to sign in." }))] }), _jsxs("div", { className: accountStyles.accountHubCard, children: [_jsx("strong", { children: "Profile & Account" }), _jsx("p", { className: managementStyles.sectionText, children: "Profile editing and account preferences will live here." })] }), _jsxs("div", { className: accountStyles.accountHubCard, children: [_jsx("strong", { children: "Billing" }), !isAuthenticated ? (_jsx("p", { className: managementStyles.sectionText, children: "Sign in to manage subscription billing." })) : runtimeMode !== 'cloud' ? (_jsx("p", { className: managementStyles.sectionText, children: "Billing remains cloud-backed in local runtime and uses your connected cloud account." })) : (_jsxs(_Fragment, { children: [billingLoading && (_jsx("p", { className: managementStyles.sectionTextSpaced, children: "Loading billing status..." })), billingError && (_jsx("p", { className: `${authStyles.loginError} ${managementStyles.errorText}`, children: billingError })), billingActionError && (_jsx("p", { className: `${authStyles.loginError} ${managementStyles.errorText}`, children: billingActionError })), billingNotice && (_jsx("p", { className: managementStyles.errorText, children: billingNotice })), _jsxs("p", { className: managementStyles.sectionTextSpaced, children: ["Plan: ", _jsx("code", { children:
|
|
31
|
+
: 'Signed in as: not signed in' }), runtimeMode === 'cloud' && authRequiredForApi && !isAuthenticated && (_jsx("p", { className: managementStyles.sectionTextTop, children: "Use the `/login` screen to sign in." }))] }), _jsxs("div", { className: accountStyles.accountHubCard, children: [_jsx("strong", { children: "Profile & Account" }), _jsx("p", { className: managementStyles.sectionText, children: "Profile editing and account preferences will live here." })] }), _jsxs("div", { className: accountStyles.accountHubCard, children: [_jsx("strong", { children: "Billing" }), !isAuthenticated ? (_jsx("p", { className: managementStyles.sectionText, children: "Sign in to manage subscription billing." })) : runtimeMode !== 'cloud' ? (_jsx("p", { className: managementStyles.sectionText, children: "Billing remains cloud-backed in local runtime and uses your connected cloud account." })) : (_jsxs(_Fragment, { children: [billingLoading && (_jsx("p", { className: managementStyles.sectionTextSpaced, children: "Loading billing status..." })), billingError && (_jsx("p", { className: `${authStyles.loginError} ${managementStyles.errorText}`, children: billingError })), billingActionError && (_jsx("p", { className: `${authStyles.loginError} ${managementStyles.errorText}`, children: billingActionError })), billingNotice && (_jsx("p", { className: managementStyles.errorText, children: billingNotice })), billingRecoveryMessage && (_jsx("p", { className: managementStyles.sectionText, children: billingRecoveryMessage })), _jsxs("p", { className: managementStyles.sectionTextSpaced, children: ["Plan: ", _jsx("code", { children: resolvedPlanLabel }), ' · ', "Entitlement: ", _jsx("code", { children: String(accountProfileSummary?.entitlementState || 'n/a') })] }), _jsxs("p", { className: managementStyles.sectionText, children: ["Stripe status: ", _jsx("code", { children: String(accountProfileSummary?.stripeStatus || 'n/a') }), accountProfileSummary?.effectiveUntil ? ` · Effective until ${new Date(accountProfileSummary.effectiveUntil).toLocaleString()}` : ''] }), _jsx("div", { className: managementStyles.wrapRow, children: _jsxs("label", { className: `${accountStyles.accountMenuMeta} ${managementStyles.inlineFieldLabel}`, children: ["Interval", _jsxs("select", { className: styles.input, value: billingIntervalChoice, onChange: (event) => onBillingIntervalChange(event.target.value === 'year' ? 'year' : 'month'), disabled: billingActionBusy, children: [_jsx("option", { value: "month", children: "Monthly" }), _jsx("option", { value: "year", children: "Yearly" })] })] }) }), _jsxs("div", { className: managementStyles.actionRowEnd, children: [_jsx("button", { className: styles.cancelBtn, onClick: onRefreshBilling, disabled: billingActionBusy || billingLoading, children: "Refresh Billing" }), _jsx("button", { className: styles.cancelBtn, onClick: onUpdateInterval, disabled: billingActionBusy || !accountProfileSummary?.stripeSubscriptionId, children: "Update Interval" }), _jsx("button", { className: styles.cancelBtn, onClick: onManageBilling, disabled: billingActionBusy || !accountProfileSummary?.stripeCustomerId, children: "Manage Billing" }), _jsx("button", { className: styles.submitBtn, onClick: onStartCheckout, disabled: billingActionBusy, children: billingActionBusy ? 'Working...' : startCheckoutLabel })] })] }))] }), _jsxs("div", { className: accountStyles.accountHubCard, children: [_jsx("strong", { children: "Workspace Audit Log" }), _jsx("p", { className: managementStyles.sectionText, children: "Audit entries are workspace-scoped for the active workspace, not global account history." }), _jsxs("p", { className: managementStyles.sectionText, children: ["Active workspace: ", _jsx("code", { children: currentWorkspaceId })] }), _jsx("div", { className: managementStyles.actionRowEnd, children: _jsx("button", { className: styles.cancelBtn, onClick: onOpenWorkspaceAudit, disabled: !canOpenTeamManagement, children: "Open Workspace Audit" }) })] }), _jsxs("div", { className: accountStyles.accountHubCard, children: [_jsx("strong", { children: "Workspace Cloud Sync" }), _jsx("p", { className: managementStyles.sectionText, children: canManageWorkspaceSync
|
|
18
32
|
? (workspaceCloudSyncEnabled
|
|
19
33
|
? `Enabled · ${syncStatusLabel}`
|
|
20
34
|
: 'Disabled')
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { fireEvent, render, screen } from '@testing-library/react';
|
|
3
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
4
|
+
import { AccountHubModal } from './AccountHubModal';
|
|
5
|
+
vi.mock('../../../ui/Modal', () => ({
|
|
6
|
+
Modal: ({ isOpen, children, title }) => (isOpen ? _jsxs("div", { children: [_jsx("h1", { children: title }), children] }) : null)
|
|
7
|
+
}));
|
|
8
|
+
describe('AccountHubModal', () => {
|
|
9
|
+
it('shows checkout recovery guidance and a plan-selection CTA for expired checkout attempts', () => {
|
|
10
|
+
const onStartCheckout = vi.fn();
|
|
11
|
+
render(_jsx(AccountHubModal, { isOpen: true, theme: "dark", runtimeMode: "cloud", authRequiredForApi: true, isAuthenticated: true, hasAuthIdentity: true, authIdentityLabel: "owner@example.com", billingLoading: false, billingError: null, billingActionError: null, billingNotice: null, billingActionBusy: false, billingIntervalChoice: "month", accountProfileSummary: {
|
|
12
|
+
planId: 'pro',
|
|
13
|
+
planName: 'Pro Plan',
|
|
14
|
+
entitlementState: 'pending_plan_selection',
|
|
15
|
+
gate: 'plan_selection_required',
|
|
16
|
+
message: 'Your latest checkout expired. Choose a plan to continue.',
|
|
17
|
+
billingConflictCode: 'CHECKOUT_SESSION_EXPIRED',
|
|
18
|
+
planSelectionRequired: true
|
|
19
|
+
}, currentWorkspaceId: "workspace-1", canOpenTeamManagement: false, canManageWorkspaceSync: false, workspaceCloudSyncEnabled: false, syncStatusLabel: "Disabled", workspaceSyncError: null, syncControlBusy: false, onClose: () => { }, onOpenWorkspaceAudit: () => { }, onBillingIntervalChange: () => { }, onRefreshBilling: () => { }, onUpdateInterval: () => { }, onManageBilling: () => { }, onStartCheckout: onStartCheckout, onToggleWorkspaceSync: () => { }, onOpenHelp: () => { } }));
|
|
20
|
+
expect(screen.getByText('Your latest checkout expired. Choose a plan to continue.')).toBeInTheDocument();
|
|
21
|
+
expect(screen.getByText('Pro Plan')).toBeInTheDocument();
|
|
22
|
+
const cta = screen.getByRole('button', { name: 'Choose Plan' });
|
|
23
|
+
fireEvent.click(cta);
|
|
24
|
+
expect(onStartCheckout).toHaveBeenCalledTimes(1);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
@@ -5,7 +5,7 @@ import modalStyles from '../../../ui/Modal.module.css';
|
|
|
5
5
|
import syncStatusStyles from '../../SyncStatusModal.module.css';
|
|
6
6
|
import styles from '../../../../Taskforce.module.css';
|
|
7
7
|
export function SyncStatusModal({ isOpen, theme, currentWorkspaceLabel, syncStatusMeta, workspaceCloudSyncEnabled, syncControlBusy, canManageWorkspaceSync, workspaceSyncSummary, workspaceSyncRepairBusy, referenceMismatchCount, syncStageLabel, workspaceSyncPendingChanges, formattedLastSyncTime, formattedLastPullTime, formattedLastPushTime, syncLastError, workspaceSyncDiagnostics, activeReferenceMismatchSummaries, syncDiagnosticsSummary, syncEventRows, syncEventsListRef, workspaceSyncRepairQueued, workspaceSyncBusy, workspaceSyncCopied, runtimeMode, isAuthenticated, onClose, onToggleWorkspaceSync, onRepairSync, onCopyReport, onOpenLogin, onRetrySync, }) {
|
|
8
|
-
return (_jsx(Modal, { isOpen: isOpen, onClose: onClose, title: "Sync Manager", size: "
|
|
8
|
+
return (_jsx(Modal, { isOpen: isOpen, onClose: onClose, title: "Sync Manager", size: "mdWide", theme: theme, draggable: true, children: _jsxs("div", { className: `${modalStyles.form} ${syncStatusStyles.syncStatusModal}`, style: { gap: '10px' }, children: [_jsxs("div", { className: syncStatusStyles.syncStatusTop, children: [_jsx("div", { className: syncStatusStyles.syncStatusHeaderInfo, children: _jsxs("div", { className: syncStatusStyles.syncStatusMetaHeader, children: [_jsx("div", { className: syncStatusStyles.syncStatusBadge, style: {
|
|
9
9
|
borderColor: syncStatusMeta.border,
|
|
10
10
|
background: syncStatusMeta.background,
|
|
11
11
|
color: syncStatusMeta.color
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { DatabaseAdapter } from '../storage/databaseAdapter.js';
|
|
2
|
-
import type {
|
|
2
|
+
import type { TaskActor } from '../shared/taskActor.js';
|
|
3
|
+
import type { DeletedTaskDetails, DeletedTaskRecord, DeletedTaskSource, TaskItem } from './Taskforce.js';
|
|
3
4
|
export interface DeletedTaskLifecycleServiceDeps {
|
|
4
5
|
db: DatabaseAdapter;
|
|
5
6
|
tenantId: string;
|
|
@@ -171,8 +171,8 @@ describe('Taskforce entitlements policy engine', () => {
|
|
|
171
171
|
email: 'member3@example.com',
|
|
172
172
|
role: 'member'
|
|
173
173
|
});
|
|
174
|
-
}).toThrowError(
|
|
175
|
-
expect(() => core.assertSeatAvailable('workspace-cap')).toThrowError(
|
|
174
|
+
}).toThrowError(/Seat limit reached/);
|
|
175
|
+
expect(() => core.assertSeatAvailable('workspace-cap')).toThrowError(/Seat limit reached/);
|
|
176
176
|
}
|
|
177
177
|
finally {
|
|
178
178
|
core.close();
|
|
@@ -11,6 +11,9 @@ function splitGlobalSettingsPayload(settings) {
|
|
|
11
11
|
if (settings.site && typeof settings.site === 'object') {
|
|
12
12
|
system.site = { ...settings.site };
|
|
13
13
|
}
|
|
14
|
+
if (settings.billing && typeof settings.billing === 'object') {
|
|
15
|
+
system.billing = { ...settings.billing };
|
|
16
|
+
}
|
|
14
17
|
if (settings.setup && typeof settings.setup === 'object') {
|
|
15
18
|
system.setup = { ...settings.setup };
|
|
16
19
|
}
|
|
@@ -98,6 +101,17 @@ export function getGlobalSettingsService(deps) {
|
|
|
98
101
|
const global = readGlobalConfigService(deps);
|
|
99
102
|
const sourcePrefs = global.schedulePreferences || {};
|
|
100
103
|
const sourceCapacity = sourcePrefs.dailyCapacity || {};
|
|
104
|
+
const billingOnboardingPolicy = global.billing?.onboardingPolicy && typeof global.billing.onboardingPolicy === 'object'
|
|
105
|
+
&& (global.billing.onboardingPolicy.mode === 'require_plan_selection'
|
|
106
|
+
|| global.billing.onboardingPolicy.mode === 'auto_grant_free'
|
|
107
|
+
|| global.billing.onboardingPolicy.mode === 'auto_start_trial')
|
|
108
|
+
? {
|
|
109
|
+
mode: global.billing.onboardingPolicy.mode,
|
|
110
|
+
planVersionId: typeof global.billing.onboardingPolicy.planVersionId === 'string'
|
|
111
|
+
? (global.billing.onboardingPolicy.planVersionId.trim() || null)
|
|
112
|
+
: null
|
|
113
|
+
}
|
|
114
|
+
: null;
|
|
101
115
|
const schedulePreferences = {
|
|
102
116
|
...(typeof sourcePrefs.showWeekends === 'boolean' ? { showWeekends: sourcePrefs.showWeekends } : {}),
|
|
103
117
|
...(typeof sourcePrefs.isCalendarSidebarOpen === 'boolean' ? { isCalendarSidebarOpen: sourcePrefs.isCalendarSidebarOpen } : {}),
|
|
@@ -163,14 +177,6 @@ export function getGlobalSettingsService(deps) {
|
|
|
163
177
|
...(typeof global.auth.authRateLimitWindowMs === 'number' && Number.isFinite(global.auth.authRateLimitWindowMs)
|
|
164
178
|
? { authRateLimitWindowMs: Math.max(1000, Math.floor(global.auth.authRateLimitWindowMs)) }
|
|
165
179
|
: {}),
|
|
166
|
-
...((global.auth.signupMonetizationStrategy === 'auto_assign_default' || global.auth.signupMonetizationStrategy === 'plan_selection_required')
|
|
167
|
-
? { signupMonetizationStrategy: global.auth.signupMonetizationStrategy }
|
|
168
|
-
: {}),
|
|
169
|
-
...(typeof global.auth.defaultSignupPlanVersionId === 'string'
|
|
170
|
-
? { defaultSignupPlanVersionId: global.auth.defaultSignupPlanVersionId.trim() || null }
|
|
171
|
-
: global.auth.defaultSignupPlanVersionId === null
|
|
172
|
-
? { defaultSignupPlanVersionId: null }
|
|
173
|
-
: {})
|
|
174
180
|
}
|
|
175
181
|
}
|
|
176
182
|
: {}),
|
|
@@ -186,6 +192,13 @@ export function getGlobalSettingsService(deps) {
|
|
|
186
192
|
}
|
|
187
193
|
}
|
|
188
194
|
: {}),
|
|
195
|
+
...(billingOnboardingPolicy
|
|
196
|
+
? {
|
|
197
|
+
billing: {
|
|
198
|
+
onboardingPolicy: billingOnboardingPolicy
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
: {}),
|
|
189
202
|
...(global.setup && typeof global.setup === 'object'
|
|
190
203
|
? {
|
|
191
204
|
setup: {
|
|
@@ -289,7 +302,24 @@ export function updateGlobalSettingsService(deps, settings) {
|
|
|
289
302
|
const currentAuth = next.auth && typeof next.auth === 'object' ? next.auth : {};
|
|
290
303
|
const incoming = settings.auth;
|
|
291
304
|
next.auth = {
|
|
292
|
-
...currentAuth
|
|
305
|
+
...(typeof currentAuth.allowSelfRegistration === 'boolean'
|
|
306
|
+
? { allowSelfRegistration: currentAuth.allowSelfRegistration }
|
|
307
|
+
: {}),
|
|
308
|
+
...(typeof currentAuth.requireVerifiedEmail === 'boolean'
|
|
309
|
+
? { requireVerifiedEmail: currentAuth.requireVerifiedEmail }
|
|
310
|
+
: {}),
|
|
311
|
+
...(typeof currentAuth.defaultNewUserBetaAccess === 'boolean'
|
|
312
|
+
? { defaultNewUserBetaAccess: currentAuth.defaultNewUserBetaAccess }
|
|
313
|
+
: {}),
|
|
314
|
+
...(typeof currentAuth.authRateLimitEnabled === 'boolean'
|
|
315
|
+
? { authRateLimitEnabled: currentAuth.authRateLimitEnabled }
|
|
316
|
+
: {}),
|
|
317
|
+
...(typeof currentAuth.authRateLimitMaxRequests === 'number' && Number.isFinite(currentAuth.authRateLimitMaxRequests)
|
|
318
|
+
? { authRateLimitMaxRequests: Math.max(1, Math.floor(currentAuth.authRateLimitMaxRequests)) }
|
|
319
|
+
: {}),
|
|
320
|
+
...(typeof currentAuth.authRateLimitWindowMs === 'number' && Number.isFinite(currentAuth.authRateLimitWindowMs)
|
|
321
|
+
? { authRateLimitWindowMs: Math.max(1000, Math.floor(currentAuth.authRateLimitWindowMs)) }
|
|
322
|
+
: {}),
|
|
293
323
|
...(typeof incoming.allowSelfRegistration === 'boolean'
|
|
294
324
|
? { allowSelfRegistration: incoming.allowSelfRegistration }
|
|
295
325
|
: {}),
|
|
@@ -307,15 +337,7 @@ export function updateGlobalSettingsService(deps, settings) {
|
|
|
307
337
|
: {}),
|
|
308
338
|
...(typeof incoming.authRateLimitWindowMs === 'number' && Number.isFinite(incoming.authRateLimitWindowMs)
|
|
309
339
|
? { authRateLimitWindowMs: Math.max(1000, Math.floor(incoming.authRateLimitWindowMs)) }
|
|
310
|
-
: {})
|
|
311
|
-
...((incoming.signupMonetizationStrategy === 'auto_assign_default' || incoming.signupMonetizationStrategy === 'plan_selection_required')
|
|
312
|
-
? { signupMonetizationStrategy: incoming.signupMonetizationStrategy }
|
|
313
|
-
: {}),
|
|
314
|
-
...(typeof incoming.defaultSignupPlanVersionId === 'string'
|
|
315
|
-
? { defaultSignupPlanVersionId: incoming.defaultSignupPlanVersionId.trim() || null }
|
|
316
|
-
: incoming.defaultSignupPlanVersionId === null
|
|
317
|
-
? { defaultSignupPlanVersionId: null }
|
|
318
|
-
: {})
|
|
340
|
+
: {})
|
|
319
341
|
};
|
|
320
342
|
}
|
|
321
343
|
if (settings.site && typeof settings.site === 'object') {
|
|
@@ -331,6 +353,44 @@ export function updateGlobalSettingsService(deps, settings) {
|
|
|
331
353
|
: {})
|
|
332
354
|
};
|
|
333
355
|
}
|
|
356
|
+
if (settings.billing && typeof settings.billing === 'object') {
|
|
357
|
+
const currentBilling = next.billing && typeof next.billing === 'object' ? next.billing : {};
|
|
358
|
+
const incoming = settings.billing;
|
|
359
|
+
const currentOnboardingPolicy = currentBilling.onboardingPolicy && typeof currentBilling.onboardingPolicy === 'object'
|
|
360
|
+
&& (currentBilling.onboardingPolicy.mode === 'require_plan_selection'
|
|
361
|
+
|| currentBilling.onboardingPolicy.mode === 'auto_grant_free'
|
|
362
|
+
|| currentBilling.onboardingPolicy.mode === 'auto_start_trial')
|
|
363
|
+
? {
|
|
364
|
+
mode: currentBilling.onboardingPolicy.mode,
|
|
365
|
+
planVersionId: typeof currentBilling.onboardingPolicy.planVersionId === 'string'
|
|
366
|
+
? (currentBilling.onboardingPolicy.planVersionId.trim() || null)
|
|
367
|
+
: null
|
|
368
|
+
}
|
|
369
|
+
: null;
|
|
370
|
+
const incomingOnboardingPolicy = incoming.onboardingPolicy && typeof incoming.onboardingPolicy === 'object'
|
|
371
|
+
&& (incoming.onboardingPolicy.mode === 'require_plan_selection'
|
|
372
|
+
|| incoming.onboardingPolicy.mode === 'auto_grant_free'
|
|
373
|
+
|| incoming.onboardingPolicy.mode === 'auto_start_trial')
|
|
374
|
+
? {
|
|
375
|
+
mode: incoming.onboardingPolicy.mode,
|
|
376
|
+
planVersionId: typeof incoming.onboardingPolicy.planVersionId === 'string'
|
|
377
|
+
? (incoming.onboardingPolicy.planVersionId.trim() || null)
|
|
378
|
+
: null
|
|
379
|
+
}
|
|
380
|
+
: null;
|
|
381
|
+
next.billing = {
|
|
382
|
+
...(currentOnboardingPolicy
|
|
383
|
+
? {
|
|
384
|
+
onboardingPolicy: currentOnboardingPolicy
|
|
385
|
+
}
|
|
386
|
+
: {}),
|
|
387
|
+
...(incomingOnboardingPolicy
|
|
388
|
+
? {
|
|
389
|
+
onboardingPolicy: incomingOnboardingPolicy
|
|
390
|
+
}
|
|
391
|
+
: {})
|
|
392
|
+
};
|
|
393
|
+
}
|
|
334
394
|
if (settings.setup && typeof settings.setup === 'object') {
|
|
335
395
|
const currentSetup = next.setup && typeof next.setup === 'object' ? next.setup : {};
|
|
336
396
|
const incoming = settings.setup;
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import type { DatabaseAdapter } from '../storage/databaseAdapter.js';
|
|
2
|
-
import type { EntitlementState, PlanFeatureAccess, PlanFeatureSnapshot, PlanFeatureCatalogItem, PlanVersionSnapshot, PlanCatalogSnapshot, AccountEntitlementSnapshot, SeatUsageSnapshot, FeatureEvaluationResult, AccountAccessStatus, GlobalTaskforceSettings,
|
|
2
|
+
import type { EntitlementState, PlanFeatureAccess, PlanFeatureSnapshot, PlanFeatureCatalogItem, PlanVersionSnapshot, PlanCatalogSnapshot, AccountEntitlementSnapshot, SeatUsageSnapshot, FeatureEvaluationResult, AccountAccessStatus, GlobalTaskforceSettings, OnboardingPolicy, OnboardingPolicyValidationResult } from './types.js';
|
|
3
3
|
export interface PlanEntitlementServiceDeps {
|
|
4
4
|
getWorkspaceOwnerUserId: (workspaceId: string) => string | null;
|
|
5
5
|
readGlobalConfig: () => GlobalTaskforceSettings;
|
|
6
6
|
writeGlobalConfig: (config: GlobalTaskforceSettings) => void;
|
|
7
7
|
}
|
|
8
|
+
type CommercialStateTransitionSource = 'registration' | 'invite-accept' | 'checkout-start' | 'checkout-confirm' | 'stripe-webhook' | 'backfill' | 'legacy-plan-mode' | 'admin' | 'system';
|
|
8
9
|
/**
|
|
9
10
|
* Handles all plan catalog, plan version, and account entitlement logic.
|
|
10
11
|
* Extracted from Taskforce.ts to reduce monolith size.
|
|
@@ -24,9 +25,15 @@ export declare class PlanEntitlementService {
|
|
|
24
25
|
private applyPlanVersionTrialDaysMetadata;
|
|
25
26
|
private sanitizePlanFeatureConfig;
|
|
26
27
|
private isEntitlementStateAllowed;
|
|
28
|
+
private isIsoTimestampExpired;
|
|
29
|
+
private readAccountEntitlementRow;
|
|
30
|
+
private normalizeExpiredCheckoutPendingRow;
|
|
31
|
+
private isBillingConfirmedTransitionSource;
|
|
32
|
+
private resolveCanonicalTransitionSource;
|
|
33
|
+
private resolvePendingPlanVersionId;
|
|
34
|
+
private buildBlockedAccessStatus;
|
|
27
35
|
private repairPlanDefaultVersionIfNeeded;
|
|
28
36
|
repairPlanCatalogDefaultsIfNeeded(): void;
|
|
29
|
-
repairSignupMonetizationSettingsIfNeeded(): void;
|
|
30
37
|
normalizeLegacyPlanDataIfNeeded(): void;
|
|
31
38
|
private removeLegacyPlanFeatureRows;
|
|
32
39
|
private migrateLegacySeatLimitFeatureConfigToVersionSeatLimit;
|
|
@@ -34,6 +41,18 @@ export declare class PlanEntitlementService {
|
|
|
34
41
|
backfillAccountEntitlements(): void;
|
|
35
42
|
private getRequiredDefaultPlanVersion;
|
|
36
43
|
private resolveCanonicalEntitlementStateFromBilling;
|
|
44
|
+
transitionCanonicalCommercialState(input: {
|
|
45
|
+
accountId: string;
|
|
46
|
+
planVersionId?: string | null;
|
|
47
|
+
state: EntitlementState;
|
|
48
|
+
effectiveAt?: string | null;
|
|
49
|
+
effectiveUntil?: string | null;
|
|
50
|
+
billingMirrorPlanId?: string | null;
|
|
51
|
+
source: CommercialStateTransitionSource;
|
|
52
|
+
updatedBy: string;
|
|
53
|
+
createdAt?: string | null;
|
|
54
|
+
updatedAt?: string | null;
|
|
55
|
+
}): AccountEntitlementSnapshot;
|
|
37
56
|
upsertCanonicalEntitlementRecord(input: {
|
|
38
57
|
accountId: string;
|
|
39
58
|
planVersionId: string;
|
|
@@ -55,18 +74,26 @@ export declare class PlanEntitlementService {
|
|
|
55
74
|
feature: PlanFeatureSnapshot | null;
|
|
56
75
|
};
|
|
57
76
|
private planHasAnyActivePricing;
|
|
77
|
+
private planVersionHasActiveFreePricing;
|
|
78
|
+
private planVersionIsTrialEligible;
|
|
58
79
|
private tryGetDefaultPlanVersionByPlanId;
|
|
59
80
|
private getActiveDefaultPlanVersions;
|
|
60
81
|
private getDefaultPlanVersionByMode;
|
|
61
82
|
private tryGetDefaultPlanVersionByMode;
|
|
62
|
-
|
|
83
|
+
private tryGetImplicitFreeOnboardingPlanVersion;
|
|
84
|
+
getOnboardingPolicy(): OnboardingPolicy;
|
|
85
|
+
validateOnboardingPolicy(input?: Partial<OnboardingPolicy>): OnboardingPolicyValidationResult;
|
|
86
|
+
resolveRegistrationOnboardingOutcome(selectedPlanVersionIdRaw?: string): {
|
|
63
87
|
requiresPlanSelection: boolean;
|
|
88
|
+
checkoutPending: boolean;
|
|
89
|
+
commercialState: EntitlementState;
|
|
64
90
|
planVersionId: string | null;
|
|
65
91
|
planId: string | null;
|
|
66
92
|
};
|
|
67
93
|
createUserBillingRecord(userId: string, options?: {
|
|
68
94
|
entitlementState?: string;
|
|
69
95
|
planId?: string | null;
|
|
96
|
+
effectiveUntil?: string | null;
|
|
70
97
|
}): void;
|
|
71
98
|
getUserEntitlement(userId: string): {
|
|
72
99
|
entitlementState: string;
|
|
@@ -98,8 +125,6 @@ export declare class PlanEntitlementService {
|
|
|
98
125
|
planId: string;
|
|
99
126
|
entitlementState: EntitlementState;
|
|
100
127
|
};
|
|
101
|
-
getSignupMonetizationSettings(): SignupMonetizationSettings;
|
|
102
|
-
validateSignupMonetizationSettings(input?: Partial<SignupMonetizationSettings>): SignupMonetizationValidationResult;
|
|
103
128
|
private isValidEntitlementTransition;
|
|
104
129
|
addEntitlementAuditLog(input: {
|
|
105
130
|
actorUserId: string;
|
|
@@ -199,3 +224,4 @@ export declare class PlanEntitlementService {
|
|
|
199
224
|
}>;
|
|
200
225
|
};
|
|
201
226
|
}
|
|
227
|
+
export {};
|