@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
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import React, { useMemo, useCallback, useState, useEffect, useRef,
|
|
2
|
+
import React, { useMemo, useCallback, useState, useEffect, useRef, Suspense } from 'react';
|
|
3
3
|
import { createPortal } from 'react-dom';
|
|
4
4
|
import { useLocation, useNavigate } from 'react-router-dom';
|
|
5
5
|
import { Settings, Plus, Filter, Layers, User, Maximize2, Minimize2, Check, ChevronLeft, ArrowUp, ArrowDown, ArrowUpDown, AlertTriangle, Save, Loader2, Folder, FileText, LogOut, NotebookTabs, ClipboardCheck, Cloud, CloudOff, Users, ClockAlert, ChevronsUp, ChevronsDown, EyeOff, Zap, HelpCircle, Bot, Image, Workflow } from 'lucide-react';
|
|
@@ -12,9 +12,11 @@ import folderBrowserStyles from './FolderBrowser.module.css';
|
|
|
12
12
|
import modalStyles from '../ui/Modal.module.css';
|
|
13
13
|
import { CreateWorkspaceConfirmModal } from './standalone/modals/CreateWorkspaceConfirmModal';
|
|
14
14
|
import { AccountHubModal } from './standalone/modals/AccountHubModal';
|
|
15
|
+
import { AvatarImageManagerModal } from './standalone/modals/AvatarImageManagerModal';
|
|
15
16
|
import { EditProfileModal } from './standalone/modals/EditProfileModal';
|
|
16
17
|
import { HelpModal } from './standalone/modals/HelpModal';
|
|
17
18
|
import { ScheduleWarningModal } from './standalone/modals/ScheduleWarningModal';
|
|
19
|
+
import { SyncEnableWarningModal } from './standalone/modals/SyncEnableWarningModal';
|
|
18
20
|
import { SyncStatusModal } from './standalone/modals/SyncStatusModal';
|
|
19
21
|
import { TeamManagementModal } from './standalone/modals/TeamManagementModal';
|
|
20
22
|
import { Modal } from '../ui/Modal';
|
|
@@ -30,11 +32,14 @@ import { getTaskReferenceLabel, parseTaskReference } from '../../utils/taskRefer
|
|
|
30
32
|
import { formatInitiativeReference, formatWorkstreamReference, parseWorkstreamReference, resolveInitiativeReference } from '../../utils/planningReferences';
|
|
31
33
|
import { DEFAULT_TYPE_VALUE } from '../../shared/taxonomyDefaults.js';
|
|
32
34
|
import { getImageReferenceLabel } from '../../utils/imageReferences';
|
|
35
|
+
import { isPlanningEntityVisibleForScope, scopePlanningSummaries } from './planningScope';
|
|
36
|
+
import { buildReferenceMismatchSummaries, buildSyncDiagnosticsSummary, buildSyncEventRows, getActiveReferenceMismatchCount, resolveSyncStageLabel, resolveSyncStatusMeta, } from '../../utils/syncStatusPresentation';
|
|
33
37
|
import { buildCustomTaxonomySortOptions } from '../../utils/taxonomySorting.js';
|
|
34
38
|
import { getLocale, t } from '../../localization';
|
|
35
39
|
import { resolveAnnotatedAttachmentDetail } from '../../utils/annotatedAttachments';
|
|
36
40
|
import { ACCOUNT_PROFILE_SUMMARY_PATH, invalidateAccountProfileSummaryCache, loadAccountProfileSummaryCached } from '../../utils/accountProfileSummaryCache';
|
|
37
|
-
import {
|
|
41
|
+
import { prepareAvatarUploadFile } from '../../utils/avatarUpload';
|
|
42
|
+
import { resolveCommercialLifecyclePresentation } from '../../utils/commercialLifecyclePresentation';
|
|
38
43
|
import { AppShellHeader } from './AppShellHeader';
|
|
39
44
|
import { getTaskforceCloudEnvironmentConfig, parseTaskforceCloudEnvironment, resolveTaskforceCloudEnvironmentFromBaseUrl } from '../../config/cloudEnvironment';
|
|
40
45
|
import { logBootstrapDebug, persistRemoteUiState } from '../../hooks/workspace/workspaceLocalState';
|
|
@@ -46,6 +51,9 @@ import { EmptyWorkspaceFilterBar, WorkspaceHeaderActions, WorkspaceHeaderDivider
|
|
|
46
51
|
import { ScheduleSidebar, SCHEDULE_SIDEBAR_WIDTH_PX } from './panels/ScheduleSidebar';
|
|
47
52
|
import { DOCUMENT_ATTACHMENT_FILTER_OPTIONS, DOCUMENT_TYPE_FILTER_OPTIONS, } from '../features/documentWorkspaceFilters';
|
|
48
53
|
import { canManageTeamWorkspace as computeCanManageTeamWorkspace, canOpenTeamManagement as computeCanOpenTeamManagement } from './teamManagementAccess';
|
|
54
|
+
import { useRecentSyncEvents } from '../../hooks/sync/useRecentSyncEvents';
|
|
55
|
+
import { useSyncStatusActions } from '../../hooks/sync/useSyncStatusActions';
|
|
56
|
+
import { useSyncStatusControls } from '../../hooks/sync/useSyncStatusControls';
|
|
49
57
|
import { toDateOnlyLocal, parseDateOnlyLocal, startOfWeek, getDayOffsetFromWeekStart, addDays, dateOnlyToIsoWeekKey, SCHEDULE_DAY_KEYS, SCHEDULE_DAY_LABELS, } from './panels/scheduleUtils';
|
|
50
58
|
import { AGENTS_WORKSPACE_FEATURE_KEY, ANNOTATED_ATTACHMENTS_WORKSPACE_FEATURE_KEY, DOCUMENT_WORKSPACE_FEATURE_KEY, INITIATIVES_WORKSPACE_FEATURE_KEY, WORKFLOWS_WORKSPACE_FEATURE_KEY, resolveFeatureAccess, } from '../../runtime/appGates';
|
|
51
59
|
// Heavy feature modules — lazy-loaded to reduce the initial app chunk size.
|
|
@@ -56,76 +64,9 @@ const WorkflowsModule = React.lazy(() => import('../features/WorkflowsModule').t
|
|
|
56
64
|
const AgentsModule = React.lazy(() => import('../features/AgentsModule').then(m => ({ default: m.AgentsModule })));
|
|
57
65
|
const InitiativesModule = React.lazy(() => import('../features/InitiativesModule').then(m => ({ default: m.InitiativesModule })));
|
|
58
66
|
const PlansPage = React.lazy(() => import('./PlansPage').then(m => ({ default: m.PlansPage })));
|
|
59
|
-
const ACTIVE_COMMERCIAL_STATES = new Set(['active', 'trialing', 'grace']);
|
|
60
67
|
const PROFILE_AVATAR_ACCEPT = 'image/png,image/jpeg,image/webp,image/gif';
|
|
61
68
|
const PROFILE_AVATAR_MAX_BYTES = 5 * 1024 * 1024;
|
|
62
69
|
const resolveSameOriginTaskforcePath = (path) => path;
|
|
63
|
-
function getSyncEventStableKey(event, index = 0) {
|
|
64
|
-
const explicitId = String(event.id ?? '').trim();
|
|
65
|
-
if (explicitId)
|
|
66
|
-
return `id:${explicitId}`;
|
|
67
|
-
return [
|
|
68
|
-
String(event.occurredAt || '').trim() || 'unknown-time',
|
|
69
|
-
String(event.eventType || '').trim() || 'sync',
|
|
70
|
-
String(event.status || '').trim() || 'unknown-status',
|
|
71
|
-
String(event.statusCode ?? ''),
|
|
72
|
-
String(event.changeCount ?? ''),
|
|
73
|
-
String(event.requestMs ?? ''),
|
|
74
|
-
String(event.errorMessage || '').trim(),
|
|
75
|
-
JSON.stringify(event.details || null),
|
|
76
|
-
String(index)
|
|
77
|
-
].join('|');
|
|
78
|
-
}
|
|
79
|
-
function mergeRecentSyncEvents(previous, incoming, limit = 15) {
|
|
80
|
-
const merged = new Map();
|
|
81
|
-
for (const [index, event] of previous.entries()) {
|
|
82
|
-
merged.set(getSyncEventStableKey(event, index), event);
|
|
83
|
-
}
|
|
84
|
-
for (const [index, event] of incoming.entries()) {
|
|
85
|
-
merged.set(getSyncEventStableKey(event, index), event);
|
|
86
|
-
}
|
|
87
|
-
return Array.from(merged.values())
|
|
88
|
-
.sort((left, right) => String(right.occurredAt || '').localeCompare(String(left.occurredAt || '')))
|
|
89
|
-
.slice(0, Math.max(1, limit));
|
|
90
|
-
}
|
|
91
|
-
function areSyncEventListsEqual(left, right) {
|
|
92
|
-
if (left.length !== right.length)
|
|
93
|
-
return false;
|
|
94
|
-
for (let index = 0; index < left.length; index += 1) {
|
|
95
|
-
if (getSyncEventStableKey(left[index], index) !== getSyncEventStableKey(right[index], index)) {
|
|
96
|
-
return false;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
return true;
|
|
100
|
-
}
|
|
101
|
-
function getActiveReferenceMismatchCount(events) {
|
|
102
|
-
let count = 0;
|
|
103
|
-
for (const event of events) {
|
|
104
|
-
const message = String(event.errorMessage || '').toLowerCase();
|
|
105
|
-
if (message.includes('reference number mismatch')) {
|
|
106
|
-
count += 1;
|
|
107
|
-
continue;
|
|
108
|
-
}
|
|
109
|
-
if (String(event.status || '').toLowerCase() === 'success') {
|
|
110
|
-
break;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
return count;
|
|
114
|
-
}
|
|
115
|
-
function getActiveReferenceMismatchEvents(events) {
|
|
116
|
-
const mismatches = [];
|
|
117
|
-
for (const event of events) {
|
|
118
|
-
const message = String(event.errorMessage || '').toLowerCase();
|
|
119
|
-
if (message.includes('reference number mismatch')) {
|
|
120
|
-
mismatches.push(event);
|
|
121
|
-
continue;
|
|
122
|
-
}
|
|
123
|
-
if (String(event.status || '').toLowerCase() === 'success') {
|
|
124
|
-
break;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
return mismatches;
|
|
128
|
-
}
|
|
129
70
|
function resolveConnectedEnvironmentLabel(explicitEnvironment, candidates) {
|
|
130
71
|
const parsedExplicitEnvironment = parseTaskforceCloudEnvironment(explicitEnvironment);
|
|
131
72
|
if (parsedExplicitEnvironment) {
|
|
@@ -203,7 +144,7 @@ export function StandaloneLayout(props) {
|
|
|
203
144
|
// Form Actions
|
|
204
145
|
handleAddComment, handleSetWorkstreamForCurrentTask, handleOpenTaskById,
|
|
205
146
|
// Other Props for Form
|
|
206
|
-
taxonomies, activeTypes, priorities, taxonomyDisplayLabels, approaches, copiedId, handleCopyId, handleToggleInProgress, handleToggleComplete, handleToggleReview, handleToggleCancel, handleArchiveTask, handleDelete, handleUnarchive, handleRestoreDeletedTask, handlePermanentlyDeleteDeletedTask, handleEmptyDeletedTasks, fetchTasks, searchQuery, setSearchQuery, filterCategories, setFilterCategories, filterTypes, setFilterTypes, filterPriorities, setFilterPriorities, filterAssignees, setFilterAssignees, assigneeOptions, taskScope, setTaskScope,
|
|
147
|
+
taxonomies, activeTypes, priorities, taxonomyDisplayLabels, approaches, copiedId, handleCopyId, handleToggleInProgress, handleToggleComplete, handleToggleReview, handleToggleCancel, handleArchiveTask, handleDelete, handleUnarchive, handleRestoreDeletedTask, handlePermanentlyDeleteDeletedTask, handleEmptyDeletedTasks, fetchTasks, searchQuery, setSearchQuery, filterCategories, setFilterCategories, filterTypes, setFilterTypes, filterPriorities, setFilterPriorities, filterAssignees, setFilterAssignees, hasInitedFilters, assigneeOptions, taskScope, setTaskScope,
|
|
207
148
|
// filterStatus, setFilterStatus, // This was duplicated, removed one instance
|
|
208
149
|
sortBy, setSortBy, clearFilters, // setSearchQuery, // This was duplicated, removed one instance
|
|
209
150
|
// filterCategories, setFilterCategories, // This was duplicated, removed one instance
|
|
@@ -214,7 +155,7 @@ export function StandaloneLayout(props) {
|
|
|
214
155
|
// clearFilters, // This was duplicated, removed one instance
|
|
215
156
|
// activeTypes, // This was duplicated, removed one instance
|
|
216
157
|
// Settings State & Props
|
|
217
|
-
settingsModel, configLoaded, currentTheme, setCurrentTheme, pathSaved, saveSettings, keyShortcut, setKeyShortcut, globalWeekStartsOn, locale, jsonBackupEnabled, setJsonBackupEnabled, mcpHostRoot, setMcpHostRoot, settingsSection, setSettingsSection, runtimeMode, workspaceSwitchingEnabled, cloudAuthConfigured, authRequiredForApi, authBlocked, isAuthenticated, authUserId, authWorkspaceId, authUserEmail, authUserDisplayName, authUserAvatarUrl, authSessionResolved, realtimeSyncEnabled, realtimeSyncFlagSource, workspaceCloudSyncEnabled, workspaceSyncPhase, workspaceSyncStatus, workspaceSyncSummary, workspaceSyncRecommendedAction, workspaceSyncBusy, workspaceSyncPendingChanges, saveWorkspaceCloudSyncSettings, pushNotice, userGlobalSyncStatus, workspaceLastSuccessfulSyncAt, workspaceLastPullAt, workspaceLastPushAt, workspaceLastErrorAt, userGlobalSyncError, workspaceLastErrorMessage, retryWorkspaceCloudSync, resetWorkspaceSyncCursorAndPull, getWorkspaceSyncDiagnostics, currentWorkspaceId, currentWorkspaceRole, availableWorkspaces, switchWorkspace, updateCurrentUserProfile, resolveCloudAuthUrl = resolveSameOriginTaskforcePath, logout, projectRoot, projectName, mcpScriptPath, serverHostRoot,
|
|
158
|
+
settingsModel, configLoaded, currentTheme, setCurrentTheme, pathSaved, saveSettings, keyShortcut, setKeyShortcut, globalWeekStartsOn, locale, jsonBackupEnabled, setJsonBackupEnabled, mcpHostRoot, setMcpHostRoot, settingsSection, setSettingsSection, runtimeMode, workspaceSwitchingEnabled, cloudAuthConfigured, authRequiredForApi, authBlocked, isAuthenticated, authUserId, authWorkspaceId, authUserEmail, authUserDisplayName, authUserAvatarUrl, authSessionResolved, realtimeSyncEnabled, realtimeSyncFlagSource, workspaceCloudSyncEnabled, workspaceSyncPhase, workspaceSyncStatus, workspaceSyncSummary, workspaceSyncRecommendedAction, workspaceSyncBusy, workspaceSyncPendingChanges, saveWorkspaceCloudSyncSettings, pushNotice, userGlobalSyncStatus, workspaceLastSuccessfulSyncAt, workspaceLastPullAt, workspaceLastPushAt, workspaceLastErrorAt, userGlobalSyncError, workspaceLastErrorMessage, retryWorkspaceCloudSync, resetWorkspaceSyncCursorAndPull, getWorkspaceSyncDiagnostics, currentWorkspaceId, currentWorkspaceRole, availableWorkspaces, switchWorkspace, updateCurrentUserProfile, resolveCloudAuthUrl = resolveSameOriginTaskforcePath, logout, fetchLoginMethods, unlinkLoginMethod, addPasswordToAccount, beginOAuthLogin, beginOAuthLink, availableAuthProviders, projectRoot, projectName, mcpScriptPath, serverHostRoot,
|
|
218
159
|
// Browser Props
|
|
219
160
|
showFolderBrowser, setShowFolderBrowser, folders, files, currentBrowsePath, fetchFolders, browserTarget, setBrowserTarget, handleSelectPath, handleAddPath, handleRemovePath,
|
|
220
161
|
// Settings Managers
|
|
@@ -284,6 +225,9 @@ export function StandaloneLayout(props) {
|
|
|
284
225
|
const [planningDraftDescription, setPlanningDraftDescription] = useState('');
|
|
285
226
|
const [planningDraftOwner, setPlanningDraftOwner] = useState('');
|
|
286
227
|
const [planningDraftInitiativeId, setPlanningDraftInitiativeId] = useState('');
|
|
228
|
+
const [isSubmittingPlanningEditor, setIsSubmittingPlanningEditor] = useState(false);
|
|
229
|
+
const planningContextAttachmentDraftsRef = useRef(new Map());
|
|
230
|
+
const planningContextAttachmentUpdateQueueRef = useRef(new Map());
|
|
287
231
|
const [documentTypeFilters, setDocumentTypeFilters] = useState(() => DOCUMENT_TYPE_FILTER_OPTIONS.map((option) => option.value));
|
|
288
232
|
const [documentAttachmentFilters, setDocumentAttachmentFilters] = useState(() => DOCUMENT_ATTACHMENT_FILTER_OPTIONS.map((option) => option.value));
|
|
289
233
|
const [requestedDocPath, setRequestedDocPath] = useState(null);
|
|
@@ -293,6 +237,7 @@ export function StandaloneLayout(props) {
|
|
|
293
237
|
const [requestedAnnotatedOpenVersion, setRequestedAnnotatedOpenVersion] = useState(0);
|
|
294
238
|
const [canonicalAnnotatedImageReferenceLabels, setCanonicalAnnotatedImageReferenceLabels] = useState({});
|
|
295
239
|
const [layoutStateReady, setLayoutStateReady] = useState(false);
|
|
240
|
+
const planningEditorSubmitInFlightRef = useRef(false);
|
|
296
241
|
const loadedLayoutStateWorkspaceRef = useRef('');
|
|
297
242
|
const requestedAnnotatedTargetRef = useRef(null);
|
|
298
243
|
const requestedAnnotatedSessionIdRef = useRef(null);
|
|
@@ -836,84 +781,103 @@ export function StandaloneLayout(props) {
|
|
|
836
781
|
: taskScope === 'deleted'
|
|
837
782
|
? deletedScopeAllTasks
|
|
838
783
|
: props.tasks.filter((task) => !task.isArchived);
|
|
839
|
-
const
|
|
840
|
-
const taskCount = scopeTasks.length;
|
|
841
|
-
const completedTaskCount = scopeTasks.filter((task) => task.isArchived || task.status === 'done' || task.status === 'cancelled').length;
|
|
784
|
+
const summarizeTaskCounts = (taskCount, completedTaskCount) => {
|
|
842
785
|
return {
|
|
843
786
|
progressPercent: taskCount > 0 ? Math.round((completedTaskCount / taskCount) * 100) : 0,
|
|
844
787
|
taskCount,
|
|
845
788
|
completedTaskCount,
|
|
846
789
|
};
|
|
847
790
|
};
|
|
791
|
+
const useBootstrapTaskSummaries = taskScope === 'open'
|
|
792
|
+
&& props.planningBootstrapTaskSummaries.length > 0
|
|
793
|
+
&& (props.loadingTasks || props.tasks.length === 0);
|
|
794
|
+
const workstreamTaskSummaryById = new Map();
|
|
795
|
+
if (useBootstrapTaskSummaries) {
|
|
796
|
+
props.planningBootstrapTaskSummaries.forEach((summary) => {
|
|
797
|
+
workstreamTaskSummaryById.set(summary.workstreamId, summary);
|
|
798
|
+
});
|
|
799
|
+
}
|
|
800
|
+
else {
|
|
801
|
+
planningScopedTasks.forEach((task) => {
|
|
802
|
+
const workstreamId = String(task.workstreamId || '').trim();
|
|
803
|
+
if (!workstreamId)
|
|
804
|
+
return;
|
|
805
|
+
const current = workstreamTaskSummaryById.get(workstreamId) || {
|
|
806
|
+
workstreamId,
|
|
807
|
+
taskCount: 0,
|
|
808
|
+
completedTaskCount: 0,
|
|
809
|
+
tasks: [],
|
|
810
|
+
};
|
|
811
|
+
current.taskCount += 1;
|
|
812
|
+
if (task.isArchived || task.status === 'done' || task.status === 'cancelled') {
|
|
813
|
+
current.completedTaskCount += 1;
|
|
814
|
+
}
|
|
815
|
+
current.tasks.push({
|
|
816
|
+
id: task.id,
|
|
817
|
+
referenceNumber: task.referenceNumber ?? null,
|
|
818
|
+
title: task.title,
|
|
819
|
+
status: task.status || null,
|
|
820
|
+
});
|
|
821
|
+
workstreamTaskSummaryById.set(workstreamId, current);
|
|
822
|
+
});
|
|
823
|
+
}
|
|
848
824
|
const workstreamTaskIds = new Map();
|
|
849
825
|
const workstreamInitiativeIds = new Map();
|
|
850
826
|
const initiativeTaskIds = new Map();
|
|
851
|
-
const
|
|
852
|
-
const initiativeById = new Map();
|
|
827
|
+
const workstreamsByInitiativeId = new Map();
|
|
853
828
|
const buildWorkstreamSummary = (workstream) => {
|
|
854
|
-
const
|
|
855
|
-
const taskPreviews =
|
|
856
|
-
id: task.id,
|
|
857
|
-
referenceNumber: task.referenceNumber ?? null,
|
|
858
|
-
title: task.title,
|
|
859
|
-
status: task.status || null,
|
|
860
|
-
}));
|
|
829
|
+
const taskSummary = workstreamTaskSummaryById.get(workstream.id);
|
|
830
|
+
const taskPreviews = taskSummary?.tasks || [];
|
|
861
831
|
const summary = {
|
|
862
832
|
id: workstream.id,
|
|
863
833
|
referenceNumber: workstream.referenceNumber ?? null,
|
|
864
834
|
title: workstream.title,
|
|
865
835
|
description: String(workstream.description || '').trim() || undefined,
|
|
866
836
|
ownerLabel: workstream.ownerId ? assigneeLabelByValue.get(String(workstream.ownerId)) || String(workstream.ownerId) : null,
|
|
867
|
-
...
|
|
837
|
+
...summarizeTaskCounts(taskSummary?.taskCount || 0, taskSummary?.completedTaskCount || 0),
|
|
868
838
|
initiativeId: workstream.initiativeId || null,
|
|
869
839
|
commentCount: Array.isArray(workstream.comments) ? workstream.comments.length : 0,
|
|
870
840
|
attachmentCount: Array.isArray(workstream.attachments) ? workstream.attachments.length : 0,
|
|
841
|
+
attachments: Array.isArray(workstream.attachments) ? workstream.attachments : [],
|
|
871
842
|
isArchived: Boolean(workstream.isArchived),
|
|
872
843
|
tasks: taskPreviews,
|
|
873
844
|
};
|
|
874
|
-
workstreamTaskIds.set(workstream.id,
|
|
875
|
-
if (workstream.initiativeId)
|
|
845
|
+
workstreamTaskIds.set(workstream.id, taskPreviews.map((task) => task.id));
|
|
846
|
+
if (workstream.initiativeId) {
|
|
876
847
|
workstreamInitiativeIds.set(workstream.id, workstream.initiativeId);
|
|
877
|
-
|
|
848
|
+
const current = workstreamsByInitiativeId.get(workstream.initiativeId) || [];
|
|
849
|
+
current.push(summary);
|
|
850
|
+
workstreamsByInitiativeId.set(workstream.initiativeId, current);
|
|
851
|
+
}
|
|
878
852
|
return summary;
|
|
879
853
|
};
|
|
880
854
|
const allWorkstreamSummaries = props.workstreams.map(buildWorkstreamSummary);
|
|
855
|
+
const scopedWorkstreamSummaries = allWorkstreamSummaries.filter((workstream) => (isPlanningEntityVisibleForScope(taskScope, Boolean(workstream.isArchived))));
|
|
881
856
|
const allInitiatives = props.initiatives.map((initiative) => {
|
|
882
|
-
const workstreams =
|
|
883
|
-
.filter((workstream) => (workstream.
|
|
884
|
-
const
|
|
885
|
-
|
|
886
|
-
.map((taskId) => hierarchyTaskById.get(taskId))
|
|
887
|
-
.filter((task) => Boolean(task));
|
|
857
|
+
const workstreams = (workstreamsByInitiativeId.get(initiative.id) || [])
|
|
858
|
+
.filter((workstream) => isPlanningEntityVisibleForScope(taskScope, Boolean(workstream.isArchived)));
|
|
859
|
+
const taskCount = workstreams.reduce((sum, workstream) => sum + workstream.taskCount, 0);
|
|
860
|
+
const completedTaskCount = workstreams.reduce((sum, workstream) => sum + workstream.completedTaskCount, 0);
|
|
888
861
|
const initiativeSummary = {
|
|
889
862
|
id: initiative.id,
|
|
890
863
|
referenceNumber: initiative.referenceNumber ?? null,
|
|
891
864
|
title: initiative.title,
|
|
892
865
|
description: String(initiative.description || '').trim() || undefined,
|
|
893
866
|
ownerLabel: initiative.ownerId ? assigneeLabelByValue.get(String(initiative.ownerId)) || String(initiative.ownerId) : null,
|
|
894
|
-
...
|
|
867
|
+
...summarizeTaskCounts(taskCount, completedTaskCount),
|
|
895
868
|
workstreamCount: workstreams.length,
|
|
896
869
|
workstreams,
|
|
897
870
|
commentCount: Array.isArray(initiative.comments) ? initiative.comments.length : 0,
|
|
898
871
|
attachmentCount: Array.isArray(initiative.attachments) ? initiative.attachments.length : 0,
|
|
872
|
+
attachments: Array.isArray(initiative.attachments) ? initiative.attachments : [],
|
|
899
873
|
isArchived: Boolean(initiative.isArchived),
|
|
900
874
|
};
|
|
901
|
-
initiativeTaskIds.set(initiative.id, initiativeScopedTasks.map((task) => task.id));
|
|
902
|
-
initiativeById.set(initiativeSummary.id, initiativeSummary);
|
|
903
875
|
return initiativeSummary;
|
|
904
876
|
});
|
|
905
|
-
const initiatives =
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
: !initiative.isArchived);
|
|
910
|
-
const standaloneWorkstreams = allWorkstreamSummaries
|
|
911
|
-
.filter((workstream) => !workstream.initiativeId)
|
|
912
|
-
.filter((workstream) => taskScope === 'deleted'
|
|
913
|
-
? false
|
|
914
|
-
: taskScope === 'archived'
|
|
915
|
-
? Boolean(workstream.isArchived)
|
|
916
|
-
: !workstream.isArchived);
|
|
877
|
+
const { initiatives, standaloneWorkstreams, visibleWorkstreams, initiativeById, workstreamById, } = scopePlanningSummaries(allInitiatives, scopedWorkstreamSummaries, taskScope);
|
|
878
|
+
initiatives.forEach((initiative) => {
|
|
879
|
+
initiativeTaskIds.set(initiative.id, initiative.workstreams.flatMap((workstream) => (workstream.tasks || []).map((task) => task.id)));
|
|
880
|
+
});
|
|
917
881
|
return {
|
|
918
882
|
initiatives,
|
|
919
883
|
standaloneWorkstreams,
|
|
@@ -923,33 +887,51 @@ export function StandaloneLayout(props) {
|
|
|
923
887
|
workstreamById,
|
|
924
888
|
initiativeById,
|
|
925
889
|
};
|
|
926
|
-
}, [
|
|
890
|
+
}, [
|
|
891
|
+
archivedScopeAllTasks,
|
|
892
|
+
assigneeLabelByValue,
|
|
893
|
+
deletedScopeAllTasks,
|
|
894
|
+
props.initiatives,
|
|
895
|
+
props.loadingTasks,
|
|
896
|
+
props.planningBootstrapTaskSummaries,
|
|
897
|
+
props.tasks,
|
|
898
|
+
props.workstreams,
|
|
899
|
+
taskScope,
|
|
900
|
+
]);
|
|
927
901
|
const initiativeFilterOptions = useMemo(() => planningStructure.initiatives.map((initiative) => {
|
|
928
902
|
const referenceLabel = formatInitiativeReference(initiative);
|
|
929
903
|
return {
|
|
930
904
|
value: initiative.id,
|
|
931
|
-
label: referenceLabel
|
|
932
|
-
selectedLabel: referenceLabel || initiative.title,
|
|
905
|
+
label: referenceLabel || initiative.title,
|
|
933
906
|
};
|
|
934
907
|
}), [planningStructure.initiatives]);
|
|
935
|
-
const workstreamFilterOptions = useMemo(() =>
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
label: referenceLabel ? `${referenceLabel} - ${workstream.title}` : workstream.title,
|
|
941
|
-
selectedLabel: referenceLabel || workstream.title,
|
|
942
|
-
};
|
|
943
|
-
})),
|
|
944
|
-
...planningStructure.standaloneWorkstreams.map((workstream) => {
|
|
908
|
+
const workstreamFilterOptions = useMemo(() => {
|
|
909
|
+
const visibleInitiativeWorkstreams = selectedPlanningInitiativeId
|
|
910
|
+
? (planningStructure.initiativeById.get(selectedPlanningInitiativeId)?.workstreams || [])
|
|
911
|
+
: planningStructure.initiatives.flatMap((initiative) => initiative.workstreams);
|
|
912
|
+
const initiativeWorkstreams = visibleInitiativeWorkstreams.map((workstream) => {
|
|
945
913
|
const referenceLabel = formatWorkstreamReference(workstream);
|
|
946
914
|
return {
|
|
947
915
|
value: workstream.id,
|
|
948
|
-
label: referenceLabel
|
|
949
|
-
selectedLabel: referenceLabel || workstream.title,
|
|
916
|
+
label: referenceLabel || workstream.title,
|
|
950
917
|
};
|
|
951
|
-
})
|
|
952
|
-
|
|
918
|
+
});
|
|
919
|
+
const standaloneWorkstreams = selectedPlanningInitiativeId
|
|
920
|
+
? []
|
|
921
|
+
: planningStructure.standaloneWorkstreams.map((workstream) => {
|
|
922
|
+
const referenceLabel = formatWorkstreamReference(workstream);
|
|
923
|
+
return {
|
|
924
|
+
value: workstream.id,
|
|
925
|
+
label: referenceLabel || workstream.title,
|
|
926
|
+
};
|
|
927
|
+
});
|
|
928
|
+
return [...initiativeWorkstreams, ...standaloneWorkstreams];
|
|
929
|
+
}, [
|
|
930
|
+
planningStructure.initiativeById,
|
|
931
|
+
planningStructure.initiatives,
|
|
932
|
+
planningStructure.standaloneWorkstreams,
|
|
933
|
+
selectedPlanningInitiativeId,
|
|
934
|
+
]);
|
|
953
935
|
const selectedPlanningTaskIds = useMemo(() => {
|
|
954
936
|
if (selectedPlanningWorkstreamId) {
|
|
955
937
|
return new Set(planningStructure.workstreamTaskIds.get(selectedPlanningWorkstreamId) || []);
|
|
@@ -1190,17 +1172,7 @@ export function StandaloneLayout(props) {
|
|
|
1190
1172
|
const [showAccountMenu, setShowAccountMenu] = useState(false);
|
|
1191
1173
|
const [showAccountHub, setShowAccountHub] = useState(false);
|
|
1192
1174
|
const [showEditProfileModal, setShowEditProfileModal] = useState(false);
|
|
1193
|
-
const [
|
|
1194
|
-
const [workspaceSyncToggleBusy, setWorkspaceSyncToggleBusy] = useState(false);
|
|
1195
|
-
const [workspaceSyncError, setWorkspaceSyncError] = useState(null);
|
|
1196
|
-
const [workspaceSyncRepairBusy, setWorkspaceSyncRepairBusy] = useState(false);
|
|
1197
|
-
const [workspaceSyncRepairQueued, setWorkspaceSyncRepairQueued] = useState(false);
|
|
1198
|
-
const [workspaceSyncCopied, setWorkspaceSyncCopied] = useState(false);
|
|
1199
|
-
const [syncRecentEvents, setSyncRecentEvents] = useState([]);
|
|
1200
|
-
const [syncRecentEventsLoading, setSyncRecentEventsLoading] = useState(false);
|
|
1201
|
-
const [syncRecentEventsError, setSyncRecentEventsError] = useState(null);
|
|
1202
|
-
const syncEventsListRef = useRef(null);
|
|
1203
|
-
const pendingSyncEventsScrollRestoreRef = useRef(null);
|
|
1175
|
+
const [showAvatarPhotoManager, setShowAvatarPhotoManager] = useState(false);
|
|
1204
1176
|
const [workspaceActionBusy, setWorkspaceActionBusy] = useState(false);
|
|
1205
1177
|
const [workspaceActionError, setWorkspaceActionError] = useState('');
|
|
1206
1178
|
const [showCreateWorkspaceConfirm, setShowCreateWorkspaceConfirm] = useState(false);
|
|
@@ -1241,7 +1213,6 @@ export function StandaloneLayout(props) {
|
|
|
1241
1213
|
const [profileSaveNotice, setProfileSaveNotice] = useState(null);
|
|
1242
1214
|
const TEAM_AUDIT_PAGE_SIZE = 25;
|
|
1243
1215
|
const accountMenuRef = useRef(null);
|
|
1244
|
-
const profileAvatarInputRef = useRef(null);
|
|
1245
1216
|
const accountSurfaceOpenStartedAtRef = useRef(null);
|
|
1246
1217
|
const accountSurfacePendingRef = useRef({
|
|
1247
1218
|
billing: false,
|
|
@@ -1266,21 +1237,11 @@ export function StandaloneLayout(props) {
|
|
|
1266
1237
|
const canManageCloudWorkspaces = runtimeMode === 'cloud' && workspaceSwitchingEnabled && isAuthenticated;
|
|
1267
1238
|
const canAccessCloudTeamManagement = isAuthenticated && (runtimeMode === 'cloud' || cloudAuthConfigured);
|
|
1268
1239
|
const canManageWorkspaceSync = runtimeMode === 'local' && isAuthenticated;
|
|
1269
|
-
const handleWorkspaceSyncToggle
|
|
1270
|
-
if (!canManageWorkspaceSync)
|
|
1271
|
-
return;
|
|
1272
|
-
setWorkspaceSyncError(null);
|
|
1273
|
-
setWorkspaceSyncToggleBusy(true);
|
|
1274
|
-
const result = await saveWorkspaceCloudSyncSettings({ enabled });
|
|
1275
|
-
if (!result.success) {
|
|
1276
|
-
setWorkspaceSyncError(result.error || (enabled ? 'Failed to enable sync.' : 'Failed to disable sync.'));
|
|
1277
|
-
}
|
|
1278
|
-
setWorkspaceSyncToggleBusy(false);
|
|
1279
|
-
}, [
|
|
1240
|
+
const { showSyncStatusModal, showSyncEnableWarning, workspaceSyncError, syncControlBusy, openSyncStatusModal, closeSyncStatusModal, handleWorkspaceSyncToggle, cancelSyncEnableWarning, confirmSyncEnableWarning, } = useSyncStatusControls({
|
|
1280
1241
|
canManageWorkspaceSync,
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1242
|
+
workspaceCloudSyncEnabled,
|
|
1243
|
+
saveWorkspaceCloudSyncSettings,
|
|
1244
|
+
});
|
|
1284
1245
|
const currentWorkspaceLabel = useMemo(() => {
|
|
1285
1246
|
const current = availableWorkspaces.find((workspace) => workspace.id === currentWorkspaceId);
|
|
1286
1247
|
const namedWorkspace = String(current?.name || '').trim();
|
|
@@ -1340,23 +1301,21 @@ export function StandaloneLayout(props) {
|
|
|
1340
1301
|
if (!normalizedDraftId || !cloudAuthConfigured)
|
|
1341
1302
|
return;
|
|
1342
1303
|
try {
|
|
1343
|
-
|
|
1344
|
-
? (props.config?.cloudAuthBaseUrl || props.config?.apiBaseUrl || '')
|
|
1345
|
-
: (props.config?.apiBaseUrl || props.config?.cloudAuthBaseUrl || '');
|
|
1346
|
-
await fetchTaskforceApi('/api/taskforce/auth/profile/avatar/discard', {
|
|
1304
|
+
await fetch(resolveCloudAuthUrl('/api/taskforce/auth/profile/avatar/discard'), {
|
|
1347
1305
|
method: 'POST',
|
|
1348
1306
|
headers: { 'Content-Type': 'application/json' },
|
|
1349
1307
|
credentials: 'include',
|
|
1350
1308
|
body: JSON.stringify({ draftId: normalizedDraftId })
|
|
1351
|
-
}
|
|
1309
|
+
});
|
|
1352
1310
|
}
|
|
1353
1311
|
catch {
|
|
1354
1312
|
// Best-effort cleanup only.
|
|
1355
1313
|
}
|
|
1356
|
-
}, [cloudAuthConfigured,
|
|
1314
|
+
}, [cloudAuthConfigured, resolveCloudAuthUrl]);
|
|
1357
1315
|
const closeEditProfileModal = useCallback(() => {
|
|
1358
1316
|
const pendingDraftId = profileAvatarDraftId;
|
|
1359
1317
|
setShowEditProfileModal(false);
|
|
1318
|
+
setShowAvatarPhotoManager(false);
|
|
1360
1319
|
setProfileSaveError(null);
|
|
1361
1320
|
setProfileSaveNotice(null);
|
|
1362
1321
|
setProfileAvatarDraftId(null);
|
|
@@ -1367,47 +1326,53 @@ export function StandaloneLayout(props) {
|
|
|
1367
1326
|
}, [authAvatarUrl, discardProfileAvatarDraft, profileAvatarDraftId]);
|
|
1368
1327
|
const handleProfileAvatarSelected = useCallback(async (file) => {
|
|
1369
1328
|
if (!file || !cloudAuthConfigured)
|
|
1370
|
-
return;
|
|
1329
|
+
return false;
|
|
1371
1330
|
if (!file.type.startsWith('image/')) {
|
|
1372
1331
|
setProfileSaveError('Profile photo must be an image file.');
|
|
1373
1332
|
setProfileSaveNotice(null);
|
|
1374
|
-
return;
|
|
1375
|
-
}
|
|
1376
|
-
if (file.size > PROFILE_AVATAR_MAX_BYTES) {
|
|
1377
|
-
setProfileSaveError('Profile photo must be 5 MB or smaller.');
|
|
1378
|
-
setProfileSaveNotice(null);
|
|
1379
|
-
return;
|
|
1333
|
+
return false;
|
|
1380
1334
|
}
|
|
1381
1335
|
setProfileAvatarBusy(true);
|
|
1382
1336
|
setProfileSaveError(null);
|
|
1383
1337
|
setProfileSaveNotice(null);
|
|
1384
1338
|
try {
|
|
1385
|
-
const
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1339
|
+
const preparedAvatar = await prepareAvatarUploadFile(file, {
|
|
1340
|
+
maxBytes: PROFILE_AVATAR_MAX_BYTES
|
|
1341
|
+
});
|
|
1342
|
+
if (preparedAvatar.exceededLimit) {
|
|
1343
|
+
const message = file.type === 'image/gif'
|
|
1344
|
+
? 'Animated GIF profile photos must be 5 MB or smaller.'
|
|
1345
|
+
: 'Profile photo must be 5 MB or smaller.';
|
|
1346
|
+
throw new Error(message);
|
|
1347
|
+
}
|
|
1348
|
+
const uploadFile = preparedAvatar.file;
|
|
1349
|
+
const initRes = await fetch(resolveCloudAuthUrl('/api/taskforce/auth/profile/avatar/init'), {
|
|
1389
1350
|
method: 'POST',
|
|
1390
1351
|
headers: { 'Content-Type': 'application/json' },
|
|
1391
1352
|
credentials: 'include',
|
|
1392
1353
|
body: JSON.stringify({
|
|
1393
|
-
originalName:
|
|
1394
|
-
mimeType:
|
|
1395
|
-
size:
|
|
1354
|
+
originalName: uploadFile.name,
|
|
1355
|
+
mimeType: uploadFile.type,
|
|
1356
|
+
size: uploadFile.size
|
|
1396
1357
|
})
|
|
1397
|
-
}
|
|
1358
|
+
});
|
|
1398
1359
|
const initData = await initRes.json().catch(() => ({}));
|
|
1399
|
-
if (!initRes.ok || !initData?.success || typeof initData?.
|
|
1360
|
+
if (!initRes.ok || !initData?.success || typeof initData?.draftId !== 'string' || typeof initData?.relativePath !== 'string') {
|
|
1400
1361
|
throw new Error(initData?.error || 'Failed to start avatar upload.');
|
|
1401
1362
|
}
|
|
1402
|
-
const uploadRes = await fetch(
|
|
1403
|
-
method:
|
|
1404
|
-
headers:
|
|
1405
|
-
|
|
1363
|
+
const uploadRes = await fetch(resolveCloudAuthUrl('/api/taskforce/auth/profile/avatar/upload'), {
|
|
1364
|
+
method: 'POST',
|
|
1365
|
+
headers: {
|
|
1366
|
+
'Content-Type': uploadFile.type || 'application/octet-stream',
|
|
1367
|
+
'x-taskforce-avatar-draft-id': initData.draftId
|
|
1368
|
+
},
|
|
1369
|
+
credentials: 'include',
|
|
1370
|
+
body: uploadFile
|
|
1406
1371
|
});
|
|
1407
1372
|
if (!uploadRes.ok) {
|
|
1408
1373
|
throw new Error('Failed to upload avatar.');
|
|
1409
1374
|
}
|
|
1410
|
-
const finalizeRes = await
|
|
1375
|
+
const finalizeRes = await fetch(resolveCloudAuthUrl('/api/taskforce/auth/profile/avatar/finalize'), {
|
|
1411
1376
|
method: 'POST',
|
|
1412
1377
|
headers: { 'Content-Type': 'application/json' },
|
|
1413
1378
|
credentials: 'include',
|
|
@@ -1415,7 +1380,7 @@ export function StandaloneLayout(props) {
|
|
|
1415
1380
|
draftId: initData.draftId,
|
|
1416
1381
|
relativePath: initData.relativePath
|
|
1417
1382
|
})
|
|
1418
|
-
}
|
|
1383
|
+
});
|
|
1419
1384
|
const finalizeData = await finalizeRes.json().catch(() => ({}));
|
|
1420
1385
|
if (!finalizeRes.ok || !finalizeData?.success || typeof finalizeData?.draftId !== 'string') {
|
|
1421
1386
|
throw new Error(finalizeData?.error || 'Failed to finalize avatar upload.');
|
|
@@ -1423,27 +1388,24 @@ export function StandaloneLayout(props) {
|
|
|
1423
1388
|
const previousDraftId = profileAvatarDraftId;
|
|
1424
1389
|
setProfileAvatarDraftId(finalizeData.draftId);
|
|
1425
1390
|
setProfileAvatarPreviewUrl(typeof finalizeData?.avatarUrl === 'string' ? finalizeData.avatarUrl : '');
|
|
1426
|
-
setProfileSaveNotice(
|
|
1391
|
+
setProfileSaveNotice(null);
|
|
1427
1392
|
if (previousDraftId && previousDraftId !== finalizeData.draftId) {
|
|
1428
1393
|
void discardProfileAvatarDraft(previousDraftId);
|
|
1429
1394
|
}
|
|
1395
|
+
return true;
|
|
1430
1396
|
}
|
|
1431
1397
|
catch (error) {
|
|
1432
1398
|
setProfileSaveError(error instanceof Error ? error.message : 'Failed to upload profile photo.');
|
|
1399
|
+
return false;
|
|
1433
1400
|
}
|
|
1434
1401
|
finally {
|
|
1435
1402
|
setProfileAvatarBusy(false);
|
|
1436
|
-
if (profileAvatarInputRef.current) {
|
|
1437
|
-
profileAvatarInputRef.current.value = '';
|
|
1438
|
-
}
|
|
1439
1403
|
}
|
|
1440
1404
|
}, [
|
|
1441
1405
|
cloudAuthConfigured,
|
|
1442
1406
|
discardProfileAvatarDraft,
|
|
1443
1407
|
profileAvatarDraftId,
|
|
1444
|
-
|
|
1445
|
-
props.config?.cloudAuthBaseUrl,
|
|
1446
|
-
runtimeMode
|
|
1408
|
+
resolveCloudAuthUrl
|
|
1447
1409
|
]);
|
|
1448
1410
|
const handleDiscardPendingProfileAvatar = useCallback(() => {
|
|
1449
1411
|
if (!profileAvatarDraftId)
|
|
@@ -1489,6 +1451,7 @@ export function StandaloneLayout(props) {
|
|
|
1489
1451
|
setProfileAvatarDraftId(null);
|
|
1490
1452
|
setProfileAvatarPreviewUrl('');
|
|
1491
1453
|
setProfileSaveBusy(false);
|
|
1454
|
+
setShowAvatarPhotoManager(false);
|
|
1492
1455
|
setShowEditProfileModal(false);
|
|
1493
1456
|
}, [authAvatarUrl, profileAvatarDraftId, profileAvatarPreviewUrl, profileDisplayNameDraft, updateCurrentUserProfile]);
|
|
1494
1457
|
const teamAdminHeaders = useCallback(() => {
|
|
@@ -1630,9 +1593,8 @@ export function StandaloneLayout(props) {
|
|
|
1630
1593
|
setTeamPlanMode(nextMode);
|
|
1631
1594
|
}, []);
|
|
1632
1595
|
const hasActiveCommercialEntitlement = useMemo(() => {
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
}, [accountProfileSummary?.entitlementState]);
|
|
1596
|
+
return resolveCommercialLifecyclePresentation(accountProfileSummary).allowReturnToApp;
|
|
1597
|
+
}, [accountProfileSummary]);
|
|
1636
1598
|
const handleStartCheckout = useCallback(async () => {
|
|
1637
1599
|
const params = new URLSearchParams();
|
|
1638
1600
|
params.set('screen', 'plans');
|
|
@@ -1808,9 +1770,9 @@ export function StandaloneLayout(props) {
|
|
|
1808
1770
|
const target = (!targetRaw || targetRaw === '/login' || !targetRaw.startsWith('/')) ? '/' : targetRaw;
|
|
1809
1771
|
setShowAccountMenu(false);
|
|
1810
1772
|
setShowAccountHub(false);
|
|
1811
|
-
|
|
1773
|
+
closeSyncStatusModal();
|
|
1812
1774
|
navigate(`/login?mode=${mode}&next=${encodeURIComponent(target)}`);
|
|
1813
|
-
}, [location.hash, location.pathname, location.search, navigate]);
|
|
1775
|
+
}, [closeSyncStatusModal, location.hash, location.pathname, location.search, navigate]);
|
|
1814
1776
|
const openWorkspaceSetup = useCallback((options) => {
|
|
1815
1777
|
setShowAccountMenu(false);
|
|
1816
1778
|
setShowCreateWorkspaceConfirm(false);
|
|
@@ -1823,38 +1785,44 @@ export function StandaloneLayout(props) {
|
|
|
1823
1785
|
const handlePlansBack = useCallback(async () => {
|
|
1824
1786
|
if (plansNavigationBusy)
|
|
1825
1787
|
return;
|
|
1788
|
+
setPlansNavigationBusy(true);
|
|
1826
1789
|
if (!isAuthenticated) {
|
|
1827
|
-
|
|
1790
|
+
try {
|
|
1791
|
+
closePlansScreen();
|
|
1792
|
+
}
|
|
1793
|
+
finally {
|
|
1794
|
+
setPlansNavigationBusy(false);
|
|
1795
|
+
}
|
|
1828
1796
|
return;
|
|
1829
1797
|
}
|
|
1830
|
-
const
|
|
1831
|
-
|
|
1832
|
-
|
|
1798
|
+
const currentSummary = accountProfileSummaryRef.current;
|
|
1799
|
+
if (currentSummary && !resolveCommercialLifecyclePresentation(currentSummary).allowReturnToApp) {
|
|
1800
|
+
setPlansNavigationBusy(false);
|
|
1833
1801
|
return;
|
|
1834
1802
|
}
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1803
|
+
closePlansScreen();
|
|
1804
|
+
void (async () => {
|
|
1805
|
+
try {
|
|
1806
|
+
const result = await props.continueAfterCommercialOnboarding();
|
|
1807
|
+
if (!result.success) {
|
|
1808
|
+
pushNotice(result.error || 'Unable to finish onboarding.', 'error');
|
|
1809
|
+
if (result.destination === 'login') {
|
|
1810
|
+
navigate('/login', { replace: true });
|
|
1811
|
+
}
|
|
1812
|
+
return;
|
|
1813
|
+
}
|
|
1814
|
+
if (result.destination === 'setup') {
|
|
1815
|
+
openWorkspaceSetup();
|
|
1842
1816
|
}
|
|
1843
|
-
return;
|
|
1844
1817
|
}
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
return;
|
|
1818
|
+
finally {
|
|
1819
|
+
setPlansNavigationBusy(false);
|
|
1848
1820
|
}
|
|
1849
|
-
|
|
1850
|
-
}
|
|
1851
|
-
finally {
|
|
1852
|
-
setPlansNavigationBusy(false);
|
|
1853
|
-
}
|
|
1821
|
+
})();
|
|
1854
1822
|
}, [
|
|
1823
|
+
accountProfileSummaryRef,
|
|
1855
1824
|
closePlansScreen,
|
|
1856
1825
|
isAuthenticated,
|
|
1857
|
-
loadAccountProfileSummary,
|
|
1858
1826
|
navigate,
|
|
1859
1827
|
openWorkspaceSetup,
|
|
1860
1828
|
plansNavigationBusy,
|
|
@@ -2040,6 +2008,7 @@ export function StandaloneLayout(props) {
|
|
|
2040
2008
|
const formattedLastPullTime = useMemo(() => formatSyncTimestamp(workspaceLastPullAt), [formatSyncTimestamp, workspaceLastPullAt]);
|
|
2041
2009
|
const formattedLastPushTime = useMemo(() => formatSyncTimestamp(workspaceLastPushAt), [formatSyncTimestamp, workspaceLastPushAt]);
|
|
2042
2010
|
const rawSyncLastError = workspaceSyncError || workspaceLastErrorMessage || userGlobalSyncError || 'None';
|
|
2011
|
+
const workspaceSyncDiagnostics = getWorkspaceSyncDiagnostics();
|
|
2043
2012
|
const headerSyncPresentation = useMemo(() => resolveHeaderWorkspaceSyncPresentation({
|
|
2044
2013
|
runtimeMode,
|
|
2045
2014
|
isAuthenticated,
|
|
@@ -2047,7 +2016,10 @@ export function StandaloneLayout(props) {
|
|
|
2047
2016
|
workspaceCloudSyncEnabled,
|
|
2048
2017
|
workspaceSyncSummary,
|
|
2049
2018
|
workspaceSyncRecommendedAction,
|
|
2050
|
-
workspaceSyncError: rawSyncLastError
|
|
2019
|
+
workspaceSyncError: rawSyncLastError,
|
|
2020
|
+
pushBlockedReason: workspaceSyncDiagnostics.pushBlockedReason || null,
|
|
2021
|
+
pullBlockedReason: workspaceSyncDiagnostics.pullBlockedReason || null,
|
|
2022
|
+
retryBlockedReason: workspaceSyncDiagnostics.retryBlockedReason || null
|
|
2051
2023
|
}), [
|
|
2052
2024
|
runtimeMode,
|
|
2053
2025
|
isAuthenticated,
|
|
@@ -2055,75 +2027,24 @@ export function StandaloneLayout(props) {
|
|
|
2055
2027
|
workspaceCloudSyncEnabled,
|
|
2056
2028
|
workspaceSyncSummary,
|
|
2057
2029
|
workspaceSyncRecommendedAction,
|
|
2058
|
-
rawSyncLastError
|
|
2030
|
+
rawSyncLastError,
|
|
2031
|
+
workspaceSyncDiagnostics.pushBlockedReason,
|
|
2032
|
+
workspaceSyncDiagnostics.pullBlockedReason,
|
|
2033
|
+
workspaceSyncDiagnostics.retryBlockedReason
|
|
2059
2034
|
]);
|
|
2060
2035
|
const syncLastError = headerSyncPresentation.lastError;
|
|
2061
2036
|
const [headerSyncStatus, setHeaderSyncStatus] = useState(headerSyncPresentation.status);
|
|
2062
|
-
const
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
return 'Initial cloud pull';
|
|
2074
|
-
case 'active':
|
|
2075
|
-
return 'Active';
|
|
2076
|
-
case 'error':
|
|
2077
|
-
return 'Error';
|
|
2078
|
-
default:
|
|
2079
|
-
return 'Idle';
|
|
2080
|
-
}
|
|
2081
|
-
}, [workspaceSyncPhase, workspaceSyncRepairBusy]);
|
|
2082
|
-
const workspaceSyncDiagnostics = getWorkspaceSyncDiagnostics();
|
|
2083
|
-
const syncDiagnosticsSummary = useMemo(() => ([
|
|
2084
|
-
{
|
|
2085
|
-
label: 'AI profile snapshot',
|
|
2086
|
-
value: `${workspaceSyncDiagnostics.aiProfileSnapshotCount} local profile${workspaceSyncDiagnostics.aiProfileSnapshotCount === 1 ? '' : 's'}`
|
|
2087
|
-
},
|
|
2088
|
-
{
|
|
2089
|
-
label: 'AI profile raw response',
|
|
2090
|
-
value: `${workspaceSyncDiagnostics.aiProfileSnapshotRawCount} from route`
|
|
2091
|
-
},
|
|
2092
|
-
{
|
|
2093
|
-
label: 'Last pushed AI profiles',
|
|
2094
|
-
value: `${workspaceSyncDiagnostics.lastPushedAiProfileCount} tracked`
|
|
2095
|
-
},
|
|
2096
|
-
{
|
|
2097
|
-
label: 'AI watermark map',
|
|
2098
|
-
value: `${workspaceSyncDiagnostics.lastPushedAiProfileWatermarkCount} tracked`
|
|
2099
|
-
},
|
|
2100
|
-
{
|
|
2101
|
-
label: 'Document snapshot',
|
|
2102
|
-
value: `${workspaceSyncDiagnostics.documentSnapshotCount} local doc${workspaceSyncDiagnostics.documentSnapshotCount === 1 ? '' : 's'}`
|
|
2103
|
-
},
|
|
2104
|
-
{
|
|
2105
|
-
label: 'Asset snapshot',
|
|
2106
|
-
value: `${workspaceSyncDiagnostics.assetSnapshotCount} local asset${workspaceSyncDiagnostics.assetSnapshotCount === 1 ? '' : 's'}`
|
|
2107
|
-
},
|
|
2108
|
-
{
|
|
2109
|
-
label: 'Queued full AI sync',
|
|
2110
|
-
value: workspaceSyncDiagnostics.forceFullAiProfilePushQueued ? 'Yes' : 'No'
|
|
2111
|
-
},
|
|
2112
|
-
{
|
|
2113
|
-
label: 'AI snapshot last fetch',
|
|
2114
|
-
value: workspaceSyncDiagnostics.aiProfileSnapshotLastFetchAt
|
|
2115
|
-
? new Date(workspaceSyncDiagnostics.aiProfileSnapshotLastFetchAt).toLocaleString()
|
|
2116
|
-
: 'Never'
|
|
2117
|
-
},
|
|
2118
|
-
{
|
|
2119
|
-
label: 'AI snapshot fetch error',
|
|
2120
|
-
value: workspaceSyncDiagnostics.aiProfileSnapshotLastFetchError || 'None'
|
|
2121
|
-
},
|
|
2122
|
-
{
|
|
2123
|
-
label: 'AI snapshot skip reason',
|
|
2124
|
-
value: workspaceSyncDiagnostics.aiProfileSnapshotLastSkipReason || 'None'
|
|
2125
|
-
}
|
|
2126
|
-
]), [workspaceSyncDiagnostics]);
|
|
2037
|
+
const syncDiagnosticsSummary = useMemo(() => buildSyncDiagnosticsSummary(workspaceSyncDiagnostics), [workspaceSyncDiagnostics]);
|
|
2038
|
+
const { syncRecentEvents, syncRecentEventsLoading, syncRecentEventsError, syncEventsListRef, loadRecentSyncEvents, } = useRecentSyncEvents({
|
|
2039
|
+
isOpen: showSyncStatusModal,
|
|
2040
|
+
workspaceId: currentWorkspaceId,
|
|
2041
|
+
refreshKeys: [
|
|
2042
|
+
workspaceLastPushAt,
|
|
2043
|
+
workspaceLastPullAt,
|
|
2044
|
+
workspaceLastErrorAt,
|
|
2045
|
+
workspaceSyncStatus
|
|
2046
|
+
]
|
|
2047
|
+
});
|
|
2127
2048
|
const shouldDelayHeaderSyncing = headerSyncPresentation.actionable
|
|
2128
2049
|
&& workspaceSyncStatus === 'syncing'
|
|
2129
2050
|
&& workspaceSyncPhase === 'active'
|
|
@@ -2138,282 +2059,34 @@ export function StandaloneLayout(props) {
|
|
|
2138
2059
|
}, 1200);
|
|
2139
2060
|
return () => window.clearTimeout(timeoutId);
|
|
2140
2061
|
}, [headerSyncPresentation.status, shouldDelayHeaderSyncing]);
|
|
2141
|
-
const
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
? event.eventType.trim()
|
|
2147
|
-
: 'sync';
|
|
2148
|
-
const status = event.status === 'error' ? 'failed' : 'succeeded';
|
|
2149
|
-
const changeCountValue = Number(event.changeCount);
|
|
2150
|
-
const changeCount = Number.isFinite(changeCountValue)
|
|
2151
|
-
? ` (${Math.max(0, Math.floor(changeCountValue))} change${Math.floor(changeCountValue) === 1 ? '' : 's'})`
|
|
2152
|
-
: '';
|
|
2153
|
-
const requestMsValue = Number(event.requestMs);
|
|
2154
|
-
const requestMs = Number.isFinite(requestMsValue)
|
|
2155
|
-
? ` in ${Math.max(0, Math.floor(requestMsValue))}ms`
|
|
2156
|
-
: '';
|
|
2157
|
-
const statusCodeValue = Number(event.statusCode);
|
|
2158
|
-
const statusCode = Number.isFinite(statusCodeValue)
|
|
2159
|
-
? ` [${Math.floor(statusCodeValue)}]`
|
|
2160
|
-
: '';
|
|
2161
|
-
const errorMessage = typeof event.errorMessage === 'string' && event.errorMessage.trim().length > 0
|
|
2162
|
-
? `: ${event.errorMessage.trim()}`
|
|
2163
|
-
: '';
|
|
2164
|
-
return `${occurredAt} ${eventType} ${status}${changeCount}${requestMs}${statusCode}${errorMessage}`;
|
|
2165
|
-
}, []);
|
|
2166
|
-
const syncEventRows = useMemo(() => {
|
|
2167
|
-
if (syncRecentEventsLoading && syncRecentEvents.length === 0) {
|
|
2168
|
-
return [{
|
|
2169
|
-
key: 'loading',
|
|
2170
|
-
text: 'Loading recent sync events...',
|
|
2171
|
-
tone: 'muted'
|
|
2172
|
-
}];
|
|
2173
|
-
}
|
|
2174
|
-
if (syncRecentEventsError) {
|
|
2175
|
-
return [{
|
|
2176
|
-
key: 'error',
|
|
2177
|
-
text: syncRecentEventsError,
|
|
2178
|
-
tone: 'error'
|
|
2179
|
-
}];
|
|
2180
|
-
}
|
|
2181
|
-
if (syncRecentEvents.length === 0) {
|
|
2182
|
-
return [{
|
|
2183
|
-
key: 'empty',
|
|
2184
|
-
text: 'No recent sync events recorded.',
|
|
2185
|
-
tone: 'muted'
|
|
2186
|
-
}];
|
|
2187
|
-
}
|
|
2188
|
-
return syncRecentEvents.map((event, index) => ({
|
|
2189
|
-
key: getSyncEventStableKey(event, index),
|
|
2190
|
-
text: formatSyncEventLine(event),
|
|
2191
|
-
tone: event.status === 'error' ? 'error' : 'default'
|
|
2192
|
-
}));
|
|
2193
|
-
}, [formatSyncEventLine, syncRecentEvents, syncRecentEventsError, syncRecentEventsLoading]);
|
|
2062
|
+
const syncEventRows = useMemo(() => buildSyncEventRows({
|
|
2063
|
+
syncRecentEvents,
|
|
2064
|
+
syncRecentEventsError,
|
|
2065
|
+
syncRecentEventsLoading
|
|
2066
|
+
}), [syncRecentEvents, syncRecentEventsError, syncRecentEventsLoading]);
|
|
2194
2067
|
const referenceMismatchCount = useMemo(() => getActiveReferenceMismatchCount(syncRecentEvents), [syncRecentEvents]);
|
|
2195
|
-
const
|
|
2196
|
-
const
|
|
2197
|
-
|
|
2198
|
-
const pathValue = String(details?.path || '').trim();
|
|
2199
|
-
const referenceLabelValue = String(details?.referenceLabel || '').trim();
|
|
2200
|
-
const taskTitleValue = String(details?.taskTitle || details?.existingTaskTitle || '').trim();
|
|
2201
|
-
const existingRefValue = Number(details?.existingReferenceNumber);
|
|
2202
|
-
const incomingRefValue = Number(details?.incomingReferenceNumber);
|
|
2203
|
-
const pathLabel = pathValue || taskTitleValue || referenceLabelValue || `Mismatch ${index + 1}`;
|
|
2204
|
-
const refsLabel = (Number.isFinite(existingRefValue)
|
|
2205
|
-
|| Number.isFinite(incomingRefValue))
|
|
2206
|
-
? `Existing ${Number.isFinite(existingRefValue) ? existingRefValue : '?'} vs incoming ${Number.isFinite(incomingRefValue) ? incomingRefValue : '?'}`
|
|
2207
|
-
: referenceLabelValue
|
|
2208
|
-
? `Both claimed ${referenceLabelValue}`
|
|
2209
|
-
: null;
|
|
2210
|
-
return {
|
|
2211
|
-
key: getSyncEventStableKey(event, index),
|
|
2212
|
-
pathLabel,
|
|
2213
|
-
refsLabel,
|
|
2214
|
-
};
|
|
2215
|
-
})), [activeReferenceMismatchEvents]);
|
|
2216
|
-
useLayoutEffect(() => {
|
|
2217
|
-
const pendingRestore = pendingSyncEventsScrollRestoreRef.current;
|
|
2218
|
-
const listEl = syncEventsListRef.current;
|
|
2219
|
-
if (!pendingRestore || !listEl)
|
|
2220
|
-
return;
|
|
2221
|
-
const delta = listEl.scrollHeight - pendingRestore.scrollHeight;
|
|
2222
|
-
listEl.scrollTop = pendingRestore.scrollTop + Math.max(0, delta);
|
|
2223
|
-
pendingSyncEventsScrollRestoreRef.current = null;
|
|
2224
|
-
}, [syncRecentEvents]);
|
|
2225
|
-
const loadRecentSyncEvents = useCallback(async () => {
|
|
2226
|
-
const res = await fetch(`/api/taskforce/sync/events?workspace_id=${encodeURIComponent(currentWorkspaceId)}&limit=15`, {
|
|
2227
|
-
method: 'GET',
|
|
2228
|
-
credentials: 'include'
|
|
2229
|
-
});
|
|
2230
|
-
if (!res.ok) {
|
|
2231
|
-
throw new Error(`Failed to load sync events (${res.status})`);
|
|
2232
|
-
}
|
|
2233
|
-
const data = await res.json().catch(() => ({}));
|
|
2234
|
-
return Array.isArray(data?.events)
|
|
2235
|
-
? data.events.filter((event) => event && typeof event === 'object')
|
|
2236
|
-
: [];
|
|
2237
|
-
}, [currentWorkspaceId]);
|
|
2238
|
-
useEffect(() => {
|
|
2239
|
-
if (!showSyncStatusModal)
|
|
2240
|
-
return;
|
|
2241
|
-
let cancelled = false;
|
|
2242
|
-
const isInitialLoad = syncRecentEvents.length === 0;
|
|
2243
|
-
if (isInitialLoad) {
|
|
2244
|
-
setSyncRecentEventsLoading(true);
|
|
2245
|
-
}
|
|
2246
|
-
void loadRecentSyncEvents()
|
|
2247
|
-
.then((events) => {
|
|
2248
|
-
if (cancelled)
|
|
2249
|
-
return;
|
|
2250
|
-
const listEl = syncEventsListRef.current;
|
|
2251
|
-
pendingSyncEventsScrollRestoreRef.current = listEl && listEl.scrollTop > 8
|
|
2252
|
-
? {
|
|
2253
|
-
scrollTop: listEl.scrollTop,
|
|
2254
|
-
scrollHeight: listEl.scrollHeight
|
|
2255
|
-
}
|
|
2256
|
-
: null;
|
|
2257
|
-
setSyncRecentEvents((current) => {
|
|
2258
|
-
const next = current.length === 0 ? events : mergeRecentSyncEvents(current, events, 15);
|
|
2259
|
-
return areSyncEventListsEqual(current, next) ? current : next;
|
|
2260
|
-
});
|
|
2261
|
-
setSyncRecentEventsError(null);
|
|
2262
|
-
})
|
|
2263
|
-
.catch((error) => {
|
|
2264
|
-
if (cancelled)
|
|
2265
|
-
return;
|
|
2266
|
-
const message = error instanceof Error && error.message.trim().length > 0
|
|
2267
|
-
? error.message.trim()
|
|
2268
|
-
: 'Unable to load recent sync events.';
|
|
2269
|
-
setSyncRecentEventsError(message);
|
|
2270
|
-
if (isInitialLoad) {
|
|
2271
|
-
setSyncRecentEvents([]);
|
|
2272
|
-
}
|
|
2273
|
-
})
|
|
2274
|
-
.finally(() => {
|
|
2275
|
-
if (cancelled)
|
|
2276
|
-
return;
|
|
2277
|
-
if (isInitialLoad) {
|
|
2278
|
-
setSyncRecentEventsLoading(false);
|
|
2279
|
-
}
|
|
2280
|
-
});
|
|
2281
|
-
return () => {
|
|
2282
|
-
cancelled = true;
|
|
2283
|
-
};
|
|
2284
|
-
}, [
|
|
2285
|
-
showSyncStatusModal,
|
|
2286
|
-
loadRecentSyncEvents,
|
|
2287
|
-
syncRecentEvents.length,
|
|
2288
|
-
workspaceLastPushAt,
|
|
2289
|
-
workspaceLastPullAt,
|
|
2290
|
-
workspaceLastErrorAt,
|
|
2291
|
-
workspaceSyncStatus
|
|
2292
|
-
]);
|
|
2293
|
-
const syncStatusMeta = useMemo(() => {
|
|
2294
|
-
switch (headerSyncStatus) {
|
|
2295
|
-
case 'off':
|
|
2296
|
-
return {
|
|
2297
|
-
label: 'Off',
|
|
2298
|
-
icon: 'off',
|
|
2299
|
-
color: '#94a3b8',
|
|
2300
|
-
border: 'rgba(148, 163, 184, 0.35)',
|
|
2301
|
-
background: 'rgba(148, 163, 184, 0.12)'
|
|
2302
|
-
};
|
|
2303
|
-
case 'syncing':
|
|
2304
|
-
return {
|
|
2305
|
-
label: 'Syncing',
|
|
2306
|
-
icon: 'cloud',
|
|
2307
|
-
color: '#60a5fa',
|
|
2308
|
-
border: 'rgba(96, 165, 250, 0.35)',
|
|
2309
|
-
background: 'rgba(59, 130, 246, 0.12)'
|
|
2310
|
-
};
|
|
2311
|
-
case 'attention':
|
|
2312
|
-
return {
|
|
2313
|
-
label: 'Needs Attention',
|
|
2314
|
-
icon: 'cloud',
|
|
2315
|
-
color: '#fda4af',
|
|
2316
|
-
border: 'rgba(253, 164, 175, 0.4)',
|
|
2317
|
-
background: 'rgba(239, 68, 68, 0.12)'
|
|
2318
|
-
};
|
|
2319
|
-
default:
|
|
2320
|
-
return {
|
|
2321
|
-
label: 'Healthy',
|
|
2322
|
-
icon: 'cloud',
|
|
2323
|
-
color: '#86efac',
|
|
2324
|
-
border: 'rgba(134, 239, 172, 0.4)',
|
|
2325
|
-
background: 'rgba(34, 197, 94, 0.12)'
|
|
2326
|
-
};
|
|
2327
|
-
}
|
|
2328
|
-
}, [headerSyncStatus]);
|
|
2329
|
-
const handleCopySyncDetails = useCallback(async () => {
|
|
2330
|
-
const details = [
|
|
2331
|
-
'Taskforce Sync Manager',
|
|
2332
|
-
`Workspace ID: ${currentWorkspaceId}`,
|
|
2333
|
-
`Status: ${syncStatusMeta.label}`,
|
|
2334
|
-
`Summary: ${headerSyncPresentation.summary}`,
|
|
2335
|
-
`Last successful sync: ${formattedLastSyncTime}`,
|
|
2336
|
-
`Last pull from cloud: ${formattedLastPullTime}`,
|
|
2337
|
-
`Last push to cloud: ${formattedLastPushTime}`,
|
|
2338
|
-
`Last error at: ${workspaceSyncDiagnostics.lastErrorAt ? new Date(workspaceSyncDiagnostics.lastErrorAt).toLocaleString() : 'Never'}`,
|
|
2339
|
-
`Pending local changes: ${workspaceSyncPendingChanges}`,
|
|
2340
|
-
`Last error: ${syncLastError}`,
|
|
2341
|
-
`Sync stage: ${syncStageLabel}`,
|
|
2342
|
-
`AI profile snapshot: ${workspaceSyncDiagnostics.aiProfileSnapshotCount}`,
|
|
2343
|
-
`AI profile raw response: ${workspaceSyncDiagnostics.aiProfileSnapshotRawCount}`,
|
|
2344
|
-
`Last pushed AI profiles tracked: ${workspaceSyncDiagnostics.lastPushedAiProfileCount}`,
|
|
2345
|
-
`AI profile watermarks tracked: ${workspaceSyncDiagnostics.lastPushedAiProfileWatermarkCount}`,
|
|
2346
|
-
`AI snapshot last fetch: ${workspaceSyncDiagnostics.aiProfileSnapshotLastFetchAt ? new Date(workspaceSyncDiagnostics.aiProfileSnapshotLastFetchAt).toLocaleString() : 'Never'}`,
|
|
2347
|
-
`AI snapshot fetch error: ${workspaceSyncDiagnostics.aiProfileSnapshotLastFetchError || 'None'}`,
|
|
2348
|
-
`AI snapshot skip reason: ${workspaceSyncDiagnostics.aiProfileSnapshotLastSkipReason || 'None'}`,
|
|
2349
|
-
`Document snapshot: ${workspaceSyncDiagnostics.documentSnapshotCount}`,
|
|
2350
|
-
`Asset snapshot: ${workspaceSyncDiagnostics.assetSnapshotCount}`,
|
|
2351
|
-
`Reference mismatches detected: ${referenceMismatchCount}`,
|
|
2352
|
-
`Queued full AI sync: ${workspaceSyncDiagnostics.forceFullAiProfilePushQueued ? 'Yes' : 'No'}`
|
|
2353
|
-
];
|
|
2354
|
-
try {
|
|
2355
|
-
const events = syncRecentEvents.length > 0 ? syncRecentEvents : await loadRecentSyncEvents();
|
|
2356
|
-
const recentEventLines = events.map((event) => formatSyncEventLine(event));
|
|
2357
|
-
const copyText = [
|
|
2358
|
-
...details,
|
|
2359
|
-
'',
|
|
2360
|
-
'Recent sync events',
|
|
2361
|
-
...(recentEventLines.length > 0 ? recentEventLines : ['No recent sync events recorded.'])
|
|
2362
|
-
].join('\n');
|
|
2363
|
-
await navigator.clipboard.writeText(copyText);
|
|
2364
|
-
setWorkspaceSyncCopied(true);
|
|
2365
|
-
window.setTimeout(() => {
|
|
2366
|
-
setWorkspaceSyncCopied(false);
|
|
2367
|
-
}, 1800);
|
|
2368
|
-
pushNotice('Sync details copied to clipboard.', 'success');
|
|
2369
|
-
}
|
|
2370
|
-
catch {
|
|
2371
|
-
setWorkspaceSyncCopied(false);
|
|
2372
|
-
pushNotice('Failed to copy sync details.', 'error');
|
|
2373
|
-
}
|
|
2374
|
-
}, [
|
|
2068
|
+
const activeReferenceMismatchSummaries = useMemo(() => buildReferenceMismatchSummaries(syncRecentEvents), [syncRecentEvents]);
|
|
2069
|
+
const syncStatusMeta = useMemo(() => resolveSyncStatusMeta(headerSyncStatus), [headerSyncStatus]);
|
|
2070
|
+
const { workspaceSyncRepairBusy, workspaceSyncRepairQueued, workspaceSyncCopied, handleCopySyncDetails, handleQueueOrRunRepairSync, } = useSyncStatusActions({
|
|
2375
2071
|
currentWorkspaceId,
|
|
2376
|
-
syncStatusMeta.label,
|
|
2377
|
-
headerSyncPresentation.summary,
|
|
2072
|
+
syncStatusLabel: syncStatusMeta.label,
|
|
2073
|
+
workspaceSyncSummary: headerSyncPresentation.summary,
|
|
2074
|
+
workspaceSyncRecommendedAction: headerSyncPresentation.recommendedAction,
|
|
2378
2075
|
formattedLastSyncTime,
|
|
2379
2076
|
formattedLastPullTime,
|
|
2380
2077
|
formattedLastPushTime,
|
|
2381
2078
|
workspaceSyncDiagnostics,
|
|
2382
2079
|
workspaceSyncPendingChanges,
|
|
2383
2080
|
syncLastError,
|
|
2384
|
-
|
|
2081
|
+
workspaceSyncPhase,
|
|
2385
2082
|
referenceMismatchCount,
|
|
2386
|
-
pushNotice,
|
|
2387
2083
|
syncRecentEvents,
|
|
2388
2084
|
loadRecentSyncEvents,
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
try {
|
|
2395
|
-
await resetWorkspaceSyncCursorAndPull();
|
|
2396
|
-
}
|
|
2397
|
-
finally {
|
|
2398
|
-
setWorkspaceSyncRepairBusy(false);
|
|
2399
|
-
}
|
|
2400
|
-
}, [resetWorkspaceSyncCursorAndPull]);
|
|
2401
|
-
const handleQueueOrRunRepairSync = useCallback(() => {
|
|
2402
|
-
if (workspaceSyncRepairBusy)
|
|
2403
|
-
return;
|
|
2404
|
-
if (workspaceSyncBusy) {
|
|
2405
|
-
setWorkspaceSyncRepairQueued(true);
|
|
2406
|
-
return;
|
|
2407
|
-
}
|
|
2408
|
-
void handleRepairSync();
|
|
2409
|
-
}, [handleRepairSync, workspaceSyncBusy, workspaceSyncRepairBusy]);
|
|
2410
|
-
useEffect(() => {
|
|
2411
|
-
if (!workspaceSyncRepairQueued)
|
|
2412
|
-
return;
|
|
2413
|
-
if (workspaceSyncBusy || workspaceSyncRepairBusy)
|
|
2414
|
-
return;
|
|
2415
|
-
void handleRepairSync();
|
|
2416
|
-
}, [handleRepairSync, workspaceSyncBusy, workspaceSyncRepairBusy, workspaceSyncRepairQueued]);
|
|
2085
|
+
pushNotice,
|
|
2086
|
+
resetWorkspaceSyncCursorAndPull,
|
|
2087
|
+
workspaceSyncBusy,
|
|
2088
|
+
});
|
|
2089
|
+
const syncStageLabel = useMemo(() => resolveSyncStageLabel(workspaceSyncPhase, workspaceSyncRepairBusy, workspaceSyncBusy), [workspaceSyncBusy, workspaceSyncPhase, workspaceSyncRepairBusy]);
|
|
2417
2090
|
const selectedDateObj = useMemo(() => parseDateOnlyLocal(scheduleSelectedDate) || new Date(), [scheduleSelectedDate]);
|
|
2418
2091
|
const scheduleWeekStart = useMemo(() => startOfWeek(selectedDateObj, globalWeekStartsOn), [selectedDateObj, globalWeekStartsOn]);
|
|
2419
2092
|
const scheduleDates = useMemo(() => {
|
|
@@ -2634,6 +2307,8 @@ export function StandaloneLayout(props) {
|
|
|
2634
2307
|
setActiveTab('add');
|
|
2635
2308
|
}, [props.workstreams, resetForm, setActiveTab, setActiveWorkspaceModule, setWorkstreamInput]);
|
|
2636
2309
|
const openPlanningEditor = useCallback((editor) => {
|
|
2310
|
+
planningEditorSubmitInFlightRef.current = false;
|
|
2311
|
+
setIsSubmittingPlanningEditor(false);
|
|
2637
2312
|
setPlanningEditor(editor);
|
|
2638
2313
|
setPlanningPrimaryCollapsed(false);
|
|
2639
2314
|
setPlanningSecondaryEditor(null);
|
|
@@ -2648,6 +2323,8 @@ export function StandaloneLayout(props) {
|
|
|
2648
2323
|
setPlanningNestedWorkstreamDetailId(null);
|
|
2649
2324
|
}, [planningStructure.initiativeById, selectedPlanningInitiativeId]);
|
|
2650
2325
|
const openPlanningSecondaryEditor = useCallback((editor, initiativeId) => {
|
|
2326
|
+
planningEditorSubmitInFlightRef.current = false;
|
|
2327
|
+
setIsSubmittingPlanningEditor(false);
|
|
2651
2328
|
setPlanningSecondaryEditor(editor);
|
|
2652
2329
|
setPlanningSecondaryCollapsed(false);
|
|
2653
2330
|
setPlanningDraftTitle('');
|
|
@@ -2661,6 +2338,8 @@ export function StandaloneLayout(props) {
|
|
|
2661
2338
|
setPlanningNestedWorkstreamDetailId(null);
|
|
2662
2339
|
}, [planningStructure.initiativeById, selectedPlanningInitiativeId]);
|
|
2663
2340
|
const closePlanningEditor = useCallback(() => {
|
|
2341
|
+
planningEditorSubmitInFlightRef.current = false;
|
|
2342
|
+
setIsSubmittingPlanningEditor(false);
|
|
2664
2343
|
setPlanningEditor(null);
|
|
2665
2344
|
setPlanningDraftTitle('');
|
|
2666
2345
|
setPlanningDraftDescription('');
|
|
@@ -2668,6 +2347,8 @@ export function StandaloneLayout(props) {
|
|
|
2668
2347
|
setPlanningDraftInitiativeId('');
|
|
2669
2348
|
}, []);
|
|
2670
2349
|
const closePlanningSecondaryEditor = useCallback(() => {
|
|
2350
|
+
planningEditorSubmitInFlightRef.current = false;
|
|
2351
|
+
setIsSubmittingPlanningEditor(false);
|
|
2671
2352
|
setPlanningSecondaryEditor(null);
|
|
2672
2353
|
setPlanningDraftTitle('');
|
|
2673
2354
|
setPlanningDraftDescription('');
|
|
@@ -2676,8 +2357,10 @@ export function StandaloneLayout(props) {
|
|
|
2676
2357
|
}, []);
|
|
2677
2358
|
const submitPlanningEditor = useCallback(async () => {
|
|
2678
2359
|
const activeEditor = planningSecondaryEditor || planningEditor;
|
|
2679
|
-
if (!activeEditor)
|
|
2360
|
+
if (!activeEditor || planningEditorSubmitInFlightRef.current)
|
|
2680
2361
|
return;
|
|
2362
|
+
planningEditorSubmitInFlightRef.current = true;
|
|
2363
|
+
setIsSubmittingPlanningEditor(true);
|
|
2681
2364
|
const isSecondaryEditor = Boolean(planningSecondaryEditor);
|
|
2682
2365
|
const ownerId = planningDraftOwner.trim() || null;
|
|
2683
2366
|
try {
|
|
@@ -2751,6 +2434,10 @@ export function StandaloneLayout(props) {
|
|
|
2751
2434
|
catch (error) {
|
|
2752
2435
|
pushNotice(error instanceof Error ? error.message : 'Failed to save planning item.', 'error');
|
|
2753
2436
|
}
|
|
2437
|
+
finally {
|
|
2438
|
+
planningEditorSubmitInFlightRef.current = false;
|
|
2439
|
+
setIsSubmittingPlanningEditor(false);
|
|
2440
|
+
}
|
|
2754
2441
|
}, [
|
|
2755
2442
|
closePlanningEditor,
|
|
2756
2443
|
closePlanningSecondaryEditor,
|
|
@@ -2960,6 +2647,54 @@ export function StandaloneLayout(props) {
|
|
|
2960
2647
|
}
|
|
2961
2648
|
await handleAssignInitiativeToWorkstream(matchedWorkstream.id, formatInitiativeReference(initiative));
|
|
2962
2649
|
}, [handleAssignInitiativeToWorkstream, planningStructure.initiativeById, props.workstreams, pushNotice]);
|
|
2650
|
+
const getPlanningContextAttachmentDraft = useCallback((entityType, entityId) => {
|
|
2651
|
+
const key = `${entityType}:${entityId}`;
|
|
2652
|
+
const draft = planningContextAttachmentDraftsRef.current.get(key);
|
|
2653
|
+
if (draft)
|
|
2654
|
+
return draft;
|
|
2655
|
+
const entity = entityType === 'initiative'
|
|
2656
|
+
? props.initiatives.find((initiative) => initiative.id === entityId)
|
|
2657
|
+
: props.workstreams.find((workstream) => workstream.id === entityId);
|
|
2658
|
+
return Array.isArray(entity?.attachments) ? entity.attachments : [];
|
|
2659
|
+
}, [props.initiatives, props.workstreams]);
|
|
2660
|
+
const updatePlanningContextAttachments = useCallback((entityType, entityId, nextAttachments) => {
|
|
2661
|
+
const key = `${entityType}:${entityId}`;
|
|
2662
|
+
planningContextAttachmentDraftsRef.current.set(key, nextAttachments);
|
|
2663
|
+
const previousUpdate = planningContextAttachmentUpdateQueueRef.current.get(key) || Promise.resolve();
|
|
2664
|
+
const nextUpdate = previousUpdate
|
|
2665
|
+
.catch(() => undefined)
|
|
2666
|
+
.then(() => (entityType === 'initiative'
|
|
2667
|
+
? props.updateInitiative(entityId, { attachments: nextAttachments })
|
|
2668
|
+
: props.updateWorkstream(entityId, { attachments: nextAttachments })));
|
|
2669
|
+
planningContextAttachmentUpdateQueueRef.current.set(key, nextUpdate);
|
|
2670
|
+
void nextUpdate.catch((error) => {
|
|
2671
|
+
pushNotice(error instanceof Error ? error.message : 'Failed to update planning context.', 'error');
|
|
2672
|
+
}).finally(() => {
|
|
2673
|
+
if (planningContextAttachmentUpdateQueueRef.current.get(key) === nextUpdate) {
|
|
2674
|
+
planningContextAttachmentUpdateQueueRef.current.delete(key);
|
|
2675
|
+
}
|
|
2676
|
+
});
|
|
2677
|
+
}, [props, pushNotice]);
|
|
2678
|
+
const handleAddPlanningContextFile = useCallback((entityType, entityId, file) => {
|
|
2679
|
+
const current = getPlanningContextAttachmentDraft(entityType, entityId);
|
|
2680
|
+
updatePlanningContextAttachments(entityType, entityId, [...current, file]);
|
|
2681
|
+
}, [getPlanningContextAttachmentDraft, updatePlanningContextAttachments]);
|
|
2682
|
+
const handleRemovePlanningContextFile = useCallback((entityType, entityId, index) => {
|
|
2683
|
+
const current = getPlanningContextAttachmentDraft(entityType, entityId);
|
|
2684
|
+
updatePlanningContextAttachments(entityType, entityId, current.filter((_, i) => i !== index));
|
|
2685
|
+
}, [getPlanningContextAttachmentDraft, updatePlanningContextAttachments]);
|
|
2686
|
+
const handleUpdatePlanningContextCaption = useCallback((entityType, entityId, index, caption) => {
|
|
2687
|
+
const current = getPlanningContextAttachmentDraft(entityType, entityId);
|
|
2688
|
+
const nextAttachments = current.map((attachment, i) => {
|
|
2689
|
+
if (i !== index)
|
|
2690
|
+
return attachment;
|
|
2691
|
+
if (typeof attachment === 'string') {
|
|
2692
|
+
return { path: attachment, caption, timestamp: new Date().toISOString() };
|
|
2693
|
+
}
|
|
2694
|
+
return { ...attachment, caption };
|
|
2695
|
+
});
|
|
2696
|
+
updatePlanningContextAttachments(entityType, entityId, nextAttachments);
|
|
2697
|
+
}, [getPlanningContextAttachmentDraft, updatePlanningContextAttachments]);
|
|
2963
2698
|
const handleInitiativeFilterChange = useCallback((initiativeId) => {
|
|
2964
2699
|
setSelectedPlanningInitiativeId(initiativeId);
|
|
2965
2700
|
setSelectedPlanningWorkstreamId('');
|
|
@@ -3196,10 +2931,12 @@ export function StandaloneLayout(props) {
|
|
|
3196
2931
|
}, title: "Workspace Settings", "aria-label": "Workspace Settings", children: _jsx(Settings, { size: 18 }) })] })] }));
|
|
3197
2932
|
}, [activeTab, handleModuleChange, handlePlanningToolToggle, planningDrawerOpen, resolvedWorkspaceModule, setSettingsSection, setActiveTab, workspaceToolModules]);
|
|
3198
2933
|
const planningDrawerPortalRef = useRef(null);
|
|
3199
|
-
const planningDrawerNode = (_jsx(PlanningDrawer, { open: planningDrawerOpen, leftOffset: WORKSPACE_TOOL_RAIL_WIDTH, initiatives: planningStructure.initiatives, standaloneWorkstreams: planningStructure.standaloneWorkstreams, activeInitiativeId: selectedPlanningInitiativeId, activeWorkstreamId: selectedPlanningWorkstreamId, expandedInitiativeIds: expandedPlanningInitiativeIds, detail: planningDrawerItem, editor: planningEditor, secondaryPane: planningSecondaryPane, assigneeOptions: assigneeOptions.map((option) => ({ value: String(option.value), label: option.label })), draftInitiativeSummary: planningDraftInitiativeSummary, draftTitle: planningDraftTitle, draftDescription: planningDraftDescription, draftOwner: planningDraftOwner, draftInitiativeId: planningDraftInitiativeId, onChangeDraftTitle: setPlanningDraftTitle, onChangeDraftDescription: setPlanningDraftDescription, onChangeDraftOwner: setPlanningDraftOwner, onChangeDraftInitiativeId: setPlanningDraftInitiativeId, onCollapseTreePane: () => setPlanningTreeCollapsed(true), onExpandTreePane: () => setPlanningTreeCollapsed(false), onCollapsePrimaryPane: () => setPlanningPrimaryCollapsed(true), onExpandPrimaryPane: () => setPlanningPrimaryCollapsed(false), onCollapseSecondaryPane: () => setPlanningSecondaryCollapsed(true), onExpandSecondaryPane: () => setPlanningSecondaryCollapsed(false), onBackFromSecondary: () => {
|
|
2934
|
+
const planningDrawerNode = (_jsx(PlanningDrawer, { open: planningDrawerOpen, leftOffset: WORKSPACE_TOOL_RAIL_WIDTH, initiatives: planningStructure.initiatives, standaloneWorkstreams: planningStructure.standaloneWorkstreams, activeInitiativeId: selectedPlanningInitiativeId, activeWorkstreamId: selectedPlanningWorkstreamId, expandedInitiativeIds: expandedPlanningInitiativeIds, detail: planningDrawerItem, editor: planningEditor, secondaryPane: planningSecondaryPane, currentWorkspaceId: currentWorkspaceId, assigneeOptions: assigneeOptions.map((option) => ({ value: String(option.value), label: option.label })), draftInitiativeSummary: planningDraftInitiativeSummary, draftTitle: planningDraftTitle, draftDescription: planningDraftDescription, draftOwner: planningDraftOwner, draftInitiativeId: planningDraftInitiativeId, onChangeDraftTitle: setPlanningDraftTitle, onChangeDraftDescription: setPlanningDraftDescription, onChangeDraftOwner: setPlanningDraftOwner, onChangeDraftInitiativeId: setPlanningDraftInitiativeId, onCollapseTreePane: () => setPlanningTreeCollapsed(true), onExpandTreePane: () => setPlanningTreeCollapsed(false), onCollapsePrimaryPane: () => setPlanningPrimaryCollapsed(true), onExpandPrimaryPane: () => setPlanningPrimaryCollapsed(false), onCollapseSecondaryPane: () => setPlanningSecondaryCollapsed(true), onExpandSecondaryPane: () => setPlanningSecondaryCollapsed(false), onBackFromSecondary: () => {
|
|
2935
|
+
planningEditorSubmitInFlightRef.current = false;
|
|
2936
|
+
setIsSubmittingPlanningEditor(false);
|
|
3200
2937
|
setPlanningSecondaryEditor(null);
|
|
3201
2938
|
setPlanningNestedWorkstreamDetailId(null);
|
|
3202
|
-
}, treeCollapsed: planningTreeCollapsed, primaryCollapsed: planningPrimaryCollapsed, secondaryCollapsed: planningSecondaryCollapsed, onCancelEditor: closePlanningEditor, onSubmitEditor: submitPlanningEditor, onCreateInitiative: () => openPlanningCreateEditor('initiative'), onCreateWorkstream: () => openPlanningCreateEditor('workstream'), onCreateTaskInWorkstream: openAddTaskModalForWorkstream, onCreateWorkstreamInInitiative: (initiativeId) => openPlanningCreateEditor('workstream', initiativeId), onAssignInitiativeToWorkstream: handleAssignInitiativeToWorkstream, onOpenTaskById: handleOpenTaskById, onAttachTaskToWorkstreamByReference: handleAttachTaskToWorkstreamByReference, onAttachWorkstreamToInitiativeByReference: handleAttachWorkstreamToInitiativeByReference, onToggleInitiative: togglePlanningInitiative, onSelectInitiative: selectPlanningInitiative, onSelectWorkstream: selectPlanningWorkstream, onOpenInitiativeDetails: openInitiativeDetails, onOpenWorkstreamDetails: openWorkstreamDetails, onOpenNestedWorkstreamDetails: openNestedWorkstreamDetails, onEditInitiative: (initiativeId) => openPlanningEditDialog('initiative', initiativeId), onEditWorkstream: (workstreamId) => openPlanningEditDialog('workstream', workstreamId), onArchiveInitiative: (initiativeId) => {
|
|
2939
|
+
}, treeCollapsed: planningTreeCollapsed, primaryCollapsed: planningPrimaryCollapsed, secondaryCollapsed: planningSecondaryCollapsed, onCancelEditor: closePlanningEditor, onSubmitEditor: submitPlanningEditor, isSubmittingEditor: isSubmittingPlanningEditor, onCreateInitiative: () => openPlanningCreateEditor('initiative'), onCreateWorkstream: () => openPlanningCreateEditor('workstream'), onCreateTaskInWorkstream: openAddTaskModalForWorkstream, onCreateWorkstreamInInitiative: (initiativeId) => openPlanningCreateEditor('workstream', initiativeId), onAssignInitiativeToWorkstream: handleAssignInitiativeToWorkstream, onOpenTaskById: handleOpenTaskById, onAttachTaskToWorkstreamByReference: handleAttachTaskToWorkstreamByReference, onAttachWorkstreamToInitiativeByReference: handleAttachWorkstreamToInitiativeByReference, onAddPlanningContextFile: handleAddPlanningContextFile, onRemovePlanningContextFile: handleRemovePlanningContextFile, onUpdatePlanningContextCaption: handleUpdatePlanningContextCaption, onToggleInitiative: togglePlanningInitiative, onSelectInitiative: selectPlanningInitiative, onSelectWorkstream: selectPlanningWorkstream, onOpenInitiativeDetails: openInitiativeDetails, onOpenWorkstreamDetails: openWorkstreamDetails, onOpenNestedWorkstreamDetails: openNestedWorkstreamDetails, onEditInitiative: (initiativeId) => openPlanningEditDialog('initiative', initiativeId), onEditWorkstream: (workstreamId) => openPlanningEditDialog('workstream', workstreamId), onArchiveInitiative: (initiativeId) => {
|
|
3203
2940
|
void props.archiveInitiative(initiativeId).then(() => {
|
|
3204
2941
|
pushNotice('Initiative archived', 'success');
|
|
3205
2942
|
}).catch((error) => {
|
|
@@ -3234,7 +2971,7 @@ export function StandaloneLayout(props) {
|
|
|
3234
2971
|
const workspacePrimaryAction = workspaceChromeDefinition.primaryAction
|
|
3235
2972
|
? primaryActionRegistry[workspaceChromeDefinition.primaryAction]
|
|
3236
2973
|
: null;
|
|
3237
|
-
return (_jsxs("div", { className: `${shellStyles.standaloneWrapper} ${zenMode ? styles.zenModeEnabled : ''}`, "data-theme": currentTheme, children: [_jsx(AppShellHeader, { projectName: projectName, currentWorkspaceId: currentWorkspaceId, runtimeMode: runtimeMode, theme: currentTheme, meta:
|
|
2974
|
+
return (_jsxs("div", { className: `${shellStyles.standaloneWrapper} ${zenMode ? styles.zenModeEnabled : ''}`, "data-theme": currentTheme, children: [_jsx(AppShellHeader, { projectName: projectName, currentWorkspaceId: currentWorkspaceId, runtimeMode: runtimeMode, theme: currentTheme, meta: (_jsxs(_Fragment, { children: [_jsx("span", { className: styles.taskCountBadge, title: activeCountTitle, children: activeCountLabel }), normalizedAuthUserId && normalizedAuthUserId !== 'anonymous' && (_jsxs(_Fragment, { children: [_jsxs("span", { className: styles.taskCountBadge, title: "Tasks assigned to me", children: [_jsx(User, { size: 13, className: styles.taskCountBadgeIcon, "aria-hidden": "true" }), assignedTaskCount] }), _jsxs("span", { className: `${styles.taskCountBadge} ${assignedOverdueTaskCount > 0 ? styles.taskCountBadgeAlert : ''}`.trim(), title: "Tasks overdue", children: [_jsx(ClockAlert, { size: 13, className: styles.taskCountBadgeIcon, "aria-hidden": "true" }), assignedOverdueTaskCount] })] })), shouldShowSyncStatus && (_jsx("button", { className: "tf-control-icon", onClick: openSyncStatusModal, title: `Sync manager: ${syncStatusMeta.label} | Last success: ${formattedLastSyncTime}`, style: {
|
|
3238
2975
|
marginLeft: '6px',
|
|
3239
2976
|
height: '24px',
|
|
3240
2977
|
width: '24px',
|
|
@@ -3246,10 +2983,25 @@ export function StandaloneLayout(props) {
|
|
|
3246
2983
|
display: 'inline-flex',
|
|
3247
2984
|
alignItems: 'center',
|
|
3248
2985
|
justifyContent: 'center'
|
|
3249
|
-
}, children: syncStatusMeta.icon === 'off' ? (_jsx(CloudOff, { size: 16 })) : (_jsx(Cloud, { size: 16 })) }))] })), actions: (_jsxs(_Fragment, { children: [isPlansScreen && (_jsx("button", { className: "tf-control-icon", onClick: () => { void
|
|
2986
|
+
}, children: syncStatusMeta.icon === 'off' ? (_jsx(CloudOff, { size: 16 })) : (_jsx(Cloud, { size: 16 })) }))] })), actions: (_jsxs(_Fragment, { children: [isPlansScreen && (_jsxs(_Fragment, { children: [isAuthenticated && accountProfileSummary?.stripeCustomerId && (_jsx("button", { className: "tf-control-icon", onClick: () => { void handleOpenBillingPortal(); }, title: "Manage billing", disabled: billingActionBusy, children: "Manage billing" })), _jsx("button", { className: "tf-control-icon", onClick: () => {
|
|
2987
|
+
if (hasActiveCommercialEntitlement) {
|
|
2988
|
+
void handlePlansBack();
|
|
2989
|
+
return;
|
|
2990
|
+
}
|
|
2991
|
+
closePlansScreen();
|
|
2992
|
+
}, title: hasActiveCommercialEntitlement ? 'Continue to Taskforce' : 'Back', disabled: plansNavigationBusy, children: hasActiveCommercialEntitlement ? 'Take Me to Taskforce' : 'Back' })] })), !isPlansScreen && (_jsxs(_Fragment, { children: [workspaceHeaderSections, workspacePrimaryAction] })), _jsx(WorkspaceHeaderActions, { actions: universalDisplayActions }), !isPlansScreen && _jsx(WorkspaceHeaderActions, { actions: universalFilterActions }), accountMenuControl] })) }), _jsx(TopNoticeLayer, { notice: uiNotice, onDismiss: clearNotice }), authBlocked && (_jsx("div", { className: authStyles.authBlockedBanner, children: "Authentication required for this environment. Use the Account menu to sign in." })), !isPlansScreen && showFilters && workspaceFilterBar, isPlansScreen ? (_jsx("div", { className: `${shellStyles.standalonePage} ${shellStyles.standaloneContent} ${styles.appScrollbar} tf-scrollbar`, style: {
|
|
2993
|
+
position: 'relative',
|
|
2994
|
+
display: 'flex',
|
|
2995
|
+
flex: 1,
|
|
2996
|
+
minHeight: 0,
|
|
2997
|
+
overflowY: 'auto',
|
|
2998
|
+
overflowX: 'hidden',
|
|
2999
|
+
scrollbarGutter: 'stable',
|
|
3000
|
+
background: 'var(--surface-page)'
|
|
3001
|
+
}, children: _jsx(Suspense, { fallback: _jsx("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'center', flex: 1 }, children: _jsx(Loader2, { size: 20, className: styles.spinner }) }), children: _jsx(PlansPage, { ...props, currentTheme: currentTheme, projectName: projectName, currentWorkspaceId: currentWorkspaceId, authUserId: authUserId, apiBaseUrl: apiBaseUrl, connectedEnvironmentSource: props.config?.cloudAuthBaseUrl || props.config?.apiBaseUrl || '', resolveCloudAuthUrl: resolveCloudAuthUrl, embedded: true, shellOwnsScroll: true, onAccountProfileSummaryChange: applyPlansAccountProfileSummary, onContinueToTaskforce: handlePlansBack, continueBusy: plansNavigationBusy }) }) })) : resolvedWorkspaceModule === 'docs' && documentsWorkspaceEnabled ? (_jsxs("div", { className: `${shellStyles.standalonePage} ${shellStyles.standaloneContent}`, style: { position: 'relative', display: 'flex', flex: 1, minHeight: 0, overflow: 'hidden', paddingLeft: `${WORKSPACE_TOOL_RAIL_WIDTH}px` }, children: [workspaceToolRail, _jsx(Suspense, { fallback: _jsx("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'center', flex: 1 }, children: _jsx(Loader2, { size: 20, className: styles.spinner }) }), children: _jsx(DocumentWorkspaceShell, { tasks: tasks, runtimeMode: runtimeMode, apiBaseUrl: props.config?.apiBaseUrl || '', cloudAuthBaseUrl: props.config?.cloudAuthBaseUrl || '', typeFilters: documentTypeFilters, attachmentFilters: documentAttachmentFilters, onTypeFiltersChange: setDocumentTypeFilters, onAttachmentFiltersChange: setDocumentAttachmentFilters, requestedDocPath: requestedDocPath, requestedDocAssetId: requestedDocAssetId, onRequestedDocHandled: () => {
|
|
3250
3002
|
setRequestedDocPath(null);
|
|
3251
3003
|
setRequestedDocAssetId(null);
|
|
3252
|
-
}, enableTaskGeneration: true }) })] })) : resolvedWorkspaceModule === 'annotate' && annotatedAttachmentsWorkspaceEnabled ? (_jsxs("div", { className: `${shellStyles.standalonePage} ${shellStyles.standaloneContent}`, style: { position: 'relative', display: 'flex', flex: 1, minHeight: 0, overflow: 'hidden', paddingLeft: `${WORKSPACE_TOOL_RAIL_WIDTH}px` }, children: [workspaceToolRail, _jsx(Suspense, { fallback: _jsx("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'center', flex: 1 }, children: _jsx(Loader2, { size: 20, className: styles.spinner }) }), children: _jsx(AnnotatedAttachmentWorkspaceShell, { runtimeMode: runtimeMode, apiBaseUrl: props.config?.apiBaseUrl || '', cloudAuthBaseUrl: props.config?.cloudAuthBaseUrl || '', workspaceId: currentWorkspaceId, sessionLoadReady: annotatedWorkspaceReady, requestedTarget: requestedAnnotatedTarget, requestedSessionId: requestedAnnotatedSessionId, requestedOpenVersion: requestedAnnotatedOpenVersion, resolveTaskReferenceLabel: resolveAnnotatedTaskReferenceLabel, resolveImageReferenceLabel: resolveAnnotatedImageReferenceLabel, onOpenTarget: handleAnnotatedAttachmentOpenTarget, onContextChange: handleAnnotatedAttachmentContextChange, onBackToTask: handleBackToTaskFromAnnotatedAttachment }) })] })) : resolvedWorkspaceModule === 'workflows' ? (_jsxs("div", { className: `${shellStyles.standalonePage} ${shellStyles.standaloneContent}`, style: { position: 'relative', display: 'flex', flex: 1, minHeight: 0, overflow: 'hidden', paddingLeft: `${WORKSPACE_TOOL_RAIL_WIDTH}px` }, children: [workspaceToolRail, _jsx(Suspense, { fallback: _jsx("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'center', flex: 1 }, children: _jsx(Loader2, { size: 20, className: styles.spinner }) }), children: _jsx(WorkflowsModule, { availableWorkflows: availableWorkflows, availableEnvironments: availableEnvironments, exportEnvironment: exportEnvironment, exportWorkflowsPath: props.exportWorkflowsPath, exportingResource: exportingResource, exportResult: exportResult, onExportEnvironmentChange: props.setExportEnvironment, onExportWorkflows: onExportWorkflows, onRefreshWorkflows: fetchWorkflows, onFetchWorkflowTemplate: props.fetchWorkflowTemplate, onFetchWorkflowOverrideNames: props.fetchWorkflowOverrideNames, onSaveWorkflowTemplateDraft: props.saveWorkflowTemplateDraft, onResetWorkflowTemplateDraft: props.resetWorkflowTemplateDraft }) })] })) : resolvedWorkspaceModule === 'agents' ? (_jsxs("div", { className: `${shellStyles.standalonePage} ${shellStyles.standaloneContent}`, style: { position: 'relative', display: 'flex', flex: 1, minHeight: 0, overflow: 'hidden', paddingLeft: `${WORKSPACE_TOOL_RAIL_WIDTH}px` }, children: [workspaceToolRail, _jsx(Suspense, { fallback: _jsx("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'center', flex: 1 }, children: _jsx(Loader2, { size: 20, className: styles.spinner }) }), children: _jsx(AgentsModule, { workspaceId: currentWorkspaceId }) })] })) : (_jsxs("div", { className: `${shellStyles.standalonePage} ${shellStyles.standaloneContent} `, style: {
|
|
3004
|
+
}, enableTaskGeneration: true }) })] })) : resolvedWorkspaceModule === 'annotate' && annotatedAttachmentsWorkspaceEnabled ? (_jsxs("div", { className: `${shellStyles.standalonePage} ${shellStyles.standaloneContent}`, style: { position: 'relative', display: 'flex', flex: 1, minHeight: 0, overflow: 'hidden', paddingLeft: `${WORKSPACE_TOOL_RAIL_WIDTH}px` }, children: [workspaceToolRail, _jsx(Suspense, { fallback: _jsx("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'center', flex: 1 }, children: _jsx(Loader2, { size: 20, className: styles.spinner }) }), children: _jsx(AnnotatedAttachmentWorkspaceShell, { runtimeMode: runtimeMode, apiBaseUrl: props.config?.apiBaseUrl || '', cloudAuthBaseUrl: props.config?.cloudAuthBaseUrl || '', workspaceId: currentWorkspaceId, sessionLoadReady: annotatedWorkspaceReady, requestedTarget: requestedAnnotatedTarget, requestedSessionId: requestedAnnotatedSessionId, requestedOpenVersion: requestedAnnotatedOpenVersion, resolveTaskReferenceLabel: resolveAnnotatedTaskReferenceLabel, resolveImageReferenceLabel: resolveAnnotatedImageReferenceLabel, onOpenTarget: handleAnnotatedAttachmentOpenTarget, onContextChange: handleAnnotatedAttachmentContextChange, onBackToTask: handleBackToTaskFromAnnotatedAttachment }) })] })) : resolvedWorkspaceModule === 'workflows' ? (_jsxs("div", { className: `${shellStyles.standalonePage} ${shellStyles.standaloneContent}`, style: { position: 'relative', display: 'flex', flex: 1, minHeight: 0, overflow: 'hidden', paddingLeft: `${WORKSPACE_TOOL_RAIL_WIDTH}px` }, children: [workspaceToolRail, _jsx(Suspense, { fallback: _jsx("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'center', flex: 1 }, children: _jsx(Loader2, { size: 20, className: styles.spinner }) }), children: _jsx(WorkflowsModule, { availableWorkflows: availableWorkflows, availableEnvironments: availableEnvironments, exportEnvironment: exportEnvironment, exportWorkflowsPath: props.exportWorkflowsPath, exportingResource: exportingResource, exportResult: exportResult, onExportEnvironmentChange: props.setExportEnvironment, onExportWorkflows: onExportWorkflows, onRefreshWorkflows: fetchWorkflows, onFetchWorkflowTemplate: props.fetchWorkflowTemplate, onFetchWorkflowOverrideNames: props.fetchWorkflowOverrideNames, onSaveWorkflowTemplateDraft: props.saveWorkflowTemplateDraft, onResetWorkflowTemplateDraft: props.resetWorkflowTemplateDraft }) })] })) : resolvedWorkspaceModule === 'agents' ? (_jsxs("div", { className: `${shellStyles.standalonePage} ${shellStyles.standaloneContent}`, style: { position: 'relative', display: 'flex', flex: 1, minHeight: 0, overflow: 'hidden', paddingLeft: `${WORKSPACE_TOOL_RAIL_WIDTH}px` }, children: [workspaceToolRail, _jsx(Suspense, { fallback: _jsx("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'center', flex: 1 }, children: _jsx(Loader2, { size: 20, className: styles.spinner }) }), children: _jsx(AgentsModule, { workspaceId: currentWorkspaceId, cloudAuthConfigured: cloudAuthConfigured, authSessionResolved: authSessionResolved, isAuthenticated: isAuthenticated, cloudAiProfileSeatUsage: accountProfileSummary?.aiProfileSeatUsage ?? null }) })] })) : (_jsxs("div", { className: `${shellStyles.standalonePage} ${shellStyles.standaloneContent} `, style: {
|
|
3253
3005
|
position: 'relative',
|
|
3254
3006
|
opacity: props.loadingTasks ? 0.7 : 1,
|
|
3255
3007
|
transition: 'opacity 0.2s ease, padding 220ms cubic-bezier(0.4, 0, 0.2, 1)',
|
|
@@ -3265,7 +3017,7 @@ export function StandaloneLayout(props) {
|
|
|
3265
3017
|
inset: 0,
|
|
3266
3018
|
zIndex: 35,
|
|
3267
3019
|
pointerEvents: 'none',
|
|
3268
|
-
} }), _jsxs("div", { style: { position: 'relative', width: '100%', minWidth: 0, flex: 1, minHeight: 0, display: 'flex', flexDirection: 'column', overflow: 'hidden' }, children: [props.loadingTasks && boardVisibleTasks.length > 0 && (_jsx("div", { className: styles.boardRefreshIndicator, "aria-live": "polite", "aria-label": "Refreshing tasks", children: _jsx(Loader2, { size: 14, className: styles.spinner }) })), _jsx(TaskKanban, { tasks: scopedBoardVisibleTasks, allTasks: scopedKanbanAllTasks, columns: kanbanColumns, groupBy: groupBy, scheduleDates: scheduleDates, searchQuery: searchQuery, filterCategories: filterCategories, filterTypes: filterTypes, filterPriorities: filterPriorities, filterStatus: filterStatus, filterAssignees: filterAssignees, assigneeOptions: assigneeOptions, scheduleFilteredTaskIds: Array.from(scheduleFilteredTaskIds), copiedId: copiedId, taxonomies: taxonomies, types: activeTypes, priorities: priorities, onUpdateTask: handleUpdateTask, onTaskClick: (task) => handleEdit(task), onOpenTaskById: handleOpenTaskById, onCopyId: handleCopyId, onToggleInProgress: handleToggleInProgress, onToggleReview: handleToggleReview, onToggleComplete: handleToggleComplete, onToggleCancel: handleToggleCancel, onSetStatus: handleSetStatus, onArchiveTask: handleArchiveTask, onAddTaskToColumn: handleAddTaskToColumn, showTaskCardStatusLabel: showTaskCardStatusLabel, onScheduleDaySelected: (dateOnly) => {
|
|
3020
|
+
} }), _jsxs("div", { style: { position: 'relative', width: '100%', minWidth: 0, flex: 1, minHeight: 0, display: 'flex', flexDirection: 'column', overflow: 'hidden' }, children: [props.loadingTasks && boardVisibleTasks.length > 0 && (_jsx("div", { className: styles.boardRefreshIndicator, "aria-live": "polite", "aria-label": "Refreshing tasks", children: _jsx(Loader2, { size: 14, className: styles.spinner }) })), _jsx(TaskKanban, { tasks: scopedBoardVisibleTasks, allTasks: scopedKanbanAllTasks, columns: kanbanColumns, groupBy: groupBy, scheduleDates: scheduleDates, searchQuery: searchQuery, filterCategories: filterCategories, filterTypes: filterTypes, filterPriorities: filterPriorities, filterStatus: filterStatus, filterAssignees: filterAssignees, filtersReady: hasInitedFilters, assigneeOptions: assigneeOptions, scheduleFilteredTaskIds: Array.from(scheduleFilteredTaskIds), copiedId: copiedId, taxonomies: taxonomies, types: activeTypes, priorities: priorities, onUpdateTask: handleUpdateTask, onTaskClick: (task) => handleEdit(task), onOpenTaskById: handleOpenTaskById, onCopyId: handleCopyId, onToggleInProgress: handleToggleInProgress, onToggleReview: handleToggleReview, onToggleComplete: handleToggleComplete, onToggleCancel: handleToggleCancel, onSetStatus: handleSetStatus, onArchiveTask: handleArchiveTask, onAddTaskToColumn: handleAddTaskToColumn, showTaskCardStatusLabel: showTaskCardStatusLabel, onScheduleDaySelected: (dateOnly) => {
|
|
3269
3021
|
setScheduleSelectedDate(dateOnly);
|
|
3270
3022
|
const parsed = parseDateOnlyLocal(dateOnly);
|
|
3271
3023
|
if (parsed) {
|
|
@@ -3376,11 +3128,15 @@ export function StandaloneLayout(props) {
|
|
|
3376
3128
|
return;
|
|
3377
3129
|
}
|
|
3378
3130
|
void handleUnarchive(taskId);
|
|
3379
|
-
}, currentTask: currentTask })] }), _jsx(Modal, { isOpen: activeTab === 'settings', onClose: handleCloseModal, title: "Settings", size: "xl", theme: currentTheme, draggable: true, isSettings: true, closeOnOverlayClick: false, children: _jsx(Suspense, { fallback: _jsx("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '2rem' }, children: _jsx(Loader2, { size: 20, className: styles.spinner }) }), children: _jsx(TaskSettings, { settingsModel: settingsModel, onSectionChange: setSettingsSection }) }) }), _jsx(ScheduleWarningModal, { prompt: scheduleWarningPrompt, theme: currentTheme, onClose: cancelScheduleWarning, onConfirm: confirmScheduleWarning }), _jsx(HelpModal, { isOpen: showHelpModal, theme: currentTheme, onClose: () => setShowHelpModal(false), onOpenSettings: () => {
|
|
3131
|
+
}, currentTask: currentTask })] }), _jsx(Modal, { isOpen: activeTab === 'settings', onClose: handleCloseModal, title: "Settings", size: "xl", theme: currentTheme, draggable: true, isSettings: true, closeOnOverlayClick: false, children: _jsx(Suspense, { fallback: _jsx("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'center', padding: '2rem' }, children: _jsx(Loader2, { size: 20, className: styles.spinner }) }), children: _jsx(TaskSettings, { settingsModel: settingsModel, onSectionChange: setSettingsSection }) }) }), _jsx(ScheduleWarningModal, { prompt: scheduleWarningPrompt, theme: currentTheme, onClose: cancelScheduleWarning, onConfirm: confirmScheduleWarning }), _jsx(SyncEnableWarningModal, { isOpen: showSyncEnableWarning, theme: currentTheme, onClose: cancelSyncEnableWarning, onConfirm: confirmSyncEnableWarning }), _jsx(HelpModal, { isOpen: showHelpModal, theme: currentTheme, onClose: () => setShowHelpModal(false), onOpenSettings: () => {
|
|
3380
3132
|
setShowHelpModal(false);
|
|
3381
3133
|
setActiveTab('settings');
|
|
3382
3134
|
setSettingsSection('general');
|
|
3383
|
-
} }), _jsx(EditProfileModal, { isOpen: showEditProfileModal, theme: currentTheme, onClose: closeEditProfileModal, onSave: () => { void handleSaveProfile(); }, displayName: profileDisplayNameDraft, email: authEmailLabel, avatarDisplayUrl: profileAvatarDisplayUrl, accountBadgeInitial: accountBadgeInitial,
|
|
3135
|
+
} }), _jsx(EditProfileModal, { isOpen: showEditProfileModal, theme: currentTheme, onClose: closeEditProfileModal, onSave: () => { void handleSaveProfile(); }, displayName: profileDisplayNameDraft, email: authEmailLabel, avatarDisplayUrl: profileAvatarDisplayUrl, accountBadgeInitial: accountBadgeInitial, saveBusy: profileSaveBusy, avatarBusy: profileAvatarBusy, saveError: profileSaveError, saveNotice: profileSaveNotice, onDisplayNameChange: setProfileDisplayNameDraft, onOpenAvatarManager: () => {
|
|
3136
|
+
setProfileSaveError(null);
|
|
3137
|
+
setProfileSaveNotice(null);
|
|
3138
|
+
setShowAvatarPhotoManager(true);
|
|
3139
|
+
} }), _jsx(AvatarImageManagerModal, { isOpen: showAvatarPhotoManager, theme: currentTheme, title: "Edit Profile Photo", currentImageUrl: profileAvatarDisplayUrl, fallbackInitial: accountBadgeInitial, accept: PROFILE_AVATAR_ACCEPT, busy: profileAvatarBusy, hasPendingImage: Boolean(profileAvatarDraftId), canRemove: Boolean(profileAvatarDisplayUrl), error: profileSaveError, notice: profileSaveNotice, onClose: () => setShowAvatarPhotoManager(false), onApplyImage: handleProfileAvatarSelected, onRemoveImage: handleRemoveProfileAvatar, onDiscardPendingImage: handleDiscardPendingProfileAvatar }), _jsx(AccountHubModal, { isOpen: showAccountHub, theme: currentTheme, runtimeMode: runtimeMode, authRequiredForApi: authRequiredForApi, isAuthenticated: isAuthenticated, hasAuthIdentity: hasAuthIdentity, authIdentityLabel: authIdentityLabel, billingLoading: billingLoading, billingError: billingError, billingActionError: billingActionError, billingNotice: billingNotice, billingActionBusy: billingActionBusy, billingIntervalChoice: billingIntervalChoice, accountProfileSummary: accountProfileSummary, currentWorkspaceId: currentWorkspaceId, canOpenTeamManagement: canOpenTeamManagement, canManageWorkspaceSync: canManageWorkspaceSync, workspaceCloudSyncEnabled: workspaceCloudSyncEnabled, syncStatusLabel: syncStatusMeta.label, workspaceSyncError: workspaceSyncError, syncControlBusy: syncControlBusy, cloudAuthEnabled: cloudAuthConfigured, availableAuthProviders: availableAuthProviders, onFetchLoginMethods: fetchLoginMethods, onUnlinkLoginMethod: unlinkLoginMethod, onAddPassword: addPasswordToAccount, onLinkProvider: (provider) => beginOAuthLink(provider), onClose: () => setShowAccountHub(false), onOpenWorkspaceAudit: () => {
|
|
3384
3140
|
setShowAccountHub(false);
|
|
3385
3141
|
void openTeamManagementModal();
|
|
3386
3142
|
setTeamManagementTab('audit');
|
|
@@ -3436,7 +3192,7 @@ export function StandaloneLayout(props) {
|
|
|
3436
3192
|
headers: teamAdminHeaders(),
|
|
3437
3193
|
credentials: 'include'
|
|
3438
3194
|
}));
|
|
3439
|
-
}, onInviteEmailChange: setTeamInviteEmail, onInviteRoleChange: setTeamInviteRole, onInvitePermissionModeChange: setTeamInvitePermissionMode, onSubmitInvite: () => { void submitTeamInvite(); }, onLoadAuditPrevious: () => { void loadTeamAuditPage(teamAuditPage - 1); }, onLoadAuditNext: () => { void loadTeamAuditPage(teamAuditPage + 1); } }), _jsx(CreateWorkspaceConfirmModal, { isOpen: showCreateWorkspaceConfirm, theme: currentTheme, onClose: () => setShowCreateWorkspaceConfirm(false), onConfirm: () => openWorkspaceSetup({ intent: 'create-workspace' }) }), _jsx(SyncStatusModal, { isOpen: showSyncStatusModal, theme: currentTheme, currentWorkspaceLabel: currentWorkspaceLabel, syncStatusMeta: syncStatusMeta, workspaceCloudSyncEnabled: workspaceCloudSyncEnabled, syncControlBusy: syncControlBusy, canManageWorkspaceSync: canManageWorkspaceSync, workspaceSyncSummary:
|
|
3195
|
+
}, onInviteEmailChange: setTeamInviteEmail, onInviteRoleChange: setTeamInviteRole, onInvitePermissionModeChange: setTeamInvitePermissionMode, onSubmitInvite: () => { void submitTeamInvite(); }, onLoadAuditPrevious: () => { void loadTeamAuditPage(teamAuditPage - 1); }, onLoadAuditNext: () => { void loadTeamAuditPage(teamAuditPage + 1); } }), _jsx(CreateWorkspaceConfirmModal, { isOpen: showCreateWorkspaceConfirm, theme: currentTheme, onClose: () => setShowCreateWorkspaceConfirm(false), onConfirm: () => openWorkspaceSetup({ intent: 'create-workspace' }) }), _jsx(SyncStatusModal, { isOpen: showSyncStatusModal, theme: currentTheme, currentWorkspaceLabel: currentWorkspaceLabel, syncStatusMeta: syncStatusMeta, workspaceCloudSyncEnabled: workspaceCloudSyncEnabled, syncControlBusy: syncControlBusy, canManageWorkspaceSync: canManageWorkspaceSync, workspaceSyncSummary: headerSyncPresentation.summary, workspaceSyncRecommendedAction: headerSyncPresentation.recommendedAction, workspaceSyncRepairBusy: workspaceSyncRepairBusy, referenceMismatchCount: referenceMismatchCount, syncStageLabel: syncStageLabel, workspaceSyncPendingChanges: workspaceSyncPendingChanges, formattedLastSyncTime: formattedLastSyncTime, formattedLastPullTime: formattedLastPullTime, formattedLastPushTime: formattedLastPushTime, syncLastError: syncLastError, workspaceSyncDiagnostics: workspaceSyncDiagnostics, activeReferenceMismatchSummaries: activeReferenceMismatchSummaries, syncDiagnosticsSummary: syncDiagnosticsSummary, syncEventRows: syncEventRows, syncEventsListRef: syncEventsListRef, workspaceSyncRepairQueued: workspaceSyncRepairQueued, workspaceSyncBusy: workspaceSyncBusy, workspaceSyncCopied: workspaceSyncCopied, runtimeMode: runtimeMode, isAuthenticated: isAuthenticated, onClose: closeSyncStatusModal, onToggleWorkspaceSync: (enabled) => {
|
|
3440
3196
|
void handleWorkspaceSyncToggle(enabled);
|
|
3441
3197
|
}, onRepairSync: handleQueueOrRunRepairSync, onCopyReport: () => { void handleCopySyncDetails(); }, onOpenLogin: () => {
|
|
3442
3198
|
openSharedLoginRoute('login');
|