@taskforcehq/taskforce 0.3.304 → 0.3.306
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +140 -247
- package/dist/Taskforce.module.css +295 -89
- package/dist/TaskforceCore.js +378 -111
- package/dist/TaskforceCore.test.js +1221 -107
- package/dist/assets/fonts/CourierPrime-Bold.woff2 +0 -0
- package/dist/assets/fonts/CourierPrime-BoldItalic.woff2 +0 -0
- package/dist/assets/fonts/CourierPrime-Italic.woff2 +0 -0
- package/dist/assets/fonts/CourierPrime-Regular.woff2 +0 -0
- package/dist/assets/fonts/Noir_medium.woff2 +0 -0
- package/dist/assets/fonts/Noir_regular.woff2 +0 -0
- package/dist/assets/fonts/RussoOne-Regular.woff2 +0 -0
- package/dist/assets/fonts/SpecialElite-Regular.woff2 +0 -0
- package/dist/compat/workspaceSyncCompat.js +52 -2
- package/dist/compat/workspaceSyncCompat.test.js +38 -1
- package/dist/components/context/ContextAttachmentManager.d.ts +14 -0
- package/dist/components/context/ContextAttachmentManager.js +549 -0
- package/dist/components/features/AgentsModule.d.ts +10 -1
- package/dist/components/features/AgentsModule.js +260 -15
- package/dist/components/features/AgentsModule.test.js +255 -5
- package/dist/components/features/DocumentIndex.d.ts +1 -1
- package/dist/components/features/DocumentIndex.js +1 -1
- package/dist/components/features/DocumentViewer.d.ts +6 -2
- package/dist/components/features/DocumentViewer.js +90 -10
- package/dist/components/features/DocumentWorkspace.d.ts +5 -22
- package/dist/components/features/DocumentWorkspace.js +12 -155
- package/dist/components/features/DocumentWorkspace.test.js +226 -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/TaskCard.js +19 -28
- package/dist/components/task/TaskCard.test.js +38 -3
- package/dist/components/task/TaskContextUpload.js +4 -526
- package/dist/components/task/TaskForm.d.ts +8 -8
- package/dist/components/task/TaskForm.js +362 -230
- package/dist/components/task/TaskForm.test.js +558 -381
- package/dist/components/task/TaskKanban.d.ts +2 -1
- package/dist/components/task/TaskKanban.js +96 -14
- package/dist/components/task/TaskKanban.test.js +36 -0
- package/dist/components/ui/EditableAvatarButton.d.ts +18 -0
- package/dist/components/ui/EditableAvatarButton.js +18 -0
- package/dist/components/views/PlanComparisonPage.d.ts +9 -1
- package/dist/components/views/PlanComparisonPage.js +70 -17
- package/dist/components/views/PlanComparisonPage.test.js +289 -41
- package/dist/components/views/PlansPage.js +239 -106
- package/dist/components/views/PlansPage.test.js +593 -42
- package/dist/components/views/StandaloneLayout.js +310 -554
- package/dist/components/views/panels/FilterToolbar.test.js +6 -4
- package/dist/components/views/panels/PlanningDrawer.d.ts +8 -1
- package/dist/components/views/panels/PlanningDrawer.js +18 -11
- package/dist/components/views/panels/PlanningDrawer.test.d.ts +1 -0
- package/dist/components/views/panels/PlanningDrawer.test.js +141 -0
- package/dist/components/views/planningScope.d.ts +20 -0
- package/dist/components/views/planningScope.js +25 -0
- package/dist/components/views/planningScope.test.d.ts +1 -0
- package/dist/components/views/planningScope.test.js +60 -0
- package/dist/components/views/standalone/modals/AccountHubModal.d.ts +27 -14
- package/dist/components/views/standalone/modals/AccountHubModal.js +87 -15
- package/dist/components/views/standalone/modals/AccountHubModal.test.js +42 -2
- package/dist/components/views/standalone/modals/AvatarImageManagerModal.d.ts +20 -0
- package/dist/components/views/standalone/modals/AvatarImageManagerModal.js +127 -0
- package/dist/components/views/standalone/modals/EditProfileModal.d.ts +2 -9
- package/dist/components/views/standalone/modals/EditProfileModal.js +3 -2
- package/dist/components/views/standalone/modals/SyncEnableWarningModal.d.ts +9 -0
- package/dist/components/views/standalone/modals/SyncEnableWarningModal.js +6 -0
- package/dist/components/views/standalone/modals/SyncEnableWarningModal.test.d.ts +1 -0
- package/dist/components/views/standalone/modals/SyncEnableWarningModal.test.js +24 -0
- package/dist/components/views/standalone/modals/SyncStatusModal.d.ts +2 -1
- package/dist/components/views/standalone/modals/SyncStatusModal.js +2 -2
- package/dist/config/envSchema.js +1 -1
- package/dist/core/AiProfileService.d.ts +45 -3
- package/dist/core/AiProfileService.js +774 -63
- package/dist/core/AiProfileService.test.js +22 -2
- package/dist/core/AiProfiles.test.js +761 -2
- package/dist/core/AttachmentLinkService.js +57 -15
- package/dist/core/AuthIdentityService.d.ts +107 -0
- package/dist/core/AuthIdentityService.js +284 -0
- package/dist/core/AuthTokenService.d.ts +4 -0
- package/dist/core/AuthTokenService.js +34 -6
- package/dist/core/AuthTokenService.test.d.ts +1 -0
- package/dist/core/AuthTokenService.test.js +78 -0
- package/dist/core/EntitlementsPolicy.test.js +77 -15
- package/dist/core/GlobalSettingsService.js +115 -6
- package/dist/core/PlanEntitlementService.d.ts +30 -3
- package/dist/core/PlanEntitlementService.js +454 -93
- package/dist/core/PlanFeatureCatalog.test.d.ts +1 -0
- package/dist/core/PlanFeatureCatalog.test.js +84 -0
- package/dist/core/PlanVersionPolicy.test.js +54 -28
- package/dist/core/PlanningEntities.test.js +52 -0
- package/dist/core/SignupMonetizationSettings.test.js +241 -44
- package/dist/core/SyncReconciliationService.js +2 -65
- package/dist/core/SystemAdmin.test.js +244 -88
- package/dist/core/TaskAttachmentsCanonical.test.js +51 -1
- package/dist/core/TaskTaxonomyValidation.test.js +53 -0
- package/dist/core/Taskforce.d.ts +228 -14
- package/dist/core/Taskforce.js +814 -201
- package/dist/core/Taskforce.listTasksSlim.test.d.ts +1 -0
- package/dist/core/Taskforce.listTasksSlim.test.js +91 -0
- package/dist/core/UserProfileAvatarDrafts.test.js +30 -3
- package/dist/core/WorkspaceLifecycleService.js +0 -5
- package/dist/core/WorkspacePermissions.test.js +25 -1
- package/dist/core/WorkspacePlanMode.test.js +128 -27
- package/dist/core/shared.d.ts +3 -1
- package/dist/core/shared.js +16 -1
- package/dist/core/types.d.ts +68 -2
- package/dist/hooks/auth/useTaskforceAuthBootstrap.js +26 -1
- package/dist/hooks/sync/auth.js +3 -1
- package/dist/hooks/sync/bootstrap.js +2 -0
- package/dist/hooks/sync/controlPlane.d.ts +37 -0
- package/dist/hooks/sync/controlPlane.js +78 -0
- package/dist/hooks/sync/controlPlane.test.d.ts +1 -0
- package/dist/hooks/sync/controlPlane.test.js +121 -0
- package/dist/hooks/sync/lifecyclePolicy.d.ts +45 -0
- package/dist/hooks/sync/lifecyclePolicy.js +93 -0
- package/dist/hooks/sync/lifecyclePolicy.test.d.ts +1 -0
- package/dist/hooks/sync/lifecyclePolicy.test.js +124 -0
- package/dist/hooks/sync/orchestratorShared.d.ts +10 -1
- package/dist/hooks/sync/orchestratorShared.js +68 -47
- package/dist/hooks/sync/orchestratorShared.test.js +50 -1
- package/dist/hooks/sync/pullLifecycle.d.ts +31 -0
- package/dist/hooks/sync/pullLifecycle.js +24 -0
- package/dist/hooks/sync/pullLifecycle.test.d.ts +1 -0
- package/dist/hooks/sync/pullLifecycle.test.js +80 -0
- package/dist/hooks/sync/recovery.d.ts +16 -2
- package/dist/hooks/sync/recovery.js +171 -53
- package/dist/hooks/sync/recovery.test.d.ts +1 -0
- package/dist/hooks/sync/recovery.test.js +292 -0
- package/dist/hooks/sync/transfers.d.ts +16 -3
- package/dist/hooks/sync/transfers.js +186 -65
- package/dist/hooks/sync/transfers.test.d.ts +1 -0
- package/dist/hooks/sync/transfers.test.js +396 -0
- package/dist/hooks/sync/useRecentSyncEvents.d.ts +21 -0
- package/dist/hooks/sync/useRecentSyncEvents.js +92 -0
- package/dist/hooks/sync/useRecentSyncEvents.test.d.ts +1 -0
- package/dist/hooks/sync/useRecentSyncEvents.test.js +124 -0
- package/dist/hooks/sync/useSyncStatusActions.d.ts +30 -0
- package/dist/hooks/sync/useSyncStatusActions.js +88 -0
- package/dist/hooks/sync/useSyncStatusActions.test.d.ts +1 -0
- package/dist/hooks/sync/useSyncStatusActions.test.js +133 -0
- package/dist/hooks/sync/useSyncStatusControls.d.ts +25 -0
- package/dist/hooks/sync/useSyncStatusControls.js +60 -0
- package/dist/hooks/sync/useSyncStatusControls.test.d.ts +1 -0
- package/dist/hooks/sync/useSyncStatusControls.test.js +88 -0
- package/dist/hooks/useSyncOrchestrator.aiProfiles.test.js +12 -0
- package/dist/hooks/useSyncOrchestrator.d.ts +16 -6
- package/dist/hooks/useSyncOrchestrator.js +424 -175
- package/dist/hooks/useSyncOrchestrator.retry-closure.test.js +487 -10
- package/dist/hooks/useTaskData.js +8 -3
- package/dist/hooks/useTaskData.test.js +45 -0
- 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 +65 -4
- package/dist/hooks/useTaskforce.js +564 -243
- package/dist/hooks/useTaskforce.runtime-routing.test.d.ts +1 -0
- package/dist/hooks/useTaskforce.runtime-routing.test.js +152 -0
- package/dist/hooks/useTaskforce.sync-behavior.test.js +2454 -208
- package/dist/hooks/useWorkspaceSyncController.d.ts +4 -1
- package/dist/hooks/useWorkspaceSyncController.js +17 -2
- package/dist/hooks/useWorkspaceSyncController.test.js +1 -0
- package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.d.ts +16 -0
- package/dist/hooks/workspace/useTaskforceWorkspaceBootstrap.js +316 -35
- package/dist/localization/locales/en-US.d.ts +0 -1
- package/dist/localization/locales/en-US.js +0 -1
- package/dist/localization/locales/es-419.js +0 -1
- package/dist/localization/locales/pt-BR.js +0 -1
- 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.d.ts +2 -0
- package/dist/mcp/runtime.js +2433 -3534
- package/dist/mcp/runtime.test.js +440 -2
- 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/migrations/taskSchemaMigrations.d.ts +1 -1
- package/dist/migrations/taskSchemaMigrations.js +171 -61
- package/dist/migrations/taskSchemaMigrations.test.js +15 -0
- package/dist/resources/templates/workflow-sources/workflowDocs.mjs +6 -6
- package/dist/resources/templates/workflows/collaborate.yaml +1 -1
- package/dist/resources/templates/workflows/evaluate.yaml +2 -2
- package/dist/resources/templates/workflows/plan.yaml +1 -1
- package/dist/resources/templates/workflows/review.yaml +2 -2
- package/dist/server/annotatedAttachmentsRoutes.test.js +3 -3
- package/dist/server/auth/providers/apple.d.ts +33 -0
- package/dist/server/auth/providers/apple.js +82 -0
- package/dist/server/auth/providers/appleClientSecret.d.ts +37 -0
- package/dist/server/auth/providers/appleClientSecret.js +65 -0
- package/dist/server/auth/providers/github.d.ts +26 -0
- package/dist/server/auth/providers/github.js +86 -0
- package/dist/server/auth/providers/google.d.ts +21 -0
- package/dist/server/auth/providers/google.js +56 -0
- package/dist/server/auth/providers/types.d.ts +21 -0
- package/dist/server/auth/providers/types.js +1 -0
- package/dist/server/auth.d.ts +2 -0
- package/dist/server/auth.js +6 -3
- package/dist/server/documentReviewRoutes.test.js +36 -3
- package/dist/server/index.cookieProxy.test.js +1 -0
- package/dist/server/index.d.ts +12 -0
- package/dist/server/index.js +120 -9
- package/dist/server/index.rateLimit.test.js +3 -0
- package/dist/server/index.test.js +106 -3
- package/dist/server/routes/admin.d.ts +4 -5
- package/dist/server/routes/admin.js +410 -80
- package/dist/server/routes/annotatedAttachments.d.ts +1 -1
- package/dist/server/routes/annotatedAttachments.js +1 -1
- package/dist/server/routes/auth.d.ts +16 -6
- package/dist/server/routes/auth.js +1015 -118
- 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 +276 -34
- package/dist/server/routes/billing.test.js +760 -52
- package/dist/server/routes/documentReviews.d.ts +1 -1
- package/dist/server/routes/documentReviews.js +1 -1
- package/dist/server/routes/documents.d.ts +5 -2
- package/dist/server/routes/documents.js +242 -74
- 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 +29 -3
- package/dist/server/routes/shared.js +69 -4
- package/dist/server/routes/sync.d.ts +1 -1
- package/dist/server/routes/sync.integration.test.js +437 -39
- package/dist/server/routes/sync.js +62 -606
- package/dist/server/routes/syncAuxRoutes.d.ts +15 -0
- package/dist/server/routes/syncAuxRoutes.js +91 -0
- package/dist/server/routes/syncAuxRoutes.test.d.ts +1 -0
- package/dist/server/routes/syncAuxRoutes.test.js +158 -0
- package/dist/server/routes/syncPullApplyRoutes.d.ts +36 -0
- package/dist/server/routes/syncPullApplyRoutes.js +204 -0
- package/dist/server/routes/syncPushRoutes.d.ts +24 -0
- package/dist/server/routes/syncPushRoutes.js +212 -0
- package/dist/server/routes/syncRouteGuards.d.ts +36 -0
- package/dist/server/routes/syncRouteGuards.js +67 -0
- package/dist/server/routes/syncRouteGuards.test.d.ts +1 -0
- package/dist/server/routes/syncRouteGuards.test.js +94 -0
- package/dist/server/routes/syncRouteTypes.d.ts +13 -0
- package/dist/server/routes/syncRouteTypes.js +1 -0
- package/dist/server/routes/syncSnapshotRoutes.d.ts +66 -0
- package/dist/server/routes/syncSnapshotRoutes.js +180 -0
- package/dist/server/routes/tasks.d.ts +1 -1
- package/dist/server/routes/tasks.js +44 -2
- package/dist/server/routes/workspaces.d.ts +1 -1
- package/dist/server/routes/workspaces.js +37 -6
- package/dist/server/routes.d.ts +2 -9
- package/dist/server/routes.js +136 -165
- package/dist/server/routes.test.js +2457 -162
- package/dist/services/workflowExportSnapshots.test.js +2 -0
- package/dist/shared/aiProfileSeatScope.d.ts +5 -0
- package/dist/shared/aiProfileSeatScope.js +21 -0
- package/dist/shared/passwordPolicy.d.ts +13 -0
- package/dist/shared/passwordPolicy.js +84 -0
- package/dist/shared/passwordPolicy.test.d.ts +1 -0
- package/dist/shared/passwordPolicy.test.js +34 -0
- package/dist/storage/workspaceAssetStore.d.ts +15 -3
- package/dist/storage/workspaceAssetStore.js +132 -44
- package/dist/storage/workspaceAssetStore.test.js +93 -6
- package/dist/styles/fonts.css +70 -0
- package/dist/sync/collaborationSyncPayload.d.ts +6 -1
- package/dist/sync/collaborationSyncPayload.js +17 -11
- package/dist/sync/collaborationSyncPayload.test.js +13 -1
- package/dist/sync/contentSyncState.d.ts +4 -8
- package/dist/sync/contentSyncState.js +33 -77
- package/dist/sync/syncApplyHandlers.d.ts +7 -0
- package/dist/sync/syncApplyHandlers.js +28 -13
- package/dist/sync/syncApplyHandlers.test.js +72 -3
- package/dist/sync/syncFieldSemantics.d.ts +12 -0
- package/dist/sync/syncFieldSemantics.js +55 -0
- package/dist/sync/syncResourceAdapters.d.ts +44 -0
- package/dist/sync/syncResourceAdapters.js +77 -0
- package/dist/sync/syncService.d.ts +6 -0
- package/dist/sync/syncService.js +60 -0
- package/dist/sync/syncService.test.js +71 -3
- package/dist/sync/taskSyncChangeKey.d.ts +2 -0
- package/dist/sync/taskSyncChangeKey.js +143 -0
- package/dist/sync/taskSyncPayload.js +3 -8
- package/dist/sync/workspacePullFeed.js +4 -3
- package/dist/sync/workspacePullFeed.test.js +43 -0
- package/dist/sync/workspaceRepair.js +5 -2
- package/dist/sync/workspaceSyncModel.d.ts +48 -0
- package/dist/sync/workspaceSyncModel.js +195 -31
- package/dist/sync/workspaceSyncModel.test.js +325 -12
- package/dist/sync/workspaceSyncState.d.ts +19 -0
- package/dist/sync/workspaceSyncState.js +3 -1
- package/dist/sync/workspaceSyncSurface.js +3 -8
- package/dist/types.d.ts +17 -0
- package/dist/ui/assets/AgentsModule-DrLawwNl.js +1 -0
- package/dist/ui/assets/{AnnotatedAttachmentWorkspace-BlS-cqnl.js → AnnotatedAttachmentWorkspace-Z9_whf7F.js} +1 -1
- package/dist/ui/assets/CourierPrime-Bold-BuLj_dpw.woff2 +0 -0
- package/dist/ui/assets/CourierPrime-BoldItalic-BrK2pUkZ.woff2 +0 -0
- package/dist/ui/assets/CourierPrime-Italic-DFUZK5Ey.woff2 +0 -0
- package/dist/ui/assets/CourierPrime-Regular-BsKphzVf.woff2 +0 -0
- package/dist/ui/assets/DocumentWorkspace-B03MaSkw.js +252 -0
- package/dist/ui/assets/DocumentWorkspace-Dhdso07p.css +1 -0
- package/dist/ui/assets/{InitiativesModule-BjR6iBf9.js → InitiativesModule-9E6ParrY.js} +1 -1
- package/dist/ui/assets/Noir_medium-bxQwKGzB.woff2 +0 -0
- package/dist/ui/assets/Noir_regular-ojf2kxlG.woff2 +0 -0
- package/dist/ui/assets/PlansPage-CvfnMiWq.css +1 -0
- package/dist/ui/assets/PlansPage-DRXscYxH.js +1 -0
- package/dist/ui/assets/RussoOne-Regular-Cu_qq_qC.woff2 +0 -0
- package/dist/ui/assets/SpecialElite-Regular-Di6gSvAS.woff2 +0 -0
- package/dist/ui/assets/TaskSettings-DZX4jk7e.js +9 -0
- package/dist/ui/assets/{WorkflowsModule-DnFqdUME.js → WorkflowsModule-BA7jcEpH.js} +1 -1
- package/dist/ui/assets/index-9eT8e0Lu.css +1 -0
- package/dist/ui/assets/index-C6k75jr8.js +6 -0
- package/dist/ui/assets/{vendor-icons-I7ZwG1z_.js → vendor-icons-DuEd_65c.js} +1 -1
- package/dist/ui/fonts/CourierPrime-Bold.ttf +0 -0
- package/dist/ui/fonts/CourierPrime-Bold.woff2 +0 -0
- package/dist/ui/fonts/CourierPrime-BoldItalic.ttf +0 -0
- package/dist/ui/fonts/CourierPrime-BoldItalic.woff2 +0 -0
- package/dist/ui/fonts/CourierPrime-Italic.ttf +0 -0
- package/dist/ui/fonts/CourierPrime-Italic.woff2 +0 -0
- package/dist/ui/fonts/CourierPrime-Regular.ttf +0 -0
- package/dist/ui/fonts/CourierPrime-Regular.woff2 +0 -0
- package/dist/ui/fonts/Noir_medium.otf +0 -0
- package/dist/ui/fonts/Noir_medium.woff2 +0 -0
- package/dist/ui/fonts/Noir_regular.otf +0 -0
- package/dist/ui/fonts/Noir_regular.woff2 +0 -0
- package/dist/ui/fonts/RussoOne-Regular.woff2 +0 -0
- package/dist/ui/fonts/SpecialElite-Regular.ttf +0 -0
- package/dist/ui/fonts/SpecialElite-Regular.woff2 +0 -0
- package/dist/ui/index.html +3 -3
- package/dist/utils/accountProfileSummaryCache.d.ts +5 -0
- package/dist/utils/assignees.d.ts +2 -0
- package/dist/utils/assignees.js +2 -0
- package/dist/utils/avatarUpload.d.ts +27 -0
- package/dist/utils/avatarUpload.js +132 -0
- package/dist/utils/avatarUpload.test.d.ts +1 -0
- package/dist/utils/avatarUpload.test.js +40 -0
- package/dist/utils/bootstrapTrace.d.ts +7 -0
- package/dist/utils/bootstrapTrace.js +29 -0
- package/dist/utils/commercialLifecyclePresentation.d.ts +19 -0
- package/dist/utils/commercialLifecyclePresentation.js +199 -0
- package/dist/utils/contextAssetEvents.d.ts +2 -0
- package/dist/utils/syncEventDetails.d.ts +3 -0
- package/dist/utils/syncEventDetails.js +64 -0
- package/dist/utils/syncEventDetails.test.d.ts +1 -0
- package/dist/utils/syncEventDetails.test.js +23 -0
- package/dist/utils/syncEventPresentation.d.ts +15 -0
- package/dist/utils/syncEventPresentation.js +129 -0
- package/dist/utils/syncEventPresentation.test.d.ts +1 -0
- package/dist/utils/syncEventPresentation.test.js +64 -0
- package/dist/utils/syncStatusPresentation.d.ts +72 -0
- package/dist/utils/syncStatusPresentation.js +217 -0
- package/dist/utils/syncStatusPresentation.test.d.ts +1 -0
- package/dist/utils/syncStatusPresentation.test.js +164 -0
- package/dist/utils/taskActivity.d.ts +2 -0
- package/dist/utils/taskActivity.js +228 -22
- package/dist/utils/taskActivity.test.js +119 -8
- package/dist/utils/workspaceSyncPresentation.d.ts +4 -0
- package/dist/utils/workspaceSyncPresentation.js +68 -2
- package/dist/utils/workspaceSyncPresentation.test.js +36 -1
- package/package.json +10 -2
- package/scripts/check-cloud-mcp.mjs +83 -0
- package/scripts/export-sqlite-to-postgres.mjs +7 -9
- package/scripts/playwright-auth.sh +5 -1
- package/scripts/run-billing-performance-smoke.sh +24 -0
- package/scripts/run-e2e-realtime.sh +17 -3
- package/scripts/run-smoke.sh +17 -5
- package/scripts/run-soak.sh +17 -5
- package/src/resources/templates/workflow-sources/workflowDocs.mjs +6 -6
- package/src/resources/templates/workflows/collaborate.yaml +1 -1
- package/src/resources/templates/workflows/evaluate.yaml +2 -2
- package/src/resources/templates/workflows/plan.yaml +1 -1
- package/src/resources/templates/workflows/review.yaml +2 -2
- package/dist/ui/assets/AgentsModule-BLWVbuKP.js +0 -1
- 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-C2dd2n1X.css +0 -1
- package/dist/ui/assets/PlansPage-CSDQ4sLa.js +0 -1
- package/dist/ui/assets/RussoOne-Regular-C3BxZIj7.ttf +0 -0
- package/dist/ui/assets/TaskSettings-CEDy34Jo.js +0 -9
- package/dist/ui/assets/index-DXUdtH1B.js +0 -6
- package/dist/ui/assets/index-Ii0B0rTO.css +0 -1
- package/scripts/migrate-planning-model.ts +0 -233
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';
|
|
@@ -15,6 +15,7 @@ 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
17
|
import { Cloud, LaptopMinimal } from 'lucide-react';
|
|
18
|
+
import { getPasswordPolicyGuidance, validatePasswordPolicy } from './shared/passwordPolicy';
|
|
18
19
|
function isDebugModeEnabled() {
|
|
19
20
|
const runtimeOverride = globalThis.__DEBUG_MODE__;
|
|
20
21
|
if (typeof runtimeOverride === 'boolean')
|
|
@@ -58,18 +59,21 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
58
59
|
const [loginEmail, setLoginEmail] = useState('');
|
|
59
60
|
const [loginDisplayName, setLoginDisplayName] = useState('');
|
|
60
61
|
const [loginPassword, setLoginPassword] = useState('');
|
|
62
|
+
const [loginPasswordConfirm, setLoginPasswordConfirm] = useState('');
|
|
61
63
|
const [authMode, setAuthMode] = useState('login');
|
|
62
64
|
const [verificationToken, setVerificationToken] = useState('');
|
|
63
65
|
const [pendingVerificationWorkspaceSetupRequired, setPendingVerificationWorkspaceSetupRequired] = useState(false);
|
|
64
66
|
const [pendingVerificationCommercialState, setPendingVerificationCommercialState] = useState(null);
|
|
65
67
|
const [pendingVerificationRedirectPath, setPendingVerificationRedirectPath] = useState(null);
|
|
66
68
|
const [pendingPostAuthRedirect, setPendingPostAuthRedirect] = useState(null);
|
|
69
|
+
const [commercialOnboardingRedirectActive, setCommercialOnboardingRedirectActive] = useState(false);
|
|
67
70
|
const [resetToken, setResetToken] = useState('');
|
|
68
71
|
const [inviteToken, setInviteToken] = useState('');
|
|
69
72
|
const [inviteEmail, setInviteEmail] = useState(null);
|
|
70
73
|
const [inviteWorkspaceId, setInviteWorkspaceId] = useState(null);
|
|
71
74
|
const [inviteResolution, setInviteResolution] = useState('unknown');
|
|
72
75
|
const [invitePasswordRequired, setInvitePasswordRequired] = useState(false);
|
|
76
|
+
const [inviteOAuthProviders, setInviteOAuthProviders] = useState([]);
|
|
73
77
|
const [loginNotice, setLoginNotice] = useState(null);
|
|
74
78
|
const [loginError, setLoginError] = useState(null);
|
|
75
79
|
const [loginErrorCode, setLoginErrorCode] = useState(null);
|
|
@@ -92,6 +96,7 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
92
96
|
const [syncNewLocalWorkspaceToCloud, setSyncNewLocalWorkspaceToCloud] = useState(false);
|
|
93
97
|
const [pendingCloudWorkspaceImport, setPendingCloudWorkspaceImport] = useState(null);
|
|
94
98
|
const [cloudWorkspaceImportFinalizing, setCloudWorkspaceImportFinalizing] = useState(false);
|
|
99
|
+
const cloudWorkspaceImportFinalizingRef = useRef(false);
|
|
95
100
|
const fallbackSetupLibraryPacks = useMemo(() => listBuiltInTaxonomyLibraryPacks(), []);
|
|
96
101
|
const [setupLibraryPacks, setSetupLibraryPacks] = useState(fallbackSetupLibraryPacks);
|
|
97
102
|
const [selectedSetupPackId, setSelectedSetupPackId] = useState(() => (fallbackSetupLibraryPacks.find((pack) => pack.isDefaultStarter)?.id || fallbackSetupLibraryPacks[0]?.id || ''));
|
|
@@ -108,7 +113,24 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
108
113
|
onTaskCountChange,
|
|
109
114
|
onClose
|
|
110
115
|
});
|
|
111
|
-
const { runtimeMode, workspaceSwitchingEnabled, currentWorkspaceId,
|
|
116
|
+
const { runtimeMode, workspaceSwitchingEnabled, currentWorkspaceId, cloudAuthConfigured, authRequiredForApi, authBlocked, isAuthenticated, hasBetaAccess, authSessionResolved, setupState, runtimeCapabilities, refreshSetupContext, retryBootstrapChecks, createWorkspace, saveWorkspaceProfile, fetchWorkspaces, applyWorkspaceSyncStateSnapshot, workspaceCloudSyncEnabled, workspaceSyncPhase, workspaceSyncSetupIntent, workspaceSyncSummary, workspaceSyncRecommendedAction, workspaceSyncBusy, workspaceLastErrorMessage, retryWorkspaceCloudSync, settingsModel, fetchTasks, bootstrapState, loginWithCredentials, beginOAuthLogin, availableAuthProviders, registerWithCredentials, requestEmailVerification, confirmEmailVerification, requestPasswordReset, confirmPasswordReset, inspectInviteAcceptance, acceptInviteWithToken, joinInviteWithToken, authUserEmail, currentTheme, logout } = taskforceState;
|
|
117
|
+
const persistWorkspaceSyncStatePatch = useCallback(async (workspaceId, patch) => {
|
|
118
|
+
const response = await fetch('/api/taskforce/ui-state', {
|
|
119
|
+
method: 'POST',
|
|
120
|
+
headers: { 'Content-Type': 'application/json' },
|
|
121
|
+
credentials: 'include',
|
|
122
|
+
body: JSON.stringify({
|
|
123
|
+
workspaceId,
|
|
124
|
+
stateKey: 'workspace-sync',
|
|
125
|
+
patch
|
|
126
|
+
})
|
|
127
|
+
});
|
|
128
|
+
const payload = await response.json().catch(() => ({}));
|
|
129
|
+
if (!response.ok || payload?.success === false) {
|
|
130
|
+
throw new Error(payload?.error || `Failed to configure workspace sync (${response.status})`);
|
|
131
|
+
}
|
|
132
|
+
return payload;
|
|
133
|
+
}, []);
|
|
112
134
|
const selectedSetupPack = useMemo(() => {
|
|
113
135
|
return setupLibraryPacks.find((pack) => pack.id === selectedSetupPackId)
|
|
114
136
|
|| setupLibraryPacks.find((pack) => pack.isDefaultStarter)
|
|
@@ -131,20 +153,35 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
131
153
|
const isLoginRoute = location.pathname === '/login';
|
|
132
154
|
const isSetupRoute = location.pathname === '/setup';
|
|
133
155
|
const isComingSoonRoute = location.pathname === '/coming-soon';
|
|
156
|
+
const cloudWorkspaceSelectionPath = '/setup?step=workspace&source=cloud&postAuth=select-cloud-workspace';
|
|
134
157
|
const wantsCloudWorkspaceSetup = !isCloudRuntime && workspaceSetupSource === 'cloud';
|
|
135
158
|
const isCloudWorkspaceSetupFlow = wantsCloudWorkspaceSetup && canUseCloudWorkspaceSetup;
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
159
|
+
const activeCloudWorkspaceImport = useMemo(() => {
|
|
160
|
+
if (pendingCloudWorkspaceImport)
|
|
161
|
+
return pendingCloudWorkspaceImport;
|
|
162
|
+
if (workspaceSyncSetupIntent !== 'attach-cloud-import')
|
|
163
|
+
return null;
|
|
164
|
+
const workspaceId = String(currentWorkspaceId || '').trim();
|
|
165
|
+
if (!workspaceId || workspaceId.toLowerCase() === 'default')
|
|
166
|
+
return null;
|
|
167
|
+
const matchingWorkspace = cloudWorkspaceOptions.find((workspace) => workspace.id === workspaceId);
|
|
168
|
+
return {
|
|
169
|
+
workspaceId,
|
|
170
|
+
name: matchingWorkspace?.name || workspaceId
|
|
171
|
+
};
|
|
172
|
+
}, [cloudWorkspaceOptions, currentWorkspaceId, pendingCloudWorkspaceImport, workspaceSyncSetupIntent]);
|
|
173
|
+
const cloudWorkspaceImportMatchesCurrentWorkspace = Boolean(activeCloudWorkspaceImport
|
|
174
|
+
&& currentWorkspaceId === activeCloudWorkspaceImport.workspaceId);
|
|
175
|
+
const isCloudWorkspaceImportErrored = Boolean(activeCloudWorkspaceImport
|
|
139
176
|
&& cloudWorkspaceImportMatchesCurrentWorkspace
|
|
140
177
|
&& workspaceSyncPhase === 'error');
|
|
141
|
-
const isCloudWorkspaceImportReady = Boolean(
|
|
178
|
+
const isCloudWorkspaceImportReady = Boolean(activeCloudWorkspaceImport
|
|
142
179
|
&& cloudWorkspaceImportMatchesCurrentWorkspace
|
|
143
180
|
&& workspaceCloudSyncEnabled
|
|
144
181
|
&& workspaceSyncPhase === 'active'
|
|
145
182
|
&& !workspaceSyncBusy);
|
|
146
183
|
const cloudWorkspaceImportProgressLines = useMemo(() => {
|
|
147
|
-
if (!
|
|
184
|
+
if (!activeCloudWorkspaceImport)
|
|
148
185
|
return [];
|
|
149
186
|
const connectingState = setupBusy ? 'In progress' : 'Complete';
|
|
150
187
|
const importingState = isCloudWorkspaceImportErrored
|
|
@@ -163,7 +200,7 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
163
200
|
`Open local workspace: ${finishingState}`
|
|
164
201
|
];
|
|
165
202
|
}, [
|
|
166
|
-
|
|
203
|
+
activeCloudWorkspaceImport,
|
|
167
204
|
setupBusy,
|
|
168
205
|
isCloudWorkspaceImportErrored,
|
|
169
206
|
workspaceSyncBusy,
|
|
@@ -183,6 +220,9 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
183
220
|
&& !isPricingRoute
|
|
184
221
|
&& !isPlansRoute
|
|
185
222
|
&& !isPlansScreen;
|
|
223
|
+
const cloudAccountAccessPending = runtimeMode === 'cloud'
|
|
224
|
+
&& isAuthenticated
|
|
225
|
+
&& (!authSessionResolved || !accountAccessGateResolved);
|
|
186
226
|
const directRegistrationEnabled = cloudAuthConfigured;
|
|
187
227
|
const authPageTitle = useMemo(() => {
|
|
188
228
|
switch (authMode) {
|
|
@@ -205,7 +245,7 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
205
245
|
const setupIntent = useMemo(() => String(new URLSearchParams(location.search).get('intent') || '').trim().toLowerCase(), [location.search]);
|
|
206
246
|
const isCreateWorkspaceIntent = isSetupRoute && setupStep === 'workspace' && setupIntent === 'create-workspace';
|
|
207
247
|
const shouldHoldForAuthBootstrap = mode !== 'widget'
|
|
208
|
-
&&
|
|
248
|
+
&& bootstrapState.authPending
|
|
209
249
|
&& !isLoginRoute
|
|
210
250
|
&& !isSetupRoute
|
|
211
251
|
&& !isPricingRoute
|
|
@@ -233,23 +273,47 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
233
273
|
const needsWorkspaceSetup = workspaceLifecycle.needsWorkspaceSetup;
|
|
234
274
|
const hasSetupReadError = workspaceLifecycle.hasSetupReadError;
|
|
235
275
|
const shouldHoldForSetupBootstrap = mode !== 'widget'
|
|
236
|
-
&&
|
|
276
|
+
&& bootstrapState.auth.resolved
|
|
237
277
|
&& !shouldForceLogin
|
|
238
278
|
&& !shouldForceComingSoon
|
|
239
279
|
&& !isPricingRoute
|
|
240
280
|
&& !isPlansRoute
|
|
241
281
|
&& !isPlansScreen
|
|
242
|
-
&&
|
|
282
|
+
&& bootstrapState.appPending
|
|
243
283
|
&& (isSetupRoute || runtimeMode === 'cloud' || isLikelyCloudAppHost);
|
|
244
|
-
const
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
284
|
+
const shouldHoldForCommercialOnboardingRedirect = runtimeMode === 'cloud'
|
|
285
|
+
&& commercialOnboardingRedirectActive
|
|
286
|
+
&& !isPlansScreen;
|
|
287
|
+
const bootstrapSubtitle = bootstrapState.subtitle;
|
|
288
|
+
const showGuidedWorkspaceSetup = isCreateWorkspaceIntent
|
|
289
|
+
|| (isSetupRoute && Boolean(activeCloudWorkspaceImport))
|
|
290
|
+
|| (isSetupRoute && setupGateActive && (hasSetupReadError || needsGlobalSetup || needsWorkspaceSetup));
|
|
291
|
+
const shellRenderState = useMemo(() => {
|
|
292
|
+
if (shouldHoldForAuthBootstrap)
|
|
293
|
+
return 'auth-bootstrap';
|
|
294
|
+
if (shouldHoldForCommercialOnboardingRedirect)
|
|
295
|
+
return 'app-bootstrap';
|
|
296
|
+
if (shouldHoldForSetupBootstrap)
|
|
297
|
+
return 'app-bootstrap';
|
|
298
|
+
if (isLoginRoute)
|
|
299
|
+
return 'login';
|
|
300
|
+
if (shouldForceLogin && !isLoginRoute)
|
|
301
|
+
return 'login-redirect';
|
|
302
|
+
if (isComingSoonRoute && shouldForceComingSoon)
|
|
303
|
+
return 'coming-soon';
|
|
304
|
+
if (showGuidedWorkspaceSetup)
|
|
305
|
+
return 'guided-setup';
|
|
306
|
+
return 'app';
|
|
307
|
+
}, [
|
|
308
|
+
isComingSoonRoute,
|
|
309
|
+
isLoginRoute,
|
|
310
|
+
shouldForceComingSoon,
|
|
311
|
+
shouldForceLogin,
|
|
312
|
+
shouldHoldForCommercialOnboardingRedirect,
|
|
313
|
+
shouldHoldForAuthBootstrap,
|
|
314
|
+
shouldHoldForSetupBootstrap,
|
|
315
|
+
showGuidedWorkspaceSetup
|
|
316
|
+
]);
|
|
253
317
|
const isPlaceholderWorkspaceName = useCallback((name, workspaceId, _description) => {
|
|
254
318
|
const normalizedName = String(name || '').trim().toLowerCase();
|
|
255
319
|
const normalizedWorkspaceId = String(workspaceId || '').trim().toLowerCase();
|
|
@@ -266,7 +330,12 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
266
330
|
setSetupModeChoice(modeValue);
|
|
267
331
|
}, [setupState?.mode]);
|
|
268
332
|
useEffect(() => {
|
|
269
|
-
if (runtimeMode !== 'cloud' || !isAuthenticated
|
|
333
|
+
if (runtimeMode !== 'cloud' || !isAuthenticated) {
|
|
334
|
+
setAccountAccessGate(null);
|
|
335
|
+
setAccountAccessGateResolved(false);
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
if (!authSessionResolved) {
|
|
270
339
|
setAccountAccessGate(null);
|
|
271
340
|
setAccountAccessGateResolved(false);
|
|
272
341
|
return;
|
|
@@ -340,6 +409,11 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
340
409
|
if (!isSetupRoute || setupStep !== 'workspace')
|
|
341
410
|
return;
|
|
342
411
|
setWorkspaceSetupPage('details');
|
|
412
|
+
if (activeCloudWorkspaceImport && canUseCloudWorkspaceSetup) {
|
|
413
|
+
setWorkspaceSetupSource('cloud');
|
|
414
|
+
setSyncNewLocalWorkspaceToCloud(false);
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
343
417
|
const source = String(new URLSearchParams(location.search).get('source') || '').trim().toLowerCase();
|
|
344
418
|
if (source === 'cloud' && canUseCloudWorkspaceSetup) {
|
|
345
419
|
setWorkspaceSetupSource('cloud');
|
|
@@ -347,7 +421,7 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
347
421
|
return;
|
|
348
422
|
}
|
|
349
423
|
setWorkspaceSetupSource('local');
|
|
350
|
-
}, [canUseCloudWorkspaceSetup, isSetupRoute, location.search, setupStep]);
|
|
424
|
+
}, [activeCloudWorkspaceImport, canUseCloudWorkspaceSetup, isSetupRoute, location.search, setupStep]);
|
|
351
425
|
const fetchCloudWorkspaceOptions = useCallback(async () => {
|
|
352
426
|
if (!canUseCloudWorkspaceSetup || !isAuthenticated) {
|
|
353
427
|
setCloudWorkspaceOptions([]);
|
|
@@ -410,7 +484,7 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
410
484
|
})
|
|
411
485
|
.then(async (res) => {
|
|
412
486
|
if (!res.ok)
|
|
413
|
-
throw new Error(`Failed to load
|
|
487
|
+
throw new Error(`Failed to load setup libraries (${res.status})`);
|
|
414
488
|
const payload = await res.json().catch(() => ({}));
|
|
415
489
|
const nextPacks = Array.isArray(payload?.packs) ? payload.packs : [];
|
|
416
490
|
if (cancelled || nextPacks.length === 0)
|
|
@@ -424,7 +498,7 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
424
498
|
if (cancelled)
|
|
425
499
|
return;
|
|
426
500
|
setSetupLibraryPacks(fallbackSetupLibraryPacks);
|
|
427
|
-
setSetupLibraryError(error instanceof Error ? error.message : 'Failed to load
|
|
501
|
+
setSetupLibraryError(error instanceof Error ? error.message : 'Failed to load setup libraries.');
|
|
428
502
|
})
|
|
429
503
|
.finally(() => {
|
|
430
504
|
if (!cancelled)
|
|
@@ -446,22 +520,24 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
446
520
|
if (!isCloudWorkspaceSetupFlow) {
|
|
447
521
|
setPendingCloudWorkspaceImport(null);
|
|
448
522
|
setCloudWorkspaceImportFinalizing(false);
|
|
523
|
+
cloudWorkspaceImportFinalizingRef.current = false;
|
|
449
524
|
}
|
|
450
525
|
}, [isCloudWorkspaceSetupFlow]);
|
|
451
526
|
useEffect(() => {
|
|
452
|
-
if (!isCloudWorkspaceImportErrored || !
|
|
527
|
+
if (!isCloudWorkspaceImportErrored || !activeCloudWorkspaceImport)
|
|
453
528
|
return;
|
|
454
529
|
setSetupNotice(null);
|
|
455
530
|
setSetupError(workspaceLastErrorMessage
|
|
456
|
-
|| `Failed to import "${
|
|
457
|
-
}, [
|
|
531
|
+
|| `Failed to import "${activeCloudWorkspaceImport.name}" from cloud. Retry import to continue.`);
|
|
532
|
+
}, [activeCloudWorkspaceImport, isCloudWorkspaceImportErrored, workspaceLastErrorMessage]);
|
|
458
533
|
useEffect(() => {
|
|
459
|
-
if (!isCloudWorkspaceImportReady || !
|
|
534
|
+
if (!isCloudWorkspaceImportReady || !activeCloudWorkspaceImport || cloudWorkspaceImportFinalizingRef.current)
|
|
460
535
|
return;
|
|
461
536
|
let cancelled = false;
|
|
537
|
+
cloudWorkspaceImportFinalizingRef.current = true;
|
|
462
538
|
setCloudWorkspaceImportFinalizing(true);
|
|
463
539
|
setSetupError(null);
|
|
464
|
-
setSetupNotice(`Workspace import complete. Opening ${
|
|
540
|
+
setSetupNotice(`Workspace import complete. Opening ${activeCloudWorkspaceImport.name}...`);
|
|
465
541
|
void (async () => {
|
|
466
542
|
try {
|
|
467
543
|
await refreshSetupContext();
|
|
@@ -471,6 +547,12 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
471
547
|
]);
|
|
472
548
|
if (cancelled)
|
|
473
549
|
return;
|
|
550
|
+
await persistWorkspaceSyncStatePatch(activeCloudWorkspaceImport.workspaceId, { setupIntent: null });
|
|
551
|
+
applyWorkspaceSyncStateSnapshot({
|
|
552
|
+
enabled: workspaceCloudSyncEnabled,
|
|
553
|
+
phase: workspaceSyncPhase,
|
|
554
|
+
setupIntent: null
|
|
555
|
+
});
|
|
474
556
|
setPendingCloudWorkspaceImport(null);
|
|
475
557
|
navigate('/', { replace: true });
|
|
476
558
|
}
|
|
@@ -478,20 +560,22 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
478
560
|
if (cancelled)
|
|
479
561
|
return;
|
|
480
562
|
setSetupNotice(null);
|
|
481
|
-
setSetupError(`Imported "${
|
|
563
|
+
setSetupError(`Imported "${activeCloudWorkspaceImport.name}" but failed to finish opening it.`);
|
|
482
564
|
setCloudWorkspaceImportFinalizing(false);
|
|
565
|
+
cloudWorkspaceImportFinalizingRef.current = false;
|
|
483
566
|
}
|
|
484
567
|
})();
|
|
485
568
|
return () => {
|
|
486
569
|
cancelled = true;
|
|
487
570
|
};
|
|
488
571
|
}, [
|
|
489
|
-
|
|
572
|
+
activeCloudWorkspaceImport,
|
|
573
|
+
applyWorkspaceSyncStateSnapshot,
|
|
490
574
|
fetchTasks,
|
|
491
575
|
fetchWorkspaces,
|
|
492
576
|
isCloudWorkspaceImportReady,
|
|
493
577
|
navigate,
|
|
494
|
-
|
|
578
|
+
persistWorkspaceSyncStatePatch,
|
|
495
579
|
refreshSetupContext
|
|
496
580
|
]);
|
|
497
581
|
const nextPath = useMemo(() => {
|
|
@@ -502,7 +586,6 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
502
586
|
return '/';
|
|
503
587
|
return raw;
|
|
504
588
|
}, [location.search]);
|
|
505
|
-
const cloudWorkspaceSelectionPath = '/setup?step=workspace&source=cloud&postAuth=select-cloud-workspace';
|
|
506
589
|
const shouldReturnToCloudWorkspaceSelection = useMemo(() => {
|
|
507
590
|
try {
|
|
508
591
|
const parsed = new URL(nextPath, 'https://taskforce.local');
|
|
@@ -590,27 +673,95 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
590
673
|
setPendingVerificationRedirectPath(null);
|
|
591
674
|
}, [authMode, isLoginRoute, loginRouteMode]);
|
|
592
675
|
useEffect(() => {
|
|
593
|
-
if (
|
|
676
|
+
if (authMode === 'register')
|
|
677
|
+
return;
|
|
678
|
+
setLoginPasswordConfirm('');
|
|
679
|
+
}, [authMode]);
|
|
680
|
+
useEffect(() => {
|
|
681
|
+
if (!pendingPostAuthRedirect || !isAuthenticated)
|
|
594
682
|
return;
|
|
595
|
-
if (
|
|
683
|
+
if (!pendingPostAuthRedirect.commercialOnboardingGate && (!authSessionResolved || cloudAccountAccessPending)) {
|
|
596
684
|
return;
|
|
597
685
|
}
|
|
598
|
-
|
|
599
|
-
|
|
686
|
+
const destination = !pendingPostAuthRedirect.commercialOnboardingGate && pendingPostAuthRedirect.workspaceSetupRequired
|
|
687
|
+
? resolveWorkspaceSetupPath()
|
|
688
|
+
: pendingPostAuthRedirect.path;
|
|
689
|
+
const currentRoute = `${location.pathname}${location.search}${location.hash || ''}`;
|
|
690
|
+
if (currentRoute !== destination) {
|
|
691
|
+
navigate(destination, { replace: true });
|
|
600
692
|
}
|
|
601
|
-
|
|
602
|
-
|
|
693
|
+
}, [
|
|
694
|
+
authSessionResolved,
|
|
695
|
+
cloudAccountAccessPending,
|
|
696
|
+
isAuthenticated,
|
|
697
|
+
location.hash,
|
|
698
|
+
location.pathname,
|
|
699
|
+
location.search,
|
|
700
|
+
navigate,
|
|
701
|
+
pendingPostAuthRedirect,
|
|
702
|
+
resolveWorkspaceSetupPath
|
|
703
|
+
]);
|
|
704
|
+
useEffect(() => {
|
|
705
|
+
if (!commercialOnboardingRedirectActive)
|
|
706
|
+
return;
|
|
707
|
+
if (!authSessionResolved) {
|
|
708
|
+
return;
|
|
709
|
+
}
|
|
710
|
+
if (!isAuthenticated) {
|
|
711
|
+
setCommercialOnboardingRedirectActive(false);
|
|
712
|
+
setPendingPostAuthRedirect(null);
|
|
713
|
+
return;
|
|
603
714
|
}
|
|
715
|
+
if (accountAccessGateResolved && accountAccessGate?.canAccessApp === true) {
|
|
716
|
+
setLoginEmail('');
|
|
717
|
+
setLoginDisplayName('');
|
|
718
|
+
setLoginPassword('');
|
|
719
|
+
setLoginPasswordConfirm('');
|
|
720
|
+
setPendingPostAuthRedirect(null);
|
|
721
|
+
setCommercialOnboardingRedirectActive(false);
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
724
|
+
const destination = pendingPostAuthRedirect?.commercialOnboardingGate
|
|
725
|
+
? pendingPostAuthRedirect.path
|
|
726
|
+
: planSelectionPath;
|
|
727
|
+
const currentRoute = `${location.pathname}${location.search}${location.hash || ''}`;
|
|
728
|
+
if (currentRoute !== destination) {
|
|
729
|
+
navigate(destination, { replace: true });
|
|
730
|
+
}
|
|
731
|
+
}, [
|
|
732
|
+
accountAccessGate?.canAccessApp,
|
|
733
|
+
accountAccessGateResolved,
|
|
734
|
+
authSessionResolved,
|
|
735
|
+
commercialOnboardingRedirectActive,
|
|
736
|
+
isAuthenticated,
|
|
737
|
+
location.hash,
|
|
738
|
+
location.pathname,
|
|
739
|
+
location.search,
|
|
740
|
+
navigate,
|
|
741
|
+
pendingPostAuthRedirect,
|
|
742
|
+
planSelectionPath
|
|
743
|
+
]);
|
|
744
|
+
useEffect(() => {
|
|
745
|
+
if (!pendingPostAuthRedirect || !isAuthenticated)
|
|
746
|
+
return;
|
|
747
|
+
const destination = !pendingPostAuthRedirect.commercialOnboardingGate && pendingPostAuthRedirect.workspaceSetupRequired
|
|
748
|
+
? resolveWorkspaceSetupPath()
|
|
749
|
+
: pendingPostAuthRedirect.path;
|
|
750
|
+
const currentRoute = `${location.pathname}${location.search}${location.hash || ''}`;
|
|
751
|
+
if (currentRoute !== destination)
|
|
752
|
+
return;
|
|
753
|
+
if (pendingPostAuthRedirect.commercialOnboardingGate)
|
|
754
|
+
return;
|
|
604
755
|
setLoginEmail('');
|
|
605
756
|
setLoginDisplayName('');
|
|
606
757
|
setLoginPassword('');
|
|
758
|
+
setLoginPasswordConfirm('');
|
|
607
759
|
setPendingPostAuthRedirect(null);
|
|
608
760
|
}, [
|
|
609
|
-
|
|
610
|
-
|
|
761
|
+
location.hash,
|
|
762
|
+
location.pathname,
|
|
763
|
+
location.search,
|
|
611
764
|
isAuthenticated,
|
|
612
|
-
isLoginRoute,
|
|
613
|
-
navigate,
|
|
614
765
|
pendingPostAuthRedirect,
|
|
615
766
|
resolveWorkspaceSetupPath,
|
|
616
767
|
runtimeMode
|
|
@@ -632,6 +783,9 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
632
783
|
setInviteEmail(inspected.email || null);
|
|
633
784
|
setInviteWorkspaceId(inspected.workspaceId || null);
|
|
634
785
|
setInvitePasswordRequired(inspected.passwordRequired === true);
|
|
786
|
+
// Capture OAuth providers available for invite-claim (pending_setup only)
|
|
787
|
+
const oauthMethods = (inspected.availableMethods || []).filter(m => m === 'google' || m === 'github' || m === 'apple');
|
|
788
|
+
setInviteOAuthProviders(inspected.inviteeState === 'pending_setup' ? oauthMethods : []);
|
|
635
789
|
const inviteMatchesAuthenticatedUser = Boolean(isAuthenticated
|
|
636
790
|
&& authUserEmail
|
|
637
791
|
&& inspected.email
|
|
@@ -657,6 +811,7 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
657
811
|
setInviteWorkspaceId(null);
|
|
658
812
|
setInviteResolution('invalid');
|
|
659
813
|
setInvitePasswordRequired(false);
|
|
814
|
+
setInviteOAuthProviders([]);
|
|
660
815
|
setLoginError(formatAuthError(inspected));
|
|
661
816
|
setLoginErrorCode(inspected.code || inspected.state || null);
|
|
662
817
|
}
|
|
@@ -683,15 +838,42 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
683
838
|
const resetCooldownSeconds = Math.max(0, Math.ceil((resetCooldownUntil - cooldownNow) / 1000));
|
|
684
839
|
const verifyCooldownActive = verifyCooldownSeconds > 0;
|
|
685
840
|
const resetCooldownActive = resetCooldownSeconds > 0;
|
|
841
|
+
const registerPasswordGuidance = useMemo(() => getPasswordPolicyGuidance(), []);
|
|
686
842
|
const formatAuthError = (result) => {
|
|
687
843
|
const message = result.error || 'Request failed.';
|
|
688
844
|
const code = result.code || result.state;
|
|
689
845
|
return code ? `[${code}] ${message}` : message;
|
|
690
846
|
};
|
|
847
|
+
const buildVerificationRequestNotice = (input) => {
|
|
848
|
+
if (input?.deliveryAttempted === false) {
|
|
849
|
+
return input?.intro || 'If that account exists and still needs verification, we sent an email.';
|
|
850
|
+
}
|
|
851
|
+
if (input?.emailSent === false) {
|
|
852
|
+
return `Verification email failed to send. ${input?.emailError || 'Try Resend Verification again.'}`;
|
|
853
|
+
}
|
|
854
|
+
return 'Verification email resent.';
|
|
855
|
+
};
|
|
856
|
+
const buildPasswordResetNotice = (input) => {
|
|
857
|
+
if (input?.deliveryAttempted === false) {
|
|
858
|
+
return 'If that account exists, we sent password reset instructions.';
|
|
859
|
+
}
|
|
860
|
+
if (input?.emailSent === false) {
|
|
861
|
+
return `Password reset email failed to send. ${input?.emailError || 'Try again in a minute.'}`;
|
|
862
|
+
}
|
|
863
|
+
return 'Password reset instructions sent.';
|
|
864
|
+
};
|
|
691
865
|
useEffect(() => {
|
|
692
866
|
if (mode === 'widget')
|
|
693
867
|
return;
|
|
694
|
-
if (
|
|
868
|
+
if (isLoginRoute && loginBusy) {
|
|
869
|
+
return;
|
|
870
|
+
}
|
|
871
|
+
const shouldBypassWorkspacePendingForAccessGate = runtimeMode === 'cloud'
|
|
872
|
+
&& isAuthenticated
|
|
873
|
+
&& accountAccessGateResolved
|
|
874
|
+
&& accountAccessGate?.canAccessApp === false
|
|
875
|
+
&& accountAccessGate?.canAccessPlans !== false;
|
|
876
|
+
if (bootstrapState.workspace.pending && !shouldBypassWorkspacePendingForAccessGate)
|
|
695
877
|
return;
|
|
696
878
|
if (shouldForceComingSoon) {
|
|
697
879
|
if (!isComingSoonRoute)
|
|
@@ -710,10 +892,11 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
710
892
|
&& isAuthenticated
|
|
711
893
|
&& !shouldStayOnInviteRoute
|
|
712
894
|
&& (Boolean(pendingPostAuthRedirect)
|
|
713
|
-
||
|
|
895
|
+
|| bootstrapState.workspace.pending
|
|
714
896
|
|| authMode === 'register'
|
|
715
897
|
|| authMode === 'verify'
|
|
716
|
-
||
|
|
898
|
+
|| !bootstrapState.config.loaded
|
|
899
|
+
|| cloudAccountAccessPending);
|
|
717
900
|
if (shouldDelayAuthenticatedLoginRedirect) {
|
|
718
901
|
return;
|
|
719
902
|
}
|
|
@@ -731,6 +914,23 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
731
914
|
if (isLoginRoute && !isAuthenticated) {
|
|
732
915
|
return;
|
|
733
916
|
}
|
|
917
|
+
if (pendingPostAuthRedirect && isAuthenticated) {
|
|
918
|
+
return;
|
|
919
|
+
}
|
|
920
|
+
const shouldHoldAuthenticatedAuthRoute = isLoginRoute
|
|
921
|
+
&& isAuthenticated
|
|
922
|
+
&& (authMode === 'register'
|
|
923
|
+
|| authMode === 'verify'
|
|
924
|
+
|| (authMode === 'invite' && inviteToken.trim().length > 0));
|
|
925
|
+
if (shouldHoldAuthenticatedAuthRoute) {
|
|
926
|
+
return;
|
|
927
|
+
}
|
|
928
|
+
if (commercialOnboardingRedirectActive) {
|
|
929
|
+
return;
|
|
930
|
+
}
|
|
931
|
+
if (isPlansScreen) {
|
|
932
|
+
return;
|
|
933
|
+
}
|
|
734
934
|
if (shouldForceAccountAccessGate) {
|
|
735
935
|
if (!isPlansScreen) {
|
|
736
936
|
const gate = encodeURIComponent(String(accountAccessGate?.gate || 'plan_selection_required'));
|
|
@@ -738,7 +938,23 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
738
938
|
}
|
|
739
939
|
return;
|
|
740
940
|
}
|
|
741
|
-
if (
|
|
941
|
+
if (activeCloudWorkspaceImport) {
|
|
942
|
+
const isCloudImportRoute = isSetupRoute
|
|
943
|
+
&& setupStep === 'workspace'
|
|
944
|
+
&& new URLSearchParams(location.search).get('source') === 'cloud';
|
|
945
|
+
if (!isCloudImportRoute) {
|
|
946
|
+
navigate(cloudWorkspaceSelectionPath, { replace: true });
|
|
947
|
+
}
|
|
948
|
+
return;
|
|
949
|
+
}
|
|
950
|
+
if (isPlansScreen && cloudAccountAccessPending) {
|
|
951
|
+
return;
|
|
952
|
+
}
|
|
953
|
+
if (isPlansScreen
|
|
954
|
+
&& runtimeMode === 'cloud'
|
|
955
|
+
&& accountAccessGateResolved
|
|
956
|
+
&& accountAccessGate?.canAccessApp === false
|
|
957
|
+
&& accountAccessGate?.canAccessPlans !== false) {
|
|
742
958
|
return;
|
|
743
959
|
}
|
|
744
960
|
if (setupGateActive) {
|
|
@@ -778,34 +994,40 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
778
994
|
isComingSoonRoute,
|
|
779
995
|
isPlansRoute,
|
|
780
996
|
isPlansScreen,
|
|
997
|
+
accountAccessGate,
|
|
781
998
|
accountAccessGate?.gate,
|
|
782
999
|
accountAccessGateResolved,
|
|
783
1000
|
authMode,
|
|
1001
|
+
commercialOnboardingRedirectActive,
|
|
1002
|
+
cloudAccountAccessPending,
|
|
784
1003
|
setupGateActive,
|
|
785
1004
|
setupState,
|
|
786
1005
|
hasSetupReadError,
|
|
787
1006
|
isCreateWorkspaceIntent,
|
|
788
|
-
|
|
1007
|
+
activeCloudWorkspaceImport,
|
|
789
1008
|
needsGlobalSetup,
|
|
790
1009
|
needsWorkspaceSetup,
|
|
791
1010
|
setupStep,
|
|
792
|
-
|
|
793
|
-
configLoaded,
|
|
1011
|
+
bootstrapState,
|
|
794
1012
|
runtimeMode,
|
|
1013
|
+
loginBusy,
|
|
1014
|
+
isAuthenticated,
|
|
1015
|
+
accountAccessGateResolved,
|
|
795
1016
|
inviteToken,
|
|
796
1017
|
location.pathname,
|
|
797
1018
|
location.search,
|
|
798
1019
|
location.hash,
|
|
799
1020
|
navigate,
|
|
800
1021
|
nextPath,
|
|
801
|
-
pendingPostAuthRedirect
|
|
1022
|
+
pendingPostAuthRedirect,
|
|
1023
|
+
cloudWorkspaceSelectionPath
|
|
802
1024
|
]);
|
|
803
1025
|
useEffect(() => {
|
|
804
1026
|
if (mode === 'widget')
|
|
805
1027
|
return;
|
|
806
1028
|
if (runtimeMode !== 'local')
|
|
807
1029
|
return;
|
|
808
|
-
if (!
|
|
1030
|
+
if (!bootstrapState.config.loaded)
|
|
809
1031
|
return;
|
|
810
1032
|
if (isLoginRoute || isSetupRoute)
|
|
811
1033
|
return;
|
|
@@ -813,7 +1035,7 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
813
1035
|
if (!workspaceId || workspaceId === 'default') {
|
|
814
1036
|
navigate('/setup?step=workspace', { replace: true });
|
|
815
1037
|
}
|
|
816
|
-
}, [mode, runtimeMode,
|
|
1038
|
+
}, [mode, runtimeMode, bootstrapState.config.loaded, isLoginRoute, isSetupRoute, currentWorkspaceId, navigate]);
|
|
817
1039
|
const handleRetryBootstrap = useCallback(async () => {
|
|
818
1040
|
setBootstrapRetryBusy(true);
|
|
819
1041
|
try {
|
|
@@ -841,13 +1063,13 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
841
1063
|
params.set('screen', 'plans');
|
|
842
1064
|
return _jsx(Navigate, { to: `/?${params.toString()}${location.hash || ''}`, replace: true });
|
|
843
1065
|
}
|
|
844
|
-
if (
|
|
1066
|
+
if (shellRenderState === 'auth-bootstrap') {
|
|
845
1067
|
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: "Checking your session..." })] }) }) }));
|
|
846
1068
|
}
|
|
847
|
-
if (
|
|
848
|
-
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: bootstrapSubtitle }),
|
|
1069
|
+
if (shellRenderState === 'app-bootstrap') {
|
|
1070
|
+
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: bootstrapSubtitle }), bootstrapState.stalled && (_jsxs(_Fragment, { children: [_jsx("p", { className: authStyles.loginError, children: bootstrapState.error || 'Startup checks timed out.' }), _jsx("button", { className: styles.submitBtn, disabled: bootstrapRetryBusy, onClick: handleRetryBootstrap, children: bootstrapRetryBusy ? 'Retrying...' : 'Retry checks' }), isCloudRuntime && (_jsx("button", { className: styles.cancelBtn, disabled: bootstrapRetryBusy, onClick: handleGoToSignIn, children: "Go to sign-in" }))] }))] }) }) }));
|
|
849
1071
|
}
|
|
850
|
-
if (
|
|
1072
|
+
if (shellRenderState === 'login') {
|
|
851
1073
|
return (_jsx("div", { className: shellStyles.standaloneWrapper, "data-theme": currentTheme, children: _jsx("div", { className: authStyles.loginView, style: {
|
|
852
1074
|
backgroundImage: `linear-gradient(rgba(15, 15, 26, 0.88), rgba(15, 15, 26, 0.9)), url(${taskforceBg})`
|
|
853
1075
|
}, 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'
|
|
@@ -869,7 +1091,7 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
869
1091
|
? (invitePasswordRequired ? 'Create password' : 'Password')
|
|
870
1092
|
: 'Password'), autoComplete: authMode === 'register' || authMode === 'reset' || (authMode === 'invite' && invitePasswordRequired)
|
|
871
1093
|
? '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 () => {
|
|
1094
|
+
: 'current-password', "data-testid": "auth-password-input", onChange: (event) => setLoginPassword(event.target.value) })] })), (authMode === 'register' || (authMode === 'invite' && invitePasswordRequired)) && (_jsx(_Fragment, { children: _jsxs("label", { className: authStyles.loginField, children: [_jsx("span", { className: authStyles.loginFieldLabel, children: "Confirm password" }), _jsx("input", { className: `${styles.input} ${authStyles.loginInput}`, type: "password", value: loginPasswordConfirm, placeholder: "Confirm password", autoComplete: "new-password", "data-testid": "auth-confirm-password-input", onChange: (event) => setLoginPasswordConfirm(event.target.value) })] }) })), (authMode === 'register' || (authMode === 'invite' && invitePasswordRequired)) && (_jsx("p", { className: authStyles.loginSubtitle, children: registerPasswordGuidance.join(' ') })), 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 () => {
|
|
873
1095
|
setLoginBusy(true);
|
|
874
1096
|
setLoginNotice(null);
|
|
875
1097
|
const sent = await requestEmailVerification(loginEmail);
|
|
@@ -878,7 +1100,7 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
878
1100
|
setLoginMode('verify');
|
|
879
1101
|
if (sent.verificationToken)
|
|
880
1102
|
setVerificationToken(sent.verificationToken);
|
|
881
|
-
setLoginNotice(
|
|
1103
|
+
setLoginNotice(buildVerificationRequestNotice(sent));
|
|
882
1104
|
setLoginError(null);
|
|
883
1105
|
setLoginErrorCode(null);
|
|
884
1106
|
}
|
|
@@ -896,6 +1118,24 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
896
1118
|
setLoginBusy(false);
|
|
897
1119
|
return;
|
|
898
1120
|
}
|
|
1121
|
+
if (authMode === 'register') {
|
|
1122
|
+
const validation = validatePasswordPolicy(loginPassword, {
|
|
1123
|
+
email: loginEmail,
|
|
1124
|
+
displayName: loginDisplayName
|
|
1125
|
+
});
|
|
1126
|
+
if (!validation.ok) {
|
|
1127
|
+
setLoginError(validation.message || 'Password does not meet the password policy.');
|
|
1128
|
+
setLoginErrorCode(validation.code || null);
|
|
1129
|
+
setLoginBusy(false);
|
|
1130
|
+
return;
|
|
1131
|
+
}
|
|
1132
|
+
if (loginPassword !== loginPasswordConfirm) {
|
|
1133
|
+
setLoginError('Passwords do not match.');
|
|
1134
|
+
setLoginErrorCode('PASSWORD_CONFIRMATION_MISMATCH');
|
|
1135
|
+
setLoginBusy(false);
|
|
1136
|
+
return;
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
899
1139
|
let result = { success: false };
|
|
900
1140
|
if (authMode === 'login') {
|
|
901
1141
|
result = await loginWithCredentials(loginEmail, loginPassword);
|
|
@@ -950,7 +1190,10 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
950
1190
|
setLoginMode('verify');
|
|
951
1191
|
if (sent.verificationToken)
|
|
952
1192
|
setVerificationToken(sent.verificationToken);
|
|
953
|
-
setLoginNotice(
|
|
1193
|
+
setLoginNotice(buildVerificationRequestNotice({
|
|
1194
|
+
...sent,
|
|
1195
|
+
intro: 'If that account exists and still needs verification, we sent an email.'
|
|
1196
|
+
}));
|
|
954
1197
|
setLoginError(null);
|
|
955
1198
|
setLoginErrorCode(null);
|
|
956
1199
|
}
|
|
@@ -985,6 +1228,7 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
985
1228
|
: 'Account created. Check your email for verification instructions.');
|
|
986
1229
|
}
|
|
987
1230
|
else {
|
|
1231
|
+
setCommercialOnboardingRedirectActive(commercialOnboardingGate);
|
|
988
1232
|
setPendingPostAuthRedirect({
|
|
989
1233
|
path: postRegistrationPath,
|
|
990
1234
|
workspaceSetupRequired: Boolean(result.workspaceSetupRequired),
|
|
@@ -996,6 +1240,7 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
996
1240
|
await retryBootstrapChecks();
|
|
997
1241
|
const commercialOnboardingGate = pendingVerificationCommercialState === 'pending_plan_selection'
|
|
998
1242
|
|| pendingVerificationCommercialState === 'checkout_pending';
|
|
1243
|
+
setCommercialOnboardingRedirectActive(commercialOnboardingGate);
|
|
999
1244
|
setPendingPostAuthRedirect({
|
|
1000
1245
|
path: pendingVerificationRedirectPath || nextPath,
|
|
1001
1246
|
workspaceSetupRequired: pendingVerificationWorkspaceSetupRequired,
|
|
@@ -1003,13 +1248,14 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
1003
1248
|
});
|
|
1004
1249
|
}
|
|
1005
1250
|
else if (authMode === 'forgot') {
|
|
1251
|
+
const passwordResetDeliveryFailed = result.deliveryAttempted === true && result.emailSent === false;
|
|
1006
1252
|
if (result.resetToken)
|
|
1007
1253
|
setResetToken(result.resetToken);
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1254
|
+
setLoginNotice(buildPasswordResetNotice(result));
|
|
1255
|
+
if (!passwordResetDeliveryFailed) {
|
|
1256
|
+
setResetCooldownUntil(Date.now() + 30_000);
|
|
1257
|
+
setLoginMode('reset');
|
|
1258
|
+
}
|
|
1013
1259
|
}
|
|
1014
1260
|
else if (authMode === 'reset') {
|
|
1015
1261
|
setLoginMode('login');
|
|
@@ -1017,9 +1263,33 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
1017
1263
|
}
|
|
1018
1264
|
else if (authMode === 'invite') {
|
|
1019
1265
|
if (inviteResolution === 'existing_user_signed_out') {
|
|
1020
|
-
|
|
1266
|
+
const normalizedInviteEmail = String(inviteEmail || '').trim().toLowerCase();
|
|
1267
|
+
const normalizedLoginEmail = loginEmail.trim().toLowerCase();
|
|
1268
|
+
// Make the invite flow deterministically join-ready as soon as
|
|
1269
|
+
// the invited account signs in successfully, instead of waiting
|
|
1270
|
+
// for a later invite inspection effect to catch up.
|
|
1271
|
+
if (normalizedInviteEmail && normalizedLoginEmail === normalizedInviteEmail) {
|
|
1272
|
+
setAuthMode('invite');
|
|
1273
|
+
setInviteResolution('existing_user_ready');
|
|
1274
|
+
setLoginPassword('');
|
|
1275
|
+
setLoginPasswordConfirm('');
|
|
1276
|
+
setLoginNotice('Signed in. Review the invite details to continue.');
|
|
1277
|
+
setLoginError(null);
|
|
1278
|
+
setLoginErrorCode(null);
|
|
1279
|
+
setLoginBusy(false);
|
|
1280
|
+
return;
|
|
1281
|
+
}
|
|
1282
|
+
if (normalizedInviteEmail) {
|
|
1283
|
+
setLoginEmail(inviteEmail || '');
|
|
1284
|
+
}
|
|
1285
|
+
setInviteResolution('existing_user_signed_out');
|
|
1021
1286
|
setLoginPassword('');
|
|
1022
|
-
|
|
1287
|
+
setLoginPasswordConfirm('');
|
|
1288
|
+
setLoginNotice(normalizedInviteEmail
|
|
1289
|
+
? `Signed in, but this invite is for ${inviteEmail}. Sign in with that account to continue.`
|
|
1290
|
+
: 'Signed in, but this invite requires the invited account. Sign in with that account to continue.');
|
|
1291
|
+
setLoginError('Sign in with the invited account to join this workspace.');
|
|
1292
|
+
setLoginErrorCode('INVITE_ACCOUNT_MISMATCH');
|
|
1023
1293
|
setLoginBusy(false);
|
|
1024
1294
|
return;
|
|
1025
1295
|
}
|
|
@@ -1034,6 +1304,7 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
1034
1304
|
if (inviteToken.trim()) {
|
|
1035
1305
|
setAuthMode('invite');
|
|
1036
1306
|
setLoginPassword('');
|
|
1307
|
+
setLoginPasswordConfirm('');
|
|
1037
1308
|
setLoginNotice('Signed in. Review the invite details to continue.');
|
|
1038
1309
|
setLoginBusy(false);
|
|
1039
1310
|
return;
|
|
@@ -1046,12 +1317,14 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
1046
1317
|
setLoginEmail('');
|
|
1047
1318
|
setLoginDisplayName('');
|
|
1048
1319
|
setLoginPassword('');
|
|
1320
|
+
setLoginPasswordConfirm('');
|
|
1049
1321
|
navigate(nextPath, { replace: true });
|
|
1050
1322
|
}
|
|
1051
1323
|
else {
|
|
1052
1324
|
setLoginEmail('');
|
|
1053
1325
|
setLoginDisplayName('');
|
|
1054
1326
|
setLoginPassword('');
|
|
1327
|
+
setLoginPasswordConfirm('');
|
|
1055
1328
|
navigate(nextPath, { replace: true });
|
|
1056
1329
|
}
|
|
1057
1330
|
setLoginBusy(false);
|
|
@@ -1064,7 +1337,7 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
1064
1337
|
? 'Reset Password'
|
|
1065
1338
|
: (inviteResolution === 'existing_user_signed_out'
|
|
1066
1339
|
? '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: () => {
|
|
1340
|
+
: (invitePasswordRequired ? 'Create Account and Join' : 'Join Workspace'))) }) }), (authMode === 'login' || authMode === 'register') && availableAuthProviders.length > 0 && (_jsxs("div", { className: authStyles.oauthProviders, children: [_jsx("div", { className: authStyles.oauthDivider, children: _jsx("span", { children: "or continue with" }) }), _jsxs("div", { className: authStyles.oauthButtons, children: [availableAuthProviders.includes('google') && (_jsx("button", { type: "button", className: authStyles.oauthButton, disabled: loginBusy, onClick: () => beginOAuthLogin('google', location.pathname !== '/login' ? location.pathname : '/'), "aria-label": "Continue with Google", children: "Google" })), availableAuthProviders.includes('github') && (_jsx("button", { type: "button", className: authStyles.oauthButton, disabled: loginBusy, onClick: () => beginOAuthLogin('github', location.pathname !== '/login' ? location.pathname : '/'), "aria-label": "Continue with GitHub", children: "GitHub" })), availableAuthProviders.includes('apple') && (_jsx("button", { type: "button", className: authStyles.oauthButton, disabled: loginBusy, onClick: () => beginOAuthLogin('apple', location.pathname !== '/login' ? location.pathname : '/'), "aria-label": "Continue with Apple", children: "Apple" }))] })] })), authMode === 'invite' && inviteResolution === 'new_user' && inviteOAuthProviders.length > 0 && (_jsxs("div", { className: authStyles.oauthProviders, children: [_jsx("div", { className: authStyles.oauthDivider, children: _jsx("span", { children: "or join with" }) }), _jsxs("div", { className: authStyles.oauthButtons, children: [inviteOAuthProviders.includes('google') && (_jsx("button", { type: "button", className: authStyles.oauthButton, disabled: loginBusy, onClick: () => beginOAuthLogin('google', '/', inviteToken), "aria-label": "Join with Google", children: "Google" })), inviteOAuthProviders.includes('github') && (_jsx("button", { type: "button", className: authStyles.oauthButton, disabled: loginBusy, onClick: () => beginOAuthLogin('github', '/', inviteToken), "aria-label": "Join with GitHub", children: "GitHub" })), inviteOAuthProviders.includes('apple') && (_jsx("button", { type: "button", className: authStyles.oauthButton, disabled: loginBusy, onClick: () => beginOAuthLogin('apple', '/', inviteToken), "aria-label": "Join with Apple", children: "Apple" }))] })] })), 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
1341
|
setLoginError(null);
|
|
1069
1342
|
setLoginErrorCode(null);
|
|
1070
1343
|
setLoginNotice(null);
|
|
@@ -1079,7 +1352,7 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
1079
1352
|
setVerifyCooldownUntil(Date.now() + 30_000);
|
|
1080
1353
|
if (sent.verificationToken)
|
|
1081
1354
|
setVerificationToken(sent.verificationToken);
|
|
1082
|
-
setLoginNotice(
|
|
1355
|
+
setLoginNotice(buildVerificationRequestNotice(sent));
|
|
1083
1356
|
}
|
|
1084
1357
|
else {
|
|
1085
1358
|
setLoginError(formatAuthError(sent));
|
|
@@ -1087,18 +1360,15 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
1087
1360
|
setLoginBusy(false);
|
|
1088
1361
|
}, 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' }))] }))] })] })] }) }) }));
|
|
1089
1362
|
}
|
|
1090
|
-
if (
|
|
1363
|
+
if (shellRenderState === 'login-redirect') {
|
|
1091
1364
|
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..." })] }) }) }));
|
|
1092
1365
|
}
|
|
1093
|
-
if (
|
|
1366
|
+
if (shellRenderState === 'coming-soon') {
|
|
1094
1367
|
return (_jsx("div", { className: shellStyles.standaloneWrapper, "data-theme": currentTheme, children: _jsx("div", { className: authStyles.loginView, style: {
|
|
1095
1368
|
backgroundImage: `linear-gradient(rgba(15, 15, 26, 0.88), rgba(15, 15, 26, 0.9)), url(${taskforceBg})`
|
|
1096
1369
|
}, 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
1370
|
}
|
|
1098
|
-
|
|
1099
|
-
|| (isSetupRoute && Boolean(pendingCloudWorkspaceImport))
|
|
1100
|
-
|| (isSetupRoute && setupGateActive && (hasSetupReadError || needsGlobalSetup || needsWorkspaceSetup));
|
|
1101
|
-
if (showGuidedWorkspaceSetup) {
|
|
1371
|
+
if (shellRenderState === 'guided-setup') {
|
|
1102
1372
|
const isError = !isCreateWorkspaceIntent && (hasSetupReadError || setupStep === 'error');
|
|
1103
1373
|
const isGlobal = !isCreateWorkspaceIntent && !isError && (needsGlobalSetup || setupStep === 'global');
|
|
1104
1374
|
const heading = isError
|
|
@@ -1114,11 +1384,11 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
1114
1384
|
const workspaceLabel = 'Workspace';
|
|
1115
1385
|
const isLocalWorkspaceSetup = isCloudRuntime || workspaceSetupSource !== 'cloud';
|
|
1116
1386
|
const isWorkspaceDetailsPage = isLocalWorkspaceSetup && workspaceSetupPage === 'details';
|
|
1117
|
-
const
|
|
1387
|
+
const isWorkspaceLibraryPage = isLocalWorkspaceSetup && workspaceSetupPage === 'library';
|
|
1118
1388
|
const showWorkspaceSourceSelector = !isCloudRuntime
|
|
1119
1389
|
&& canUseCloudWorkspaceSetup
|
|
1120
1390
|
&& isWorkspaceDetailsPage
|
|
1121
|
-
&& !
|
|
1391
|
+
&& !activeCloudWorkspaceImport;
|
|
1122
1392
|
const runtimeIcon = runtimeCapabilities?.runtimeMode === 'cloud' ? Cloud : LaptopMinimal;
|
|
1123
1393
|
const starterCategoryItems = selectedSetupPack ? formatStarterSectionItems(selectedSetupPack.categories) : '';
|
|
1124
1394
|
const starterTypeItems = selectedSetupPack ? formatStarterSectionItems(selectedSetupPack.types) : '';
|
|
@@ -1126,23 +1396,23 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
1126
1396
|
const setupPrimaryActionLabel = setupBusy
|
|
1127
1397
|
? t('setup.saving')
|
|
1128
1398
|
: wantsCloudWorkspaceSetup
|
|
1129
|
-
? (
|
|
1399
|
+
? (activeCloudWorkspaceImport
|
|
1130
1400
|
? (isCloudWorkspaceImportErrored ? 'Retry Import' : 'Importing Workspace...')
|
|
1131
1401
|
: 'Connect Workspace')
|
|
1132
1402
|
: isWorkspaceDetailsPage
|
|
1133
1403
|
? 'Continue'
|
|
1134
|
-
:
|
|
1404
|
+
: isWorkspaceLibraryPage
|
|
1135
1405
|
? 'Create Workspace'
|
|
1136
1406
|
: 'Connect Workspace';
|
|
1137
1407
|
return (_jsx("div", { className: shellStyles.standaloneWrapper, "data-theme": currentTheme, children: _jsx("div", { className: authStyles.loginView, style: {
|
|
1138
1408
|
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: [!
|
|
1409
|
+
}, 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
1410
|
navigate(`/login?mode=login&next=${encodeURIComponent(cloudWorkspaceSelectionPath)}`, { replace: true });
|
|
1141
1411
|
}, children: "Sign In" }), directRegistrationEnabled && (_jsx("button", { className: styles.submitBtn, disabled: setupBusy, onClick: () => {
|
|
1142
1412
|
navigate(`/login?mode=register&next=${encodeURIComponent(cloudWorkspaceSelectionPath)}`, { replace: true });
|
|
1143
|
-
}, children: "Register" }))] })] })), !
|
|
1413
|
+
}, 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
1414
|
? '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 })] })),
|
|
1415
|
+
: (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
1416
|
setSetupError(null);
|
|
1147
1417
|
setSetupNotice(null);
|
|
1148
1418
|
setWorkspaceSetupPage('details');
|
|
@@ -1150,7 +1420,7 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
1150
1420
|
setSetupError(null);
|
|
1151
1421
|
setSetupNotice(null);
|
|
1152
1422
|
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(
|
|
1423
|
+
}, 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
1424
|
setSetupBusy(true);
|
|
1155
1425
|
setSetupNotice(null);
|
|
1156
1426
|
setSetupError(null);
|
|
@@ -1177,19 +1447,19 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
1177
1447
|
setSetupBusy(false);
|
|
1178
1448
|
}
|
|
1179
1449
|
}, children: setupBusy ? t('setup.saving') : t('setup.saveGlobalSetup') })) : !isError ? (_jsx("button", { className: styles.submitBtn, disabled: setupBusy
|
|
1180
|
-
|| (isCloudWorkspaceSetupFlow && !
|
|
1181
|
-
if (isCloudWorkspaceSetupFlow &&
|
|
1450
|
+
|| (isCloudWorkspaceSetupFlow && !activeCloudWorkspaceImport && !isAuthenticated), onClick: async () => {
|
|
1451
|
+
if (isCloudWorkspaceSetupFlow && activeCloudWorkspaceImport) {
|
|
1182
1452
|
if (!isCloudWorkspaceImportErrored)
|
|
1183
1453
|
return;
|
|
1184
1454
|
setSetupBusy(true);
|
|
1185
|
-
setSetupNotice(`Retrying import for ${
|
|
1455
|
+
setSetupNotice(`Retrying import for ${activeCloudWorkspaceImport.name}...`);
|
|
1186
1456
|
setSetupError(null);
|
|
1187
1457
|
try {
|
|
1188
1458
|
await retryWorkspaceCloudSync();
|
|
1189
1459
|
}
|
|
1190
1460
|
catch {
|
|
1191
1461
|
setSetupNotice(null);
|
|
1192
|
-
setSetupError(`Failed to retry import for "${
|
|
1462
|
+
setSetupError(`Failed to retry import for "${activeCloudWorkspaceImport.name}".`);
|
|
1193
1463
|
}
|
|
1194
1464
|
finally {
|
|
1195
1465
|
setSetupBusy(false);
|
|
@@ -1229,33 +1499,28 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
1229
1499
|
setSetupBusy(false);
|
|
1230
1500
|
return;
|
|
1231
1501
|
}
|
|
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})`);
|
|
1502
|
+
try {
|
|
1503
|
+
await persistWorkspaceSyncStatePatch(workspaceIdForSync, {
|
|
1504
|
+
version: 2,
|
|
1505
|
+
enabled: true,
|
|
1506
|
+
phase: 'attach-cloud',
|
|
1507
|
+
setupIntent: 'attach-cloud-import',
|
|
1508
|
+
pullCursor: null,
|
|
1509
|
+
lastPullAt: null,
|
|
1510
|
+
lastPushAt: null,
|
|
1511
|
+
lastSyncedAt: null,
|
|
1512
|
+
lastErrorMessage: null
|
|
1513
|
+
});
|
|
1514
|
+
}
|
|
1515
|
+
catch (error) {
|
|
1516
|
+
setSetupError(error instanceof Error ? error.message : 'Failed to configure workspace sync.');
|
|
1253
1517
|
setSetupBusy(false);
|
|
1254
1518
|
return;
|
|
1255
1519
|
}
|
|
1256
1520
|
applyWorkspaceSyncStateSnapshot({
|
|
1257
1521
|
enabled: true,
|
|
1258
1522
|
phase: 'attach-cloud',
|
|
1523
|
+
setupIntent: 'attach-cloud-import',
|
|
1259
1524
|
pullCursor: null
|
|
1260
1525
|
});
|
|
1261
1526
|
await refreshSetupContext();
|
|
@@ -1276,10 +1541,10 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
1276
1541
|
}
|
|
1277
1542
|
setSetupError(null);
|
|
1278
1543
|
setSetupNotice(null);
|
|
1279
|
-
setWorkspaceSetupPage('
|
|
1544
|
+
setWorkspaceSetupPage('library');
|
|
1280
1545
|
return;
|
|
1281
1546
|
}
|
|
1282
|
-
if (
|
|
1547
|
+
if (isWorkspaceLibraryPage) {
|
|
1283
1548
|
// fall through to final create/save behavior below
|
|
1284
1549
|
}
|
|
1285
1550
|
setSetupBusy(true);
|
|
@@ -1324,7 +1589,7 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
1324
1589
|
workspaceIdOverride: workspaceIdForSetup
|
|
1325
1590
|
});
|
|
1326
1591
|
if (!applyResult?.success) {
|
|
1327
|
-
setSetupError(applyResult?.error || 'Failed to apply
|
|
1592
|
+
setSetupError(applyResult?.error || 'Failed to apply setup library.');
|
|
1328
1593
|
setSetupBusy(false);
|
|
1329
1594
|
return;
|
|
1330
1595
|
}
|
|
@@ -1342,6 +1607,7 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
1342
1607
|
version: 2,
|
|
1343
1608
|
enabled: shouldEnableCloudSync,
|
|
1344
1609
|
phase: shouldEnableCloudSync ? 'provision-local' : 'idle',
|
|
1610
|
+
setupIntent: null,
|
|
1345
1611
|
pullCursor: null,
|
|
1346
1612
|
lastPullAt: null,
|
|
1347
1613
|
lastPushAt: null,
|
|
@@ -1358,6 +1624,7 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
|
|
|
1358
1624
|
applyWorkspaceSyncStateSnapshot({
|
|
1359
1625
|
enabled: shouldEnableCloudSync,
|
|
1360
1626
|
phase: shouldEnableCloudSync ? 'provision-local' : 'idle',
|
|
1627
|
+
setupIntent: null,
|
|
1361
1628
|
pullCursor: null
|
|
1362
1629
|
});
|
|
1363
1630
|
if (shouldEnableCloudSync) {
|