@taskforcehq/taskforce 0.3.304 → 0.3.305
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +140 -247
- package/dist/TaskforceCore.js +165 -70
- package/dist/TaskforceCore.test.js +799 -90
- package/dist/compat/workspaceSyncCompat.js +6 -0
- package/dist/components/features/DocumentIndex.d.ts +1 -1
- package/dist/components/features/DocumentIndex.js +1 -1
- package/dist/components/features/DocumentViewer.d.ts +1 -1
- package/dist/components/features/DocumentViewer.js +32 -9
- package/dist/components/features/DocumentWorkspace.d.ts +5 -22
- package/dist/components/features/DocumentWorkspace.js +12 -155
- package/dist/components/features/DocumentWorkspace.test.js +193 -6
- package/dist/components/features/TaskSettings.js +29 -3
- package/dist/components/features/TaskSettings.test.js +42 -1
- package/dist/components/features/documentWorkspaceModel.d.ts +24 -0
- package/dist/components/features/documentWorkspaceModel.js +57 -0
- package/dist/components/features/useDocumentWorkspaceController.d.ts +34 -0
- package/dist/components/features/useDocumentWorkspaceController.js +113 -0
- package/dist/components/task/TaskForm.d.ts +8 -8
- package/dist/components/task/TaskForm.js +358 -221
- package/dist/components/task/TaskForm.test.js +456 -381
- package/dist/components/task/TaskKanban.js +2 -1
- package/dist/components/views/PlanComparisonPage.js +5 -8
- package/dist/components/views/PlanComparisonPage.test.js +51 -41
- package/dist/components/views/PlansPage.js +126 -84
- package/dist/components/views/PlansPage.test.js +308 -39
- package/dist/components/views/StandaloneLayout.js +28 -22
- package/dist/components/views/panels/FilterToolbar.test.js +6 -4
- package/dist/components/views/standalone/modals/AccountHubModal.d.ts +2 -14
- package/dist/components/views/standalone/modals/AccountHubModal.js +12 -14
- package/dist/components/views/standalone/modals/AccountHubModal.test.js +24 -1
- package/dist/config/envSchema.js +1 -1
- package/dist/core/AiProfileService.d.ts +6 -1
- package/dist/core/AiProfileService.js +161 -16
- package/dist/core/AiProfileService.test.js +3 -1
- package/dist/core/AiProfiles.test.js +200 -0
- package/dist/core/AuthTokenService.test.d.ts +1 -0
- package/dist/core/AuthTokenService.test.js +78 -0
- package/dist/core/EntitlementsPolicy.test.js +75 -13
- package/dist/core/PlanEntitlementService.d.ts +7 -1
- package/dist/core/PlanEntitlementService.js +117 -42
- package/dist/core/PlanVersionPolicy.test.js +19 -26
- package/dist/core/SignupMonetizationSettings.test.js +46 -21
- package/dist/core/SystemAdmin.test.js +212 -56
- package/dist/core/TaskTaxonomyValidation.test.js +53 -0
- package/dist/core/Taskforce.d.ts +3 -0
- package/dist/core/Taskforce.js +50 -10
- package/dist/core/WorkspacePlanMode.test.js +12 -12
- package/dist/core/shared.d.ts +2 -0
- package/dist/core/shared.js +11 -0
- package/dist/hooks/sync/auth.js +3 -1
- package/dist/hooks/sync/transfers.d.ts +2 -1
- package/dist/hooks/sync/transfers.js +8 -4
- package/dist/hooks/useSyncOrchestrator.d.ts +7 -4
- package/dist/hooks/useSyncOrchestrator.js +75 -7
- package/dist/hooks/useSyncOrchestrator.retry-closure.test.js +80 -10
- package/dist/hooks/useTaskMutations.d.ts +2 -1
- package/dist/hooks/useTaskMutations.js +12 -1
- package/dist/hooks/useTaskMutations.test.js +30 -1
- package/dist/hooks/useTaskforce.d.ts +5 -2
- package/dist/hooks/useTaskforce.js +17 -5
- package/dist/hooks/useTaskforce.sync-behavior.test.js +78 -0
- package/dist/hooks/useWorkspaceSyncController.d.ts +3 -1
- package/dist/hooks/useWorkspaceSyncController.js +11 -1
- package/dist/hooks/useWorkspaceSyncController.test.js +1 -0
- package/dist/mcp/adminWorkspaceRegistrar.d.ts +2 -0
- package/dist/mcp/adminWorkspaceRegistrar.js +154 -0
- package/dist/mcp/collaborationRegistrar.d.ts +2 -0
- package/dist/mcp/collaborationRegistrar.js +71 -0
- package/dist/mcp/documentAssetRegistrar.d.ts +2 -0
- package/dist/mcp/documentAssetRegistrar.js +346 -0
- package/dist/mcp/runtime.js +2362 -3530
- package/dist/mcp/runtime.test.js +159 -0
- package/dist/mcp/taskPlanningRegistrar.d.ts +2 -0
- package/dist/mcp/taskPlanningRegistrar.js +418 -0
- package/dist/mcp/toolCatalog.d.ts +35 -0
- package/dist/mcp/toolCatalog.js +3 -0
- package/dist/server/routes/admin.d.ts +1 -5
- package/dist/server/routes/admin.js +138 -61
- package/dist/server/routes/annotatedAttachments.d.ts +1 -1
- package/dist/server/routes/annotatedAttachments.js +1 -1
- package/dist/server/routes/auth.d.ts +1 -4
- package/dist/server/routes/auth.js +23 -63
- package/dist/server/routes/authSupport.d.ts +30 -0
- package/dist/server/routes/authSupport.js +81 -0
- package/dist/server/routes/billing.d.ts +1 -1
- package/dist/server/routes/billing.js +104 -188
- package/dist/server/routes/billing.test.js +214 -86
- package/dist/server/routes/documentReviews.d.ts +1 -1
- package/dist/server/routes/documentReviews.js +1 -1
- package/dist/server/routes/documents.d.ts +4 -2
- package/dist/server/routes/documents.js +73 -7
- package/dist/server/routes/primitives.d.ts +11 -0
- package/dist/server/routes/primitives.js +73 -0
- package/dist/server/routes/resources.d.ts +1 -1
- package/dist/server/routes/resources.js +1 -1
- package/dist/server/routes/shared.d.ts +1 -1
- package/dist/server/routes/sync.d.ts +1 -1
- package/dist/server/routes/sync.js +1 -1
- package/dist/server/routes/tasks.d.ts +1 -1
- package/dist/server/routes/tasks.js +1 -1
- package/dist/server/routes/workspaces.d.ts +1 -1
- package/dist/server/routes/workspaces.js +1 -1
- package/dist/server/routes.d.ts +2 -9
- package/dist/server/routes.js +76 -162
- package/dist/server/routes.test.js +672 -30
- package/dist/sync/contentSyncState.js +16 -7
- package/dist/sync/syncApplyHandlers.test.js +2 -0
- package/dist/sync/syncService.d.ts +1 -0
- package/dist/sync/syncService.js +8 -0
- package/dist/sync/syncService.test.js +14 -0
- package/dist/sync/workspaceSyncModel.d.ts +3 -0
- package/dist/sync/workspaceSyncModel.js +12 -3
- package/dist/sync/workspaceSyncModel.test.js +34 -0
- package/dist/sync/workspaceSyncState.d.ts +2 -0
- package/dist/sync/workspaceSyncState.js +1 -0
- package/dist/ui/assets/{AgentsModule-BLWVbuKP.js → AgentsModule-N2MnQBZk.js} +1 -1
- package/dist/ui/assets/{AnnotatedAttachmentWorkspace-BlS-cqnl.js → AnnotatedAttachmentWorkspace-BG6P_GVW.js} +1 -1
- package/dist/ui/assets/DocumentWorkspace-B3nV_Gc5.css +1 -0
- package/dist/ui/assets/DocumentWorkspace-DRbDMwh8.js +252 -0
- package/dist/ui/assets/{InitiativesModule-BjR6iBf9.js → InitiativesModule-Dhm7M8e7.js} +1 -1
- package/dist/ui/assets/PlansPage-Cq0zXj3I.js +1 -0
- package/dist/ui/assets/TaskSettings-ln0aF7xc.js +9 -0
- package/dist/ui/assets/{WorkflowsModule-DnFqdUME.js → WorkflowsModule-BcS4afRn.js} +1 -1
- package/dist/ui/assets/{index-Ii0B0rTO.css → index-C2-OXZV7.css} +1 -1
- package/dist/ui/assets/index-DcSI5F86.js +6 -0
- package/dist/ui/assets/{vendor-icons-I7ZwG1z_.js → vendor-icons-BSUaRwF8.js} +1 -1
- package/dist/ui/index.html +3 -3
- package/dist/utils/commercialLifecyclePresentation.d.ts +19 -0
- package/dist/utils/commercialLifecyclePresentation.js +196 -0
- package/package.json +8 -1
- package/scripts/check-cloud-mcp.mjs +83 -0
- package/scripts/playwright-auth.sh +5 -1
- package/scripts/run-billing-performance-smoke.sh +24 -0
- package/dist/ui/assets/DocumentWorkspace-BH_6DF6x.js +0 -250
- package/dist/ui/assets/DocumentWorkspace-C_8T8oz-.css +0 -1
- package/dist/ui/assets/PlansPage-CSDQ4sLa.js +0 -1
- package/dist/ui/assets/TaskSettings-CEDy34Jo.js +0 -9
- package/dist/ui/assets/index-DXUdtH1B.js +0 -6
package/dist/TaskforceCore.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import React, { useCallback, useEffect, useMemo, useState } from 'react';
|
|
2
|
+
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
3
3
|
import { MemoryRouter, Navigate, useInRouterContext, useLocation, useNavigate } from 'react-router-dom';
|
|
4
4
|
import { useTaskforce } from './hooks/useTaskforce';
|
|
5
5
|
import { WidgetView } from './components/views/WidgetView';
|
|
@@ -92,6 +92,7 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
92
92
|
const [syncNewLocalWorkspaceToCloud, setSyncNewLocalWorkspaceToCloud] = useState(false);
|
|
93
93
|
const [pendingCloudWorkspaceImport, setPendingCloudWorkspaceImport] = useState(null);
|
|
94
94
|
const [cloudWorkspaceImportFinalizing, setCloudWorkspaceImportFinalizing] = useState(false);
|
|
95
|
+
const cloudWorkspaceImportFinalizingRef = useRef(false);
|
|
95
96
|
const fallbackSetupLibraryPacks = useMemo(() => listBuiltInTaxonomyLibraryPacks(), []);
|
|
96
97
|
const [setupLibraryPacks, setSetupLibraryPacks] = useState(fallbackSetupLibraryPacks);
|
|
97
98
|
const [selectedSetupPackId, setSelectedSetupPackId] = useState(() => (fallbackSetupLibraryPacks.find((pack) => pack.isDefaultStarter)?.id || fallbackSetupLibraryPacks[0]?.id || ''));
|
|
@@ -108,7 +109,24 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
108
109
|
onTaskCountChange,
|
|
109
110
|
onClose
|
|
110
111
|
});
|
|
111
|
-
const { runtimeMode, workspaceSwitchingEnabled, currentWorkspaceId, configLoaded, cloudAuthConfigured, authRequiredForApi, authBlocked, isAuthenticated, hasBetaAccess, authSessionResolved, workspaceBootstrapPending, bootstrapPhase, bootstrapError, setupState, runtimeCapabilities, refreshSetupContext, retryBootstrapChecks, createWorkspace, saveWorkspaceProfile, fetchWorkspaces, applyWorkspaceSyncStateSnapshot, workspaceCloudSyncEnabled, workspaceSyncPhase, workspaceSyncSummary, workspaceSyncRecommendedAction, workspaceSyncBusy, workspaceLastErrorMessage, retryWorkspaceCloudSync, settingsModel, fetchTasks, loginWithCredentials, registerWithCredentials, requestEmailVerification, confirmEmailVerification, requestPasswordReset, confirmPasswordReset, inspectInviteAcceptance, acceptInviteWithToken, joinInviteWithToken, authUserEmail, currentTheme, logout } = taskforceState;
|
|
112
|
+
const { runtimeMode, workspaceSwitchingEnabled, currentWorkspaceId, configLoaded, cloudAuthConfigured, authRequiredForApi, authBlocked, isAuthenticated, hasBetaAccess, authSessionResolved, workspaceBootstrapPending, bootstrapPhase, bootstrapError, setupState, runtimeCapabilities, refreshSetupContext, retryBootstrapChecks, createWorkspace, saveWorkspaceProfile, fetchWorkspaces, applyWorkspaceSyncStateSnapshot, workspaceCloudSyncEnabled, workspaceSyncPhase, workspaceSyncSetupIntent, workspaceSyncSummary, workspaceSyncRecommendedAction, workspaceSyncBusy, workspaceLastErrorMessage, retryWorkspaceCloudSync, settingsModel, fetchTasks, loginWithCredentials, registerWithCredentials, requestEmailVerification, confirmEmailVerification, requestPasswordReset, confirmPasswordReset, inspectInviteAcceptance, acceptInviteWithToken, joinInviteWithToken, authUserEmail, currentTheme, logout } = taskforceState;
|
|
113
|
+
const persistWorkspaceSyncStatePatch = useCallback(async (workspaceId, patch) => {
|
|
114
|
+
const response = await fetch('/api/taskforce/ui-state', {
|
|
115
|
+
method: 'POST',
|
|
116
|
+
headers: { 'Content-Type': 'application/json' },
|
|
117
|
+
credentials: 'include',
|
|
118
|
+
body: JSON.stringify({
|
|
119
|
+
workspaceId,
|
|
120
|
+
stateKey: 'workspace-sync',
|
|
121
|
+
patch
|
|
122
|
+
})
|
|
123
|
+
});
|
|
124
|
+
const payload = await response.json().catch(() => ({}));
|
|
125
|
+
if (!response.ok || payload?.success === false) {
|
|
126
|
+
throw new Error(payload?.error || `Failed to configure workspace sync (${response.status})`);
|
|
127
|
+
}
|
|
128
|
+
return payload;
|
|
129
|
+
}, []);
|
|
112
130
|
const selectedSetupPack = useMemo(() => {
|
|
113
131
|
return setupLibraryPacks.find((pack) => pack.id === selectedSetupPackId)
|
|
114
132
|
|| setupLibraryPacks.find((pack) => pack.isDefaultStarter)
|
|
@@ -131,20 +149,35 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
131
149
|
const isLoginRoute = location.pathname === '/login';
|
|
132
150
|
const isSetupRoute = location.pathname === '/setup';
|
|
133
151
|
const isComingSoonRoute = location.pathname === '/coming-soon';
|
|
152
|
+
const cloudWorkspaceSelectionPath = '/setup?step=workspace&source=cloud&postAuth=select-cloud-workspace';
|
|
134
153
|
const wantsCloudWorkspaceSetup = !isCloudRuntime && workspaceSetupSource === 'cloud';
|
|
135
154
|
const isCloudWorkspaceSetupFlow = wantsCloudWorkspaceSetup && canUseCloudWorkspaceSetup;
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
155
|
+
const activeCloudWorkspaceImport = useMemo(() => {
|
|
156
|
+
if (pendingCloudWorkspaceImport)
|
|
157
|
+
return pendingCloudWorkspaceImport;
|
|
158
|
+
if (workspaceSyncSetupIntent !== 'attach-cloud-import')
|
|
159
|
+
return null;
|
|
160
|
+
const workspaceId = String(currentWorkspaceId || '').trim();
|
|
161
|
+
if (!workspaceId || workspaceId.toLowerCase() === 'default')
|
|
162
|
+
return null;
|
|
163
|
+
const matchingWorkspace = cloudWorkspaceOptions.find((workspace) => workspace.id === workspaceId);
|
|
164
|
+
return {
|
|
165
|
+
workspaceId,
|
|
166
|
+
name: matchingWorkspace?.name || workspaceId
|
|
167
|
+
};
|
|
168
|
+
}, [cloudWorkspaceOptions, currentWorkspaceId, pendingCloudWorkspaceImport, workspaceSyncSetupIntent]);
|
|
169
|
+
const cloudWorkspaceImportMatchesCurrentWorkspace = Boolean(activeCloudWorkspaceImport
|
|
170
|
+
&& currentWorkspaceId === activeCloudWorkspaceImport.workspaceId);
|
|
171
|
+
const isCloudWorkspaceImportErrored = Boolean(activeCloudWorkspaceImport
|
|
139
172
|
&& cloudWorkspaceImportMatchesCurrentWorkspace
|
|
140
173
|
&& workspaceSyncPhase === 'error');
|
|
141
|
-
const isCloudWorkspaceImportReady = Boolean(
|
|
174
|
+
const isCloudWorkspaceImportReady = Boolean(activeCloudWorkspaceImport
|
|
142
175
|
&& cloudWorkspaceImportMatchesCurrentWorkspace
|
|
143
176
|
&& workspaceCloudSyncEnabled
|
|
144
177
|
&& workspaceSyncPhase === 'active'
|
|
145
178
|
&& !workspaceSyncBusy);
|
|
146
179
|
const cloudWorkspaceImportProgressLines = useMemo(() => {
|
|
147
|
-
if (!
|
|
180
|
+
if (!activeCloudWorkspaceImport)
|
|
148
181
|
return [];
|
|
149
182
|
const connectingState = setupBusy ? 'In progress' : 'Complete';
|
|
150
183
|
const importingState = isCloudWorkspaceImportErrored
|
|
@@ -163,7 +196,7 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
163
196
|
`Open local workspace: ${finishingState}`
|
|
164
197
|
];
|
|
165
198
|
}, [
|
|
166
|
-
|
|
199
|
+
activeCloudWorkspaceImport,
|
|
167
200
|
setupBusy,
|
|
168
201
|
isCloudWorkspaceImportErrored,
|
|
169
202
|
workspaceSyncBusy,
|
|
@@ -340,6 +373,11 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
340
373
|
if (!isSetupRoute || setupStep !== 'workspace')
|
|
341
374
|
return;
|
|
342
375
|
setWorkspaceSetupPage('details');
|
|
376
|
+
if (activeCloudWorkspaceImport && canUseCloudWorkspaceSetup) {
|
|
377
|
+
setWorkspaceSetupSource('cloud');
|
|
378
|
+
setSyncNewLocalWorkspaceToCloud(false);
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
343
381
|
const source = String(new URLSearchParams(location.search).get('source') || '').trim().toLowerCase();
|
|
344
382
|
if (source === 'cloud' && canUseCloudWorkspaceSetup) {
|
|
345
383
|
setWorkspaceSetupSource('cloud');
|
|
@@ -347,7 +385,7 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
347
385
|
return;
|
|
348
386
|
}
|
|
349
387
|
setWorkspaceSetupSource('local');
|
|
350
|
-
}, [canUseCloudWorkspaceSetup, isSetupRoute, location.search, setupStep]);
|
|
388
|
+
}, [activeCloudWorkspaceImport, canUseCloudWorkspaceSetup, isSetupRoute, location.search, setupStep]);
|
|
351
389
|
const fetchCloudWorkspaceOptions = useCallback(async () => {
|
|
352
390
|
if (!canUseCloudWorkspaceSetup || !isAuthenticated) {
|
|
353
391
|
setCloudWorkspaceOptions([]);
|
|
@@ -410,7 +448,7 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
410
448
|
})
|
|
411
449
|
.then(async (res) => {
|
|
412
450
|
if (!res.ok)
|
|
413
|
-
throw new Error(`Failed to load
|
|
451
|
+
throw new Error(`Failed to load setup libraries (${res.status})`);
|
|
414
452
|
const payload = await res.json().catch(() => ({}));
|
|
415
453
|
const nextPacks = Array.isArray(payload?.packs) ? payload.packs : [];
|
|
416
454
|
if (cancelled || nextPacks.length === 0)
|
|
@@ -424,7 +462,7 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
424
462
|
if (cancelled)
|
|
425
463
|
return;
|
|
426
464
|
setSetupLibraryPacks(fallbackSetupLibraryPacks);
|
|
427
|
-
setSetupLibraryError(error instanceof Error ? error.message : 'Failed to load
|
|
465
|
+
setSetupLibraryError(error instanceof Error ? error.message : 'Failed to load setup libraries.');
|
|
428
466
|
})
|
|
429
467
|
.finally(() => {
|
|
430
468
|
if (!cancelled)
|
|
@@ -446,22 +484,24 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
446
484
|
if (!isCloudWorkspaceSetupFlow) {
|
|
447
485
|
setPendingCloudWorkspaceImport(null);
|
|
448
486
|
setCloudWorkspaceImportFinalizing(false);
|
|
487
|
+
cloudWorkspaceImportFinalizingRef.current = false;
|
|
449
488
|
}
|
|
450
489
|
}, [isCloudWorkspaceSetupFlow]);
|
|
451
490
|
useEffect(() => {
|
|
452
|
-
if (!isCloudWorkspaceImportErrored || !
|
|
491
|
+
if (!isCloudWorkspaceImportErrored || !activeCloudWorkspaceImport)
|
|
453
492
|
return;
|
|
454
493
|
setSetupNotice(null);
|
|
455
494
|
setSetupError(workspaceLastErrorMessage
|
|
456
|
-
|| `Failed to import "${
|
|
457
|
-
}, [
|
|
495
|
+
|| `Failed to import "${activeCloudWorkspaceImport.name}" from cloud. Retry import to continue.`);
|
|
496
|
+
}, [activeCloudWorkspaceImport, isCloudWorkspaceImportErrored, workspaceLastErrorMessage]);
|
|
458
497
|
useEffect(() => {
|
|
459
|
-
if (!isCloudWorkspaceImportReady || !
|
|
498
|
+
if (!isCloudWorkspaceImportReady || !activeCloudWorkspaceImport || cloudWorkspaceImportFinalizingRef.current)
|
|
460
499
|
return;
|
|
461
500
|
let cancelled = false;
|
|
501
|
+
cloudWorkspaceImportFinalizingRef.current = true;
|
|
462
502
|
setCloudWorkspaceImportFinalizing(true);
|
|
463
503
|
setSetupError(null);
|
|
464
|
-
setSetupNotice(`Workspace import complete. Opening ${
|
|
504
|
+
setSetupNotice(`Workspace import complete. Opening ${activeCloudWorkspaceImport.name}...`);
|
|
465
505
|
void (async () => {
|
|
466
506
|
try {
|
|
467
507
|
await refreshSetupContext();
|
|
@@ -471,6 +511,12 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
471
511
|
]);
|
|
472
512
|
if (cancelled)
|
|
473
513
|
return;
|
|
514
|
+
await persistWorkspaceSyncStatePatch(activeCloudWorkspaceImport.workspaceId, { setupIntent: null });
|
|
515
|
+
applyWorkspaceSyncStateSnapshot({
|
|
516
|
+
enabled: workspaceCloudSyncEnabled,
|
|
517
|
+
phase: workspaceSyncPhase,
|
|
518
|
+
setupIntent: null
|
|
519
|
+
});
|
|
474
520
|
setPendingCloudWorkspaceImport(null);
|
|
475
521
|
navigate('/', { replace: true });
|
|
476
522
|
}
|
|
@@ -478,20 +524,22 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
478
524
|
if (cancelled)
|
|
479
525
|
return;
|
|
480
526
|
setSetupNotice(null);
|
|
481
|
-
setSetupError(`Imported "${
|
|
527
|
+
setSetupError(`Imported "${activeCloudWorkspaceImport.name}" but failed to finish opening it.`);
|
|
482
528
|
setCloudWorkspaceImportFinalizing(false);
|
|
529
|
+
cloudWorkspaceImportFinalizingRef.current = false;
|
|
483
530
|
}
|
|
484
531
|
})();
|
|
485
532
|
return () => {
|
|
486
533
|
cancelled = true;
|
|
487
534
|
};
|
|
488
535
|
}, [
|
|
489
|
-
|
|
536
|
+
activeCloudWorkspaceImport,
|
|
537
|
+
applyWorkspaceSyncStateSnapshot,
|
|
490
538
|
fetchTasks,
|
|
491
539
|
fetchWorkspaces,
|
|
492
540
|
isCloudWorkspaceImportReady,
|
|
493
541
|
navigate,
|
|
494
|
-
|
|
542
|
+
persistWorkspaceSyncStatePatch,
|
|
495
543
|
refreshSetupContext
|
|
496
544
|
]);
|
|
497
545
|
const nextPath = useMemo(() => {
|
|
@@ -502,7 +550,6 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
502
550
|
return '/';
|
|
503
551
|
return raw;
|
|
504
552
|
}, [location.search]);
|
|
505
|
-
const cloudWorkspaceSelectionPath = '/setup?step=workspace&source=cloud&postAuth=select-cloud-workspace';
|
|
506
553
|
const shouldReturnToCloudWorkspaceSelection = useMemo(() => {
|
|
507
554
|
try {
|
|
508
555
|
const parsed = new URL(nextPath, 'https://taskforce.local');
|
|
@@ -688,6 +735,24 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
688
735
|
const code = result.code || result.state;
|
|
689
736
|
return code ? `[${code}] ${message}` : message;
|
|
690
737
|
};
|
|
738
|
+
const buildVerificationRequestNotice = (input) => {
|
|
739
|
+
if (input?.deliveryAttempted === false) {
|
|
740
|
+
return input?.intro || 'If that account exists and still needs verification, we sent an email.';
|
|
741
|
+
}
|
|
742
|
+
if (input?.emailSent === false) {
|
|
743
|
+
return `Verification email failed to send. ${input?.emailError || 'Try Resend Verification again.'}`;
|
|
744
|
+
}
|
|
745
|
+
return 'Verification email resent.';
|
|
746
|
+
};
|
|
747
|
+
const buildPasswordResetNotice = (input) => {
|
|
748
|
+
if (input?.deliveryAttempted === false) {
|
|
749
|
+
return 'If that account exists, we sent password reset instructions.';
|
|
750
|
+
}
|
|
751
|
+
if (input?.emailSent === false) {
|
|
752
|
+
return `Password reset email failed to send. ${input?.emailError || 'Try again in a minute.'}`;
|
|
753
|
+
}
|
|
754
|
+
return 'Password reset instructions sent.';
|
|
755
|
+
};
|
|
691
756
|
useEffect(() => {
|
|
692
757
|
if (mode === 'widget')
|
|
693
758
|
return;
|
|
@@ -738,7 +803,13 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
738
803
|
}
|
|
739
804
|
return;
|
|
740
805
|
}
|
|
741
|
-
if (
|
|
806
|
+
if (activeCloudWorkspaceImport) {
|
|
807
|
+
const isCloudImportRoute = isSetupRoute
|
|
808
|
+
&& setupStep === 'workspace'
|
|
809
|
+
&& new URLSearchParams(location.search).get('source') === 'cloud';
|
|
810
|
+
if (!isCloudImportRoute) {
|
|
811
|
+
navigate(cloudWorkspaceSelectionPath, { replace: true });
|
|
812
|
+
}
|
|
742
813
|
return;
|
|
743
814
|
}
|
|
744
815
|
if (setupGateActive) {
|
|
@@ -785,7 +856,7 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
785
856
|
setupState,
|
|
786
857
|
hasSetupReadError,
|
|
787
858
|
isCreateWorkspaceIntent,
|
|
788
|
-
|
|
859
|
+
activeCloudWorkspaceImport,
|
|
789
860
|
needsGlobalSetup,
|
|
790
861
|
needsWorkspaceSetup,
|
|
791
862
|
setupStep,
|
|
@@ -798,7 +869,8 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
798
869
|
location.hash,
|
|
799
870
|
navigate,
|
|
800
871
|
nextPath,
|
|
801
|
-
pendingPostAuthRedirect
|
|
872
|
+
pendingPostAuthRedirect,
|
|
873
|
+
cloudWorkspaceSelectionPath
|
|
802
874
|
]);
|
|
803
875
|
useEffect(() => {
|
|
804
876
|
if (mode === 'widget')
|
|
@@ -878,7 +950,7 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
878
950
|
setLoginMode('verify');
|
|
879
951
|
if (sent.verificationToken)
|
|
880
952
|
setVerificationToken(sent.verificationToken);
|
|
881
|
-
setLoginNotice(
|
|
953
|
+
setLoginNotice(buildVerificationRequestNotice(sent));
|
|
882
954
|
setLoginError(null);
|
|
883
955
|
setLoginErrorCode(null);
|
|
884
956
|
}
|
|
@@ -950,7 +1022,10 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
950
1022
|
setLoginMode('verify');
|
|
951
1023
|
if (sent.verificationToken)
|
|
952
1024
|
setVerificationToken(sent.verificationToken);
|
|
953
|
-
setLoginNotice(
|
|
1025
|
+
setLoginNotice(buildVerificationRequestNotice({
|
|
1026
|
+
...sent,
|
|
1027
|
+
intro: 'If that account exists and still needs verification, we sent an email.'
|
|
1028
|
+
}));
|
|
954
1029
|
setLoginError(null);
|
|
955
1030
|
setLoginErrorCode(null);
|
|
956
1031
|
}
|
|
@@ -1003,13 +1078,14 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
1003
1078
|
});
|
|
1004
1079
|
}
|
|
1005
1080
|
else if (authMode === 'forgot') {
|
|
1081
|
+
const passwordResetDeliveryFailed = result.deliveryAttempted === true && result.emailSent === false;
|
|
1006
1082
|
if (result.resetToken)
|
|
1007
1083
|
setResetToken(result.resetToken);
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1084
|
+
setLoginNotice(buildPasswordResetNotice(result));
|
|
1085
|
+
if (!passwordResetDeliveryFailed) {
|
|
1086
|
+
setResetCooldownUntil(Date.now() + 30_000);
|
|
1087
|
+
setLoginMode('reset');
|
|
1088
|
+
}
|
|
1013
1089
|
}
|
|
1014
1090
|
else if (authMode === 'reset') {
|
|
1015
1091
|
setLoginMode('login');
|
|
@@ -1017,9 +1093,31 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
1017
1093
|
}
|
|
1018
1094
|
else if (authMode === 'invite') {
|
|
1019
1095
|
if (inviteResolution === 'existing_user_signed_out') {
|
|
1020
|
-
|
|
1096
|
+
const normalizedInviteEmail = String(inviteEmail || '').trim().toLowerCase();
|
|
1097
|
+
const normalizedLoginEmail = loginEmail.trim().toLowerCase();
|
|
1098
|
+
// Make the invite flow deterministically join-ready as soon as
|
|
1099
|
+
// the invited account signs in successfully, instead of waiting
|
|
1100
|
+
// for a later invite inspection effect to catch up.
|
|
1101
|
+
if (normalizedInviteEmail && normalizedLoginEmail === normalizedInviteEmail) {
|
|
1102
|
+
setAuthMode('invite');
|
|
1103
|
+
setInviteResolution('existing_user_ready');
|
|
1104
|
+
setLoginPassword('');
|
|
1105
|
+
setLoginNotice('Signed in. Review the invite details to continue.');
|
|
1106
|
+
setLoginError(null);
|
|
1107
|
+
setLoginErrorCode(null);
|
|
1108
|
+
setLoginBusy(false);
|
|
1109
|
+
return;
|
|
1110
|
+
}
|
|
1111
|
+
if (normalizedInviteEmail) {
|
|
1112
|
+
setLoginEmail(inviteEmail || '');
|
|
1113
|
+
}
|
|
1114
|
+
setInviteResolution('existing_user_signed_out');
|
|
1021
1115
|
setLoginPassword('');
|
|
1022
|
-
setLoginNotice(
|
|
1116
|
+
setLoginNotice(normalizedInviteEmail
|
|
1117
|
+
? `Signed in, but this invite is for ${inviteEmail}. Sign in with that account to continue.`
|
|
1118
|
+
: 'Signed in, but this invite requires the invited account. Sign in with that account to continue.');
|
|
1119
|
+
setLoginError('Sign in with the invited account to join this workspace.');
|
|
1120
|
+
setLoginErrorCode('INVITE_ACCOUNT_MISMATCH');
|
|
1023
1121
|
setLoginBusy(false);
|
|
1024
1122
|
return;
|
|
1025
1123
|
}
|
|
@@ -1079,7 +1177,7 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
1079
1177
|
setVerifyCooldownUntil(Date.now() + 30_000);
|
|
1080
1178
|
if (sent.verificationToken)
|
|
1081
1179
|
setVerificationToken(sent.verificationToken);
|
|
1082
|
-
setLoginNotice(
|
|
1180
|
+
setLoginNotice(buildVerificationRequestNotice(sent));
|
|
1083
1181
|
}
|
|
1084
1182
|
else {
|
|
1085
1183
|
setLoginError(formatAuthError(sent));
|
|
@@ -1096,7 +1194,7 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
1096
1194
|
}, children: _jsxs("div", { className: authStyles.loginCard, children: [_jsxs("div", { className: authStyles.authBrandRow, children: [_jsx("img", { src: authBrandLogo, alt: "Taskforce", className: authStyles.loginLogo }), _jsx("h1", { className: authStyles.loginTitle, children: "Private Beta" })] }), _jsx("p", { className: authStyles.loginSubtitle, children: "You are signed in, but your account is not in the beta allowlist yet." }), _jsx("p", { className: authStyles.loginSubtitle, children: "You will see the full app as soon as beta access is enabled." }), _jsx("button", { className: styles.cancelBtn, onClick: async () => { await taskforceState.logout(); navigate('/login', { replace: true }); }, children: "Sign Out" })] }) }) }));
|
|
1097
1195
|
}
|
|
1098
1196
|
const showGuidedWorkspaceSetup = isCreateWorkspaceIntent
|
|
1099
|
-
|| (isSetupRoute && Boolean(
|
|
1197
|
+
|| (isSetupRoute && Boolean(activeCloudWorkspaceImport))
|
|
1100
1198
|
|| (isSetupRoute && setupGateActive && (hasSetupReadError || needsGlobalSetup || needsWorkspaceSetup));
|
|
1101
1199
|
if (showGuidedWorkspaceSetup) {
|
|
1102
1200
|
const isError = !isCreateWorkspaceIntent && (hasSetupReadError || setupStep === 'error');
|
|
@@ -1114,11 +1212,11 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
1114
1212
|
const workspaceLabel = 'Workspace';
|
|
1115
1213
|
const isLocalWorkspaceSetup = isCloudRuntime || workspaceSetupSource !== 'cloud';
|
|
1116
1214
|
const isWorkspaceDetailsPage = isLocalWorkspaceSetup && workspaceSetupPage === 'details';
|
|
1117
|
-
const
|
|
1215
|
+
const isWorkspaceLibraryPage = isLocalWorkspaceSetup && workspaceSetupPage === 'library';
|
|
1118
1216
|
const showWorkspaceSourceSelector = !isCloudRuntime
|
|
1119
1217
|
&& canUseCloudWorkspaceSetup
|
|
1120
1218
|
&& isWorkspaceDetailsPage
|
|
1121
|
-
&& !
|
|
1219
|
+
&& !activeCloudWorkspaceImport;
|
|
1122
1220
|
const runtimeIcon = runtimeCapabilities?.runtimeMode === 'cloud' ? Cloud : LaptopMinimal;
|
|
1123
1221
|
const starterCategoryItems = selectedSetupPack ? formatStarterSectionItems(selectedSetupPack.categories) : '';
|
|
1124
1222
|
const starterTypeItems = selectedSetupPack ? formatStarterSectionItems(selectedSetupPack.types) : '';
|
|
@@ -1126,23 +1224,23 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
1126
1224
|
const setupPrimaryActionLabel = setupBusy
|
|
1127
1225
|
? t('setup.saving')
|
|
1128
1226
|
: wantsCloudWorkspaceSetup
|
|
1129
|
-
? (
|
|
1227
|
+
? (activeCloudWorkspaceImport
|
|
1130
1228
|
? (isCloudWorkspaceImportErrored ? 'Retry Import' : 'Importing Workspace...')
|
|
1131
1229
|
: 'Connect Workspace')
|
|
1132
1230
|
: isWorkspaceDetailsPage
|
|
1133
1231
|
? 'Continue'
|
|
1134
|
-
:
|
|
1232
|
+
: isWorkspaceLibraryPage
|
|
1135
1233
|
? 'Create Workspace'
|
|
1136
1234
|
: 'Connect Workspace';
|
|
1137
1235
|
return (_jsx("div", { className: shellStyles.standaloneWrapper, "data-theme": currentTheme, children: _jsx("div", { className: authStyles.loginView, style: {
|
|
1138
1236
|
backgroundImage: `linear-gradient(rgba(15, 15, 26, 0.88), rgba(15, 15, 26, 0.9)), url(${taskforceBg})`
|
|
1139
|
-
}, children: _jsxs("div", { className: authStyles.loginCard, children: [_jsxs("div", { className: authStyles.setupHeaderRow, children: [_jsxs("div", { className: authStyles.authBrandRow, children: [_jsx("img", { src: authBrandLogo, alt: "Taskforce", className: authStyles.loginLogo }), _jsxs("h1", { className: authStyles.loginTitle, children: ["TASKFORCE ", _jsx("span", { className: authStyles.loginTitleAccent, children: "HQ" })] })] }), runtimeCapabilities && (_jsx("span", { className: authStyles.runtimeIconBadge, title: runtimeCapabilities.runtimeMode === 'cloud' ? 'Cloud runtime' : 'Local runtime', "aria-label": runtimeCapabilities.runtimeMode === 'cloud' ? 'Cloud runtime' : 'Local runtime', children: React.createElement(runtimeIcon, { size: 16, 'aria-hidden': true }) }))] }), _jsx("p", { className: authStyles.loginSubtitle, children: _jsx("strong", { children: heading }) }), subtitle ? _jsx("p", { className: authStyles.loginSubtitle, children: subtitle }) : null, setupNotice && _jsx("p", { className: authStyles.loginSubtitle, children: setupNotice }), setupError && _jsx("p", { className: authStyles.loginError, children: setupError }), isGlobal && (_jsxs("div", { className: authStyles.optionGrid, children: [_jsxs("label", { className: `${authStyles.authModeLink} ${authStyles.optionRow}`, children: [_jsx("input", { type: "radio", name: "setup-mode", value: "core", checked: setupModeChoice === 'core', onChange: () => setSetupModeChoice('core'), disabled: setupBusy }), _jsx("span", { children: t('setup.coreModeOption', { workspaceLabel }) })] }), _jsxs("label", { className: `${authStyles.authModeLink} ${authStyles.optionRow}`, children: [_jsx("input", { type: "radio", name: "setup-mode", value: "operations", checked: setupModeChoice === 'operations', onChange: () => setSetupModeChoice('operations'), disabled: setupBusy }), _jsx("span", { children: t('setup.operationsModeOption') })] })] })), !isError && !isGlobal && (_jsxs("div", { className: authStyles.optionGrid, children: [showWorkspaceSourceSelector && (_jsxs("div", { className: authStyles.optionGroup, children: [_jsxs("label", { className: `${authStyles.authModeLink} ${authStyles.optionRow}`, children: [_jsx("input", { type: "radio", name: "workspace-setup-source", value: "local", checked: workspaceSetupSource === 'local', onChange: () => setWorkspaceSetupSource('local'), disabled: setupBusy }), _jsxs("span", { children: ["Create new local ", workspaceLabel.toLowerCase()] })] }), _jsxs("label", { className: `${authStyles.authModeLink} ${authStyles.optionRow}`, children: [_jsx("input", { type: "radio", name: "workspace-setup-source", value: "cloud", checked: workspaceSetupSource === 'cloud', onChange: () => setWorkspaceSetupSource('cloud'), disabled: setupBusy }), _jsxs("span", { children: ["Sync existing cloud ", workspaceLabel.toLowerCase()] })] })] })), wantsCloudWorkspaceSetup ? (_jsxs("div", { className: authStyles.optionGroup, children: [!
|
|
1237
|
+
}, children: _jsxs("div", { className: authStyles.loginCard, children: [_jsxs("div", { className: authStyles.setupHeaderRow, children: [_jsxs("div", { className: authStyles.authBrandRow, children: [_jsx("img", { src: authBrandLogo, alt: "Taskforce", className: authStyles.loginLogo }), _jsxs("h1", { className: authStyles.loginTitle, children: ["TASKFORCE ", _jsx("span", { className: authStyles.loginTitleAccent, children: "HQ" })] })] }), runtimeCapabilities && (_jsx("span", { className: authStyles.runtimeIconBadge, title: runtimeCapabilities.runtimeMode === 'cloud' ? 'Cloud runtime' : 'Local runtime', "aria-label": runtimeCapabilities.runtimeMode === 'cloud' ? 'Cloud runtime' : 'Local runtime', children: React.createElement(runtimeIcon, { size: 16, 'aria-hidden': true }) }))] }), _jsx("p", { className: authStyles.loginSubtitle, children: _jsx("strong", { children: heading }) }), subtitle ? _jsx("p", { className: authStyles.loginSubtitle, children: subtitle }) : null, setupNotice && _jsx("p", { className: authStyles.loginSubtitle, children: setupNotice }), setupError && _jsx("p", { className: authStyles.loginError, children: setupError }), isGlobal && (_jsxs("div", { className: authStyles.optionGrid, children: [_jsxs("label", { className: `${authStyles.authModeLink} ${authStyles.optionRow}`, children: [_jsx("input", { type: "radio", name: "setup-mode", value: "core", checked: setupModeChoice === 'core', onChange: () => setSetupModeChoice('core'), disabled: setupBusy }), _jsx("span", { children: t('setup.coreModeOption', { workspaceLabel }) })] }), _jsxs("label", { className: `${authStyles.authModeLink} ${authStyles.optionRow}`, children: [_jsx("input", { type: "radio", name: "setup-mode", value: "operations", checked: setupModeChoice === 'operations', onChange: () => setSetupModeChoice('operations'), disabled: setupBusy }), _jsx("span", { children: t('setup.operationsModeOption') })] })] })), !isError && !isGlobal && (_jsxs("div", { className: authStyles.optionGrid, children: [showWorkspaceSourceSelector && (_jsxs("div", { className: authStyles.optionGroup, children: [_jsxs("label", { className: `${authStyles.authModeLink} ${authStyles.optionRow}`, children: [_jsx("input", { type: "radio", name: "workspace-setup-source", value: "local", checked: workspaceSetupSource === 'local', onChange: () => setWorkspaceSetupSource('local'), disabled: setupBusy }), _jsxs("span", { children: ["Create new local ", workspaceLabel.toLowerCase()] })] }), _jsxs("label", { className: `${authStyles.authModeLink} ${authStyles.optionRow}`, children: [_jsx("input", { type: "radio", name: "workspace-setup-source", value: "cloud", checked: workspaceSetupSource === 'cloud', onChange: () => setWorkspaceSetupSource('cloud'), disabled: setupBusy }), _jsxs("span", { children: ["Sync existing cloud ", workspaceLabel.toLowerCase()] })] })] })), wantsCloudWorkspaceSetup ? (_jsxs("div", { className: authStyles.optionGroup, children: [!activeCloudWorkspaceImport && !canUseCloudWorkspaceSetup && (_jsx("p", { className: authStyles.loginError, children: "Cloud workspace sync is unavailable right now. Switch back to local setup or refresh runtime configuration." })), !activeCloudWorkspaceImport && canUseCloudWorkspaceSetup && !isAuthenticated && (_jsxs(_Fragment, { children: [_jsx("p", { className: authStyles.inlineHint, children: "Sign in to choose one of your cloud projects." }), _jsxs("div", { className: authStyles.actionRowEnd, children: [_jsx("button", { className: styles.cancelBtn, disabled: setupBusy, onClick: () => {
|
|
1140
1238
|
navigate(`/login?mode=login&next=${encodeURIComponent(cloudWorkspaceSelectionPath)}`, { replace: true });
|
|
1141
1239
|
}, children: "Sign In" }), directRegistrationEnabled && (_jsx("button", { className: styles.submitBtn, disabled: setupBusy, onClick: () => {
|
|
1142
1240
|
navigate(`/login?mode=register&next=${encodeURIComponent(cloudWorkspaceSelectionPath)}`, { replace: true });
|
|
1143
|
-
}, children: "Register" }))] })] })), !
|
|
1241
|
+
}, children: "Register" }))] })] })), !activeCloudWorkspaceImport && canUseCloudWorkspaceSetup && isAuthenticated && (_jsxs(_Fragment, { children: [_jsxs("select", { className: styles.input, value: cloudWorkspaceSelection, onChange: (event) => setCloudWorkspaceSelection(event.target.value), disabled: setupBusy || cloudWorkspaceLoading || cloudWorkspaceOptions.length === 0, children: [cloudWorkspaceOptions.length === 0 && (_jsx("option", { value: "", children: cloudWorkspaceLoading ? 'Loading cloud projects...' : 'No cloud projects found' })), cloudWorkspaceOptions.map((workspace) => (_jsxs("option", { value: workspace.id, children: [workspace.name, " (", workspace.id, ")"] }, workspace.id)))] }), _jsx("div", { className: authStyles.actionRowEnd, children: _jsx("button", { className: styles.cancelBtn, disabled: setupBusy || cloudWorkspaceLoading, onClick: () => void fetchCloudWorkspaceOptions(), children: "Refresh Cloud Projects" }) }), cloudWorkspaceError && _jsx("p", { className: authStyles.loginError, children: cloudWorkspaceError })] })), activeCloudWorkspaceImport && (_jsxs(_Fragment, { children: [_jsxs("p", { className: authStyles.inlineHint, children: ["Importing \"", activeCloudWorkspaceImport.name, "\" from cloud into local."] }), _jsx("div", { className: authStyles.optionGroup, children: cloudWorkspaceImportProgressLines.map((line) => (_jsx("p", { className: authStyles.inlineHint, children: line }, line))) }), _jsx("p", { className: authStyles.inlineHint, children: cloudWorkspaceImportFinalizing
|
|
1144
1242
|
? 'Finishing setup and opening your workspace.'
|
|
1145
|
-
: (workspaceSyncSummary || 'Pulling tasks, taxonomies, and settings into local.') }), !cloudWorkspaceImportFinalizing && workspaceSyncRecommendedAction && (_jsx("p", { className: authStyles.inlineHint, children: workspaceSyncRecommendedAction }))] }))] })) : (_jsxs(_Fragment, { children: [isWorkspaceDetailsPage && (_jsxs(_Fragment, { children: [_jsx("p", { className: authStyles.loginSubtitle, children: "Workspace name" }), _jsx("input", { className: styles.input, type: "text", value: workspaceNameInput, placeholder: "Workspace name", onChange: (event) => setWorkspaceNameInput(event.target.value), disabled: setupBusy }), _jsx("p", { className: authStyles.loginSubtitle, children: "Workspace description" }), _jsx("textarea", { className: styles.textarea, value: workspaceDescriptionInput, placeholder: "Workspace description (optional)", onChange: (event) => setWorkspaceDescriptionInput(event.target.value), disabled: setupBusy, rows: 4 })] })),
|
|
1243
|
+
: (workspaceSyncSummary || 'Pulling tasks, taxonomies, and settings into local.') }), !cloudWorkspaceImportFinalizing && workspaceSyncRecommendedAction && (_jsx("p", { className: authStyles.inlineHint, children: workspaceSyncRecommendedAction }))] }))] })) : (_jsxs(_Fragment, { children: [isWorkspaceDetailsPage && (_jsxs(_Fragment, { children: [_jsx("p", { className: authStyles.loginSubtitle, children: "Workspace name" }), _jsx("input", { className: styles.input, type: "text", value: workspaceNameInput, placeholder: "Workspace name", onChange: (event) => setWorkspaceNameInput(event.target.value), disabled: setupBusy }), _jsx("p", { className: authStyles.loginSubtitle, children: "Workspace description" }), _jsx("textarea", { className: styles.textarea, value: workspaceDescriptionInput, placeholder: "Workspace description (optional)", onChange: (event) => setWorkspaceDescriptionInput(event.target.value), disabled: setupBusy, rows: 4 })] })), isWorkspaceLibraryPage && (_jsxs("div", { className: authStyles.optionGroup, children: [_jsx("p", { className: authStyles.loginSubtitle, children: "What are you working on?" }), _jsxs("select", { className: styles.input, value: selectedSetupPackId, onChange: (event) => setSelectedSetupPackId(event.target.value), disabled: setupBusy || setupLibraryLoading || setupLibraryPacks.length === 0, children: [setupLibraryPacks.length === 0 && (_jsx("option", { value: "", children: setupLibraryLoading ? 'Loading setup options...' : 'No setup options available' })), setupLibraryPacks.map((pack) => (_jsx("option", { value: pack.id, children: pack.label }, pack.id)))] }), selectedSetupPack ? (_jsx(_Fragment, { children: selectedSetupPack.description ? (_jsx("p", { className: authStyles.inlineHint, children: selectedSetupPack.description })) : null })) : null, _jsxs("div", { className: authStyles.optionGroup, children: [_jsx("p", { className: authStyles.loginSubtitle, children: "Apply Setup Sections" }), _jsxs("label", { className: `${authStyles.authModeLink} ${authStyles.optionRow}`, children: [_jsx("input", { type: "checkbox", checked: setupTaxonomySections.categories, onChange: () => setSetupTaxonomySections((current) => ({ ...current, categories: !current.categories })), disabled: setupBusy || !selectedSetupPack?.categories.length }), _jsx("span", { children: "Categories" })] }), starterCategoryItems ? (_jsx("p", { className: authStyles.inlineHint, children: starterCategoryItems })) : null, _jsxs("label", { className: `${authStyles.authModeLink} ${authStyles.optionRow}`, children: [_jsx("input", { type: "checkbox", checked: setupTaxonomySections.types, onChange: () => setSetupTaxonomySections((current) => ({ ...current, types: !current.types })), disabled: setupBusy || !selectedSetupPack?.types.length }), _jsx("span", { children: "Task Types" })] }), starterTypeItems ? (_jsx("p", { className: authStyles.inlineHint, children: starterTypeItems })) : null, _jsxs("label", { className: `${authStyles.authModeLink} ${authStyles.optionRow}`, children: [_jsx("input", { type: "checkbox", checked: setupTaxonomySections.priorities, onChange: () => setSetupTaxonomySections((current) => ({ ...current, priorities: !current.priorities })), disabled: setupBusy || !selectedSetupPack?.priorities.length }), _jsx("span", { children: "Priorities" })] }), starterPriorityItems ? (_jsx("p", { className: authStyles.inlineHint, children: starterPriorityItems })) : null] }), setupLibraryLoading ? (_jsx("p", { className: authStyles.inlineHint, children: "Loading setup options\u2026" })) : null, setupLibraryError ? (_jsx("p", { className: authStyles.loginError, children: setupLibraryError })) : null] })), !isCloudRuntime && canUseCloudWorkspaceSetup && isAuthenticated && isWorkspaceDetailsPage && (_jsxs("div", { className: authStyles.optionGroup, children: [_jsxs("label", { className: `${authStyles.authModeLink} ${authStyles.optionRow}`, children: [_jsx("input", { type: "checkbox", checked: syncNewLocalWorkspaceToCloud, onChange: (event) => setSyncNewLocalWorkspaceToCloud(event.target.checked), disabled: setupBusy }), _jsx("span", { children: "Sync to cloud after setup" })] }), _jsx("p", { className: authStyles.inlineHint, children: "When enabled, this project will connect to cloud sync as soon as setup finishes." })] }))] }))] })), _jsxs("div", { className: authStyles.authModeLinks, children: [isWorkspaceLibraryPage ? (_jsx("button", { className: styles.cancelBtn, onClick: () => {
|
|
1146
1244
|
setSetupError(null);
|
|
1147
1245
|
setSetupNotice(null);
|
|
1148
1246
|
setWorkspaceSetupPage('details');
|
|
@@ -1150,7 +1248,7 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
1150
1248
|
setSetupError(null);
|
|
1151
1249
|
setSetupNotice(null);
|
|
1152
1250
|
navigate('/', { replace: true });
|
|
1153
|
-
}, children: "Cancel" })) : runtimeMode === 'cloud' ? (_jsx("button", { className: styles.cancelBtn, onClick: async () => { await taskforceState.logout(); navigate('/login', { replace: true }); }, children: t('setup.signOut') })) : null, isGlobal ? (_jsx("button", { className: styles.submitBtn, disabled: setupBusy || (isCloudWorkspaceSetupFlow && Boolean(
|
|
1251
|
+
}, children: "Cancel" })) : runtimeMode === 'cloud' ? (_jsx("button", { className: styles.cancelBtn, onClick: async () => { await taskforceState.logout(); navigate('/login', { replace: true }); }, children: t('setup.signOut') })) : null, isGlobal ? (_jsx("button", { className: styles.submitBtn, disabled: setupBusy || (isCloudWorkspaceSetupFlow && Boolean(activeCloudWorkspaceImport) && !isCloudWorkspaceImportErrored), onClick: async () => {
|
|
1154
1252
|
setSetupBusy(true);
|
|
1155
1253
|
setSetupNotice(null);
|
|
1156
1254
|
setSetupError(null);
|
|
@@ -1177,19 +1275,19 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
1177
1275
|
setSetupBusy(false);
|
|
1178
1276
|
}
|
|
1179
1277
|
}, children: setupBusy ? t('setup.saving') : t('setup.saveGlobalSetup') })) : !isError ? (_jsx("button", { className: styles.submitBtn, disabled: setupBusy
|
|
1180
|
-
|| (isCloudWorkspaceSetupFlow && !
|
|
1181
|
-
if (isCloudWorkspaceSetupFlow &&
|
|
1278
|
+
|| (isCloudWorkspaceSetupFlow && !activeCloudWorkspaceImport && !isAuthenticated), onClick: async () => {
|
|
1279
|
+
if (isCloudWorkspaceSetupFlow && activeCloudWorkspaceImport) {
|
|
1182
1280
|
if (!isCloudWorkspaceImportErrored)
|
|
1183
1281
|
return;
|
|
1184
1282
|
setSetupBusy(true);
|
|
1185
|
-
setSetupNotice(`Retrying import for ${
|
|
1283
|
+
setSetupNotice(`Retrying import for ${activeCloudWorkspaceImport.name}...`);
|
|
1186
1284
|
setSetupError(null);
|
|
1187
1285
|
try {
|
|
1188
1286
|
await retryWorkspaceCloudSync();
|
|
1189
1287
|
}
|
|
1190
1288
|
catch {
|
|
1191
1289
|
setSetupNotice(null);
|
|
1192
|
-
setSetupError(`Failed to retry import for "${
|
|
1290
|
+
setSetupError(`Failed to retry import for "${activeCloudWorkspaceImport.name}".`);
|
|
1193
1291
|
}
|
|
1194
1292
|
finally {
|
|
1195
1293
|
setSetupBusy(false);
|
|
@@ -1229,33 +1327,28 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
1229
1327
|
setSetupBusy(false);
|
|
1230
1328
|
return;
|
|
1231
1329
|
}
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
}
|
|
1248
|
-
})
|
|
1249
|
-
});
|
|
1250
|
-
const statePayload = await stateRes.json().catch(() => ({}));
|
|
1251
|
-
if (!stateRes.ok || statePayload?.success === false) {
|
|
1252
|
-
setSetupError(statePayload?.error || `Failed to configure workspace sync (${stateRes.status})`);
|
|
1330
|
+
try {
|
|
1331
|
+
await persistWorkspaceSyncStatePatch(workspaceIdForSync, {
|
|
1332
|
+
version: 2,
|
|
1333
|
+
enabled: true,
|
|
1334
|
+
phase: 'attach-cloud',
|
|
1335
|
+
setupIntent: 'attach-cloud-import',
|
|
1336
|
+
pullCursor: null,
|
|
1337
|
+
lastPullAt: null,
|
|
1338
|
+
lastPushAt: null,
|
|
1339
|
+
lastSyncedAt: null,
|
|
1340
|
+
lastErrorMessage: null
|
|
1341
|
+
});
|
|
1342
|
+
}
|
|
1343
|
+
catch (error) {
|
|
1344
|
+
setSetupError(error instanceof Error ? error.message : 'Failed to configure workspace sync.');
|
|
1253
1345
|
setSetupBusy(false);
|
|
1254
1346
|
return;
|
|
1255
1347
|
}
|
|
1256
1348
|
applyWorkspaceSyncStateSnapshot({
|
|
1257
1349
|
enabled: true,
|
|
1258
1350
|
phase: 'attach-cloud',
|
|
1351
|
+
setupIntent: 'attach-cloud-import',
|
|
1259
1352
|
pullCursor: null
|
|
1260
1353
|
});
|
|
1261
1354
|
await refreshSetupContext();
|
|
@@ -1276,10 +1369,10 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
1276
1369
|
}
|
|
1277
1370
|
setSetupError(null);
|
|
1278
1371
|
setSetupNotice(null);
|
|
1279
|
-
setWorkspaceSetupPage('
|
|
1372
|
+
setWorkspaceSetupPage('library');
|
|
1280
1373
|
return;
|
|
1281
1374
|
}
|
|
1282
|
-
if (
|
|
1375
|
+
if (isWorkspaceLibraryPage) {
|
|
1283
1376
|
// fall through to final create/save behavior below
|
|
1284
1377
|
}
|
|
1285
1378
|
setSetupBusy(true);
|
|
@@ -1324,7 +1417,7 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
1324
1417
|
workspaceIdOverride: workspaceIdForSetup
|
|
1325
1418
|
});
|
|
1326
1419
|
if (!applyResult?.success) {
|
|
1327
|
-
setSetupError(applyResult?.error || 'Failed to apply
|
|
1420
|
+
setSetupError(applyResult?.error || 'Failed to apply setup library.');
|
|
1328
1421
|
setSetupBusy(false);
|
|
1329
1422
|
return;
|
|
1330
1423
|
}
|
|
@@ -1342,6 +1435,7 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
1342
1435
|
version: 2,
|
|
1343
1436
|
enabled: shouldEnableCloudSync,
|
|
1344
1437
|
phase: shouldEnableCloudSync ? 'provision-local' : 'idle',
|
|
1438
|
+
setupIntent: null,
|
|
1345
1439
|
pullCursor: null,
|
|
1346
1440
|
lastPullAt: null,
|
|
1347
1441
|
lastPushAt: null,
|
|
@@ -1358,6 +1452,7 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
1358
1452
|
applyWorkspaceSyncStateSnapshot({
|
|
1359
1453
|
enabled: shouldEnableCloudSync,
|
|
1360
1454
|
phase: shouldEnableCloudSync ? 'provision-local' : 'idle',
|
|
1455
|
+
setupIntent: null,
|
|
1361
1456
|
pullCursor: null
|
|
1362
1457
|
});
|
|
1363
1458
|
if (shouldEnableCloudSync) {
|