@taskforcehq/taskforce 0.3.302 → 0.3.304
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Taskforce.module.css +113 -5
- package/dist/TaskforceCore.js +560 -208
- package/dist/TaskforceCore.test.js +1833 -91
- package/dist/annotatedAttachments/service.d.ts +1 -1
- package/dist/annotatedAttachments/types.d.ts +1 -1
- package/dist/components/features/AgentsModule.js +1 -1
- package/dist/components/features/AgentsModule.test.js +1 -1
- package/dist/components/features/TaskSettings.js +58 -38
- package/dist/components/features/TaskSettings.test.js +139 -40
- package/dist/components/task/TaskActionHeader.d.ts +2 -1
- package/dist/components/task/TaskActionHeader.js +3 -3
- package/dist/components/task/TaskActionHeader.test.js +8 -0
- package/dist/components/task/TaskCard.js +16 -1
- package/dist/components/task/TaskCard.test.js +31 -0
- package/dist/components/task/TaskForm.d.ts +2 -1
- package/dist/components/task/TaskForm.js +22 -4
- package/dist/components/task/TaskForm.test.js +46 -0
- package/dist/components/task/TaskKanban.d.ts +1 -1
- package/dist/components/task/TaskKanban.js +31 -160
- package/dist/components/task/TaskKanban.test.js +17 -0
- package/dist/components/task/TaskStatusActions.d.ts +6 -1
- package/dist/components/task/TaskStatusActions.js +8 -3
- package/dist/components/task/TaskStatusActions.test.js +9 -0
- package/dist/components/task/taskKanbanDropRules.d.ts +58 -0
- package/dist/components/task/taskKanbanDropRules.js +187 -0
- package/dist/components/task/taskKanbanDropRules.test.d.ts +1 -0
- package/dist/components/task/taskKanbanDropRules.test.js +114 -0
- package/dist/components/ui/Modal.d.ts +1 -1
- package/dist/components/ui/Modal.js +1 -0
- package/dist/components/views/AppShellHeader.js +5 -1
- package/dist/components/views/AppShellHeader.test.js +4 -0
- package/dist/components/views/PlanComparisonPage.d.ts +3 -2
- package/dist/components/views/PlanComparisonPage.js +4 -2
- package/dist/components/views/PlanComparisonPage.test.js +4 -0
- package/dist/components/views/PlansPage.d.ts +7 -1
- package/dist/components/views/PlansPage.js +139 -53
- package/dist/components/views/PlansPage.test.d.ts +1 -0
- package/dist/components/views/PlansPage.test.js +157 -0
- package/dist/components/views/StandaloneLayout.d.ts +1 -0
- package/dist/components/views/StandaloneLayout.js +216 -186
- package/dist/components/views/WidgetView.js +11 -2
- package/dist/components/views/standalone/modals/AccountHubModal.d.ts +8 -3
- package/dist/components/views/standalone/modals/AccountHubModal.js +16 -2
- package/dist/components/views/standalone/modals/AccountHubModal.test.d.ts +1 -0
- package/dist/components/views/standalone/modals/AccountHubModal.test.js +26 -0
- package/dist/components/views/standalone/modals/SyncStatusModal.js +1 -1
- package/dist/core/DeletedTaskLifecycleService.d.ts +2 -1
- package/dist/core/EntitlementsPolicy.test.js +2 -2
- package/dist/core/GlobalSettingsService.js +78 -18
- package/dist/core/PlanEntitlementService.d.ts +31 -5
- package/dist/core/PlanEntitlementService.js +350 -222
- package/dist/core/SignupMonetizationSettings.test.js +208 -49
- package/dist/core/SystemAdmin.test.js +186 -96
- package/dist/core/TaskActivityService.d.ts +4 -1
- package/dist/core/TaskActivityService.js +45 -0
- package/dist/core/TaskAuditTimeline.test.js +52 -1
- package/dist/core/Taskforce.d.ts +65 -20
- package/dist/core/Taskforce.js +261 -51
- package/dist/core/Taskforce.mcpAuth.test.js +0 -2
- package/dist/core/UserProfileAvatarDrafts.test.js +12 -3
- package/dist/core/WorkspacePlanMode.test.js +65 -0
- package/dist/core/types.d.ts +11 -12
- package/dist/documentReviews/service.d.ts +1 -1
- package/dist/documentReviews/types.d.ts +1 -1
- package/dist/hooks/auth/useTaskforceAuthBootstrap.d.ts +48 -0
- package/dist/hooks/auth/useTaskforceAuthBootstrap.js +219 -0
- package/dist/hooks/sync/auth.d.ts +37 -0
- package/dist/hooks/sync/auth.js +61 -0
- package/dist/hooks/sync/bootstrap.d.ts +34 -0
- package/dist/hooks/sync/bootstrap.js +52 -0
- package/dist/hooks/sync/orchestratorShared.d.ts +36 -0
- package/dist/hooks/sync/orchestratorShared.js +209 -0
- package/dist/hooks/sync/orchestratorShared.test.d.ts +1 -0
- package/dist/hooks/sync/orchestratorShared.test.js +49 -0
- package/dist/hooks/sync/realtime.d.ts +25 -0
- package/dist/hooks/sync/realtime.js +60 -0
- package/dist/hooks/sync/recovery.d.ts +69 -0
- package/dist/hooks/sync/recovery.js +118 -0
- package/dist/hooks/sync/transfers.d.ts +122 -0
- package/dist/hooks/sync/transfers.js +431 -0
- package/dist/hooks/sync/useDataVersionRefresh.d.ts +16 -0
- package/dist/hooks/sync/useDataVersionRefresh.js +73 -0
- package/dist/hooks/ui/useResourceExport.d.ts +3 -1
- package/dist/hooks/ui/useResourceExport.js +5 -2
- package/dist/hooks/ui/useWorkflowTemplates.js +11 -6
- package/dist/hooks/useRealtimeSync.js +2 -1
- package/dist/hooks/useSyncOrchestrator.aiProfiles.test.js +8 -0
- package/dist/hooks/useSyncOrchestrator.context-assets.test.d.ts +1 -0
- package/dist/hooks/useSyncOrchestrator.context-assets.test.js +228 -0
- package/dist/hooks/useSyncOrchestrator.d.ts +5 -4
- package/dist/hooks/useSyncOrchestrator.js +315 -860
- package/dist/hooks/useSyncOrchestrator.retry-closure.test.js +665 -8
- package/dist/hooks/useTaskMutations.js +45 -6
- package/dist/hooks/useTaskMutations.test.js +147 -0
- package/dist/hooks/useTaskforce.d.ts +11 -2
- package/dist/hooks/useTaskforce.js +290 -812
- package/dist/hooks/useWorkspaceSyncController.d.ts +1 -0
- package/dist/hooks/useWorkspaceSyncController.js +22 -10
- package/dist/hooks/workspace/useTaskforceApiRouting.test.js +17 -0
- package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.d.ts +166 -0
- package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.js +434 -0
- package/dist/hooks/workspace/workspaceLocalState.d.ts +19 -0
- package/dist/hooks/workspace/workspaceLocalState.js +100 -0
- package/dist/hooks/workspace/workspaceLocalState.test.d.ts +1 -0
- package/dist/hooks/workspace/workspaceLocalState.test.js +59 -0
- package/dist/localization/locales/en-US.js +2 -2
- package/dist/mcp/canonicalAssetHelpers.d.ts +70 -0
- package/dist/mcp/canonicalAssetHelpers.js +259 -0
- package/dist/mcp/clientIntegrations.d.ts +12 -1
- package/dist/mcp/clientIntegrations.js +161 -6
- package/dist/mcp/clientIntegrations.test.js +76 -4
- package/dist/mcp/documentHelpers.d.ts +100 -0
- package/dist/mcp/documentHelpers.js +161 -0
- package/dist/mcp/runtime.js +264 -505
- package/dist/mcp/runtime.test.js +353 -89
- package/dist/mcp/taskAttachmentHelpers.d.ts +41 -0
- package/dist/mcp/taskAttachmentHelpers.js +60 -0
- package/dist/mcp/taskAttachmentTypes.d.ts +37 -0
- package/dist/mcp/taskAttachmentTypes.js +1 -0
- package/dist/migrations/billingSchemaParity.test.js +3 -0
- package/dist/migrations/taskSchemaMigrations.js +15 -0
- package/dist/plugins/vite.js +1 -1
- package/dist/runtime/appGateDefinitions.d.ts +1 -1
- package/dist/runtime/appGateDefinitions.js +1 -1
- package/dist/server/index.cookieProxy.test.js +1 -0
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.js +48 -3
- package/dist/server/index.test.js +14 -2
- package/dist/server/mockStripe.d.ts +1 -0
- package/dist/server/mockStripe.js +2 -0
- package/dist/server/routes/admin.js +91 -40
- package/dist/server/routes/auth.d.ts +1 -0
- package/dist/server/routes/auth.js +231 -55
- package/dist/server/routes/billing.js +473 -39
- package/dist/server/routes/billing.test.js +972 -37
- package/dist/server/routes/shared.d.ts +30 -0
- package/dist/server/routes/shared.js +31 -3
- package/dist/server/routes/sync.integration.test.js +180 -0
- package/dist/server/routes/sync.js +125 -242
- package/dist/server/routes/tasks.js +49 -7
- package/dist/server/routes.js +11 -22
- package/dist/server/routes.test.js +530 -81
- package/dist/shared/taskActor.d.ts +10 -0
- package/dist/shared/taskActor.js +1 -0
- package/dist/sync/cloudSyncApi.d.ts +1 -0
- package/dist/sync/collaborationSyncPayload.js +4 -0
- package/dist/sync/collaborationSyncPayload.test.js +8 -0
- package/dist/sync/collaborationSyncState.d.ts +3 -1
- package/dist/sync/collaborationSyncState.js +19 -0
- package/dist/sync/contentSyncState.d.ts +10 -0
- package/dist/sync/contentSyncState.js +277 -14
- package/dist/sync/syncApplyHandlers.d.ts +87 -1
- package/dist/sync/syncApplyHandlers.js +370 -8
- package/dist/sync/syncApplyHandlers.test.d.ts +1 -0
- package/dist/sync/syncApplyHandlers.test.js +377 -0
- package/dist/sync/syncService.d.ts +9 -1
- package/dist/sync/syncService.js +57 -10
- package/dist/sync/syncService.test.js +48 -0
- package/dist/sync/workspacePullFeed.d.ts +11 -0
- package/dist/sync/workspacePullFeed.js +123 -68
- package/dist/sync/workspacePullFeed.test.js +180 -0
- package/dist/sync/workspaceSyncModel.d.ts +22 -1
- package/dist/sync/workspaceSyncModel.js +65 -0
- package/dist/sync/workspaceSyncModel.test.js +119 -0
- package/dist/types.d.ts +2 -10
- package/dist/ui/assets/AgentsModule-BLWVbuKP.js +1 -0
- package/dist/ui/assets/{AnnotatedAttachmentWorkspace-Dvi02Y7q.js → AnnotatedAttachmentWorkspace-BlS-cqnl.js} +1 -1
- package/dist/ui/assets/{DocumentWorkspace-iys3zfca.js → DocumentWorkspace-BH_6DF6x.js} +1 -1
- package/dist/ui/assets/{InitiativesModule-DJvLJI3A.js → InitiativesModule-BjR6iBf9.js} +1 -1
- package/dist/ui/assets/{PlansPage-BlVC_lRq.css → PlansPage-C2dd2n1X.css} +1 -1
- package/dist/ui/assets/PlansPage-CSDQ4sLa.js +1 -0
- package/dist/ui/assets/TaskSettings-CEDy34Jo.js +9 -0
- package/dist/ui/assets/{WorkflowsModule-BguHeQaV.js → WorkflowsModule-DnFqdUME.js} +1 -1
- package/dist/ui/assets/index-DXUdtH1B.js +6 -0
- package/dist/ui/assets/index-Ii0B0rTO.css +1 -0
- package/dist/ui/assets/{vendor-icons-C8EED3Oh.js → vendor-icons-I7ZwG1z_.js} +1 -1
- package/dist/ui/index.html +4 -4
- package/dist/utils/accountProfileSummaryCache.d.ts +31 -0
- package/dist/utils/accountProfileSummaryCache.js +97 -0
- package/dist/utils/accountProfileSummaryCache.test.d.ts +1 -0
- package/dist/utils/accountProfileSummaryCache.test.js +63 -0
- package/dist/utils/taskActivity.js +4 -0
- package/dist/utils/taskActivity.test.js +25 -1
- package/package.json +5 -3
- package/scripts/playwright-auth.sh +4 -0
- package/dist/ui/assets/AgentsModule-JG5jc3he.js +0 -1
- package/dist/ui/assets/PlansPage-BMPlpFpw.js +0 -1
- package/dist/ui/assets/TaskSettings-Bqd4cCzW.js +0 -8
- package/dist/ui/assets/index-MXeWoebC.css +0 -1
- package/dist/ui/assets/index-YJ4k5QNa.js +0 -6
- package/dist/utils/billingStatusCache.d.ts +0 -21
- package/dist/utils/billingStatusCache.js +0 -70
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 { useCallback, useEffect, useMemo, useState } from 'react';
|
|
2
|
+
import React, { useCallback, useEffect, useMemo, 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';
|
|
@@ -14,6 +14,7 @@ import logoLight from './assets/branding/logos/logo_light.svg';
|
|
|
14
14
|
import logoDark from './assets/branding/logos/logo_dark.svg';
|
|
15
15
|
import taskforceBg from './assets/images/taskforce.png';
|
|
16
16
|
import { isLightThemeFamily } from './utils/theme';
|
|
17
|
+
import { Cloud, LaptopMinimal } from 'lucide-react';
|
|
17
18
|
function isDebugModeEnabled() {
|
|
18
19
|
const runtimeOverride = globalThis.__DEBUG_MODE__;
|
|
19
20
|
if (typeof runtimeOverride === 'boolean')
|
|
@@ -27,6 +28,22 @@ function defaultSetupTaxonomySections(pack) {
|
|
|
27
28
|
priorities: Boolean(pack?.priorities?.length),
|
|
28
29
|
};
|
|
29
30
|
}
|
|
31
|
+
function formatStarterSectionItems(values) {
|
|
32
|
+
return (Array.isArray(values) ? values : [])
|
|
33
|
+
.map((entry) => String(entry.label || entry.value || '').trim())
|
|
34
|
+
.filter(Boolean)
|
|
35
|
+
.join(', ');
|
|
36
|
+
}
|
|
37
|
+
function buildPlansPath(params = {}) {
|
|
38
|
+
const query = new URLSearchParams();
|
|
39
|
+
query.set('screen', 'plans');
|
|
40
|
+
for (const [key, value] of Object.entries(params)) {
|
|
41
|
+
const normalized = String(value || '').trim();
|
|
42
|
+
if (normalized)
|
|
43
|
+
query.set(key, normalized);
|
|
44
|
+
}
|
|
45
|
+
return `/?${query.toString()}`;
|
|
46
|
+
}
|
|
30
47
|
function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange, onHeaderMouseDown, isDragging, onClose, mode = 'standalone' }) {
|
|
31
48
|
const location = useLocation();
|
|
32
49
|
const routeQuery = useMemo(() => new URLSearchParams(location.search), [location.search]);
|
|
@@ -36,13 +53,17 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
36
53
|
const isPricingRoute = location.pathname === '/pricing';
|
|
37
54
|
const isPlansRoute = location.pathname === '/plans';
|
|
38
55
|
const isPlansScreen = location.pathname === '/' && String(routeQuery.get('screen') || '').trim().toLowerCase() === 'plans';
|
|
39
|
-
const planSelectionPath = '
|
|
56
|
+
const planSelectionPath = buildPlansPath({ gate: 'plan_selection_required' });
|
|
40
57
|
const navigate = useNavigate();
|
|
41
58
|
const [loginEmail, setLoginEmail] = useState('');
|
|
42
59
|
const [loginDisplayName, setLoginDisplayName] = useState('');
|
|
43
60
|
const [loginPassword, setLoginPassword] = useState('');
|
|
44
61
|
const [authMode, setAuthMode] = useState('login');
|
|
45
62
|
const [verificationToken, setVerificationToken] = useState('');
|
|
63
|
+
const [pendingVerificationWorkspaceSetupRequired, setPendingVerificationWorkspaceSetupRequired] = useState(false);
|
|
64
|
+
const [pendingVerificationCommercialState, setPendingVerificationCommercialState] = useState(null);
|
|
65
|
+
const [pendingVerificationRedirectPath, setPendingVerificationRedirectPath] = useState(null);
|
|
66
|
+
const [pendingPostAuthRedirect, setPendingPostAuthRedirect] = useState(null);
|
|
46
67
|
const [resetToken, setResetToken] = useState('');
|
|
47
68
|
const [inviteToken, setInviteToken] = useState('');
|
|
48
69
|
const [inviteEmail, setInviteEmail] = useState(null);
|
|
@@ -63,11 +84,14 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
63
84
|
const [workspaceNameInput, setWorkspaceNameInput] = useState('');
|
|
64
85
|
const [workspaceDescriptionInput, setWorkspaceDescriptionInput] = useState('');
|
|
65
86
|
const [workspaceSetupSource, setWorkspaceSetupSource] = useState('local');
|
|
87
|
+
const [workspaceSetupPage, setWorkspaceSetupPage] = useState('details');
|
|
66
88
|
const [cloudWorkspaceOptions, setCloudWorkspaceOptions] = useState([]);
|
|
67
89
|
const [cloudWorkspaceSelection, setCloudWorkspaceSelection] = useState('');
|
|
68
90
|
const [cloudWorkspaceLoading, setCloudWorkspaceLoading] = useState(false);
|
|
69
91
|
const [cloudWorkspaceError, setCloudWorkspaceError] = useState(null);
|
|
70
92
|
const [syncNewLocalWorkspaceToCloud, setSyncNewLocalWorkspaceToCloud] = useState(false);
|
|
93
|
+
const [pendingCloudWorkspaceImport, setPendingCloudWorkspaceImport] = useState(null);
|
|
94
|
+
const [cloudWorkspaceImportFinalizing, setCloudWorkspaceImportFinalizing] = useState(false);
|
|
71
95
|
const fallbackSetupLibraryPacks = useMemo(() => listBuiltInTaxonomyLibraryPacks(), []);
|
|
72
96
|
const [setupLibraryPacks, setSetupLibraryPacks] = useState(fallbackSetupLibraryPacks);
|
|
73
97
|
const [selectedSetupPackId, setSelectedSetupPackId] = useState(() => (fallbackSetupLibraryPacks.find((pack) => pack.isDefaultStarter)?.id || fallbackSetupLibraryPacks[0]?.id || ''));
|
|
@@ -76,6 +100,7 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
76
100
|
const [setupLibraryError, setSetupLibraryError] = useState(null);
|
|
77
101
|
const [bootstrapRetryBusy, setBootstrapRetryBusy] = useState(false);
|
|
78
102
|
const [accountAccessGate, setAccountAccessGate] = useState(null);
|
|
103
|
+
const [accountAccessGateResolved, setAccountAccessGateResolved] = useState(false);
|
|
79
104
|
// Initialize the hook
|
|
80
105
|
const taskforceState = useTaskforce({
|
|
81
106
|
config,
|
|
@@ -83,7 +108,7 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
83
108
|
onTaskCountChange,
|
|
84
109
|
onClose
|
|
85
110
|
});
|
|
86
|
-
const { runtimeMode, workspaceSwitchingEnabled, currentWorkspaceId, configLoaded, cloudAuthConfigured, authRequiredForApi, authBlocked, isAuthenticated, hasBetaAccess, authSessionResolved, workspaceBootstrapPending, bootstrapPhase, bootstrapError, setupState, runtimeCapabilities, refreshSetupContext, retryBootstrapChecks, createWorkspace, saveWorkspaceProfile, fetchWorkspaces, applyWorkspaceSyncStateSnapshot, settingsModel, fetchTasks, loginWithCredentials, registerWithCredentials, requestEmailVerification, confirmEmailVerification, requestPasswordReset, confirmPasswordReset, inspectInviteAcceptance, acceptInviteWithToken, joinInviteWithToken, authUserEmail, currentTheme, logout } = taskforceState;
|
|
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;
|
|
87
112
|
const selectedSetupPack = useMemo(() => {
|
|
88
113
|
return setupLibraryPacks.find((pack) => pack.id === selectedSetupPackId)
|
|
89
114
|
|| setupLibraryPacks.find((pack) => pack.isDefaultStarter)
|
|
@@ -106,6 +131,46 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
106
131
|
const isLoginRoute = location.pathname === '/login';
|
|
107
132
|
const isSetupRoute = location.pathname === '/setup';
|
|
108
133
|
const isComingSoonRoute = location.pathname === '/coming-soon';
|
|
134
|
+
const wantsCloudWorkspaceSetup = !isCloudRuntime && workspaceSetupSource === 'cloud';
|
|
135
|
+
const isCloudWorkspaceSetupFlow = wantsCloudWorkspaceSetup && canUseCloudWorkspaceSetup;
|
|
136
|
+
const cloudWorkspaceImportMatchesCurrentWorkspace = Boolean(pendingCloudWorkspaceImport
|
|
137
|
+
&& currentWorkspaceId === pendingCloudWorkspaceImport.workspaceId);
|
|
138
|
+
const isCloudWorkspaceImportErrored = Boolean(pendingCloudWorkspaceImport
|
|
139
|
+
&& cloudWorkspaceImportMatchesCurrentWorkspace
|
|
140
|
+
&& workspaceSyncPhase === 'error');
|
|
141
|
+
const isCloudWorkspaceImportReady = Boolean(pendingCloudWorkspaceImport
|
|
142
|
+
&& cloudWorkspaceImportMatchesCurrentWorkspace
|
|
143
|
+
&& workspaceCloudSyncEnabled
|
|
144
|
+
&& workspaceSyncPhase === 'active'
|
|
145
|
+
&& !workspaceSyncBusy);
|
|
146
|
+
const cloudWorkspaceImportProgressLines = useMemo(() => {
|
|
147
|
+
if (!pendingCloudWorkspaceImport)
|
|
148
|
+
return [];
|
|
149
|
+
const connectingState = setupBusy ? 'In progress' : 'Complete';
|
|
150
|
+
const importingState = isCloudWorkspaceImportErrored
|
|
151
|
+
? 'Needs attention'
|
|
152
|
+
: (workspaceSyncBusy || workspaceSyncPhase === 'attach-cloud')
|
|
153
|
+
? 'In progress'
|
|
154
|
+
: (workspaceSyncPhase === 'active' ? 'Complete' : 'Waiting');
|
|
155
|
+
const finishingState = cloudWorkspaceImportFinalizing
|
|
156
|
+
? 'In progress'
|
|
157
|
+
: isCloudWorkspaceImportReady
|
|
158
|
+
? 'Ready'
|
|
159
|
+
: 'Waiting';
|
|
160
|
+
return [
|
|
161
|
+
`Connect workspace: ${connectingState}`,
|
|
162
|
+
`Import cloud data: ${importingState}`,
|
|
163
|
+
`Open local workspace: ${finishingState}`
|
|
164
|
+
];
|
|
165
|
+
}, [
|
|
166
|
+
pendingCloudWorkspaceImport,
|
|
167
|
+
setupBusy,
|
|
168
|
+
isCloudWorkspaceImportErrored,
|
|
169
|
+
workspaceSyncBusy,
|
|
170
|
+
workspaceSyncPhase,
|
|
171
|
+
cloudWorkspaceImportFinalizing,
|
|
172
|
+
isCloudWorkspaceImportReady
|
|
173
|
+
]);
|
|
109
174
|
const shouldForceLogin = isLikelyCloudAppHost
|
|
110
175
|
? ((!isAuthenticated || authBlocked) && !isPricingRoute && !isPlansRoute && !isPlansScreen)
|
|
111
176
|
: (((isCloudAuthRequired && !isAuthenticated) || shouldForceForAuthBlocked) && !isPricingRoute && !isPlansRoute && !isPlansScreen);
|
|
@@ -119,6 +184,23 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
119
184
|
&& !isPlansRoute
|
|
120
185
|
&& !isPlansScreen;
|
|
121
186
|
const directRegistrationEnabled = cloudAuthConfigured;
|
|
187
|
+
const authPageTitle = useMemo(() => {
|
|
188
|
+
switch (authMode) {
|
|
189
|
+
case 'register':
|
|
190
|
+
return 'Create Account | Taskforce';
|
|
191
|
+
case 'verify':
|
|
192
|
+
return 'Verify Email | Taskforce';
|
|
193
|
+
case 'forgot':
|
|
194
|
+
return 'Reset Password | Taskforce';
|
|
195
|
+
case 'reset':
|
|
196
|
+
return 'Set New Password | Taskforce';
|
|
197
|
+
case 'invite':
|
|
198
|
+
return 'Accept Invite | Taskforce';
|
|
199
|
+
case 'login':
|
|
200
|
+
default:
|
|
201
|
+
return 'Sign In | Taskforce';
|
|
202
|
+
}
|
|
203
|
+
}, [authMode]);
|
|
122
204
|
const setupStep = useMemo(() => String(new URLSearchParams(location.search).get('step') || '').trim().toLowerCase(), [location.search]);
|
|
123
205
|
const setupIntent = useMemo(() => String(new URLSearchParams(location.search).get('intent') || '').trim().toLowerCase(), [location.search]);
|
|
124
206
|
const isCreateWorkspaceIntent = isSetupRoute && setupStep === 'workspace' && setupIntent === 'create-workspace';
|
|
@@ -186,9 +268,11 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
186
268
|
useEffect(() => {
|
|
187
269
|
if (runtimeMode !== 'cloud' || !isAuthenticated || !authSessionResolved) {
|
|
188
270
|
setAccountAccessGate(null);
|
|
271
|
+
setAccountAccessGateResolved(false);
|
|
189
272
|
return;
|
|
190
273
|
}
|
|
191
274
|
setAccountAccessGate(null);
|
|
275
|
+
setAccountAccessGateResolved(false);
|
|
192
276
|
let cancelled = false;
|
|
193
277
|
(async () => {
|
|
194
278
|
try {
|
|
@@ -197,23 +281,32 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
197
281
|
credentials: 'include'
|
|
198
282
|
});
|
|
199
283
|
const payload = await res.json().catch(() => ({}));
|
|
200
|
-
if (cancelled
|
|
284
|
+
if (cancelled)
|
|
201
285
|
return;
|
|
286
|
+
if (!res.ok) {
|
|
287
|
+
setAccountAccessGate(null);
|
|
288
|
+
setAccountAccessGateResolved(true);
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
202
291
|
const gate = String(payload?.gate || '').trim().toLowerCase();
|
|
203
|
-
if (gate !== 'ok' && gate !== 'plan_selection_required' && gate !== 'misconfigured' && gate !== 'missing_entitlement') {
|
|
292
|
+
if (gate !== 'ok' && gate !== 'plan_selection_required' && gate !== 'checkout_pending' && gate !== 'misconfigured' && gate !== 'missing_entitlement') {
|
|
204
293
|
setAccountAccessGate(null);
|
|
294
|
+
setAccountAccessGateResolved(true);
|
|
205
295
|
return;
|
|
206
296
|
}
|
|
207
297
|
setAccountAccessGate({
|
|
208
|
-
gate,
|
|
298
|
+
gate: gate,
|
|
209
299
|
canAccessApp: payload?.canAccessApp === true,
|
|
210
300
|
canAccessPlans: payload?.canAccessPlans !== false,
|
|
211
301
|
message: typeof payload?.message === 'string' ? payload.message : null
|
|
212
302
|
});
|
|
303
|
+
setAccountAccessGateResolved(true);
|
|
213
304
|
}
|
|
214
305
|
catch {
|
|
215
|
-
if (!cancelled)
|
|
306
|
+
if (!cancelled) {
|
|
216
307
|
setAccountAccessGate(null);
|
|
308
|
+
setAccountAccessGateResolved(true);
|
|
309
|
+
}
|
|
217
310
|
}
|
|
218
311
|
})();
|
|
219
312
|
return () => {
|
|
@@ -246,6 +339,7 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
246
339
|
useEffect(() => {
|
|
247
340
|
if (!isSetupRoute || setupStep !== 'workspace')
|
|
248
341
|
return;
|
|
342
|
+
setWorkspaceSetupPage('details');
|
|
249
343
|
const source = String(new URLSearchParams(location.search).get('source') || '').trim().toLowerCase();
|
|
250
344
|
if (source === 'cloud' && canUseCloudWorkspaceSetup) {
|
|
251
345
|
setWorkspaceSetupSource('cloud');
|
|
@@ -343,6 +437,63 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
343
437
|
useEffect(() => {
|
|
344
438
|
setSetupTaxonomySections(defaultSetupTaxonomySections(selectedSetupPack));
|
|
345
439
|
}, [selectedSetupPackId, selectedSetupPack]);
|
|
440
|
+
useEffect(() => {
|
|
441
|
+
if (workspaceSetupSource === 'cloud') {
|
|
442
|
+
setWorkspaceSetupPage('details');
|
|
443
|
+
}
|
|
444
|
+
}, [workspaceSetupSource]);
|
|
445
|
+
useEffect(() => {
|
|
446
|
+
if (!isCloudWorkspaceSetupFlow) {
|
|
447
|
+
setPendingCloudWorkspaceImport(null);
|
|
448
|
+
setCloudWorkspaceImportFinalizing(false);
|
|
449
|
+
}
|
|
450
|
+
}, [isCloudWorkspaceSetupFlow]);
|
|
451
|
+
useEffect(() => {
|
|
452
|
+
if (!isCloudWorkspaceImportErrored || !pendingCloudWorkspaceImport)
|
|
453
|
+
return;
|
|
454
|
+
setSetupNotice(null);
|
|
455
|
+
setSetupError(workspaceLastErrorMessage
|
|
456
|
+
|| `Failed to import "${pendingCloudWorkspaceImport.name}" from cloud. Retry import to continue.`);
|
|
457
|
+
}, [isCloudWorkspaceImportErrored, pendingCloudWorkspaceImport, workspaceLastErrorMessage]);
|
|
458
|
+
useEffect(() => {
|
|
459
|
+
if (!isCloudWorkspaceImportReady || !pendingCloudWorkspaceImport || cloudWorkspaceImportFinalizing)
|
|
460
|
+
return;
|
|
461
|
+
let cancelled = false;
|
|
462
|
+
setCloudWorkspaceImportFinalizing(true);
|
|
463
|
+
setSetupError(null);
|
|
464
|
+
setSetupNotice(`Workspace import complete. Opening ${pendingCloudWorkspaceImport.name}...`);
|
|
465
|
+
void (async () => {
|
|
466
|
+
try {
|
|
467
|
+
await refreshSetupContext();
|
|
468
|
+
await Promise.all([
|
|
469
|
+
fetchWorkspaces(),
|
|
470
|
+
fetchTasks(true)
|
|
471
|
+
]);
|
|
472
|
+
if (cancelled)
|
|
473
|
+
return;
|
|
474
|
+
setPendingCloudWorkspaceImport(null);
|
|
475
|
+
navigate('/', { replace: true });
|
|
476
|
+
}
|
|
477
|
+
catch {
|
|
478
|
+
if (cancelled)
|
|
479
|
+
return;
|
|
480
|
+
setSetupNotice(null);
|
|
481
|
+
setSetupError(`Imported "${pendingCloudWorkspaceImport.name}" but failed to finish opening it.`);
|
|
482
|
+
setCloudWorkspaceImportFinalizing(false);
|
|
483
|
+
}
|
|
484
|
+
})();
|
|
485
|
+
return () => {
|
|
486
|
+
cancelled = true;
|
|
487
|
+
};
|
|
488
|
+
}, [
|
|
489
|
+
cloudWorkspaceImportFinalizing,
|
|
490
|
+
fetchTasks,
|
|
491
|
+
fetchWorkspaces,
|
|
492
|
+
isCloudWorkspaceImportReady,
|
|
493
|
+
navigate,
|
|
494
|
+
pendingCloudWorkspaceImport,
|
|
495
|
+
refreshSetupContext
|
|
496
|
+
]);
|
|
346
497
|
const nextPath = useMemo(() => {
|
|
347
498
|
const raw = new URLSearchParams(location.search).get('next') || '/';
|
|
348
499
|
if (!raw.startsWith('/'))
|
|
@@ -351,6 +502,50 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
351
502
|
return '/';
|
|
352
503
|
return raw;
|
|
353
504
|
}, [location.search]);
|
|
505
|
+
const cloudWorkspaceSelectionPath = '/setup?step=workspace&source=cloud&postAuth=select-cloud-workspace';
|
|
506
|
+
const shouldReturnToCloudWorkspaceSelection = useMemo(() => {
|
|
507
|
+
try {
|
|
508
|
+
const parsed = new URL(nextPath, 'https://taskforce.local');
|
|
509
|
+
return parsed.pathname === '/setup'
|
|
510
|
+
&& parsed.searchParams.get('step') === 'workspace'
|
|
511
|
+
&& parsed.searchParams.get('source') === 'cloud'
|
|
512
|
+
&& parsed.searchParams.get('postAuth') === 'select-cloud-workspace';
|
|
513
|
+
}
|
|
514
|
+
catch {
|
|
515
|
+
return false;
|
|
516
|
+
}
|
|
517
|
+
}, [nextPath]);
|
|
518
|
+
const resolveWorkspaceSetupPath = useCallback(() => (shouldReturnToCloudWorkspaceSelection
|
|
519
|
+
? cloudWorkspaceSelectionPath
|
|
520
|
+
: '/setup?step=workspace'), [shouldReturnToCloudWorkspaceSelection]);
|
|
521
|
+
const resolvedSignupInterval = selectedSignupInterval === 'year' ? 'year' : 'month';
|
|
522
|
+
const hasCommercialOnboardingGate = useCallback((input) => {
|
|
523
|
+
return Boolean(input?.planSelectionRequired
|
|
524
|
+
|| input?.checkoutPending
|
|
525
|
+
|| input?.commercialState === 'pending_plan_selection'
|
|
526
|
+
|| input?.commercialState === 'checkout_pending');
|
|
527
|
+
}, []);
|
|
528
|
+
const resolvePostRegistrationPath = useCallback((input) => {
|
|
529
|
+
if (input?.planSelectionRequired || input?.commercialState === 'pending_plan_selection') {
|
|
530
|
+
return planSelectionPath;
|
|
531
|
+
}
|
|
532
|
+
if (input?.checkoutPending || input?.commercialState === 'checkout_pending') {
|
|
533
|
+
return buildPlansPath({
|
|
534
|
+
gate: 'checkout_pending',
|
|
535
|
+
checkout: selectedSignupPlanVersionId ? 'start' : null,
|
|
536
|
+
planId: selectedSignupPlanId || null,
|
|
537
|
+
planVersionId: selectedSignupPlanVersionId || null,
|
|
538
|
+
interval: selectedSignupPlanVersionId ? resolvedSignupInterval : null
|
|
539
|
+
});
|
|
540
|
+
}
|
|
541
|
+
return nextPath;
|
|
542
|
+
}, [
|
|
543
|
+
nextPath,
|
|
544
|
+
planSelectionPath,
|
|
545
|
+
resolvedSignupInterval,
|
|
546
|
+
selectedSignupPlanId,
|
|
547
|
+
selectedSignupPlanVersionId
|
|
548
|
+
]);
|
|
354
549
|
const loginRouteMode = useMemo(() => {
|
|
355
550
|
const modeRaw = String(new URLSearchParams(location.search).get('mode') || '').trim().toLowerCase();
|
|
356
551
|
if (modeRaw === 'register') {
|
|
@@ -374,26 +569,6 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
374
569
|
const query = params.toString();
|
|
375
570
|
navigate(`/login${query ? `?${query}` : ''}`, { replace: true });
|
|
376
571
|
}, [directRegistrationEnabled, location.search, navigate]);
|
|
377
|
-
const routeToWorkspaceSetupIfNeeded = useCallback(async (hintWorkspaceSetupRequired) => {
|
|
378
|
-
if (!workspaceSwitchingEnabled)
|
|
379
|
-
return false;
|
|
380
|
-
const workspacesResult = await fetchWorkspaces();
|
|
381
|
-
if (workspacesResult.success) {
|
|
382
|
-
const workspaceCount = Array.isArray(workspacesResult.workspaces)
|
|
383
|
-
? workspacesResult.workspaces.length
|
|
384
|
-
: 0;
|
|
385
|
-
if (workspaceCount === 0) {
|
|
386
|
-
navigate('/setup?step=workspace', { replace: true });
|
|
387
|
-
return true;
|
|
388
|
-
}
|
|
389
|
-
return false;
|
|
390
|
-
}
|
|
391
|
-
if (hintWorkspaceSetupRequired) {
|
|
392
|
-
navigate('/setup?step=workspace', { replace: true });
|
|
393
|
-
return true;
|
|
394
|
-
}
|
|
395
|
-
return false;
|
|
396
|
-
}, [fetchWorkspaces, navigate, workspaceSwitchingEnabled]);
|
|
397
572
|
useEffect(() => {
|
|
398
573
|
if (!isLoginRoute)
|
|
399
574
|
return;
|
|
@@ -405,6 +580,46 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
405
580
|
if (loginRouteMode === 'invite' && loginRouteToken)
|
|
406
581
|
setInviteToken(loginRouteToken);
|
|
407
582
|
}, [isLoginRoute, loginRouteMode, loginRouteToken]);
|
|
583
|
+
useEffect(() => {
|
|
584
|
+
if (authMode === 'verify')
|
|
585
|
+
return;
|
|
586
|
+
if (isLoginRoute && loginRouteMode === 'verify')
|
|
587
|
+
return;
|
|
588
|
+
setPendingVerificationWorkspaceSetupRequired(false);
|
|
589
|
+
setPendingVerificationCommercialState(null);
|
|
590
|
+
setPendingVerificationRedirectPath(null);
|
|
591
|
+
}, [authMode, isLoginRoute, loginRouteMode]);
|
|
592
|
+
useEffect(() => {
|
|
593
|
+
if (!isLoginRoute || !pendingPostAuthRedirect || !authSessionResolved || !isAuthenticated)
|
|
594
|
+
return;
|
|
595
|
+
if (runtimeMode === 'cloud' && !pendingPostAuthRedirect.commercialOnboardingGate && !accountAccessGateResolved) {
|
|
596
|
+
return;
|
|
597
|
+
}
|
|
598
|
+
if (!pendingPostAuthRedirect.commercialOnboardingGate && pendingPostAuthRedirect.workspaceSetupRequired) {
|
|
599
|
+
navigate(resolveWorkspaceSetupPath(), { replace: true });
|
|
600
|
+
}
|
|
601
|
+
else {
|
|
602
|
+
navigate(pendingPostAuthRedirect.path, { replace: true });
|
|
603
|
+
}
|
|
604
|
+
setLoginEmail('');
|
|
605
|
+
setLoginDisplayName('');
|
|
606
|
+
setLoginPassword('');
|
|
607
|
+
setPendingPostAuthRedirect(null);
|
|
608
|
+
}, [
|
|
609
|
+
accountAccessGateResolved,
|
|
610
|
+
authSessionResolved,
|
|
611
|
+
isAuthenticated,
|
|
612
|
+
isLoginRoute,
|
|
613
|
+
navigate,
|
|
614
|
+
pendingPostAuthRedirect,
|
|
615
|
+
resolveWorkspaceSetupPath,
|
|
616
|
+
runtimeMode
|
|
617
|
+
]);
|
|
618
|
+
useEffect(() => {
|
|
619
|
+
if (!isLoginRoute)
|
|
620
|
+
return;
|
|
621
|
+
document.title = authPageTitle;
|
|
622
|
+
}, [authPageTitle, isLoginRoute]);
|
|
408
623
|
useEffect(() => {
|
|
409
624
|
if (!isLoginRoute || authMode !== 'invite' || !inviteToken.trim())
|
|
410
625
|
return;
|
|
@@ -491,6 +706,17 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
491
706
|
const shouldStayOnInviteRoute = isLoginRoute
|
|
492
707
|
&& authMode === 'invite'
|
|
493
708
|
&& inviteToken.trim().length > 0;
|
|
709
|
+
const shouldDelayAuthenticatedLoginRedirect = isLoginRoute
|
|
710
|
+
&& isAuthenticated
|
|
711
|
+
&& !shouldStayOnInviteRoute
|
|
712
|
+
&& (Boolean(pendingPostAuthRedirect)
|
|
713
|
+
|| workspaceBootstrapPending
|
|
714
|
+
|| authMode === 'register'
|
|
715
|
+
|| authMode === 'verify'
|
|
716
|
+
|| (runtimeMode === 'cloud' && (!configLoaded || !accountAccessGateResolved)));
|
|
717
|
+
if (shouldDelayAuthenticatedLoginRedirect) {
|
|
718
|
+
return;
|
|
719
|
+
}
|
|
494
720
|
if (isLoginRoute && isAuthenticated && !shouldStayOnInviteRoute)
|
|
495
721
|
navigate(nextPath, { replace: true });
|
|
496
722
|
}
|
|
@@ -502,6 +728,9 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
502
728
|
}
|
|
503
729
|
return;
|
|
504
730
|
}
|
|
731
|
+
if (isLoginRoute && !isAuthenticated) {
|
|
732
|
+
return;
|
|
733
|
+
}
|
|
505
734
|
if (shouldForceAccountAccessGate) {
|
|
506
735
|
if (!isPlansScreen) {
|
|
507
736
|
const gate = encodeURIComponent(String(accountAccessGate?.gate || 'plan_selection_required'));
|
|
@@ -509,6 +738,9 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
509
738
|
}
|
|
510
739
|
return;
|
|
511
740
|
}
|
|
741
|
+
if (isSetupRoute && pendingCloudWorkspaceImport) {
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
512
744
|
if (setupGateActive) {
|
|
513
745
|
if (hasSetupReadError) {
|
|
514
746
|
if (!isSetupRoute || setupStep !== 'error') {
|
|
@@ -524,7 +756,7 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
524
756
|
}
|
|
525
757
|
if (needsWorkspaceSetup) {
|
|
526
758
|
if (!isSetupRoute || setupStep !== 'workspace') {
|
|
527
|
-
navigate(
|
|
759
|
+
navigate(resolveWorkspaceSetupPath(), { replace: true });
|
|
528
760
|
}
|
|
529
761
|
return;
|
|
530
762
|
}
|
|
@@ -547,19 +779,26 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
547
779
|
isPlansRoute,
|
|
548
780
|
isPlansScreen,
|
|
549
781
|
accountAccessGate?.gate,
|
|
782
|
+
accountAccessGateResolved,
|
|
783
|
+
authMode,
|
|
550
784
|
setupGateActive,
|
|
551
785
|
setupState,
|
|
552
786
|
hasSetupReadError,
|
|
553
787
|
isCreateWorkspaceIntent,
|
|
788
|
+
pendingCloudWorkspaceImport,
|
|
554
789
|
needsGlobalSetup,
|
|
555
790
|
needsWorkspaceSetup,
|
|
556
791
|
setupStep,
|
|
557
792
|
workspaceBootstrapPending,
|
|
793
|
+
configLoaded,
|
|
794
|
+
runtimeMode,
|
|
795
|
+
inviteToken,
|
|
558
796
|
location.pathname,
|
|
559
797
|
location.search,
|
|
560
798
|
location.hash,
|
|
561
799
|
navigate,
|
|
562
|
-
nextPath
|
|
800
|
+
nextPath,
|
|
801
|
+
pendingPostAuthRedirect
|
|
563
802
|
]);
|
|
564
803
|
useEffect(() => {
|
|
565
804
|
if (mode === 'widget')
|
|
@@ -611,18 +850,26 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
611
850
|
if (isLoginRoute) {
|
|
612
851
|
return (_jsx("div", { className: shellStyles.standaloneWrapper, "data-theme": currentTheme, children: _jsx("div", { className: authStyles.loginView, style: {
|
|
613
852
|
backgroundImage: `linear-gradient(rgba(15, 15, 26, 0.88), rgba(15, 15, 26, 0.9)), url(${taskforceBg})`
|
|
614
|
-
}, children: _jsxs("div", { className: authStyles.loginCard, children: [!isCloudRuntime && !shouldForceLogin && (_jsx("button", { className: authStyles.loginCloseBtn, "aria-label": "Close sign in", onClick: () => navigate(nextPath, { replace: true }), children: "\u00D7" })), _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" })] })] }), _jsxs("p", { className: authStyles.loginSubtitle, children: [authMode === 'login' && 'Sign in with your account credentials.', authMode === 'register' && 'Create your Taskforce account.', authMode === 'verify' && 'Enter your verification token.', authMode === 'forgot' && 'Request a password reset link.', authMode === 'reset' && 'Set a new password using your reset token.', authMode === 'invite' && (inviteResolution === 'existing_user_ready'
|
|
853
|
+
}, children: _jsxs("div", { className: authStyles.loginCard, children: [!isCloudRuntime && !shouldForceLogin && (_jsx("button", { className: authStyles.loginCloseBtn, "aria-label": "Close sign in", "data-testid": "auth-close-button", onClick: () => navigate(nextPath, { replace: true }), children: "\u00D7" })), _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" })] })] }), _jsxs("p", { className: authStyles.loginSubtitle, children: [authMode === 'login' && 'Sign in with your account credentials.', authMode === 'register' && 'Create your Taskforce account.', authMode === 'verify' && 'Enter your verification token.', authMode === 'forgot' && 'Request a password reset link.', authMode === 'reset' && 'Set a new password using your reset token.', authMode === 'invite' && (inviteResolution === 'existing_user_ready'
|
|
615
854
|
? `You've been invited to join ${inviteWorkspaceId || 'this workspace'}.`
|
|
616
855
|
: inviteResolution === 'existing_user_signed_out'
|
|
617
856
|
? 'Sign in to join this workspace.'
|
|
618
|
-
: 'Create your account to join this workspace.')] }), (authMode === 'login'
|
|
857
|
+
: 'Create your account to join this workspace.')] }), (authMode === 'login'
|
|
858
|
+
|| authMode === 'register'
|
|
859
|
+
|| authMode === 'verify'
|
|
860
|
+
|| authMode === 'forgot'
|
|
861
|
+
|| (authMode === 'invite' && inviteResolution === 'existing_user_signed_out')) && (_jsxs("label", { className: authStyles.loginField, children: [_jsx("span", { className: authStyles.loginFieldLabel, children: "Email" }), _jsx("input", { className: `${styles.input} ${authStyles.loginInput}`, type: "email", value: loginEmail, placeholder: "Email", autoComplete: "email", "data-testid": "auth-email-input", onChange: (event) => setLoginEmail(event.target.value) })] })), authMode === 'register' && (_jsxs("label", { className: authStyles.loginField, children: [_jsx("span", { className: authStyles.loginFieldLabel, children: "Display name" }), _jsx("input", { className: `${styles.input} ${authStyles.loginInput}`, type: "text", value: loginDisplayName, placeholder: "Display name", autoComplete: "nickname", "data-testid": "auth-display-name-input", onChange: (event) => setLoginDisplayName(event.target.value) })] })), (authMode === 'login'
|
|
619
862
|
|| authMode === 'register'
|
|
620
863
|
|| authMode === 'reset'
|
|
621
|
-
|| (authMode === 'invite' && (invitePasswordRequired || inviteResolution === 'existing_user_signed_out'))) && (
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
864
|
+
|| (authMode === 'invite' && (invitePasswordRequired || inviteResolution === 'existing_user_signed_out'))) && (_jsxs("label", { className: authStyles.loginField, children: [_jsx("span", { className: authStyles.loginFieldLabel, children: authMode === 'reset'
|
|
865
|
+
? 'New password'
|
|
866
|
+
: (authMode === 'invite' && invitePasswordRequired ? 'Create password' : 'Password') }), _jsx("input", { className: `${styles.input} ${authStyles.loginInput}`, type: "password", value: loginPassword, placeholder: authMode === 'reset'
|
|
867
|
+
? 'New password'
|
|
868
|
+
: (authMode === 'invite'
|
|
869
|
+
? (invitePasswordRequired ? 'Create password' : 'Password')
|
|
870
|
+
: 'Password'), autoComplete: authMode === 'register' || authMode === 'reset' || (authMode === 'invite' && invitePasswordRequired)
|
|
871
|
+
? 'new-password'
|
|
872
|
+
: 'current-password', "data-testid": "auth-password-input", onChange: (event) => setLoginPassword(event.target.value) })] })), authMode === 'verify' && (_jsxs("label", { className: authStyles.loginField, children: [_jsx("span", { className: authStyles.loginFieldLabel, children: "Verification token" }), _jsx("input", { className: `${styles.input} ${authStyles.loginInput}`, type: "text", value: verificationToken, placeholder: "Verification token", autoComplete: "one-time-code", "data-testid": "auth-verification-token-input", onChange: (event) => setVerificationToken(event.target.value) })] })), authMode === 'reset' && (_jsxs("label", { className: authStyles.loginField, children: [_jsx("span", { className: authStyles.loginFieldLabel, children: "Reset token" }), _jsx("input", { className: `${styles.input} ${authStyles.loginInput}`, type: "text", value: resetToken, placeholder: "Reset token", autoComplete: "one-time-code", "data-testid": "auth-reset-token-input", onChange: (event) => setResetToken(event.target.value) })] })), authMode === 'invite' && (_jsxs(_Fragment, { children: [_jsxs("label", { className: authStyles.loginField, children: [_jsx("span", { className: authStyles.loginFieldLabel, children: "Invite token" }), _jsx("input", { className: `${styles.input} ${authStyles.loginInput}`, type: "text", value: inviteToken, placeholder: "Invite token", "data-testid": "auth-invite-token-input", onChange: (event) => setInviteToken(event.target.value) })] }), inviteEmail && (_jsxs("p", { className: authStyles.loginSubtitle, children: ["Invite for: ", _jsx("strong", { children: inviteEmail }), inviteWorkspaceId ? ` · Workspace: ${inviteWorkspaceId}` : ''] }))] })), loginNotice && _jsx("p", { className: authStyles.loginSubtitle, children: loginNotice }), loginError && _jsx("p", { className: authStyles.loginError, children: loginError }), authMode === 'login' && loginErrorCode === 'EMAIL_NOT_VERIFIED' && loginEmail.trim() && (_jsx("button", { className: authStyles.authModeLink, disabled: loginBusy || verifyCooldownActive, onClick: async () => {
|
|
626
873
|
setLoginBusy(true);
|
|
627
874
|
setLoginNotice(null);
|
|
628
875
|
const sent = await requestEmailVerification(loginEmail);
|
|
@@ -639,172 +886,206 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
639
886
|
setLoginError(formatAuthError(sent));
|
|
640
887
|
}
|
|
641
888
|
setLoginBusy(false);
|
|
642
|
-
}, children: verifyCooldownActive ? `Resend in ${verifyCooldownSeconds}s` : 'Resend Verification Email' })), _jsx("button", { className: `${styles.submitBtn} ${authStyles.loginPrimaryBtn}`, disabled: loginBusy || (authMode === '
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
result = await loginWithCredentials(loginEmail, loginPassword);
|
|
655
|
-
}
|
|
656
|
-
else if (authMode === 'register') {
|
|
657
|
-
result = await registerWithCredentials(loginEmail, loginPassword, {
|
|
658
|
-
displayName: loginDisplayName,
|
|
659
|
-
planId: selectedSignupPlanId || undefined,
|
|
660
|
-
planVersionId: selectedSignupPlanVersionId || undefined,
|
|
661
|
-
interval: selectedSignupInterval || undefined
|
|
662
|
-
});
|
|
663
|
-
}
|
|
664
|
-
else if (authMode === 'verify') {
|
|
665
|
-
result = await confirmEmailVerification(verificationToken);
|
|
666
|
-
}
|
|
667
|
-
else if (authMode === 'forgot') {
|
|
668
|
-
result = await requestPasswordReset(loginEmail);
|
|
669
|
-
}
|
|
670
|
-
else if (authMode === 'reset') {
|
|
671
|
-
result = await confirmPasswordReset(resetToken, loginPassword);
|
|
672
|
-
}
|
|
673
|
-
else if (authMode === 'invite') {
|
|
674
|
-
if (inviteResolution === 'existing_user_signed_out') {
|
|
889
|
+
}, children: verifyCooldownActive ? `Resend in ${verifyCooldownSeconds}s` : 'Resend Verification Email' })), _jsx("div", { className: authStyles.authPrimaryActions, role: "group", "aria-label": "Primary authentication action", children: _jsx("button", { className: `${styles.submitBtn} ${authStyles.loginPrimaryBtn}`, disabled: loginBusy || (authMode === 'forgot' && resetCooldownActive), "data-testid": "auth-submit-button", onClick: async () => {
|
|
890
|
+
setLoginBusy(true);
|
|
891
|
+
setLoginNotice(null);
|
|
892
|
+
setLoginError(null);
|
|
893
|
+
setLoginErrorCode(null);
|
|
894
|
+
if (authMode === 'register' && !loginDisplayName.trim()) {
|
|
895
|
+
setLoginError('Display name is required.');
|
|
896
|
+
setLoginBusy(false);
|
|
897
|
+
return;
|
|
898
|
+
}
|
|
899
|
+
let result = { success: false };
|
|
900
|
+
if (authMode === 'login') {
|
|
675
901
|
result = await loginWithCredentials(loginEmail, loginPassword);
|
|
676
902
|
}
|
|
677
|
-
else {
|
|
678
|
-
result =
|
|
679
|
-
|
|
680
|
-
:
|
|
903
|
+
else if (authMode === 'register') {
|
|
904
|
+
result = await registerWithCredentials(loginEmail, loginPassword, {
|
|
905
|
+
displayName: loginDisplayName,
|
|
906
|
+
planId: selectedSignupPlanId || undefined,
|
|
907
|
+
planVersionId: selectedSignupPlanVersionId || undefined,
|
|
908
|
+
interval: selectedSignupInterval || undefined
|
|
909
|
+
});
|
|
681
910
|
}
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
if (authMode === 'register'
|
|
685
|
-
&& (result.code === 'SIGNUP_PLAN_NOT_ENABLED'
|
|
686
|
-
|| result.code === 'SIGNUP_PLAN_VERSION_NOT_FOUND'
|
|
687
|
-
|| result.code === 'SIGNUP_PLAN_VERSION_REQUIRED')) {
|
|
688
|
-
navigate(planSelectionPath, { replace: true });
|
|
689
|
-
setLoginBusy(false);
|
|
690
|
-
return;
|
|
911
|
+
else if (authMode === 'verify') {
|
|
912
|
+
result = await confirmEmailVerification(verificationToken);
|
|
691
913
|
}
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
if (authMode === 'login' && (result.code === 'WORKSPACE_NOT_FOUND' || result.code === 'WORKSPACE_ID_REQUIRED')) {
|
|
695
|
-
navigate('/setup?step=workspace', { replace: true });
|
|
696
|
-
setLoginBusy(false);
|
|
697
|
-
return;
|
|
914
|
+
else if (authMode === 'forgot') {
|
|
915
|
+
result = await requestPasswordReset(loginEmail);
|
|
698
916
|
}
|
|
699
|
-
if (authMode === '
|
|
700
|
-
|
|
701
|
-
|
|
917
|
+
else if (authMode === 'reset') {
|
|
918
|
+
result = await confirmPasswordReset(resetToken, loginPassword);
|
|
919
|
+
}
|
|
920
|
+
else if (authMode === 'invite') {
|
|
921
|
+
if (inviteResolution === 'existing_user_signed_out') {
|
|
922
|
+
result = await loginWithCredentials(loginEmail, loginPassword);
|
|
923
|
+
}
|
|
924
|
+
else {
|
|
925
|
+
result = invitePasswordRequired
|
|
926
|
+
? await acceptInviteWithToken(inviteToken, loginPassword)
|
|
927
|
+
: await joinInviteWithToken(inviteToken);
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
if (!result.success) {
|
|
931
|
+
if (authMode === 'register'
|
|
932
|
+
&& (result.code === 'SIGNUP_PLAN_NOT_ENABLED'
|
|
933
|
+
|| result.code === 'SIGNUP_PLAN_VERSION_NOT_FOUND'
|
|
934
|
+
|| result.code === 'SIGNUP_PLAN_VERSION_REQUIRED')) {
|
|
935
|
+
navigate(planSelectionPath, { replace: true });
|
|
936
|
+
setLoginBusy(false);
|
|
937
|
+
return;
|
|
938
|
+
}
|
|
939
|
+
setLoginError(formatAuthError(result));
|
|
940
|
+
setLoginErrorCode(result.code || null);
|
|
941
|
+
if (authMode === 'login' && (result.code === 'WORKSPACE_NOT_FOUND' || result.code === 'WORKSPACE_ID_REQUIRED')) {
|
|
942
|
+
navigate('/setup?step=workspace', { replace: true });
|
|
943
|
+
setLoginBusy(false);
|
|
944
|
+
return;
|
|
945
|
+
}
|
|
946
|
+
if (authMode === 'login' && result.code === 'EMAIL_NOT_VERIFIED' && loginEmail.trim()) {
|
|
947
|
+
const sent = await requestEmailVerification(loginEmail);
|
|
948
|
+
if (sent.success) {
|
|
949
|
+
setVerifyCooldownUntil(Date.now() + 30_000);
|
|
950
|
+
setLoginMode('verify');
|
|
951
|
+
if (sent.verificationToken)
|
|
952
|
+
setVerificationToken(sent.verificationToken);
|
|
953
|
+
setLoginNotice('Email not verified. A verification token was sent.');
|
|
954
|
+
setLoginError(null);
|
|
955
|
+
setLoginErrorCode(null);
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
if (authMode === 'register' && result.code === 'EMAIL_ALREADY_EXISTS_UNVERIFIED' && loginEmail.trim()) {
|
|
702
959
|
setVerifyCooldownUntil(Date.now() + 30_000);
|
|
703
960
|
setLoginMode('verify');
|
|
704
|
-
if (
|
|
705
|
-
setVerificationToken(
|
|
706
|
-
setLoginNotice(
|
|
961
|
+
if (result.verificationToken)
|
|
962
|
+
setVerificationToken(result.verificationToken);
|
|
963
|
+
setLoginNotice(result.emailSent === false
|
|
964
|
+
? `This email already has an unverified account, but verification email delivery failed. ${result.emailError || 'Try Resend Verification again.'}`
|
|
965
|
+
: 'This email already has an unverified account. Check your email for verification instructions.');
|
|
707
966
|
setLoginError(null);
|
|
708
967
|
setLoginErrorCode(null);
|
|
709
968
|
}
|
|
710
969
|
}
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
970
|
+
else if (authMode === 'register') {
|
|
971
|
+
const postRegistrationPath = resolvePostRegistrationPath(result);
|
|
972
|
+
const commercialOnboardingGate = hasCommercialOnboardingGate(result);
|
|
973
|
+
if (result.verificationRequired) {
|
|
974
|
+
setPendingVerificationWorkspaceSetupRequired(Boolean(result.workspaceSetupRequired));
|
|
975
|
+
setPendingVerificationCommercialState(commercialOnboardingGate
|
|
976
|
+
? String(result.commercialState || '').trim() || (result.planSelectionRequired ? 'pending_plan_selection' : 'checkout_pending')
|
|
977
|
+
: null);
|
|
978
|
+
setPendingVerificationRedirectPath(postRegistrationPath);
|
|
979
|
+
if (result.verificationToken)
|
|
980
|
+
setVerificationToken(result.verificationToken);
|
|
981
|
+
setVerifyCooldownUntil(Date.now() + 30_000);
|
|
982
|
+
setLoginMode('verify');
|
|
983
|
+
setLoginNotice(result.emailSent === false
|
|
984
|
+
? `Account created, but verification email failed to send. ${result.emailError || 'Try Resend Verification again.'}`
|
|
985
|
+
: 'Account created. Check your email for verification instructions.');
|
|
986
|
+
}
|
|
987
|
+
else {
|
|
988
|
+
setPendingPostAuthRedirect({
|
|
989
|
+
path: postRegistrationPath,
|
|
990
|
+
workspaceSetupRequired: Boolean(result.workspaceSetupRequired),
|
|
991
|
+
commercialOnboardingGate
|
|
992
|
+
});
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
else if (authMode === 'verify') {
|
|
996
|
+
await retryBootstrapChecks();
|
|
997
|
+
const commercialOnboardingGate = pendingVerificationCommercialState === 'pending_plan_selection'
|
|
998
|
+
|| pendingVerificationCommercialState === 'checkout_pending';
|
|
999
|
+
setPendingPostAuthRedirect({
|
|
1000
|
+
path: pendingVerificationRedirectPath || nextPath,
|
|
1001
|
+
workspaceSetupRequired: pendingVerificationWorkspaceSetupRequired,
|
|
1002
|
+
commercialOnboardingGate
|
|
1003
|
+
});
|
|
716
1004
|
}
|
|
717
|
-
if (
|
|
718
|
-
if (result.
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
setLoginMode('
|
|
1005
|
+
else if (authMode === 'forgot') {
|
|
1006
|
+
if (result.resetToken)
|
|
1007
|
+
setResetToken(result.resetToken);
|
|
1008
|
+
setResetCooldownUntil(Date.now() + 30_000);
|
|
1009
|
+
setLoginMode('reset');
|
|
722
1010
|
setLoginNotice(result.emailSent === false
|
|
723
|
-
? `
|
|
724
|
-
: '
|
|
1011
|
+
? `Password reset email failed to send. ${result.emailError || 'Try again in a minute.'}`
|
|
1012
|
+
: 'Password reset instructions sent.');
|
|
725
1013
|
}
|
|
726
|
-
else {
|
|
1014
|
+
else if (authMode === 'reset') {
|
|
1015
|
+
setLoginMode('login');
|
|
1016
|
+
setLoginNotice('Password updated. Sign in with your new password.');
|
|
1017
|
+
}
|
|
1018
|
+
else if (authMode === 'invite') {
|
|
1019
|
+
if (inviteResolution === 'existing_user_signed_out') {
|
|
1020
|
+
setAuthMode('invite');
|
|
1021
|
+
setLoginPassword('');
|
|
1022
|
+
setLoginNotice('Signed in. Review the invite details to continue.');
|
|
1023
|
+
setLoginBusy(false);
|
|
1024
|
+
return;
|
|
1025
|
+
}
|
|
1026
|
+
if (result.workspaceSetupRequired) {
|
|
1027
|
+
navigate(resolveWorkspaceSetupPath(), { replace: true });
|
|
1028
|
+
setLoginBusy(false);
|
|
1029
|
+
return;
|
|
1030
|
+
}
|
|
1031
|
+
navigate(nextPath, { replace: true });
|
|
1032
|
+
}
|
|
1033
|
+
else if (authMode === 'login') {
|
|
1034
|
+
if (inviteToken.trim()) {
|
|
1035
|
+
setAuthMode('invite');
|
|
1036
|
+
setLoginPassword('');
|
|
1037
|
+
setLoginNotice('Signed in. Review the invite details to continue.');
|
|
1038
|
+
setLoginBusy(false);
|
|
1039
|
+
return;
|
|
1040
|
+
}
|
|
1041
|
+
if (result.workspaceSetupRequired) {
|
|
1042
|
+
navigate(resolveWorkspaceSetupPath(), { replace: true });
|
|
1043
|
+
setLoginBusy(false);
|
|
1044
|
+
return;
|
|
1045
|
+
}
|
|
727
1046
|
setLoginEmail('');
|
|
728
1047
|
setLoginDisplayName('');
|
|
729
1048
|
setLoginPassword('');
|
|
730
1049
|
navigate(nextPath, { replace: true });
|
|
731
1050
|
}
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
setLoginEmail('');
|
|
736
|
-
setLoginPassword('');
|
|
737
|
-
navigate(nextPath, { replace: true });
|
|
738
|
-
}
|
|
739
|
-
else if (authMode === 'forgot') {
|
|
740
|
-
if (result.resetToken)
|
|
741
|
-
setResetToken(result.resetToken);
|
|
742
|
-
setResetCooldownUntil(Date.now() + 30_000);
|
|
743
|
-
setLoginMode('reset');
|
|
744
|
-
setLoginNotice(result.emailSent === false
|
|
745
|
-
? `Password reset email failed to send. ${result.emailError || 'Try again in a minute.'}`
|
|
746
|
-
: 'Password reset instructions sent.');
|
|
747
|
-
}
|
|
748
|
-
else if (authMode === 'reset') {
|
|
749
|
-
setLoginMode('login');
|
|
750
|
-
setLoginNotice('Password updated. Sign in with your new password.');
|
|
751
|
-
}
|
|
752
|
-
else if (authMode === 'invite') {
|
|
753
|
-
if (inviteResolution === 'existing_user_signed_out') {
|
|
754
|
-
setAuthMode('invite');
|
|
755
|
-
setLoginPassword('');
|
|
756
|
-
setLoginNotice('Signed in. Review the invite details to continue.');
|
|
757
|
-
setLoginBusy(false);
|
|
758
|
-
return;
|
|
759
|
-
}
|
|
760
|
-
if (result.workspaceSetupRequired) {
|
|
761
|
-
navigate('/setup?step=workspace', { replace: true });
|
|
762
|
-
setLoginBusy(false);
|
|
763
|
-
return;
|
|
764
|
-
}
|
|
765
|
-
navigate(nextPath, { replace: true });
|
|
766
|
-
}
|
|
767
|
-
else if (authMode === 'login') {
|
|
768
|
-
if (inviteToken.trim()) {
|
|
769
|
-
setAuthMode('invite');
|
|
1051
|
+
else {
|
|
1052
|
+
setLoginEmail('');
|
|
1053
|
+
setLoginDisplayName('');
|
|
770
1054
|
setLoginPassword('');
|
|
771
|
-
|
|
772
|
-
setLoginBusy(false);
|
|
773
|
-
return;
|
|
774
|
-
}
|
|
775
|
-
if (result.workspaceSetupRequired) {
|
|
776
|
-
navigate('/setup?step=workspace', { replace: true });
|
|
777
|
-
setLoginBusy(false);
|
|
778
|
-
return;
|
|
1055
|
+
navigate(nextPath, { replace: true });
|
|
779
1056
|
}
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
1057
|
+
setLoginBusy(false);
|
|
1058
|
+
}, children: loginBusy ? 'Working...' : (authMode === 'login' ? 'Sign In'
|
|
1059
|
+
: authMode === 'register' ? 'Create Account'
|
|
1060
|
+
: authMode === 'verify' ? 'Verify Email'
|
|
1061
|
+
: authMode === 'forgot'
|
|
1062
|
+
? (resetCooldownActive ? `Retry in ${resetCooldownSeconds}s` : 'Send Reset Link')
|
|
1063
|
+
: authMode === 'reset'
|
|
1064
|
+
? 'Reset Password'
|
|
1065
|
+
: (inviteResolution === 'existing_user_signed_out'
|
|
1066
|
+
? 'Sign In to Continue'
|
|
1067
|
+
: (invitePasswordRequired ? 'Create Account and Join' : 'Join Workspace'))) }) }), authMode === 'register' && (_jsxs("p", { className: authStyles.registerConsent, children: ["By creating an account, you agree to the", ' ', _jsx("a", { className: authStyles.registerConsentLink, href: "https://taskforcehq.com/legal/terms", target: "_blank", rel: "noopener noreferrer", children: "Terms of Service" }), ' ', "and acknowledge the", ' ', _jsx("a", { className: authStyles.registerConsentLink, href: "https://taskforcehq.com/legal/privacy", target: "_blank", rel: "noopener noreferrer", children: "Privacy Policy" }), "."] })), _jsxs("div", { className: authStyles.authSecondaryActions, role: "group", "aria-label": "Authentication navigation", children: [directRegistrationEnabled && (authMode === 'login' || authMode === 'register') && (_jsxs("p", { className: authStyles.authModeSwitch, children: [authMode === 'login' ? 'Need an account?' : 'Already have an account?', ' ', _jsx("button", { className: authStyles.authModeSwitchLink, disabled: loginBusy, "data-testid": "auth-switch-mode-button", onClick: () => {
|
|
1068
|
+
setLoginError(null);
|
|
1069
|
+
setLoginErrorCode(null);
|
|
1070
|
+
setLoginNotice(null);
|
|
1071
|
+
setLoginMode(authMode === 'login' ? 'register' : 'login');
|
|
1072
|
+
}, children: authMode === 'login' ? 'Register' : 'Sign In' })] })), _jsxs("div", { className: authStyles.authModeLinks, children: [(authMode === 'login' || authMode === 'register') && (_jsxs(_Fragment, { children: [_jsx("button", { className: authStyles.authModeLink, disabled: loginBusy || resetCooldownActive, "data-testid": "auth-forgot-password-button", onClick: () => { setLoginError(null); setLoginErrorCode(null); setLoginNotice(null); setLoginMode('forgot'); }, children: resetCooldownActive ? `Forgot Password (${resetCooldownSeconds}s)` : 'Forgot Password' }), _jsx("button", { className: authStyles.authModeLink, disabled: loginBusy || verifyCooldownActive, "data-testid": "auth-resend-verification-button", onClick: () => { setLoginError(null); setLoginErrorCode(null); setLoginNotice(null); setLoginMode('verify'); }, children: verifyCooldownActive ? `Resend Verification (${verifyCooldownSeconds}s)` : 'Resend Verification' }), _jsx("button", { className: authStyles.authModeLink, disabled: loginBusy, "data-testid": "auth-accept-invite-button", onClick: () => { setLoginError(null); setLoginErrorCode(null); setLoginNotice(null); setLoginMode('invite'); }, children: "Accept Invite" })] })), (authMode !== 'login' && authMode !== 'register') && (_jsxs(_Fragment, { children: [authMode === 'verify' && (_jsx("button", { className: authStyles.authModeLink, disabled: loginBusy || verifyCooldownActive || !loginEmail.trim(), "data-testid": "auth-resend-verification-button", onClick: async () => {
|
|
1073
|
+
setLoginBusy(true);
|
|
1074
|
+
setLoginNotice(null);
|
|
1075
|
+
setLoginError(null);
|
|
1076
|
+
setLoginErrorCode(null);
|
|
1077
|
+
const sent = await requestEmailVerification(loginEmail);
|
|
1078
|
+
if (sent.success) {
|
|
1079
|
+
setVerifyCooldownUntil(Date.now() + 30_000);
|
|
1080
|
+
if (sent.verificationToken)
|
|
1081
|
+
setVerificationToken(sent.verificationToken);
|
|
1082
|
+
setLoginNotice('Verification email resent.');
|
|
1083
|
+
}
|
|
1084
|
+
else {
|
|
1085
|
+
setLoginError(formatAuthError(sent));
|
|
1086
|
+
}
|
|
1087
|
+
setLoginBusy(false);
|
|
1088
|
+
}, children: verifyCooldownActive ? `Resend Verification (${verifyCooldownSeconds}s)` : 'Resend Verification' })), _jsx("button", { className: authStyles.authModeLink, disabled: loginBusy, "data-testid": "auth-return-sign-in-button", onClick: () => { setLoginError(null); setLoginErrorCode(null); setLoginNotice(null); setLoginMode('login'); }, children: authMode === 'verify' ? 'Back to Sign In' : 'Sign In' }), directRegistrationEnabled && authMode !== 'verify' && (_jsx("button", { className: authStyles.authModeLink, disabled: loginBusy, "data-testid": "auth-return-register-button", onClick: () => { setLoginError(null); setLoginErrorCode(null); setLoginNotice(null); setLoginMode('register'); }, children: "Register" })), authMode === 'reset' && (_jsx("button", { className: authStyles.authModeLink, disabled: loginBusy || resetCooldownActive, "data-testid": "auth-return-forgot-password-button", onClick: () => { setLoginError(null); setLoginErrorCode(null); setLoginNotice(null); setLoginMode('forgot'); }, children: resetCooldownActive ? `Forgot Password (${resetCooldownSeconds}s)` : 'Forgot Password' }))] }))] })] })] }) }) }));
|
|
808
1089
|
}
|
|
809
1090
|
if (shouldForceLogin && !isLoginRoute) {
|
|
810
1091
|
return (_jsx("div", { className: shellStyles.standaloneWrapper, "data-theme": currentTheme, children: _jsx("div", { className: authStyles.loginView, 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: "Loading Taskforce" })] }), _jsx("p", { className: authStyles.loginSubtitle, children: "Redirecting to sign in..." })] }) }) }));
|
|
@@ -815,6 +1096,7 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
815
1096
|
}, 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" })] }) }) }));
|
|
816
1097
|
}
|
|
817
1098
|
const showGuidedWorkspaceSetup = isCreateWorkspaceIntent
|
|
1099
|
+
|| (isSetupRoute && Boolean(pendingCloudWorkspaceImport))
|
|
818
1100
|
|| (isSetupRoute && setupGateActive && (hasSetupReadError || needsGlobalSetup || needsWorkspaceSetup));
|
|
819
1101
|
if (showGuidedWorkspaceSetup) {
|
|
820
1102
|
const isError = !isCreateWorkspaceIntent && (hasSetupReadError || setupStep === 'error');
|
|
@@ -823,25 +1105,52 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
823
1105
|
? t('setup.headingCheckFailed')
|
|
824
1106
|
: (isGlobal
|
|
825
1107
|
? t('setup.headingGlobalRequired')
|
|
826
|
-
:
|
|
1108
|
+
: t('setup.headingWorkspaceRequired'));
|
|
827
1109
|
const subtitle = isError
|
|
828
1110
|
? t('setup.subtitleUnreadable')
|
|
829
1111
|
: isGlobal
|
|
830
1112
|
? t('setup.subtitleGlobalRequired')
|
|
831
|
-
:
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
const
|
|
835
|
-
|
|
836
|
-
|
|
1113
|
+
: null;
|
|
1114
|
+
const workspaceLabel = 'Workspace';
|
|
1115
|
+
const isLocalWorkspaceSetup = isCloudRuntime || workspaceSetupSource !== 'cloud';
|
|
1116
|
+
const isWorkspaceDetailsPage = isLocalWorkspaceSetup && workspaceSetupPage === 'details';
|
|
1117
|
+
const isWorkspaceStarterPage = isLocalWorkspaceSetup && workspaceSetupPage === 'starter';
|
|
1118
|
+
const showWorkspaceSourceSelector = !isCloudRuntime
|
|
1119
|
+
&& canUseCloudWorkspaceSetup
|
|
1120
|
+
&& isWorkspaceDetailsPage
|
|
1121
|
+
&& !pendingCloudWorkspaceImport;
|
|
1122
|
+
const runtimeIcon = runtimeCapabilities?.runtimeMode === 'cloud' ? Cloud : LaptopMinimal;
|
|
1123
|
+
const starterCategoryItems = selectedSetupPack ? formatStarterSectionItems(selectedSetupPack.categories) : '';
|
|
1124
|
+
const starterTypeItems = selectedSetupPack ? formatStarterSectionItems(selectedSetupPack.types) : '';
|
|
1125
|
+
const starterPriorityItems = selectedSetupPack ? formatStarterSectionItems(selectedSetupPack.priorities) : '';
|
|
1126
|
+
const setupPrimaryActionLabel = setupBusy
|
|
1127
|
+
? t('setup.saving')
|
|
1128
|
+
: wantsCloudWorkspaceSetup
|
|
1129
|
+
? (pendingCloudWorkspaceImport
|
|
1130
|
+
? (isCloudWorkspaceImportErrored ? 'Retry Import' : 'Importing Workspace...')
|
|
1131
|
+
: 'Connect Workspace')
|
|
1132
|
+
: isWorkspaceDetailsPage
|
|
1133
|
+
? 'Continue'
|
|
1134
|
+
: isWorkspaceStarterPage
|
|
1135
|
+
? 'Create Workspace'
|
|
1136
|
+
: 'Connect Workspace';
|
|
837
1137
|
return (_jsx("div", { className: shellStyles.standaloneWrapper, "data-theme": currentTheme, children: _jsx("div", { className: authStyles.loginView, style: {
|
|
838
1138
|
backgroundImage: `linear-gradient(rgba(15, 15, 26, 0.88), rgba(15, 15, 26, 0.9)), url(${taskforceBg})`
|
|
839
|
-
}, children: _jsxs("div", { className: authStyles.loginCard, 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" })] })] }), _jsx("
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
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: [!pendingCloudWorkspaceImport && !canUseCloudWorkspaceSetup && (_jsx("p", { className: authStyles.loginError, children: "Cloud workspace sync is unavailable right now. Switch back to local setup or refresh runtime configuration." })), !pendingCloudWorkspaceImport && 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
|
+
navigate(`/login?mode=login&next=${encodeURIComponent(cloudWorkspaceSelectionPath)}`, { replace: true });
|
|
1141
|
+
}, children: "Sign In" }), directRegistrationEnabled && (_jsx("button", { className: styles.submitBtn, disabled: setupBusy, onClick: () => {
|
|
1142
|
+
navigate(`/login?mode=register&next=${encodeURIComponent(cloudWorkspaceSelectionPath)}`, { replace: true });
|
|
1143
|
+
}, children: "Register" }))] })] })), !pendingCloudWorkspaceImport && 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 })] })), pendingCloudWorkspaceImport && (_jsxs(_Fragment, { children: [_jsxs("p", { className: authStyles.inlineHint, children: ["Importing \"", pendingCloudWorkspaceImport.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
|
+
? '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 })] })), isWorkspaceStarterPage && (_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 starter options...' : 'No starter 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 Starter 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 starter 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: [isWorkspaceStarterPage ? (_jsx("button", { className: styles.cancelBtn, onClick: () => {
|
|
1146
|
+
setSetupError(null);
|
|
1147
|
+
setSetupNotice(null);
|
|
1148
|
+
setWorkspaceSetupPage('details');
|
|
1149
|
+
}, children: "Back" })) : isCreateWorkspaceIntent ? (_jsx("button", { className: styles.cancelBtn, onClick: () => {
|
|
1150
|
+
setSetupError(null);
|
|
1151
|
+
setSetupNotice(null);
|
|
1152
|
+
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(pendingCloudWorkspaceImport) && !isCloudWorkspaceImportErrored), onClick: async () => {
|
|
845
1154
|
setSetupBusy(true);
|
|
846
1155
|
setSetupNotice(null);
|
|
847
1156
|
setSetupError(null);
|
|
@@ -867,11 +1176,32 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
867
1176
|
finally {
|
|
868
1177
|
setSetupBusy(false);
|
|
869
1178
|
}
|
|
870
|
-
}, children: setupBusy ? t('setup.saving') : t('setup.saveGlobalSetup') })) : !isError ? (_jsx("button", { className: styles.submitBtn, disabled: setupBusy
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
1179
|
+
}, children: setupBusy ? t('setup.saving') : t('setup.saveGlobalSetup') })) : !isError ? (_jsx("button", { className: styles.submitBtn, disabled: setupBusy
|
|
1180
|
+
|| (isCloudWorkspaceSetupFlow && !pendingCloudWorkspaceImport && !isAuthenticated), onClick: async () => {
|
|
1181
|
+
if (isCloudWorkspaceSetupFlow && pendingCloudWorkspaceImport) {
|
|
1182
|
+
if (!isCloudWorkspaceImportErrored)
|
|
1183
|
+
return;
|
|
1184
|
+
setSetupBusy(true);
|
|
1185
|
+
setSetupNotice(`Retrying import for ${pendingCloudWorkspaceImport.name}...`);
|
|
1186
|
+
setSetupError(null);
|
|
1187
|
+
try {
|
|
1188
|
+
await retryWorkspaceCloudSync();
|
|
1189
|
+
}
|
|
1190
|
+
catch {
|
|
1191
|
+
setSetupNotice(null);
|
|
1192
|
+
setSetupError(`Failed to retry import for "${pendingCloudWorkspaceImport.name}".`);
|
|
1193
|
+
}
|
|
1194
|
+
finally {
|
|
1195
|
+
setSetupBusy(false);
|
|
1196
|
+
}
|
|
1197
|
+
return;
|
|
1198
|
+
}
|
|
1199
|
+
if (wantsCloudWorkspaceSetup) {
|
|
1200
|
+
if (!canUseCloudWorkspaceSetup) {
|
|
1201
|
+
setSetupError('Cloud workspace sync is unavailable right now.');
|
|
1202
|
+
setSetupBusy(false);
|
|
1203
|
+
return;
|
|
1204
|
+
}
|
|
875
1205
|
if (!isAuthenticated) {
|
|
876
1206
|
setSetupError('Sign in is required before syncing a cloud project.');
|
|
877
1207
|
setSetupBusy(false);
|
|
@@ -929,14 +1259,40 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
929
1259
|
pullCursor: null
|
|
930
1260
|
});
|
|
931
1261
|
await refreshSetupContext();
|
|
932
|
-
|
|
1262
|
+
setPendingCloudWorkspaceImport({
|
|
1263
|
+
workspaceId: workspaceIdForSync,
|
|
1264
|
+
name: selectedWorkspace.name || selectedWorkspace.id
|
|
1265
|
+
});
|
|
1266
|
+
setCloudWorkspaceImportFinalizing(false);
|
|
1267
|
+
setSetupNotice(`Importing ${selectedWorkspace.name || selectedWorkspace.id} from cloud...`);
|
|
1268
|
+
setSetupBusy(false);
|
|
1269
|
+
return;
|
|
933
1270
|
}
|
|
934
|
-
|
|
1271
|
+
if (isWorkspaceDetailsPage) {
|
|
1272
|
+
const normalizedWorkspaceName = workspaceNameInput.trim();
|
|
1273
|
+
if (!normalizedWorkspaceName) {
|
|
1274
|
+
setSetupError('Workspace name is required before continuing.');
|
|
1275
|
+
return;
|
|
1276
|
+
}
|
|
1277
|
+
setSetupError(null);
|
|
1278
|
+
setSetupNotice(null);
|
|
1279
|
+
setWorkspaceSetupPage('starter');
|
|
1280
|
+
return;
|
|
1281
|
+
}
|
|
1282
|
+
if (isWorkspaceStarterPage) {
|
|
1283
|
+
// fall through to final create/save behavior below
|
|
1284
|
+
}
|
|
1285
|
+
setSetupBusy(true);
|
|
1286
|
+
setSetupNotice(null);
|
|
1287
|
+
setSetupError(null);
|
|
1288
|
+
{
|
|
935
1289
|
const creatingNewWorkspace = isCreateWorkspaceIntent || setupState?.workspaceSetupState === 'missing';
|
|
936
|
-
const
|
|
1290
|
+
const shouldCreateWorkspace = isCreateWorkspaceIntent
|
|
1291
|
+
|| (workspaceSwitchingEnabled && creatingNewWorkspace);
|
|
1292
|
+
const createResult = shouldCreateWorkspace
|
|
937
1293
|
? await createWorkspace(workspaceNameInput, workspaceDescriptionInput || undefined)
|
|
938
1294
|
: null;
|
|
939
|
-
const saveResult =
|
|
1295
|
+
const saveResult = shouldCreateWorkspace
|
|
940
1296
|
? null
|
|
941
1297
|
: await saveWorkspaceProfile({
|
|
942
1298
|
workspaceId: creatingNewWorkspace ? undefined : setupState?.workspaceId,
|
|
@@ -1018,11 +1374,7 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
1018
1374
|
}
|
|
1019
1375
|
}
|
|
1020
1376
|
setSetupBusy(false);
|
|
1021
|
-
}, children:
|
|
1022
|
-
setSetupError(null);
|
|
1023
|
-
setSetupNotice(null);
|
|
1024
|
-
navigate('/', { replace: true });
|
|
1025
|
-
}, children: "Cancel" })) : runtimeMode === 'cloud' ? (_jsx("button", { className: styles.cancelBtn, onClick: async () => { await taskforceState.logout(); navigate('/login', { replace: true }); }, children: t('setup.signOut') })) : null] })] }) }) }));
|
|
1377
|
+
}, children: setupPrimaryActionLabel })) : (_jsx("button", { className: styles.submitBtn, disabled: setupBusy, onClick: refreshSetupContext, children: t('setup.retrySetupCheck') }))] })] }) }) }));
|
|
1026
1378
|
}
|
|
1027
1379
|
return (_jsx(StandaloneLayout, { ...taskforceState, onHeaderMouseDown: onHeaderMouseDown, isDragging: isDragging, onClose: onClose }));
|
|
1028
1380
|
}
|