@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
|
@@ -53,6 +53,7 @@ interface TaskKanbanProps {
|
|
|
53
53
|
filterPriorities?: number[];
|
|
54
54
|
filterStatus?: string[];
|
|
55
55
|
filterAssignees?: TaskAssignee[];
|
|
56
|
+
filtersReady?: boolean;
|
|
56
57
|
scheduleFilteredTaskIds?: string[];
|
|
57
58
|
categories?: CategoryConfig[];
|
|
58
59
|
compressed?: boolean;
|
|
@@ -71,5 +72,5 @@ interface TaskKanbanProps {
|
|
|
71
72
|
workstreams?: WorkstreamItem[];
|
|
72
73
|
initiatives?: InitiativeItem[];
|
|
73
74
|
}
|
|
74
|
-
export declare function TaskKanban({ tasks, columns: kanbanColumns, groupBy, searchQuery, copiedId, taxonomies, types, priorities, approaches, assigneeOptions, onUpdateTask, onTaskClick, onOpenTaskById, onCopyId, onToggleInProgress, onToggleReview, onToggleComplete, onToggleCancel, onSetStatus, onArchiveTask, onUnarchive, onDelete, allTasks, onAddTaskToColumn, emptyColumnMode, filterCategories, filterTypes, filterPriorities, filterStatus, filterAssignees, scheduleFilteredTaskIds, categories, compressed, readOnlyMode, recentlyChangedTaskIds, scheduleDates, onScheduleDaySelected, persistedScrollLeft, onScrollLeftChange, sortBy, sortOrder, planningDropTargets, onAssignTaskToWorkstream, onAssignWorkstreamToInitiative, showTaskCardStatusLabel, workstreams, initiatives, }: TaskKanbanProps): import("react/jsx-runtime").JSX.Element;
|
|
75
|
+
export declare function TaskKanban({ tasks, columns: kanbanColumns, groupBy, searchQuery, copiedId, taxonomies, types, priorities, approaches, assigneeOptions, onUpdateTask, onTaskClick, onOpenTaskById, onCopyId, onToggleInProgress, onToggleReview, onToggleComplete, onToggleCancel, onSetStatus, onArchiveTask, onUnarchive, onDelete, allTasks, onAddTaskToColumn, emptyColumnMode, filterCategories, filterTypes, filterPriorities, filterStatus, filterAssignees, filtersReady, scheduleFilteredTaskIds, categories, compressed, readOnlyMode, recentlyChangedTaskIds, scheduleDates, onScheduleDaySelected, persistedScrollLeft, onScrollLeftChange, sortBy, sortOrder, planningDropTargets, onAssignTaskToWorkstream, onAssignWorkstreamToInitiative, showTaskCardStatusLabel, workstreams, initiatives, }: TaskKanbanProps): import("react/jsx-runtime").JSX.Element;
|
|
75
76
|
export {};
|
|
@@ -11,7 +11,7 @@ import { buildScheduleDropResult, buildStatusTransitionUpdates, getGroupedTaskSt
|
|
|
11
11
|
import * as Icons from 'lucide-react';
|
|
12
12
|
import { IconMap, resolveColor } from '../../utils/constants';
|
|
13
13
|
const EXPIRED_CARD_ID_PREFIX = 'expired::';
|
|
14
|
-
export function TaskKanban({ tasks, columns: kanbanColumns, groupBy, searchQuery, copiedId, taxonomies, types, priorities, approaches, assigneeOptions = [], onUpdateTask, onTaskClick, onOpenTaskById, onCopyId, onToggleInProgress, onToggleReview, onToggleComplete, onToggleCancel, onSetStatus, onArchiveTask, onUnarchive, onDelete, allTasks = [], onAddTaskToColumn, emptyColumnMode = 'show', filterCategories = [], filterTypes = [], filterPriorities = [], filterStatus = [], filterAssignees = [], scheduleFilteredTaskIds, categories = [], compressed = false, readOnlyMode = null, recentlyChangedTaskIds = [], scheduleDates, onScheduleDaySelected, persistedScrollLeft, onScrollLeftChange, sortBy = 'created', sortOrder = 'desc', planningDropTargets = null, onAssignTaskToWorkstream, onAssignWorkstreamToInitiative, showTaskCardStatusLabel = true, workstreams = [], initiatives = [], }) {
|
|
14
|
+
export function TaskKanban({ tasks, columns: kanbanColumns, groupBy, searchQuery, copiedId, taxonomies, types, priorities, approaches, assigneeOptions = [], onUpdateTask, onTaskClick, onOpenTaskById, onCopyId, onToggleInProgress, onToggleReview, onToggleComplete, onToggleCancel, onSetStatus, onArchiveTask, onUnarchive, onDelete, allTasks = [], onAddTaskToColumn, emptyColumnMode = 'show', filterCategories = [], filterTypes = [], filterPriorities = [], filterStatus = [], filterAssignees = [], filtersReady = true, scheduleFilteredTaskIds, categories = [], compressed = false, readOnlyMode = null, recentlyChangedTaskIds = [], scheduleDates, onScheduleDaySelected, persistedScrollLeft, onScrollLeftChange, sortBy = 'created', sortOrder = 'desc', planningDropTargets = null, onAssignTaskToWorkstream, onAssignWorkstreamToInitiative, showTaskCardStatusLabel = true, workstreams = [], initiatives = [], }) {
|
|
15
15
|
const [activeId, setActiveId] = useState(null);
|
|
16
16
|
const [isHeaderPanning, setIsHeaderPanning] = useState(false);
|
|
17
17
|
const [headerPanEnabled, setHeaderPanEnabled] = useState(false);
|
|
@@ -53,7 +53,8 @@ export function TaskKanban({ tasks, columns: kanbanColumns, groupBy, searchQuery
|
|
|
53
53
|
// Sensors
|
|
54
54
|
const sensors = useSensors(useSensor(PointerSensor, {
|
|
55
55
|
activationConstraint: {
|
|
56
|
-
|
|
56
|
+
// Give normal clicks a bit more tolerance so slight trackpad drift still opens the task.
|
|
57
|
+
distance: 10,
|
|
57
58
|
},
|
|
58
59
|
}), useSensor(KeyboardSensor, {
|
|
59
60
|
coordinateGetter: sortableKeyboardCoordinates,
|
|
@@ -129,20 +130,99 @@ export function TaskKanban({ tasks, columns: kanbanColumns, groupBy, searchQuery
|
|
|
129
130
|
]);
|
|
130
131
|
return dateToColumn.get(scheduledDate) || '__offweek';
|
|
131
132
|
};
|
|
133
|
+
const inferredKanbanColumns = useMemo(() => {
|
|
134
|
+
if (groupBy === 'schedule')
|
|
135
|
+
return [];
|
|
136
|
+
const existingValues = new Set(kanbanColumns.map((column) => String(column.value)));
|
|
137
|
+
const existingLabels = new Set(kanbanColumns.map((column) => column.label));
|
|
138
|
+
const inferred = [];
|
|
139
|
+
const buildColumn = (task) => {
|
|
140
|
+
const rawValue = groupBy === 'status'
|
|
141
|
+
? getTaskStatus(task)
|
|
142
|
+
: task[groupBy];
|
|
143
|
+
const value = String(rawValue ?? '');
|
|
144
|
+
if (!value || existingValues.has(value) || existingLabels.has(value))
|
|
145
|
+
return null;
|
|
146
|
+
if (groupBy === 'category') {
|
|
147
|
+
const category = categories.find((entry) => entry.value === value || entry.label === value);
|
|
148
|
+
return {
|
|
149
|
+
value,
|
|
150
|
+
label: category?.label || value,
|
|
151
|
+
icon: category?.icon || 'Folder',
|
|
152
|
+
color: category?.color,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
if (groupBy === 'type') {
|
|
156
|
+
const type = types?.find((entry) => entry.value === value || entry.label === value);
|
|
157
|
+
return {
|
|
158
|
+
value,
|
|
159
|
+
label: type?.label || value,
|
|
160
|
+
icon: type?.icon || 'CheckSquare',
|
|
161
|
+
color: type?.color,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
if (groupBy === 'priority') {
|
|
165
|
+
const priority = priorities?.find((entry) => Number(entry.value) === Number(value));
|
|
166
|
+
return {
|
|
167
|
+
value: Number.isFinite(Number(value)) ? Number(value) : value,
|
|
168
|
+
label: priority?.label || value,
|
|
169
|
+
icon: priority?.icon || 'Flag',
|
|
170
|
+
color: priority?.color,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
if (groupBy === 'complexity') {
|
|
174
|
+
return {
|
|
175
|
+
value: Number.isFinite(Number(value)) ? Number(value) : value,
|
|
176
|
+
label: value,
|
|
177
|
+
icon: 'Gauge',
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
if (groupBy === 'status') {
|
|
181
|
+
return {
|
|
182
|
+
value,
|
|
183
|
+
label: value,
|
|
184
|
+
icon: 'Circle',
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
if (groupBy === 'assignee') {
|
|
188
|
+
const option = assigneeOptions.find((entry) => entry.value === value);
|
|
189
|
+
return {
|
|
190
|
+
value,
|
|
191
|
+
label: option?.label || value,
|
|
192
|
+
icon: option?.icon === 'HelpCircle' ? 'Circle' : option?.icon,
|
|
193
|
+
color: option?.color,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
return {
|
|
197
|
+
value,
|
|
198
|
+
label: value,
|
|
199
|
+
};
|
|
200
|
+
};
|
|
201
|
+
for (const task of tasks) {
|
|
202
|
+
const column = buildColumn(task);
|
|
203
|
+
if (!column)
|
|
204
|
+
continue;
|
|
205
|
+
inferred.push(column);
|
|
206
|
+
existingValues.add(String(column.value));
|
|
207
|
+
existingLabels.add(column.label);
|
|
208
|
+
}
|
|
209
|
+
return inferred;
|
|
210
|
+
}, [assigneeOptions, categories, groupBy, kanbanColumns, priorities, tasks, types]);
|
|
211
|
+
const effectiveKanbanColumns = useMemo(() => [...kanbanColumns, ...inferredKanbanColumns], [inferredKanbanColumns, kanbanColumns]);
|
|
132
212
|
const columnLookup = useMemo(() => {
|
|
133
213
|
const map = new Map();
|
|
134
|
-
for (const column of
|
|
214
|
+
for (const column of effectiveKanbanColumns) {
|
|
135
215
|
map.set(String(column.value), column);
|
|
136
216
|
if (!map.has(column.label)) {
|
|
137
217
|
map.set(column.label, column);
|
|
138
218
|
}
|
|
139
219
|
}
|
|
140
220
|
return map;
|
|
141
|
-
}, [
|
|
221
|
+
}, [effectiveKanbanColumns]);
|
|
142
222
|
// Totals for "X / Y" display
|
|
143
223
|
const columnTotals = useMemo(() => {
|
|
144
224
|
const counts = {};
|
|
145
|
-
|
|
225
|
+
effectiveKanbanColumns.forEach(col => { counts[String(col.value)] = 0; });
|
|
146
226
|
const totalsSource = allTasks.length > 0 ? allTasks : tasks;
|
|
147
227
|
totalsSource.forEach(task => {
|
|
148
228
|
let stringValue = '';
|
|
@@ -169,12 +249,12 @@ export function TaskKanban({ tasks, columns: kanbanColumns, groupBy, searchQuery
|
|
|
169
249
|
}
|
|
170
250
|
});
|
|
171
251
|
return counts;
|
|
172
|
-
}, [allTasks, tasks,
|
|
252
|
+
}, [allTasks, tasks, effectiveKanbanColumns, groupBy, columnLookup, todayDateOnly]);
|
|
173
253
|
// Group items by the groupBy field
|
|
174
254
|
const groupedTasks = useMemo(() => {
|
|
175
255
|
const groups = {};
|
|
176
256
|
// Initialize all columns
|
|
177
|
-
|
|
257
|
+
effectiveKanbanColumns.forEach(col => {
|
|
178
258
|
groups[String(col.value)] = [];
|
|
179
259
|
});
|
|
180
260
|
// Distribute tasks
|
|
@@ -231,7 +311,7 @@ export function TaskKanban({ tasks, columns: kanbanColumns, groupBy, searchQuery
|
|
|
231
311
|
});
|
|
232
312
|
}
|
|
233
313
|
return groups;
|
|
234
|
-
}, [tasks, allTasks,
|
|
314
|
+
}, [tasks, allTasks, effectiveKanbanColumns, groupBy, columnLookup, scheduleFilteredTaskIdSet, todayDateOnly, sortBy, sortOrder]);
|
|
235
315
|
const activeTaskId = activeId ? getTaskIdFromCardId(activeId) : null;
|
|
236
316
|
const activeTaskOverlay = activeTaskId ? tasks.find(t => t.id === activeTaskId) : null;
|
|
237
317
|
// Scroll Syncing Refs and Logic
|
|
@@ -247,6 +327,8 @@ export function TaskKanban({ tasks, columns: kanbanColumns, groupBy, searchQuery
|
|
|
247
327
|
const lastReportedScrollLeftRef = useRef(-1);
|
|
248
328
|
// Calculate total width for scroll syncing
|
|
249
329
|
const getIsColumnHidden = (columnValue, title) => {
|
|
330
|
+
if (!filtersReady)
|
|
331
|
+
return false;
|
|
250
332
|
if (groupBy === 'category')
|
|
251
333
|
return !filterCategories.includes(String(columnValue));
|
|
252
334
|
if (groupBy === 'type')
|
|
@@ -268,7 +350,7 @@ export function TaskKanban({ tasks, columns: kanbanColumns, groupBy, searchQuery
|
|
|
268
350
|
const COL_WIDTH = 320;
|
|
269
351
|
const COL_COLLAPSED = 72;
|
|
270
352
|
let width = 0;
|
|
271
|
-
|
|
353
|
+
effectiveKanbanColumns.forEach((col, index) => {
|
|
272
354
|
const items = groupedTasks[String(col.value)] || [];
|
|
273
355
|
const isExplicitlyHidden = getIsColumnHidden(col.value, col.label);
|
|
274
356
|
const isAutoHidden = groupBy !== 'schedule' && emptyColumnMode === 'hide' && items.length === 0;
|
|
@@ -279,7 +361,7 @@ export function TaskKanban({ tasks, columns: kanbanColumns, groupBy, searchQuery
|
|
|
279
361
|
width += GAP;
|
|
280
362
|
});
|
|
281
363
|
return width;
|
|
282
|
-
}, [
|
|
364
|
+
}, [effectiveKanbanColumns, groupedTasks, emptyColumnMode, groupBy, filterCategories, filterTypes, filterPriorities, filterStatus, filterAssignees, filtersReady]);
|
|
283
365
|
const emitScrollLeft = useCallback((scrollLeft) => {
|
|
284
366
|
if (!onScrollLeftChange)
|
|
285
367
|
return;
|
|
@@ -352,7 +434,7 @@ export function TaskKanban({ tasks, columns: kanbanColumns, groupBy, searchQuery
|
|
|
352
434
|
return () => {
|
|
353
435
|
window.removeEventListener('resize', updatePanAvailability);
|
|
354
436
|
};
|
|
355
|
-
}, [totalWidth,
|
|
437
|
+
}, [totalWidth, effectiveKanbanColumns.length, groupedTasks]);
|
|
356
438
|
useEffect(() => {
|
|
357
439
|
const handlePointerMove = (event) => {
|
|
358
440
|
const pan = panStateRef.current;
|
|
@@ -473,7 +555,7 @@ export function TaskKanban({ tasks, columns: kanbanColumns, groupBy, searchQuery
|
|
|
473
555
|
return;
|
|
474
556
|
// Find the column definition
|
|
475
557
|
const column = columnLookup.get(containerId)
|
|
476
|
-
||
|
|
558
|
+
|| effectiveKanbanColumns.find(c => String(c.value) === containerId || c.label === containerId);
|
|
477
559
|
if (column) {
|
|
478
560
|
if (groupBy === 'schedule') {
|
|
479
561
|
const targetColumn = String(column.value);
|
|
@@ -680,12 +762,12 @@ export function TaskKanban({ tasks, columns: kanbanColumns, groupBy, searchQuery
|
|
|
680
762
|
return (_jsxs(DndContext, { sensors: sensors, collisionDetection: customCollisionDetection, onDragStart: handleDragStart, onDragOver: handleDragOver, onDragEnd: handleDragEnd, onDragCancel: (event) => {
|
|
681
763
|
lastPlanningOverRef.current = null;
|
|
682
764
|
setActiveId(null);
|
|
683
|
-
}, children: [planningDropTargets, _jsxs("div", { className: styles.kanbanWrapper, children: [_jsx("div", { ref: topScrollRef, className: `${styles.kanbanTopScroll} tf-scrollbar`, children: _jsx("div", { className: styles.kanbanTopScrollSpacer, style: { width: `${totalWidth}px` } }) }), _jsx("div", { className: styles.kanbanContainer, ref: containerRef, children:
|
|
765
|
+
}, children: [planningDropTargets, _jsxs("div", { className: styles.kanbanWrapper, children: [_jsx("div", { ref: topScrollRef, className: `${styles.kanbanTopScroll} tf-scrollbar`, children: _jsx("div", { className: styles.kanbanTopScrollSpacer, style: { width: `${totalWidth}px` } }) }), _jsx("div", { className: styles.kanbanContainer, ref: containerRef, children: effectiveKanbanColumns.map((column) => {
|
|
684
766
|
const columnTasks = groupedTasks[String(column.value)] || [];
|
|
685
767
|
const isExplicitlyHidden = getIsColumnHidden(column.value, column.label);
|
|
686
768
|
const isAutoHidden = groupBy !== 'schedule' && emptyColumnMode === 'hide' && columnTasks.length === 0;
|
|
687
769
|
const isScheduleMode = groupBy === 'schedule';
|
|
688
|
-
const hasBacklogColumn = isScheduleMode &&
|
|
770
|
+
const hasBacklogColumn = isScheduleMode && effectiveKanbanColumns.some((entry) => String(entry.value) === 'backlog');
|
|
689
771
|
const isBacklog = isScheduleMode && String(column.value) === 'backlog';
|
|
690
772
|
const isExpired = isScheduleMode && String(column.value) === 'expired';
|
|
691
773
|
const backlogTasks = groupedTasks.backlog || [];
|
|
@@ -61,3 +61,39 @@ describe('TaskKanban schedule expired behavior', () => {
|
|
|
61
61
|
expect(screen.getByText('1 / 2')).toBeInTheDocument();
|
|
62
62
|
});
|
|
63
63
|
});
|
|
64
|
+
describe('TaskKanban filter bootstrap behavior', () => {
|
|
65
|
+
it('keeps task columns visible while filters are still initializing', () => {
|
|
66
|
+
const task = makeTask({
|
|
67
|
+
id: 'task-filter-bootstrap',
|
|
68
|
+
title: 'Filter bootstrap visible task',
|
|
69
|
+
category: 'shared'
|
|
70
|
+
});
|
|
71
|
+
render(_jsx(TaskKanban, { tasks: [task], allTasks: [task], columns: [
|
|
72
|
+
{ value: 'shared', label: 'Shared', color: '#3b82f6', icon: 'Folder' }
|
|
73
|
+
], groupBy: "category", filterCategories: [], filtersReady: false, onUpdateTask: vi.fn(), onTaskClick: vi.fn() }));
|
|
74
|
+
expect(screen.getByText('Shared')).toBeInTheDocument();
|
|
75
|
+
expect(screen.getByText('Filter bootstrap visible task')).toBeInTheDocument();
|
|
76
|
+
});
|
|
77
|
+
it('renders a discoverable task when category columns are not loaded yet', () => {
|
|
78
|
+
const task = makeTask({
|
|
79
|
+
id: 'task-missing-category-columns',
|
|
80
|
+
title: 'Visible before categories load',
|
|
81
|
+
category: 'default'
|
|
82
|
+
});
|
|
83
|
+
render(_jsx(TaskKanban, { tasks: [task], allTasks: [task], columns: [], groupBy: "category", filterCategories: [], filtersReady: false, onUpdateTask: vi.fn(), onTaskClick: vi.fn() }));
|
|
84
|
+
expect(screen.getByText('default')).toBeInTheDocument();
|
|
85
|
+
expect(screen.getByText('Visible before categories load')).toBeInTheDocument();
|
|
86
|
+
});
|
|
87
|
+
it('hides filtered-out columns after filters initialize', () => {
|
|
88
|
+
const task = makeTask({
|
|
89
|
+
id: 'task-filter-ready',
|
|
90
|
+
title: 'Filter ready hidden task',
|
|
91
|
+
category: 'shared'
|
|
92
|
+
});
|
|
93
|
+
render(_jsx(TaskKanban, { tasks: [task], allTasks: [task], columns: [
|
|
94
|
+
{ value: 'shared', label: 'Shared', color: '#3b82f6', icon: 'Folder' }
|
|
95
|
+
], groupBy: "category", filterCategories: [], filtersReady: true, onUpdateTask: vi.fn(), onTaskClick: vi.fn() }));
|
|
96
|
+
expect(screen.queryByText('Shared')).not.toBeInTheDocument();
|
|
97
|
+
expect(screen.queryByText('Filter ready hidden task')).not.toBeInTheDocument();
|
|
98
|
+
});
|
|
99
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
interface EditableAvatarButtonProps {
|
|
3
|
+
label: string;
|
|
4
|
+
imageUrl?: string | null;
|
|
5
|
+
fallback?: React.ReactNode;
|
|
6
|
+
accentColor?: string | null;
|
|
7
|
+
size?: number;
|
|
8
|
+
width?: number;
|
|
9
|
+
height?: number;
|
|
10
|
+
radius?: number | string;
|
|
11
|
+
editBadgeSize?: number | string;
|
|
12
|
+
editIconSize?: number;
|
|
13
|
+
disabled?: boolean;
|
|
14
|
+
className?: string;
|
|
15
|
+
onClick: () => void;
|
|
16
|
+
}
|
|
17
|
+
export declare function EditableAvatarButton({ label, imageUrl, fallback, accentColor, size, width, height, radius, editBadgeSize, editIconSize, disabled, className, onClick, }: EditableAvatarButtonProps): import("react/jsx-runtime").JSX.Element;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Pencil } from 'lucide-react';
|
|
3
|
+
import styles from './EditableAvatarButton.module.css';
|
|
4
|
+
export function EditableAvatarButton({ label, imageUrl, fallback, accentColor, size = 52, width, height, radius, editBadgeSize, editIconSize = 16, disabled = false, className = '', onClick, }) {
|
|
5
|
+
const normalizedImageUrl = String(imageUrl || '').trim();
|
|
6
|
+
const resolvedWidth = width ?? size;
|
|
7
|
+
const resolvedHeight = height ?? size;
|
|
8
|
+
const resolvedRadius = typeof radius === 'number' ? `${radius}px` : radius;
|
|
9
|
+
const resolvedEditBadgeSize = typeof editBadgeSize === 'number' ? `${editBadgeSize}px` : editBadgeSize;
|
|
10
|
+
return (_jsxs("button", { type: "button", className: `${styles.editableAvatarButton} ${className}`.trim(), "aria-label": label, title: label, onClick: onClick, disabled: disabled, style: {
|
|
11
|
+
['--editable-avatar-size']: `${size}px`,
|
|
12
|
+
['--editable-avatar-width']: `${resolvedWidth}px`,
|
|
13
|
+
['--editable-avatar-height']: `${resolvedHeight}px`,
|
|
14
|
+
...(resolvedRadius ? { ['--editable-avatar-radius']: resolvedRadius } : {}),
|
|
15
|
+
...(resolvedEditBadgeSize ? { ['--editable-avatar-badge-size']: resolvedEditBadgeSize } : {}),
|
|
16
|
+
...(accentColor ? { ['--editable-avatar-accent']: accentColor } : {})
|
|
17
|
+
}, children: [normalizedImageUrl ? (_jsx("img", { src: normalizedImageUrl, alt: "", className: styles.editableAvatarImage })) : (_jsx("span", { className: styles.editableAvatarFallback, children: fallback })), _jsx("span", { className: styles.editableAvatarBadge, "aria-hidden": "true", children: _jsx(Pencil, { size: editIconSize }) })] }));
|
|
18
|
+
}
|
|
@@ -4,6 +4,7 @@ export type BillingPricingType = 'stripe' | 'free';
|
|
|
4
4
|
export interface PublicPricingPlan {
|
|
5
5
|
planId: string;
|
|
6
6
|
displayName: string;
|
|
7
|
+
description?: string | null;
|
|
7
8
|
status: string;
|
|
8
9
|
defaultVersion: {
|
|
9
10
|
planVersionId: string;
|
|
@@ -15,6 +16,10 @@ export interface PublicPricingPlan {
|
|
|
15
16
|
featureKey: string;
|
|
16
17
|
access: string;
|
|
17
18
|
config?: Record<string, unknown>;
|
|
19
|
+
label?: string | null;
|
|
20
|
+
description?: string | null;
|
|
21
|
+
publicLabel?: string | null;
|
|
22
|
+
publicDescription?: string | null;
|
|
18
23
|
}>;
|
|
19
24
|
pricing: {
|
|
20
25
|
month: {
|
|
@@ -55,6 +60,7 @@ interface PlanComparisonPageProps {
|
|
|
55
60
|
currentPlanId?: string | null;
|
|
56
61
|
currentPlanVersionId?: string | null;
|
|
57
62
|
currentEntitlementState?: string | null;
|
|
63
|
+
markCurrentPlan?: boolean;
|
|
58
64
|
canManageCurrentPlan?: boolean;
|
|
59
65
|
pricingEndpoint?: string;
|
|
60
66
|
statusBanner?: {
|
|
@@ -67,8 +73,10 @@ interface PlanComparisonPageProps {
|
|
|
67
73
|
topActions?: React.ReactNode;
|
|
68
74
|
shellOwnsScroll?: boolean;
|
|
69
75
|
showPageBackButton?: boolean;
|
|
76
|
+
showHeaderBarWithChrome?: boolean;
|
|
77
|
+
currentPlanCardActionMode?: 'manage' | 'indicator' | 'hidden';
|
|
70
78
|
fallbackPricingSource?: PricingSourceDetails | null;
|
|
71
79
|
onSelectPlan: (intent: PlanSelectionIntent) => void | Promise<void>;
|
|
72
80
|
}
|
|
73
|
-
export declare function PlanComparisonPage({ heading, subtitle, variant, chrome, footer, isAuthenticated, authSessionResolved, currentPlanId, currentPlanVersionId, currentEntitlementState, canManageCurrentPlan, pricingEndpoint, statusBanner, onBack, backLabel, backDisabled, topActions, shellOwnsScroll, showPageBackButton, fallbackPricingSource, onSelectPlan }: PlanComparisonPageProps): import("react/jsx-runtime").JSX.Element;
|
|
81
|
+
export declare function PlanComparisonPage({ heading, subtitle, variant, chrome, footer, isAuthenticated, authSessionResolved, currentPlanId, currentPlanVersionId, currentEntitlementState, markCurrentPlan, canManageCurrentPlan, pricingEndpoint, statusBanner, onBack, backLabel, backDisabled, topActions, shellOwnsScroll, showPageBackButton, showHeaderBarWithChrome, currentPlanCardActionMode, fallbackPricingSource, onSelectPlan }: PlanComparisonPageProps): import("react/jsx-runtime").JSX.Element;
|
|
74
82
|
export {};
|
|
@@ -3,6 +3,7 @@ import { useEffect, useMemo, useState } from 'react';
|
|
|
3
3
|
import { CheckCircle2, Loader2 } from 'lucide-react';
|
|
4
4
|
import taskforceStyles from '../../Taskforce.module.css';
|
|
5
5
|
import styles from './PlansPage.module.css';
|
|
6
|
+
import { isCommercialEntitlementActive } from '../../utils/commercialLifecyclePresentation';
|
|
6
7
|
function isDebugModeEnabled() {
|
|
7
8
|
const runtimeOverride = globalThis.__DEBUG_MODE__;
|
|
8
9
|
if (typeof runtimeOverride === 'boolean')
|
|
@@ -60,11 +61,49 @@ function getPlanSortRank(plan) {
|
|
|
60
61
|
return Math.min(...numericAmounts);
|
|
61
62
|
return Number.POSITIVE_INFINITY;
|
|
62
63
|
}
|
|
63
|
-
function
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
function formatFeatureKeyFallback(featureKey) {
|
|
65
|
+
return String(featureKey || '')
|
|
66
|
+
.replace(/[_\-.]+/g, ' ')
|
|
67
|
+
.replace(/\b\w/g, (c) => c.toUpperCase());
|
|
66
68
|
}
|
|
67
|
-
|
|
69
|
+
function readConfiguredFeatureLimit(config, key) {
|
|
70
|
+
const rawValue = config?.[key];
|
|
71
|
+
const numericValue = Number(rawValue);
|
|
72
|
+
if (!Number.isFinite(numericValue))
|
|
73
|
+
return null;
|
|
74
|
+
const normalized = Math.max(1, Math.floor(numericValue));
|
|
75
|
+
return normalized;
|
|
76
|
+
}
|
|
77
|
+
function getFeatureDisplayParts(feature, seatLimit) {
|
|
78
|
+
const baseLabel = String(feature.publicLabel || feature.label || '').trim() || formatFeatureKeyFallback(feature.featureKey);
|
|
79
|
+
const config = feature.config && typeof feature.config === 'object' ? feature.config : {};
|
|
80
|
+
if (feature.featureKey === 'context.uploads') {
|
|
81
|
+
const storageLimitMb = readConfiguredFeatureLimit(config, 'storageLimitMb');
|
|
82
|
+
if (storageLimitMb !== null) {
|
|
83
|
+
return { limitValue: String(storageLimitMb), limitUnit: 'MB', baseLabel };
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (feature.featureKey === 'workspace.workspaces') {
|
|
87
|
+
const maxWorkspaces = readConfiguredFeatureLimit(config, 'maxWorkspaces');
|
|
88
|
+
if (maxWorkspaces !== null) {
|
|
89
|
+
return { limitValue: String(maxWorkspaces), limitUnit: 'x', baseLabel };
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
if (feature.featureKey === 'collaboration.ai_profiles') {
|
|
93
|
+
const maxAiProfiles = readConfiguredFeatureLimit(config, 'maxAiProfiles');
|
|
94
|
+
if (maxAiProfiles !== null) {
|
|
95
|
+
return { limitValue: String(maxAiProfiles), limitUnit: 'x', baseLabel };
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (feature.featureKey === 'collaboration.team_management') {
|
|
99
|
+
const normalizedSeatLimit = Number(seatLimit);
|
|
100
|
+
if (Number.isFinite(normalizedSeatLimit) && normalizedSeatLimit > 0) {
|
|
101
|
+
return { limitValue: String(Math.floor(normalizedSeatLimit)), limitUnit: 'x', baseLabel };
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return { limitValue: null, limitUnit: null, baseLabel };
|
|
105
|
+
}
|
|
106
|
+
export function PlanComparisonPage({ heading, subtitle, variant = 'app', chrome, footer, isAuthenticated, authSessionResolved = true, currentPlanId, currentPlanVersionId, currentEntitlementState, markCurrentPlan, canManageCurrentPlan = true, pricingEndpoint, statusBanner, onBack, backLabel, backDisabled = false, topActions, shellOwnsScroll = false, showPageBackButton = true, showHeaderBarWithChrome = false, currentPlanCardActionMode = 'manage', fallbackPricingSource = null, onSelectPlan }) {
|
|
68
107
|
const [plans, setPlans] = useState([]);
|
|
69
108
|
const [pricingSource, setPricingSource] = useState(null);
|
|
70
109
|
const [loading, setLoading] = useState(true);
|
|
@@ -117,6 +156,7 @@ export function PlanComparisonPage({ heading, subtitle, variant = 'app', chrome,
|
|
|
117
156
|
return String(a.displayName || a.planId).localeCompare(String(b.displayName || b.planId));
|
|
118
157
|
}), [plans]);
|
|
119
158
|
const pricingSourceLabel = useMemo(() => formatPricingSource(pricingSource || fallbackPricingSource), [fallbackPricingSource, pricingSource]);
|
|
159
|
+
const planSelectionEnabled = authSessionResolved;
|
|
120
160
|
const contentClassName = `${styles.plansWrapper} ${shellOwnsScroll ? styles.shellOwnsScroll : 'tf-scrollbar'} ${variant === 'site' ? styles.siteVariant : ''} ${chrome ? styles.withChrome : ''}`.trim();
|
|
121
161
|
const wrapPage = (content) => {
|
|
122
162
|
if (shellOwnsScroll) {
|
|
@@ -124,13 +164,13 @@ export function PlanComparisonPage({ heading, subtitle, variant = 'app', chrome,
|
|
|
124
164
|
}
|
|
125
165
|
return (_jsxs("div", { className: taskforceStyles.standaloneWrapper, "data-theme": "dark", children: [chrome, content, footer] }));
|
|
126
166
|
};
|
|
127
|
-
if (loading
|
|
167
|
+
if (loading) {
|
|
128
168
|
return wrapPage(_jsx(_Fragment, { children: _jsx("div", { className: contentClassName, children: _jsx("div", { className: styles.contentFrame, children: _jsxs("div", { className: styles.loader, children: [_jsx(Loader2, { size: 48, className: styles.spinner }), _jsx("p", { children: "Loading plans..." })] }) }) }) }));
|
|
129
169
|
}
|
|
130
170
|
if (error) {
|
|
131
171
|
return wrapPage(_jsx(_Fragment, { children: _jsx("div", { className: contentClassName, children: _jsx("div", { className: styles.contentFrame, children: _jsxs("div", { className: styles.emptyState, children: [_jsx("h2", { children: "Connecting to plan pricing..." }), _jsx("p", { children: error }), _jsx("button", { className: styles.backBtn, onClick: () => setRetryNonce((value) => value + 1), children: "Retry" })] }) }) }) }));
|
|
132
172
|
}
|
|
133
|
-
return wrapPage(_jsx(_Fragment, { children: _jsx("div", { className: contentClassName, children: _jsxs("div", { className: styles.contentFrame, children: [_jsxs("div", { className: styles.header, children: [!chrome && (showPageBackButton || topActions) && (_jsxs("div", { className: styles.headerBar, children: [_jsx("div", { children: showPageBackButton ? (_jsx("button", { className: styles.backBtn, onClick: onBack, disabled: backDisabled, children: backLabel })) : null }), _jsx("div", { className: styles.headerActions, children: topActions })] })), _jsx("h1", { className: styles.title, children: heading }), subtitle && _jsx("p", { className: styles.subtitle, children: subtitle }), isDebugModeEnabled() && pricingSourceLabel ? (_jsxs("p", { className: styles.debugMeta, children: ["Pricing source: ", pricingSourceLabel] })) : null, statusBanner && (_jsx("div", { className: `${styles.statusBanner} ${statusBanner.type === 'success'
|
|
173
|
+
return wrapPage(_jsx(_Fragment, { children: _jsx("div", { className: contentClassName, children: _jsxs("div", { className: styles.contentFrame, children: [_jsxs("div", { className: styles.header, children: [(!chrome || showHeaderBarWithChrome) && (showPageBackButton || topActions) && (_jsxs("div", { className: styles.headerBar, children: [_jsx("div", { children: showPageBackButton ? (_jsx("button", { className: styles.backBtn, onClick: onBack, disabled: backDisabled, children: backLabel })) : null }), _jsx("div", { className: styles.headerActions, children: topActions })] })), _jsx("h1", { className: styles.title, children: heading }), subtitle && _jsx("p", { className: styles.subtitle, children: subtitle }), isDebugModeEnabled() && pricingSourceLabel ? (_jsxs("p", { className: styles.debugMeta, children: ["Pricing source: ", pricingSourceLabel] })) : null, statusBanner && (_jsx("div", { className: `${styles.statusBanner} ${statusBanner.type === 'success'
|
|
134
174
|
? styles.successBanner
|
|
135
175
|
: (statusBanner.type === 'info' ? styles.infoBanner : styles.errorBanner)}`, children: statusBanner.message }))] }), sortedPlans.length === 0 ? (_jsxs("div", { className: `${styles.emptyState} ${styles.marketingEmptyState}`, children: [_jsx("span", { className: styles.emptyBadge, children: "Coming Soon" }), _jsx("h2", { children: "Paid plans are getting their final polish." }), _jsx("p", { children: "Taskforce pricing is on the way with flexible options for solo operators, teams, and larger rollouts. Check back soon for launch tiers, feature bundles, and early access details." }), _jsxs("div", { className: styles.emptyHighlights, "aria-label": "Upcoming pricing highlights", children: [_jsx("span", { children: "Launch-ready tiers" }), _jsx("span", { children: "Team billing controls" }), _jsx("span", { children: "Feature-based packaging" })] })] })) : (_jsx("div", { className: styles.grid, children: sortedPlans.map((plan) => {
|
|
136
176
|
const monthPrice = plan.pricing.month;
|
|
@@ -138,18 +178,28 @@ export function PlanComparisonPage({ heading, subtitle, variant = 'app', chrome,
|
|
|
138
178
|
const hasMonth = isSelectablePrice(monthPrice);
|
|
139
179
|
const hasYear = isSelectablePrice(yearPrice);
|
|
140
180
|
const planVersionId = String(plan.defaultVersion?.planVersionId || '').trim();
|
|
141
|
-
const
|
|
181
|
+
const hasActiveEntitlement = isCommercialEntitlementActive(currentEntitlementState);
|
|
182
|
+
const shouldMarkCurrentPlan = markCurrentPlan ?? hasActiveEntitlement;
|
|
183
|
+
const hasKnownLinkedPlan = Boolean(planVersionId
|
|
142
184
|
&& currentPlanVersionId
|
|
143
|
-
&& currentPlanVersionId === planVersionId
|
|
144
|
-
&& isActiveEntitlementState(currentEntitlementState)) || (!currentPlanVersionId
|
|
185
|
+
&& currentPlanVersionId === planVersionId) || Boolean(!currentPlanVersionId
|
|
145
186
|
&& Boolean(currentPlanId && currentPlanId === plan.planId));
|
|
187
|
+
const isCurrent = shouldMarkCurrentPlan && hasKnownLinkedPlan && currentEntitlementState !== 'canceled';
|
|
146
188
|
const defaultInterval = hasMonth ? 'month' : (hasYear ? 'year' : null);
|
|
147
189
|
const defaultPrice = defaultInterval === 'year' ? yearPrice : monthPrice;
|
|
148
|
-
const
|
|
190
|
+
const currentPlanShowsManageAction = currentPlanCardActionMode === 'manage';
|
|
191
|
+
const currentPlanShowsIndicator = currentPlanCardActionMode === 'indicator';
|
|
192
|
+
const canSelectCard = planSelectionEnabled
|
|
193
|
+
&& Boolean(planVersionId)
|
|
194
|
+
&& (isCurrent
|
|
195
|
+
? (currentPlanShowsManageAction && canManageCurrentPlan)
|
|
196
|
+
: Boolean(defaultInterval));
|
|
149
197
|
const handleCardSelect = () => {
|
|
150
198
|
if (!planVersionId)
|
|
151
199
|
return;
|
|
152
200
|
if (isCurrent) {
|
|
201
|
+
if (!currentPlanShowsManageAction)
|
|
202
|
+
return;
|
|
153
203
|
if (!canManageCurrentPlan)
|
|
154
204
|
return;
|
|
155
205
|
void onSelectPlan({
|
|
@@ -176,23 +226,26 @@ export function PlanComparisonPage({ heading, subtitle, variant = 'app', chrome,
|
|
|
176
226
|
event.preventDefault();
|
|
177
227
|
handleCardSelect();
|
|
178
228
|
}
|
|
179
|
-
} : undefined, children: [isCurrent && _jsx("span", { className: styles.currentBadge, children: "Current Plan" }), _jsx("h3", { className: styles.planName, children: plan.displayName }),
|
|
229
|
+
} : undefined, children: [isCurrent && _jsx("span", { className: styles.currentBadge, children: "Current Plan" }), _jsx("h3", { className: styles.planName, children: plan.displayName }), String(plan.description || '').trim() && (_jsx("p", { className: styles.planDescription, children: String(plan.description || '').trim() })), _jsx("div", { className: styles.featuresList, children: Array.isArray(plan.features) && plan.features.length > 0 ? plan.features.map((feature, idx) => (_jsxs("div", { className: styles.featureItem, children: [_jsx(CheckCircle2, { className: styles.featureIcon }), _jsxs("div", { className: styles.featureTextBlock, children: [(() => {
|
|
230
|
+
const { limitValue, limitUnit, baseLabel } = getFeatureDisplayParts(feature, plan.defaultVersion?.seatLimit);
|
|
231
|
+
return (_jsxs("span", { className: styles.featureText, children: [limitValue && (_jsxs("span", { className: styles.featureLimitAccent, children: [_jsx("span", { children: limitValue }), limitUnit ? (_jsx("span", { className: limitUnit === 'MB' ? styles.featureLimitUnit : undefined, children: limitUnit })) : null] })), _jsx("span", { children: limitValue ? ` ${baseLabel}` : baseLabel })] }));
|
|
232
|
+
})(), String(feature.publicDescription || feature.description || '').trim() && (_jsx("span", { className: styles.featureDescription, children: String(feature.publicDescription || feature.description || '').trim() }))] })] }, `${feature.featureKey}-${idx}`))) : (_jsx("span", { className: styles.featureText, children: "No additional features included" })) }), (hasMonth || hasYear) ? (_jsxs("div", { className: styles.priceRows, children: [hasMonth ? (_jsxs("div", { className: styles.priceRow, children: [_jsx("span", { className: styles.priceRowAmount, children: renderPriceLabel(monthPrice) }), _jsx("span", { className: styles.priceRowInterval, children: "/ month" })] })) : null, hasYear ? (_jsxs("div", { className: styles.priceRow, children: [_jsx("span", { className: styles.priceRowAmount, children: renderPriceLabel(yearPrice) }), _jsx("span", { className: styles.priceRowInterval, children: "/ year" })] })) : null] })) : null, isCurrent ? (currentPlanShowsManageAction ? (_jsx("button", { className: `${styles.callToAction} ${styles.currentCta}`, "data-testid": `plan-manage-${plan.planId}`, onClick: (event) => {
|
|
180
233
|
event.stopPropagation();
|
|
181
234
|
if (!canManageCurrentPlan)
|
|
182
235
|
return;
|
|
183
236
|
void onSelectPlan({ planId: plan.planId, planVersionId, interval: defaultInterval || 'month', pricingType: defaultPrice?.pricingType || 'stripe', isCurrentPlan: true });
|
|
184
|
-
}, disabled: !planVersionId || !canManageCurrentPlan, children: isAuthenticated
|
|
237
|
+
}, disabled: !planVersionId || !canManageCurrentPlan || !planSelectionEnabled, children: isAuthenticated
|
|
185
238
|
? (canManageCurrentPlan ? 'Manage Plan' : 'Current Plan')
|
|
186
|
-
: 'Current Plan' })) : (_jsxs("div", { className: styles.ctaRow, children: [_jsx("button", { className: `${styles.callToAction} ${styles.secondaryCta}`, "data-testid": `plan-select-${plan.planId}-month`, onClick: (event) => {
|
|
239
|
+
: 'Current Plan' })) : (currentPlanShowsIndicator ? (_jsx("button", { className: `${styles.callToAction} ${styles.currentCta}`, "data-testid": `plan-current-${plan.planId}`, disabled: true, children: "Current Plan" })) : null)) : (_jsxs("div", { className: styles.ctaRow, children: [hasMonth ? (_jsx("button", { className: `${styles.callToAction} ${styles.secondaryCta}`, "data-testid": `plan-select-${plan.planId}-month`, onClick: (event) => {
|
|
187
240
|
event.stopPropagation();
|
|
188
241
|
void onSelectPlan({ planId: plan.planId, planVersionId, interval: 'month', pricingType: monthPrice?.pricingType || 'stripe', isCurrentPlan: false });
|
|
189
|
-
}, disabled: !planVersionId || !
|
|
242
|
+
}, disabled: !planVersionId || !planSelectionEnabled, children: monthPrice?.pricingType === 'free'
|
|
190
243
|
? (isAuthenticated ? 'Select Free' : 'Get Started Free')
|
|
191
|
-
: (isAuthenticated ? 'Choose Monthly' : 'Get Started Monthly') }), _jsx("button", { className: `${styles.callToAction} ${styles.primaryCta}`, "data-testid": `plan-select-${plan.planId}-year`, onClick: (event) => {
|
|
244
|
+
: (isAuthenticated ? 'Choose Monthly' : 'Get Started Monthly') })) : null, hasYear ? (_jsx("button", { className: `${styles.callToAction} ${styles.primaryCta}`, "data-testid": `plan-select-${plan.planId}-year`, onClick: (event) => {
|
|
192
245
|
event.stopPropagation();
|
|
193
246
|
void onSelectPlan({ planId: plan.planId, planVersionId, interval: 'year', pricingType: yearPrice?.pricingType || 'stripe', isCurrentPlan: false });
|
|
194
|
-
}, disabled: !planVersionId || !
|
|
247
|
+
}, disabled: !planVersionId || !planSelectionEnabled, children: yearPrice?.pricingType === 'free'
|
|
195
248
|
? (isAuthenticated ? 'Select Free' : 'Get Started Free')
|
|
196
|
-
: (isAuthenticated ? 'Choose Yearly' : 'Get Started Yearly') })] }))] }, plan.planId));
|
|
249
|
+
: (isAuthenticated ? 'Choose Yearly' : 'Get Started Yearly') })) : null] }))] }, plan.planId));
|
|
197
250
|
}) }))] }) }) }));
|
|
198
251
|
}
|