@taskforcehq/taskforce 0.3.303 → 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 +102 -5
- package/dist/TaskforceCore.js +514 -212
- package/dist/TaskforceCore.test.js +1172 -133
- 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/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 +5 -1
- package/dist/components/views/PlansPage.js +39 -12
- package/dist/components/views/PlansPage.test.js +59 -2
- package/dist/components/views/StandaloneLayout.js +113 -38
- package/dist/components/views/WidgetView.js +11 -2
- package/dist/components/views/standalone/modals/SyncStatusModal.js +1 -1
- package/dist/core/DeletedTaskLifecycleService.d.ts +2 -1
- package/dist/core/PlanEntitlementService.d.ts +3 -1
- package/dist/core/PlanEntitlementService.js +61 -11
- package/dist/core/SignupMonetizationSettings.test.js +37 -2
- 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 +15 -11
- package/dist/core/Taskforce.js +43 -2
- package/dist/core/WorkspacePlanMode.test.js +65 -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 +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 +314 -873
- package/dist/hooks/useSyncOrchestrator.retry-closure.test.js +568 -3
- package/dist/hooks/useTaskMutations.js +45 -6
- package/dist/hooks/useTaskMutations.test.js +147 -0
- package/dist/hooks/useTaskforce.d.ts +8 -2
- package/dist/hooks/useTaskforce.js +285 -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 +174 -476
- package/dist/mcp/runtime.test.js +81 -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/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.js +13 -19
- package/dist/server/routes/auth.js +37 -2
- package/dist/server/routes/billing.js +18 -67
- package/dist/server/routes/billing.test.js +43 -0
- package/dist/server/routes/shared.d.ts +30 -0
- package/dist/server/routes/shared.js +26 -0
- package/dist/server/routes/sync.integration.test.js +20 -0
- package/dist/server/routes/sync.js +110 -237
- package/dist/server/routes/tasks.js +49 -7
- package/dist/server/routes.js +8 -19
- package/dist/server/routes.test.js +243 -5
- 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 +12 -10
- 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 +8 -1
- package/dist/sync/syncService.js +36 -2
- package/dist/sync/syncService.test.js +48 -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 +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-C_TmXZwA.js → AnnotatedAttachmentWorkspace-BlS-cqnl.js} +1 -1
- package/dist/ui/assets/{DocumentWorkspace-C3GyzrWm.js → DocumentWorkspace-BH_6DF6x.js} +1 -1
- package/dist/ui/assets/{InitiativesModule-4-Rh2K2n.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-CGk9s3NJ.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-pWocEipT.js → vendor-icons-I7ZwG1z_.js} +1 -1
- package/dist/ui/index.html +4 -4
- package/dist/utils/taskActivity.js +4 -0
- package/dist/utils/taskActivity.test.js +25 -1
- package/package.json +3 -1
- package/scripts/playwright-auth.sh +4 -0
- package/dist/ui/assets/AgentsModule-CpsTmrIW.js +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
|
@@ -37,7 +37,7 @@ import { ACCOUNT_PROFILE_SUMMARY_PATH, invalidateAccountProfileSummaryCache, loa
|
|
|
37
37
|
import { fetchTaskforceApi } from '../../utils/taskforceApiClient';
|
|
38
38
|
import { AppShellHeader } from './AppShellHeader';
|
|
39
39
|
import { getTaskforceCloudEnvironmentConfig, parseTaskforceCloudEnvironment, resolveTaskforceCloudEnvironmentFromBaseUrl } from '../../config/cloudEnvironment';
|
|
40
|
-
import { logBootstrapDebug } from '../../hooks/workspace/workspaceLocalState';
|
|
40
|
+
import { logBootstrapDebug, persistRemoteUiState } from '../../hooks/workspace/workspaceLocalState';
|
|
41
41
|
import { FilterToolbar } from './panels/FilterToolbar';
|
|
42
42
|
import { DocumentFilterToolbar } from './panels/DocumentFilterToolbar';
|
|
43
43
|
import { PlanningDrawer, getPlanningDrawerWidthPx, } from './panels/PlanningDrawer';
|
|
@@ -56,6 +56,7 @@ const WorkflowsModule = React.lazy(() => import('../features/WorkflowsModule').t
|
|
|
56
56
|
const AgentsModule = React.lazy(() => import('../features/AgentsModule').then(m => ({ default: m.AgentsModule })));
|
|
57
57
|
const InitiativesModule = React.lazy(() => import('../features/InitiativesModule').then(m => ({ default: m.InitiativesModule })));
|
|
58
58
|
const PlansPage = React.lazy(() => import('./PlansPage').then(m => ({ default: m.PlansPage })));
|
|
59
|
+
const ACTIVE_COMMERCIAL_STATES = new Set(['active', 'trialing', 'grace']);
|
|
59
60
|
const PROFILE_AVATAR_ACCEPT = 'image/png,image/jpeg,image/webp,image/gif';
|
|
60
61
|
const PROFILE_AVATAR_MAX_BYTES = 5 * 1024 * 1024;
|
|
61
62
|
const resolveSameOriginTaskforcePath = (path) => path;
|
|
@@ -192,9 +193,6 @@ export function StandaloneLayout(props) {
|
|
|
192
193
|
const nextQuery = params.toString();
|
|
193
194
|
navigate(`${location.pathname === '/' ? '/' : location.pathname}${nextQuery ? `?${nextQuery}` : ''}${location.hash || ''}`);
|
|
194
195
|
}, [location.hash, location.pathname, location.search, navigate]);
|
|
195
|
-
const handlePlansBack = useCallback(() => {
|
|
196
|
-
closePlansScreen();
|
|
197
|
-
}, [closePlansScreen]);
|
|
198
196
|
const {
|
|
199
197
|
// State
|
|
200
198
|
activeTab, setActiveTab, activeCategories, groupBy, setGroupBy, activeWorkspaceModule, setActiveWorkspaceModule, emptyColumnMode, setEmptyColumnMode, zenMode, setZenMode, filterStatus, setFilterStatus,
|
|
@@ -504,34 +502,31 @@ export function StandaloneLayout(props) {
|
|
|
504
502
|
return;
|
|
505
503
|
const timeoutId = window.setTimeout(async () => {
|
|
506
504
|
try {
|
|
507
|
-
await
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
annotatedSessionId: requestedAnnotatedSessionId
|
|
533
|
-
}
|
|
534
|
-
})
|
|
505
|
+
await persistRemoteUiState({
|
|
506
|
+
stateKey: 'layout',
|
|
507
|
+
workspaceId: currentWorkspaceId,
|
|
508
|
+
headers: workspaceUiStateHeaders,
|
|
509
|
+
patch: {
|
|
510
|
+
scheduleShowWeekends,
|
|
511
|
+
scheduleShowBacklog,
|
|
512
|
+
scheduleOnlyExpired,
|
|
513
|
+
scheduleSidebarOpen,
|
|
514
|
+
scheduleSelectedDate,
|
|
515
|
+
scheduleCalendarMonth,
|
|
516
|
+
scheduleScrollLeft,
|
|
517
|
+
showFilters,
|
|
518
|
+
documentTypeFilters,
|
|
519
|
+
documentAttachmentFilters,
|
|
520
|
+
planningDrawerOpen,
|
|
521
|
+
planningTreeCollapsed,
|
|
522
|
+
planningPrimaryCollapsed,
|
|
523
|
+
planningSecondaryCollapsed,
|
|
524
|
+
planningExpandedInitiativeIds: Array.from(expandedPlanningInitiativeIds),
|
|
525
|
+
planningDrawerDetail,
|
|
526
|
+
planningNestedWorkstreamDetailId,
|
|
527
|
+
annotatedTarget: requestedAnnotatedTarget,
|
|
528
|
+
annotatedSessionId: requestedAnnotatedSessionId
|
|
529
|
+
}
|
|
535
530
|
});
|
|
536
531
|
}
|
|
537
532
|
catch {
|
|
@@ -828,8 +823,13 @@ export function StandaloneLayout(props) {
|
|
|
828
823
|
assigneeOptions.forEach((option) => {
|
|
829
824
|
map.set(String(option.value), option.label);
|
|
830
825
|
});
|
|
826
|
+
const normalizedAuthUserId = String(authUserId || '').trim();
|
|
827
|
+
const normalizedAuthIdentityLabel = String(authUserDisplayName || authUserEmail || '').trim();
|
|
828
|
+
if (normalizedAuthUserId && normalizedAuthIdentityLabel) {
|
|
829
|
+
map.set(normalizedAuthUserId, normalizedAuthIdentityLabel);
|
|
830
|
+
}
|
|
831
831
|
return map;
|
|
832
|
-
}, [assigneeOptions]);
|
|
832
|
+
}, [assigneeOptions, authUserDisplayName, authUserEmail, authUserId]);
|
|
833
833
|
const planningStructure = useMemo(() => {
|
|
834
834
|
const planningScopedTasks = taskScope === 'archived'
|
|
835
835
|
? archivedScopeAllTasks
|
|
@@ -1229,6 +1229,7 @@ export function StandaloneLayout(props) {
|
|
|
1229
1229
|
const [billingError, setBillingError] = useState(null);
|
|
1230
1230
|
const [billingActionBusy, setBillingActionBusy] = useState(false);
|
|
1231
1231
|
const [billingActionError, setBillingActionError] = useState(null);
|
|
1232
|
+
const [plansNavigationBusy, setPlansNavigationBusy] = useState(false);
|
|
1232
1233
|
const [billingNotice, setBillingNotice] = useState(null);
|
|
1233
1234
|
const [billingIntervalChoice, setBillingIntervalChoice] = useState('month');
|
|
1234
1235
|
const [profileDisplayNameDraft, setProfileDisplayNameDraft] = useState('');
|
|
@@ -1543,7 +1544,7 @@ export function StandaloneLayout(props) {
|
|
|
1543
1544
|
});
|
|
1544
1545
|
accountSurfaceOpenStartedAtRef.current = null;
|
|
1545
1546
|
}, [accountProfileSummary?.planId, canOpenTeamManagement, showAccountHub, showAccountMenu]);
|
|
1546
|
-
const loadAccountProfileSummary = useCallback(async () => {
|
|
1547
|
+
const loadAccountProfileSummary = useCallback(async (options) => {
|
|
1547
1548
|
const accountProfileSummaryUrl = resolveCloudAuthUrl(ACCOUNT_PROFILE_SUMMARY_PATH);
|
|
1548
1549
|
if (!isAuthenticated) {
|
|
1549
1550
|
setAccountProfileSummary(null);
|
|
@@ -1563,7 +1564,10 @@ export function StandaloneLayout(props) {
|
|
|
1563
1564
|
setTeamManagementAccessLoading(canAccessCloudTeamManagement);
|
|
1564
1565
|
setTeamManagementAccessError(null);
|
|
1565
1566
|
try {
|
|
1566
|
-
const payload = await loadAccountProfileSummaryCached(accountProfileSummaryUrl, {
|
|
1567
|
+
const payload = await loadAccountProfileSummaryCached(accountProfileSummaryUrl, {
|
|
1568
|
+
identityKey: authUserId,
|
|
1569
|
+
force: options?.force === true
|
|
1570
|
+
});
|
|
1567
1571
|
const nextRole = payload?.workspaceRole === 'owner'
|
|
1568
1572
|
|| payload?.workspaceRole === 'admin'
|
|
1569
1573
|
|| payload?.workspaceRole === 'member'
|
|
@@ -1611,6 +1615,24 @@ export function StandaloneLayout(props) {
|
|
|
1611
1615
|
markAccountSurfaceLoadSettled('teamManagement');
|
|
1612
1616
|
}
|
|
1613
1617
|
}, [authUserId, canAccessCloudTeamManagement, isAuthenticated, markAccountSurfaceLoadSettled, resolveCloudAuthUrl]);
|
|
1618
|
+
const applyPlansAccountProfileSummary = useCallback((payload) => {
|
|
1619
|
+
setAccountProfileSummary(payload);
|
|
1620
|
+
const nextRole = payload?.workspaceRole === 'owner'
|
|
1621
|
+
|| payload?.workspaceRole === 'admin'
|
|
1622
|
+
|| payload?.workspaceRole === 'member'
|
|
1623
|
+
|| payload?.workspaceRole === 'read-only'
|
|
1624
|
+
? payload.workspaceRole
|
|
1625
|
+
: null;
|
|
1626
|
+
const nextMode = payload?.teamPlanMode === 'team' || payload?.teamPlanMode === 'personal'
|
|
1627
|
+
? payload.teamPlanMode
|
|
1628
|
+
: 'unknown';
|
|
1629
|
+
setTeamWorkspaceRole(nextRole);
|
|
1630
|
+
setTeamPlanMode(nextMode);
|
|
1631
|
+
}, []);
|
|
1632
|
+
const hasActiveCommercialEntitlement = useMemo(() => {
|
|
1633
|
+
const entitlementState = String(accountProfileSummary?.entitlementState || '').trim().toLowerCase();
|
|
1634
|
+
return ACTIVE_COMMERCIAL_STATES.has(entitlementState);
|
|
1635
|
+
}, [accountProfileSummary?.entitlementState]);
|
|
1614
1636
|
const handleStartCheckout = useCallback(async () => {
|
|
1615
1637
|
const params = new URLSearchParams();
|
|
1616
1638
|
params.set('screen', 'plans');
|
|
@@ -1798,6 +1820,47 @@ export function StandaloneLayout(props) {
|
|
|
1798
1820
|
}
|
|
1799
1821
|
navigate(`/setup?${params.toString()}`, { replace: true });
|
|
1800
1822
|
}, [navigate]);
|
|
1823
|
+
const handlePlansBack = useCallback(async () => {
|
|
1824
|
+
if (plansNavigationBusy)
|
|
1825
|
+
return;
|
|
1826
|
+
if (!isAuthenticated) {
|
|
1827
|
+
closePlansScreen();
|
|
1828
|
+
return;
|
|
1829
|
+
}
|
|
1830
|
+
const summary = await loadAccountProfileSummary({ force: true });
|
|
1831
|
+
const entitlementState = String(summary?.entitlementState || '').trim().toLowerCase();
|
|
1832
|
+
if (!ACTIVE_COMMERCIAL_STATES.has(entitlementState)) {
|
|
1833
|
+
return;
|
|
1834
|
+
}
|
|
1835
|
+
setPlansNavigationBusy(true);
|
|
1836
|
+
try {
|
|
1837
|
+
const result = await props.continueAfterCommercialOnboarding();
|
|
1838
|
+
if (!result.success) {
|
|
1839
|
+
pushNotice(result.error || 'Unable to finish onboarding.', 'error');
|
|
1840
|
+
if (result.destination === 'login') {
|
|
1841
|
+
navigate('/login', { replace: true });
|
|
1842
|
+
}
|
|
1843
|
+
return;
|
|
1844
|
+
}
|
|
1845
|
+
if (result.destination === 'setup') {
|
|
1846
|
+
openWorkspaceSetup();
|
|
1847
|
+
return;
|
|
1848
|
+
}
|
|
1849
|
+
closePlansScreen();
|
|
1850
|
+
}
|
|
1851
|
+
finally {
|
|
1852
|
+
setPlansNavigationBusy(false);
|
|
1853
|
+
}
|
|
1854
|
+
}, [
|
|
1855
|
+
closePlansScreen,
|
|
1856
|
+
isAuthenticated,
|
|
1857
|
+
loadAccountProfileSummary,
|
|
1858
|
+
navigate,
|
|
1859
|
+
openWorkspaceSetup,
|
|
1860
|
+
plansNavigationBusy,
|
|
1861
|
+
props,
|
|
1862
|
+
pushNotice
|
|
1863
|
+
]);
|
|
1801
1864
|
const handleSwitchWorkspace = useCallback(async (workspaceId) => {
|
|
1802
1865
|
if (!workspaceId || workspaceActionBusy)
|
|
1803
1866
|
return;
|
|
@@ -2545,6 +2608,9 @@ export function StandaloneLayout(props) {
|
|
|
2545
2608
|
}, role: "menuitem", children: [_jsx(User, { size: 15 }), "Sign In / Register"] })), isAuthenticated && (_jsxs("button", { className: accountStyles.accountMenuItem, onClick: async () => {
|
|
2546
2609
|
setShowAccountMenu(false);
|
|
2547
2610
|
await logout();
|
|
2611
|
+
if (runtimeMode === 'cloud') {
|
|
2612
|
+
navigate('/login', { replace: true });
|
|
2613
|
+
}
|
|
2548
2614
|
}, role: "menuitem", children: [_jsx(LogOut, { size: 15 }), "Sign Out"] }))] }))] }));
|
|
2549
2615
|
const cycleEmptyColumnVisibility = useCallback(() => {
|
|
2550
2616
|
setEmptyColumnMode((prev) => {
|
|
@@ -3180,7 +3246,7 @@ export function StandaloneLayout(props) {
|
|
|
3180
3246
|
display: 'inline-flex',
|
|
3181
3247
|
alignItems: 'center',
|
|
3182
3248
|
justifyContent: 'center'
|
|
3183
|
-
}, children: syncStatusMeta.icon === 'off' ? (_jsx(CloudOff, { size: 16 })) : (_jsx(Cloud, { size: 16 })) }))] })), actions: (_jsxs(_Fragment, { children: [isPlansScreen && (_jsx("button", { className: "tf-control-icon", onClick: handlePlansBack, title:
|
|
3249
|
+
}, children: syncStatusMeta.icon === 'off' ? (_jsx(CloudOff, { size: 16 })) : (_jsx(Cloud, { size: 16 })) }))] })), actions: (_jsxs(_Fragment, { children: [isPlansScreen && (_jsx("button", { className: "tf-control-icon", onClick: () => { void handlePlansBack(); }, title: isAuthenticated && !hasActiveCommercialEntitlement ? 'Choose a plan to continue.' : 'Continue to Taskforce', disabled: plansNavigationBusy || (isAuthenticated && !hasActiveCommercialEntitlement), children: hasActiveCommercialEntitlement ? 'Take Me to Taskforce' : 'Back' })), !isPlansScreen && (_jsxs(_Fragment, { children: [workspaceHeaderSections, workspacePrimaryAction] })), !isPlansScreen && _jsx(WorkspaceHeaderActions, { actions: universalDisplayActions }), !isPlansScreen && _jsx(WorkspaceHeaderActions, { actions: universalFilterActions }), accountMenuControl] })) }), _jsx(TopNoticeLayer, { notice: uiNotice, onDismiss: clearNotice }), authBlocked && (_jsx("div", { className: authStyles.authBlockedBanner, children: "Authentication required for this environment. Use the Account menu to sign in." })), !isPlansScreen && showFilters && workspaceFilterBar, isPlansScreen ? (_jsx("div", { className: `${shellStyles.standalonePage} ${shellStyles.standaloneContent} ${styles.appScrollbar} tf-scrollbar`, style: { position: 'relative', display: 'flex', flex: 1, minHeight: 0, overflowY: 'auto', overflowX: 'hidden', scrollbarGutter: 'stable' }, children: _jsx(Suspense, { fallback: _jsx("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'center', flex: 1 }, children: _jsx(Loader2, { size: 20, className: styles.spinner }) }), children: _jsx(PlansPage, { ...props, currentTheme: currentTheme, projectName: projectName, currentWorkspaceId: currentWorkspaceId, authUserId: authUserId, apiBaseUrl: apiBaseUrl, connectedEnvironmentSource: props.config?.cloudAuthBaseUrl || props.config?.apiBaseUrl || '', resolveCloudAuthUrl: resolveCloudAuthUrl, embedded: true, shellOwnsScroll: true, onAccountProfileSummaryChange: applyPlansAccountProfileSummary, onContinueToTaskforce: handlePlansBack, continueBusy: plansNavigationBusy }) }) })) : resolvedWorkspaceModule === 'docs' && documentsWorkspaceEnabled ? (_jsxs("div", { className: `${shellStyles.standalonePage} ${shellStyles.standaloneContent}`, style: { position: 'relative', display: 'flex', flex: 1, minHeight: 0, overflow: 'hidden', paddingLeft: `${WORKSPACE_TOOL_RAIL_WIDTH}px` }, children: [workspaceToolRail, _jsx(Suspense, { fallback: _jsx("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'center', flex: 1 }, children: _jsx(Loader2, { size: 20, className: styles.spinner }) }), children: _jsx(DocumentWorkspaceShell, { tasks: tasks, runtimeMode: runtimeMode, apiBaseUrl: props.config?.apiBaseUrl || '', cloudAuthBaseUrl: props.config?.cloudAuthBaseUrl || '', typeFilters: documentTypeFilters, attachmentFilters: documentAttachmentFilters, onTypeFiltersChange: setDocumentTypeFilters, onAttachmentFiltersChange: setDocumentAttachmentFilters, requestedDocPath: requestedDocPath, requestedDocAssetId: requestedDocAssetId, onRequestedDocHandled: () => {
|
|
3184
3250
|
setRequestedDocPath(null);
|
|
3185
3251
|
setRequestedDocAssetId(null);
|
|
3186
3252
|
}, enableTaskGeneration: true }) })] })) : resolvedWorkspaceModule === 'annotate' && annotatedAttachmentsWorkspaceEnabled ? (_jsxs("div", { className: `${shellStyles.standalonePage} ${shellStyles.standaloneContent}`, style: { position: 'relative', display: 'flex', flex: 1, minHeight: 0, overflow: 'hidden', paddingLeft: `${WORKSPACE_TOOL_RAIL_WIDTH}px` }, children: [workspaceToolRail, _jsx(Suspense, { fallback: _jsx("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'center', flex: 1 }, children: _jsx(Loader2, { size: 20, className: styles.spinner }) }), children: _jsx(AnnotatedAttachmentWorkspaceShell, { runtimeMode: runtimeMode, apiBaseUrl: props.config?.apiBaseUrl || '', cloudAuthBaseUrl: props.config?.cloudAuthBaseUrl || '', workspaceId: currentWorkspaceId, sessionLoadReady: annotatedWorkspaceReady, requestedTarget: requestedAnnotatedTarget, requestedSessionId: requestedAnnotatedSessionId, requestedOpenVersion: requestedAnnotatedOpenVersion, resolveTaskReferenceLabel: resolveAnnotatedTaskReferenceLabel, resolveImageReferenceLabel: resolveAnnotatedImageReferenceLabel, onOpenTarget: handleAnnotatedAttachmentOpenTarget, onContextChange: handleAnnotatedAttachmentContextChange, onBackToTask: handleBackToTaskFromAnnotatedAttachment }) })] })) : resolvedWorkspaceModule === 'workflows' ? (_jsxs("div", { className: `${shellStyles.standalonePage} ${shellStyles.standaloneContent}`, style: { position: 'relative', display: 'flex', flex: 1, minHeight: 0, overflow: 'hidden', paddingLeft: `${WORKSPACE_TOOL_RAIL_WIDTH}px` }, children: [workspaceToolRail, _jsx(Suspense, { fallback: _jsx("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'center', flex: 1 }, children: _jsx(Loader2, { size: 20, className: styles.spinner }) }), children: _jsx(WorkflowsModule, { availableWorkflows: availableWorkflows, availableEnvironments: availableEnvironments, exportEnvironment: exportEnvironment, exportWorkflowsPath: props.exportWorkflowsPath, exportingResource: exportingResource, exportResult: exportResult, onExportEnvironmentChange: props.setExportEnvironment, onExportWorkflows: onExportWorkflows, onRefreshWorkflows: fetchWorkflows, onFetchWorkflowTemplate: props.fetchWorkflowTemplate, onFetchWorkflowOverrideNames: props.fetchWorkflowOverrideNames, onSaveWorkflowTemplateDraft: props.saveWorkflowTemplateDraft, onResetWorkflowTemplateDraft: props.resetWorkflowTemplateDraft }) })] })) : resolvedWorkspaceModule === 'agents' ? (_jsxs("div", { className: `${shellStyles.standalonePage} ${shellStyles.standaloneContent}`, style: { position: 'relative', display: 'flex', flex: 1, minHeight: 0, overflow: 'hidden', paddingLeft: `${WORKSPACE_TOOL_RAIL_WIDTH}px` }, children: [workspaceToolRail, _jsx(Suspense, { fallback: _jsx("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'center', flex: 1 }, children: _jsx(Loader2, { size: 20, className: styles.spinner }) }), children: _jsx(AgentsModule, { workspaceId: currentWorkspaceId }) })] })) : (_jsxs("div", { className: `${shellStyles.standalonePage} ${shellStyles.standaloneContent} `, style: {
|
|
@@ -3240,7 +3306,16 @@ export function StandaloneLayout(props) {
|
|
|
3240
3306
|
top: '10px',
|
|
3241
3307
|
right: '10px',
|
|
3242
3308
|
zIndex: 20,
|
|
3243
|
-
}, children: _jsx(ChevronLeft, { size: 16 }) }))] }), groupBy === 'schedule' && (_jsx(ScheduleSidebar, { open: scheduleSidebarOpen, onClose: () => setScheduleSidebarOpen(false), scheduleSelectedDate: scheduleSelectedDate, setScheduleSelectedDate: setScheduleSelectedDate, scheduleCalendarMonth: scheduleCalendarMonth, setScheduleCalendarMonth: setScheduleCalendarMonth, scheduleShowWeekends: scheduleShowWeekends, setScheduleShowWeekends: setScheduleShowWeekends, scheduleShowBacklog: scheduleShowBacklog, setScheduleShowBacklog: setScheduleShowBacklog, scheduleOnlyExpired: scheduleOnlyExpired, setScheduleOnlyExpired: setScheduleOnlyExpired, expiredScheduledCount: expiredScheduledCount, overdueDueCount: overdueDueCount, expiredLeafCandidates: expiredLeafCandidates, expiredRecoveryCandidates: expiredRecoveryCandidates, onMoveExpiredToSelectedWeek: handleMoveExpiredToSelectedWeek, onMoveExpiredToBacklog: handleMoveExpiredToBacklog, scheduleBulkBusy: scheduleBulkBusy, globalWeekStartsOn: globalWeekStartsOn, resolvedLocale: resolvedLocale, todayDateOnly: todayDateOnly, scheduleBaseTasks: scheduleBaseTasks, scheduleWeekStart: scheduleWeekStart, scheduleWeekLabel: scheduleWeekLabel }))] })), _jsxs(Modal, { isOpen: activeTab === 'add', onClose: handleCloseModal, title: editingTaskId ? (currentTask?.isDeleted ? 'Deleted Task' : 'Edit Task') : 'New Task', size: taskModalFullscreen ? 'full' : 'xl', theme: currentTheme, headerActions: (_jsx("button", { className: "tf-control-icon", onClick: () => setTaskModalFullscreen((value) => !value), title: taskModalFullscreen ? 'Exit full screen' : 'Enter full screen', "aria-label": taskModalFullscreen ? 'Exit full screen' : 'Enter full screen', children: taskModalFullscreen ? _jsx(Minimize2, { size: 18 }) : _jsx(Maximize2, { size: 18 }) })), draggable: !taskModalFullscreen, closeOnOverlayClick: false, children: [_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, handleSetStatus: handleSetStatus, handleArchiveTask: handleArchiveTask,
|
|
3309
|
+
}, children: _jsx(ChevronLeft, { size: 16 }) }))] }), groupBy === 'schedule' && (_jsx(ScheduleSidebar, { open: scheduleSidebarOpen, onClose: () => setScheduleSidebarOpen(false), scheduleSelectedDate: scheduleSelectedDate, setScheduleSelectedDate: setScheduleSelectedDate, scheduleCalendarMonth: scheduleCalendarMonth, setScheduleCalendarMonth: setScheduleCalendarMonth, scheduleShowWeekends: scheduleShowWeekends, setScheduleShowWeekends: setScheduleShowWeekends, scheduleShowBacklog: scheduleShowBacklog, setScheduleShowBacklog: setScheduleShowBacklog, scheduleOnlyExpired: scheduleOnlyExpired, setScheduleOnlyExpired: setScheduleOnlyExpired, expiredScheduledCount: expiredScheduledCount, overdueDueCount: overdueDueCount, expiredLeafCandidates: expiredLeafCandidates, expiredRecoveryCandidates: expiredRecoveryCandidates, onMoveExpiredToSelectedWeek: handleMoveExpiredToSelectedWeek, onMoveExpiredToBacklog: handleMoveExpiredToBacklog, scheduleBulkBusy: scheduleBulkBusy, globalWeekStartsOn: globalWeekStartsOn, resolvedLocale: resolvedLocale, todayDateOnly: todayDateOnly, scheduleBaseTasks: scheduleBaseTasks, scheduleWeekStart: scheduleWeekStart, scheduleWeekLabel: scheduleWeekLabel }))] })), _jsxs(Modal, { isOpen: activeTab === 'add', onClose: handleCloseModal, title: editingTaskId ? (currentTask?.isDeleted ? 'Deleted Task' : 'Edit Task') : 'New Task', size: taskModalFullscreen ? 'full' : 'xl', theme: currentTheme, headerActions: (_jsx("button", { className: "tf-control-icon", onClick: () => setTaskModalFullscreen((value) => !value), title: taskModalFullscreen ? 'Exit full screen' : 'Enter full screen', "aria-label": taskModalFullscreen ? 'Exit full screen' : 'Enter full screen', children: taskModalFullscreen ? _jsx(Minimize2, { size: 18 }) : _jsx(Maximize2, { size: 18 }) })), draggable: !taskModalFullscreen, closeOnOverlayClick: false, children: [_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, handleSetStatus: handleSetStatus, handleArchiveTask: handleArchiveTask, handleUnarchiveTask: (taskId) => {
|
|
3310
|
+
if (currentTask?.isDeleted) {
|
|
3311
|
+
const deletedRecord = deletedTaskRecordByTaskId.get(taskId);
|
|
3312
|
+
if (!deletedRecord)
|
|
3313
|
+
return;
|
|
3314
|
+
void handleRestoreDeletedTask(deletedRecord.id);
|
|
3315
|
+
return;
|
|
3316
|
+
}
|
|
3317
|
+
void handleUnarchive(taskId);
|
|
3318
|
+
}, handleRestoreDeletedTask: currentTask?.deletedRecordId
|
|
3244
3319
|
? (deletedRecordId) => {
|
|
3245
3320
|
void handleRestoreDeletedTask(deletedRecordId);
|
|
3246
3321
|
}
|
|
@@ -3253,7 +3328,7 @@ export function StandaloneLayout(props) {
|
|
|
3253
3328
|
resetForm();
|
|
3254
3329
|
setActiveTab('tasks');
|
|
3255
3330
|
}
|
|
3256
|
-
: undefined }), _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: (id, val) => setFormTaxonomies(prev => ({ ...prev, [id]: val })), taxonomies: taxonomies, comments: comments, newCommentText: newCommentText, contextFiles: attachments, currentWorkspaceId: currentWorkspaceId, apiBaseUrl: 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) => {
|
|
3331
|
+
: undefined }), _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: (id, val) => setFormTaxonomies(prev => ({ ...prev, [id]: val })), taxonomies: taxonomies, comments: comments, newCommentText: newCommentText, contextFiles: attachments, currentWorkspaceId: currentWorkspaceId, apiBaseUrl: 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) => {
|
|
3257
3332
|
setSettingsSection(section);
|
|
3258
3333
|
setActiveTab('settings');
|
|
3259
3334
|
}, onSubmit: handleSubmit, commentsEndRef: commentsEndRef, onAddComment: () => handleAddComment(newCommentText), onOpenTaskById: handleOpenTaskById, onAddContextFile: (file) => {
|
|
@@ -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');
|
|
@@ -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;
|
|
@@ -5,7 +5,7 @@ export interface PlanEntitlementServiceDeps {
|
|
|
5
5
|
readGlobalConfig: () => GlobalTaskforceSettings;
|
|
6
6
|
writeGlobalConfig: (config: GlobalTaskforceSettings) => void;
|
|
7
7
|
}
|
|
8
|
-
type CommercialStateTransitionSource = 'registration' | 'checkout-start' | 'checkout-confirm' | 'stripe-webhook' | 'backfill' | 'legacy-plan-mode' | 'admin' | 'system';
|
|
8
|
+
type CommercialStateTransitionSource = 'registration' | 'invite-accept' | 'checkout-start' | 'checkout-confirm' | 'stripe-webhook' | 'backfill' | 'legacy-plan-mode' | 'admin' | 'system';
|
|
9
9
|
/**
|
|
10
10
|
* Handles all plan catalog, plan version, and account entitlement logic.
|
|
11
11
|
* Extracted from Taskforce.ts to reduce monolith size.
|
|
@@ -80,6 +80,7 @@ export declare class PlanEntitlementService {
|
|
|
80
80
|
private getActiveDefaultPlanVersions;
|
|
81
81
|
private getDefaultPlanVersionByMode;
|
|
82
82
|
private tryGetDefaultPlanVersionByMode;
|
|
83
|
+
private tryGetImplicitFreeOnboardingPlanVersion;
|
|
83
84
|
getOnboardingPolicy(): OnboardingPolicy;
|
|
84
85
|
validateOnboardingPolicy(input?: Partial<OnboardingPolicy>): OnboardingPolicyValidationResult;
|
|
85
86
|
resolveRegistrationOnboardingOutcome(selectedPlanVersionIdRaw?: string): {
|
|
@@ -92,6 +93,7 @@ export declare class PlanEntitlementService {
|
|
|
92
93
|
createUserBillingRecord(userId: string, options?: {
|
|
93
94
|
entitlementState?: string;
|
|
94
95
|
planId?: string | null;
|
|
96
|
+
effectiveUntil?: string | null;
|
|
95
97
|
}): void;
|
|
96
98
|
getUserEntitlement(userId: string): {
|
|
97
99
|
entitlementState: string;
|
|
@@ -152,7 +152,8 @@ export class PlanEntitlementService {
|
|
|
152
152
|
`).run('pending_plan_selection', 'checkout-expired', now, this.tenantId, accountId);
|
|
153
153
|
this.createUserBillingRecord(accountId, {
|
|
154
154
|
entitlementState: 'pending_plan_selection',
|
|
155
|
-
planId: null
|
|
155
|
+
planId: null,
|
|
156
|
+
effectiveUntil: null
|
|
156
157
|
});
|
|
157
158
|
return this.readAccountEntitlementRow(accountId);
|
|
158
159
|
}
|
|
@@ -442,7 +443,8 @@ export class PlanEntitlementService {
|
|
|
442
443
|
entitlementState: nextState,
|
|
443
444
|
planId: input.billingMirrorPlanId !== undefined
|
|
444
445
|
? input.billingMirrorPlanId
|
|
445
|
-
: (nextState === 'pending_plan_selection' ? null : planVersion.planId)
|
|
446
|
+
: (nextState === 'pending_plan_selection' ? null : planVersion.planId),
|
|
447
|
+
effectiveUntil: input.effectiveUntil || null
|
|
446
448
|
});
|
|
447
449
|
return this.getAccountEntitlement(accountId);
|
|
448
450
|
}
|
|
@@ -627,18 +629,58 @@ export class PlanEntitlementService {
|
|
|
627
629
|
throw error;
|
|
628
630
|
}
|
|
629
631
|
}
|
|
632
|
+
tryGetImplicitFreeOnboardingPlanVersion() {
|
|
633
|
+
const rows = this.db.prepare(`
|
|
634
|
+
SELECT DISTINCT v.plan_version_id
|
|
635
|
+
FROM plan_versions v
|
|
636
|
+
JOIN plan_catalog p
|
|
637
|
+
ON p.tenant_id = v.tenant_id
|
|
638
|
+
AND p.plan_id = v.plan_id
|
|
639
|
+
JOIN billing_price_mappings bpm
|
|
640
|
+
ON bpm.tenant_id = v.tenant_id
|
|
641
|
+
AND bpm.plan_version_id = v.plan_version_id
|
|
642
|
+
WHERE v.tenant_id = ?
|
|
643
|
+
AND COALESCE(p.status, 'active') = 'active'
|
|
644
|
+
AND bpm.active = 1
|
|
645
|
+
AND bpm.pricing_type = 'free'
|
|
646
|
+
ORDER BY v.is_default DESC, p.updated_at DESC, p.created_at DESC, v.version_number DESC, v.created_at DESC
|
|
647
|
+
`).all(this.tenantId);
|
|
648
|
+
for (const row of rows) {
|
|
649
|
+
const planVersionId = String(row.plan_version_id || '').trim();
|
|
650
|
+
if (!planVersionId)
|
|
651
|
+
continue;
|
|
652
|
+
const version = this.getPlanVersionSnapshot(planVersionId);
|
|
653
|
+
if (!this.isTeamPlanVersion(version)) {
|
|
654
|
+
return version;
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
return null;
|
|
658
|
+
}
|
|
630
659
|
getOnboardingPolicy() {
|
|
631
660
|
const billing = this.deps.readGlobalConfig().billing || {};
|
|
632
661
|
const policy = billing.onboardingPolicy && typeof billing.onboardingPolicy === 'object'
|
|
633
662
|
? billing.onboardingPolicy
|
|
634
663
|
: null;
|
|
664
|
+
if (policy?.mode === 'require_plan_selection'
|
|
665
|
+
|| policy?.mode === 'auto_grant_free'
|
|
666
|
+
|| policy?.mode === 'auto_start_trial') {
|
|
667
|
+
return {
|
|
668
|
+
mode: policy.mode,
|
|
669
|
+
planVersionId: typeof policy.planVersionId === 'string'
|
|
670
|
+
? (policy.planVersionId.trim() || null)
|
|
671
|
+
: null
|
|
672
|
+
};
|
|
673
|
+
}
|
|
674
|
+
const implicitFreeVersion = this.tryGetImplicitFreeOnboardingPlanVersion();
|
|
675
|
+
if (implicitFreeVersion) {
|
|
676
|
+
return {
|
|
677
|
+
mode: 'auto_grant_free',
|
|
678
|
+
planVersionId: implicitFreeVersion.planVersionId
|
|
679
|
+
};
|
|
680
|
+
}
|
|
635
681
|
return {
|
|
636
|
-
mode:
|
|
637
|
-
|
|
638
|
-
: 'require_plan_selection',
|
|
639
|
-
planVersionId: typeof policy?.planVersionId === 'string'
|
|
640
|
-
? (policy.planVersionId.trim() || null)
|
|
641
|
-
: null
|
|
682
|
+
mode: 'require_plan_selection',
|
|
683
|
+
planVersionId: null
|
|
642
684
|
};
|
|
643
685
|
}
|
|
644
686
|
validateOnboardingPolicy(input) {
|
|
@@ -761,14 +803,22 @@ export class PlanEntitlementService {
|
|
|
761
803
|
const planId = typeof options?.planId === 'string'
|
|
762
804
|
? options.planId.trim().toLowerCase()
|
|
763
805
|
: '';
|
|
806
|
+
const shouldUpdateEffectiveUntil = options ? Object.prototype.hasOwnProperty.call(options, 'effectiveUntil') : false;
|
|
807
|
+
const effectiveUntil = shouldUpdateEffectiveUntil
|
|
808
|
+
? (typeof options?.effectiveUntil === 'string' ? options.effectiveUntil : null)
|
|
809
|
+
: null;
|
|
764
810
|
this.db.prepare(`
|
|
765
|
-
INSERT INTO user_billing (user_id, tenant_id, entitlement_state, tier, created_at, updated_at)
|
|
766
|
-
VALUES (?, ?, ?, ?, ?, ?)
|
|
811
|
+
INSERT INTO user_billing (user_id, tenant_id, entitlement_state, tier, effective_until, created_at, updated_at)
|
|
812
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
767
813
|
ON CONFLICT (user_id) DO UPDATE SET
|
|
768
814
|
entitlement_state = excluded.entitlement_state,
|
|
769
815
|
tier = excluded.tier,
|
|
816
|
+
effective_until = CASE
|
|
817
|
+
WHEN ? THEN excluded.effective_until
|
|
818
|
+
ELSE user_billing.effective_until
|
|
819
|
+
END,
|
|
770
820
|
updated_at = excluded.updated_at
|
|
771
|
-
`).run(userId, this.tenantId, entitlementState, planId, now, now);
|
|
821
|
+
`).run(userId, this.tenantId, entitlementState, planId, effectiveUntil, now, now, shouldUpdateEffectiveUntil ? 1 : 0);
|
|
772
822
|
}
|
|
773
823
|
getUserEntitlement(userId) {
|
|
774
824
|
const account = this.getAccountEntitlement(userId);
|
|
@@ -22,7 +22,12 @@ function openCore(projectRoot) {
|
|
|
22
22
|
storagePath: path.join(projectRoot, '.taskforce'),
|
|
23
23
|
tenantId: path.basename(projectRoot)
|
|
24
24
|
});
|
|
25
|
-
core.upsertPlanCatalog({ planId: 'starter', displayName: 'Starter' });
|
|
25
|
+
core.upsertPlanCatalog({ planId: 'starter', displayName: 'Starter', status: 'active' });
|
|
26
|
+
core.updatePlanVersion({
|
|
27
|
+
planId: 'starter',
|
|
28
|
+
versionNumber: 1,
|
|
29
|
+
seatLimit: 1
|
|
30
|
+
});
|
|
26
31
|
core.updatePlanVersionFeatures({
|
|
27
32
|
planVersionId: 'starter-v1',
|
|
28
33
|
features: [
|
|
@@ -33,9 +38,39 @@ function openCore(projectRoot) {
|
|
|
33
38
|
return core;
|
|
34
39
|
}
|
|
35
40
|
describe('TaskforceCore onboarding policy', () => {
|
|
36
|
-
it('defaults to
|
|
41
|
+
it('defaults to auto_grant_free when a free personal plan version is available', () => {
|
|
37
42
|
const projectRoot = makeProjectRoot();
|
|
38
43
|
const core = openCore(projectRoot);
|
|
44
|
+
try {
|
|
45
|
+
expect(core.getOnboardingPolicy()).toEqual({
|
|
46
|
+
mode: 'auto_grant_free',
|
|
47
|
+
planVersionId: 'starter-v1'
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
finally {
|
|
51
|
+
core.close();
|
|
52
|
+
fs.rmSync(projectRoot, { recursive: true, force: true });
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
it('falls back to require_plan_selection when no free onboarding plan is available', () => {
|
|
56
|
+
const projectRoot = makeProjectRoot();
|
|
57
|
+
const core = new TaskforceCore({
|
|
58
|
+
projectRoot,
|
|
59
|
+
storagePath: path.join(projectRoot, '.taskforce'),
|
|
60
|
+
tenantId: path.basename(projectRoot)
|
|
61
|
+
});
|
|
62
|
+
core.upsertPlanCatalog({ planId: 'starter', displayName: 'Starter', status: 'active' });
|
|
63
|
+
core.updatePlanVersion({
|
|
64
|
+
planId: 'starter',
|
|
65
|
+
versionNumber: 1,
|
|
66
|
+
seatLimit: 1
|
|
67
|
+
});
|
|
68
|
+
core.updatePlanVersionFeatures({
|
|
69
|
+
planVersionId: 'starter-v1',
|
|
70
|
+
features: [
|
|
71
|
+
{ featureKey: 'workspace.switching', access: 'enabled' }
|
|
72
|
+
]
|
|
73
|
+
});
|
|
39
74
|
try {
|
|
40
75
|
expect(core.getOnboardingPolicy()).toEqual({
|
|
41
76
|
mode: 'require_plan_selection',
|
|
@@ -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 { Comment, TaskActivityItem, TaskEvent, TaskEventDetails } from './Taskforce.js';
|
|
3
4
|
export interface TaskActivityServiceDeps {
|
|
4
5
|
db: DatabaseAdapter;
|
|
5
6
|
tenantId: string;
|
|
@@ -7,6 +8,7 @@ export interface TaskActivityServiceDeps {
|
|
|
7
8
|
normalizeWorkspaceId: (workspaceId: string | undefined) => string;
|
|
8
9
|
resolveWorkspaceActor: (workspaceId: string, actorRef: unknown) => TaskActor | null;
|
|
9
10
|
}
|
|
11
|
+
export declare function listWorkspaceTaskEventsService(deps: TaskActivityServiceDeps, workspaceId: string): TaskEvent[];
|
|
10
12
|
export declare function replaceTaskCommentsService(deps: TaskActivityServiceDeps, taskId: string, workspaceId: string, comments: Comment[]): void;
|
|
11
13
|
export declare function listTaskEventsInternalService(deps: TaskActivityServiceDeps, taskId: string, workspaceId: string): TaskEvent[];
|
|
12
14
|
export declare function buildTaskActivityService(comments: Comment[], taskEvents: TaskEvent[]): TaskActivityItem[];
|
|
@@ -19,3 +21,4 @@ export declare function addTaskEventService(deps: TaskActivityServiceDeps, input
|
|
|
19
21
|
details?: TaskEventDetails;
|
|
20
22
|
}): void;
|
|
21
23
|
export declare function replaceTaskEventsService(deps: TaskActivityServiceDeps, taskId: string, workspaceId: string, taskEvents: TaskEvent[]): void;
|
|
24
|
+
export declare function upsertTaskEventForSyncService(deps: TaskActivityServiceDeps, event: TaskEvent): TaskEvent;
|
|
@@ -39,6 +39,17 @@ function mapTaskEventRow(deps, row) {
|
|
|
39
39
|
actorProfile: actor === 'system' ? null : deps.resolveWorkspaceActor(workspaceId, actor)
|
|
40
40
|
};
|
|
41
41
|
}
|
|
42
|
+
export function listWorkspaceTaskEventsService(deps, workspaceId) {
|
|
43
|
+
const normalizedWorkspaceId = deps.normalizeWorkspaceId(workspaceId);
|
|
44
|
+
const rows = deps.db.prepare(`
|
|
45
|
+
SELECT id, task_id, workspace_id, action, actor, actor_type, details_json, created_at
|
|
46
|
+
FROM task_events
|
|
47
|
+
WHERE tenant_id = ?
|
|
48
|
+
AND workspace_id = ?
|
|
49
|
+
ORDER BY created_at ASC, id ASC
|
|
50
|
+
`).all(deps.tenantId, normalizedWorkspaceId);
|
|
51
|
+
return rows.map((row) => mapTaskEventRow(deps, row));
|
|
52
|
+
}
|
|
42
53
|
export function replaceTaskCommentsService(deps, taskId, workspaceId, comments) {
|
|
43
54
|
const normalizedWorkspaceId = deps.normalizeWorkspaceId(workspaceId);
|
|
44
55
|
deps.db.prepare('DELETE FROM comments WHERE tenant_id = ? AND workspace_id = ? AND task_id = ?')
|
|
@@ -128,3 +139,37 @@ export function replaceTaskEventsService(deps, taskId, workspaceId, taskEvents)
|
|
|
128
139
|
insertTaskEvent.run(event.id, deps.tenantId, normalizedWorkspaceId, taskId, event.action, event.actor, event.actorType, JSON.stringify(normalizeTaskEventDetails(event.details)), event.createdAt);
|
|
129
140
|
}
|
|
130
141
|
}
|
|
142
|
+
export function upsertTaskEventForSyncService(deps, event) {
|
|
143
|
+
const workspaceId = deps.normalizeWorkspaceId(event.workspaceId);
|
|
144
|
+
const normalizedEvent = {
|
|
145
|
+
id: String(event.id || '').trim(),
|
|
146
|
+
taskId: String(event.taskId || '').trim(),
|
|
147
|
+
workspaceId,
|
|
148
|
+
action: String(event.action || '').trim(),
|
|
149
|
+
actor: String(event.actor || 'system').trim() || 'system',
|
|
150
|
+
actorType: event.actorType === 'ai' || event.actorType === 'human' || event.actorType === 'system'
|
|
151
|
+
? event.actorType
|
|
152
|
+
: 'system',
|
|
153
|
+
createdAt: String(event.createdAt || '').trim() || new Date().toISOString(),
|
|
154
|
+
details: normalizeTaskEventDetails(event.details)
|
|
155
|
+
};
|
|
156
|
+
deps.db.prepare(`
|
|
157
|
+
INSERT INTO task_events (
|
|
158
|
+
id, tenant_id, workspace_id, task_id, action, actor, actor_type, details_json, created_at
|
|
159
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
160
|
+
ON CONFLICT (id) DO UPDATE SET
|
|
161
|
+
workspace_id = excluded.workspace_id,
|
|
162
|
+
task_id = excluded.task_id,
|
|
163
|
+
action = excluded.action,
|
|
164
|
+
actor = excluded.actor,
|
|
165
|
+
actor_type = excluded.actor_type,
|
|
166
|
+
details_json = excluded.details_json,
|
|
167
|
+
created_at = excluded.created_at
|
|
168
|
+
`).run(normalizedEvent.id, deps.tenantId, workspaceId, normalizedEvent.taskId, normalizedEvent.action, normalizedEvent.actor, normalizedEvent.actorType, JSON.stringify(normalizedEvent.details), normalizedEvent.createdAt);
|
|
169
|
+
return {
|
|
170
|
+
...normalizedEvent,
|
|
171
|
+
actorProfile: normalizedEvent.actor === 'system'
|
|
172
|
+
? null
|
|
173
|
+
: deps.resolveWorkspaceActor(workspaceId, normalizedEvent.actor)
|
|
174
|
+
};
|
|
175
|
+
}
|