@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,42 +1,44 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import React from
|
|
3
|
-
import { DndContext, PointerSensor, closestCenter, useSensor, useSensors } from
|
|
4
|
-
import { SortableContext, arrayMove, useSortable, verticalListSortingStrategy } from
|
|
5
|
-
import { CSS } from
|
|
6
|
-
import * as Icons from
|
|
7
|
-
import { HelpCircle, X, Send, Check, ChevronDown, ChevronRight, User, ClipboardList, Plus, Bot, GripVertical } from
|
|
8
|
-
import styles from
|
|
9
|
-
import formStyles from
|
|
10
|
-
import { DEFAULT_CATEGORY_LABEL, DEFAULT_CATEGORY_VALUE } from
|
|
11
|
-
import { LevelSelector } from
|
|
12
|
-
import { TaxonomyDropdown } from
|
|
13
|
-
import { Markdown } from
|
|
14
|
-
import { IconMap, resolveColor } from
|
|
15
|
-
import { formatDate } from
|
|
16
|
-
import { t } from
|
|
17
|
-
import { TaskContextUpload } from
|
|
18
|
-
import { summarizeTaskChange, summarizeTaskEvent } from
|
|
19
|
-
import { ensureAssigneeOptionPresent, getAssigneeKind, getAssigneeLabel, } from
|
|
20
|
-
import { getTaskReferenceLabel } from
|
|
21
|
-
import { getVisibleTaskFormTaxonomies } from
|
|
22
|
-
import { getInitiativeReferenceLabel, getWorkstreamReferenceLabel } from
|
|
23
|
-
function SortableChecklistRow({ id, item, onToggle, onRemove }) {
|
|
2
|
+
import React from "react";
|
|
3
|
+
import { DndContext, PointerSensor, closestCenter, useSensor, useSensors, } from "@dnd-kit/core";
|
|
4
|
+
import { SortableContext, arrayMove, useSortable, verticalListSortingStrategy, } from "@dnd-kit/sortable";
|
|
5
|
+
import { CSS } from "@dnd-kit/utilities";
|
|
6
|
+
import * as Icons from "lucide-react";
|
|
7
|
+
import { HelpCircle, X, Send, Check, ChevronDown, ChevronRight, User, ClipboardList, Plus, Bot, GripVertical, } from "lucide-react";
|
|
8
|
+
import styles from "../../Taskforce.module.css";
|
|
9
|
+
import formStyles from "./TaskForm.module.css";
|
|
10
|
+
import { DEFAULT_CATEGORY_LABEL, DEFAULT_CATEGORY_VALUE, } from "../../shared/taxonomyDefaults.js";
|
|
11
|
+
import { LevelSelector } from "../ui/LevelSelector";
|
|
12
|
+
import { TaxonomyDropdown } from "../ui/TaxonomyDropdown";
|
|
13
|
+
import { Markdown } from "../ui/Markdown";
|
|
14
|
+
import { IconMap, resolveColor } from "../../utils/constants";
|
|
15
|
+
import { formatDate } from "../../utils/formatting";
|
|
16
|
+
import { t } from "../../localization";
|
|
17
|
+
import { TaskContextUpload } from "./TaskContextUpload";
|
|
18
|
+
import { getTaskActivityItems, getTaskEventPrimaryChangeField, summarizeTaskChange, summarizeTaskEvent, } from "../../utils/taskActivity";
|
|
19
|
+
import { ensureAssigneeOptionPresent, getAssigneeKind, getAssigneeLabel, } from "../../utils/assignees";
|
|
20
|
+
import { getTaskReferenceLabel } from "../../utils/taskReferences";
|
|
21
|
+
import { getVisibleTaskFormTaxonomies } from "../../utils/taxonomyFormDefaults.js";
|
|
22
|
+
import { getInitiativeReferenceLabel, getWorkstreamReferenceLabel, } from "../../utils/planningReferences";
|
|
23
|
+
function SortableChecklistRow({ id, item, onToggle, onRemove, }) {
|
|
24
24
|
const { attributes, listeners, setNodeRef, transform, transition, isDragging, } = useSortable({ id });
|
|
25
25
|
const style = {
|
|
26
26
|
transform: CSS.Transform.toString(transform),
|
|
27
27
|
transition,
|
|
28
28
|
opacity: isDragging ? 0.88 : 1,
|
|
29
29
|
};
|
|
30
|
-
return (_jsxs("div", { ref: setNodeRef, style: style, className: `${formStyles.checklistRow} ${isDragging ? formStyles.checklistRowDragging :
|
|
30
|
+
return (_jsxs("div", { ref: setNodeRef, style: style, className: `${formStyles.checklistRow} ${isDragging ? formStyles.checklistRowDragging : ""}`.trim(), children: [_jsx("button", { type: "button", className: formStyles.checklistHandleBtn, title: "Reorder checklist item", "aria-label": "Reorder checklist item", ...attributes, ...listeners, children: _jsx(GripVertical, { size: 14 }) }), _jsx("button", { type: "button", className: `${formStyles.checklistCheckboxBtn} ${item.isCompleted ? formStyles.checklistCheckboxBtnChecked : ""}`.trim(), onClick: onToggle, title: item.isCompleted ? "Mark incomplete" : "Mark complete", "aria-label": item.isCompleted
|
|
31
|
+
? "Mark checklist item incomplete"
|
|
32
|
+
: "Mark checklist item complete", children: item.isCompleted ? _jsx(Check, { size: 14, strokeWidth: 2.1 }) : null }), _jsx("span", { className: `${formStyles.checklistItemText} ${item.isCompleted ? formStyles.checklistItemTextCompleted : ""}`.trim(), children: item.title }), _jsx("button", { type: "button", className: formStyles.checklistRemoveBtn, onClick: onRemove, title: "Remove checklist item", "aria-label": "Remove checklist item", children: _jsx(X, { size: 15, strokeWidth: 1.9 }) })] }));
|
|
31
33
|
}
|
|
32
34
|
function hexToRgba(hex, opacity) {
|
|
33
35
|
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
|
34
36
|
if (!result)
|
|
35
|
-
return
|
|
37
|
+
return "";
|
|
36
38
|
return `rgba(${parseInt(result[1], 16)}, ${parseInt(result[2], 16)}, ${parseInt(result[3], 16)}, ${opacity})`;
|
|
37
39
|
}
|
|
38
40
|
export function TaskForm(props) {
|
|
39
|
-
const { editingTaskId, error, title, description, category, type, priority, complexity, manualComplexityEnabled = false, assignee, scheduledDate, dueDate, workstreamInput =
|
|
41
|
+
const { editingTaskId, error, title, description, category, type, priority, complexity, manualComplexityEnabled = false, assignee, scheduledDate, dueDate, workstreamInput = "", checklistItems, comments, newCommentText, contextFiles, currentWorkspaceId, apiBaseUrl, descriptionFocused, showMarkdownHelp, showChecklist = false, checklistEnabled = true, formTaxonomies, onTaxonomyChange, onOpenSettings, categories, types, priorities, taxonomyDisplayLabels, assigneeOptions, taxonomies, workstreams = [], initiatives = [], copiedId, onTitleChange, onDescriptionChange, onCategoryChange, onTypeChange, onPriorityChange, onComplexityChange, onAssigneeChange, onScheduledDateChange, onDueDateChange, onWorkstreamInputChange = () => { }, onChecklistItemsChange, onNewCommentTextChange, onDescriptionFocusedChange, onShowMarkdownHelpChange, onShowChecklistChange = () => { }, onSubmit, onAddComment, onAddContextFile, onRemoveContextFile, onUpdateContextCaption, onCopyId, onToggleInProgress, onToggleReview, onToggleComplete, onToggleCancel, onSetStatus, onArchiveTask, onUnarchive, onOpenTaskById, currentTask, commentsEndRef, } = props;
|
|
40
42
|
const currentTaskReferenceLabel = getTaskReferenceLabel(currentTask);
|
|
41
43
|
const visibleTaxonomies = React.useMemo(() => getVisibleTaskFormTaxonomies(taxonomies, formTaxonomies), [taxonomies, formTaxonomies]);
|
|
42
44
|
const categoryOptions = React.useMemo(() => {
|
|
@@ -44,40 +46,40 @@ export function TaskForm(props) {
|
|
|
44
46
|
const defaultOption = {
|
|
45
47
|
value: DEFAULT_CATEGORY_VALUE,
|
|
46
48
|
label: configuredDefault?.label || DEFAULT_CATEGORY_LABEL,
|
|
47
|
-
icon: configuredDefault?.icon ||
|
|
48
|
-
color: configuredDefault?.color ||
|
|
49
|
+
icon: configuredDefault?.icon || "HelpCircle",
|
|
50
|
+
color: configuredDefault?.color || "var(--text-secondary)",
|
|
49
51
|
};
|
|
50
52
|
const selectedCategory = categories.find((cat) => cat.value === category);
|
|
51
53
|
const availableCategories = categories.filter((cat) => !cat.disabled || cat.value === selectedCategory?.value);
|
|
52
54
|
const nonDefault = availableCategories
|
|
53
|
-
.filter(cat => cat.value !== DEFAULT_CATEGORY_VALUE)
|
|
54
|
-
.map(cat => ({
|
|
55
|
+
.filter((cat) => cat.value !== DEFAULT_CATEGORY_VALUE)
|
|
56
|
+
.map((cat) => ({
|
|
55
57
|
value: cat.value,
|
|
56
58
|
label: cat.disabled ? `${cat.label} (Legacy)` : cat.label,
|
|
57
59
|
icon: cat.icon,
|
|
58
|
-
color: cat.color
|
|
60
|
+
color: cat.color,
|
|
59
61
|
}));
|
|
60
62
|
return [defaultOption, ...nonDefault];
|
|
61
63
|
}, [categories, category]);
|
|
62
64
|
const typeOptions = React.useMemo(() => {
|
|
63
65
|
const selectedType = types.find((entry) => entry.value === type);
|
|
64
|
-
const availableTypes = types.filter((entry) => entry.status !==
|
|
66
|
+
const availableTypes = types.filter((entry) => entry.status !== "retired" || entry.value === selectedType?.value);
|
|
65
67
|
return availableTypes.map((entry) => ({
|
|
66
68
|
value: entry.value,
|
|
67
|
-
label: entry.status ===
|
|
68
|
-
icon: entry.icon ||
|
|
69
|
-
color: entry.color ||
|
|
69
|
+
label: entry.status === "retired" ? `${entry.label} (Retired)` : entry.label,
|
|
70
|
+
icon: entry.icon || "Box",
|
|
71
|
+
color: entry.color || "violet-500",
|
|
70
72
|
}));
|
|
71
73
|
}, [types, type]);
|
|
72
74
|
const formatMetaDate = (value) => {
|
|
73
75
|
if (!value)
|
|
74
76
|
return null;
|
|
75
77
|
const formatted = formatDate(value, {
|
|
76
|
-
month:
|
|
77
|
-
day:
|
|
78
|
-
year:
|
|
79
|
-
hour:
|
|
80
|
-
minute:
|
|
78
|
+
month: "short",
|
|
79
|
+
day: "numeric",
|
|
80
|
+
year: "numeric",
|
|
81
|
+
hour: "2-digit",
|
|
82
|
+
minute: "2-digit",
|
|
81
83
|
});
|
|
82
84
|
return formatted || null;
|
|
83
85
|
};
|
|
@@ -87,29 +89,32 @@ export function TaskForm(props) {
|
|
|
87
89
|
const todayDateOnly = React.useMemo(() => {
|
|
88
90
|
const now = new Date();
|
|
89
91
|
const y = now.getFullYear();
|
|
90
|
-
const m = String(now.getMonth() + 1).padStart(2,
|
|
91
|
-
const d = String(now.getDate()).padStart(2,
|
|
92
|
+
const m = String(now.getMonth() + 1).padStart(2, "0");
|
|
93
|
+
const d = String(now.getDate()).padStart(2, "0");
|
|
92
94
|
return `${y}-${m}-${d}`;
|
|
93
95
|
}, []);
|
|
94
96
|
const dueBeforeScheduledWarning = Boolean(dueDate && scheduledDate && dueDate < scheduledDate);
|
|
95
97
|
const overdueWarning = Boolean(dueDate &&
|
|
96
98
|
dueDate < todayDateOnly &&
|
|
97
|
-
(!currentTask ||
|
|
98
|
-
|
|
99
|
-
const
|
|
99
|
+
(!currentTask ||
|
|
100
|
+
(currentTask.status !== "done" && currentTask.status !== "cancelled")));
|
|
101
|
+
const completedChecklistCount = checklistItems.filter((item) => item.isCompleted).length;
|
|
102
|
+
const [newChecklistItemText, setNewChecklistItemText] = React.useState("");
|
|
100
103
|
const [manualWorkstreamEntry, setManualWorkstreamEntry] = React.useState(false);
|
|
101
104
|
const isArchivedReadOnly = Boolean(currentTask?.isArchived);
|
|
102
105
|
const isDeletedReadOnly = Boolean(currentTask?.isDeleted);
|
|
103
|
-
const workstreamOptions = React.useMemo(() =>
|
|
106
|
+
const workstreamOptions = React.useMemo(() => workstreams.map((workstream) => {
|
|
104
107
|
const reference = getWorkstreamReferenceLabel(workstream);
|
|
105
108
|
return {
|
|
106
109
|
value: reference || workstream.id,
|
|
107
|
-
label: reference
|
|
108
|
-
|
|
109
|
-
|
|
110
|
+
label: reference
|
|
111
|
+
? `${reference} · ${workstream.title}`
|
|
112
|
+
: workstream.title,
|
|
113
|
+
icon: "Folder",
|
|
114
|
+
color: "var(--text-secondary)",
|
|
110
115
|
};
|
|
111
|
-
})
|
|
112
|
-
const hasMatchingWorkstreamSelection = React.useMemo(() =>
|
|
116
|
+
}), [workstreams]);
|
|
117
|
+
const hasMatchingWorkstreamSelection = React.useMemo(() => workstreamOptions.some((option) => String(option.value) === String(workstreamInput || "").trim()), [workstreamInput, workstreamOptions]);
|
|
113
118
|
React.useEffect(() => {
|
|
114
119
|
if (editingTaskId) {
|
|
115
120
|
if (manualWorkstreamEntry)
|
|
@@ -126,7 +131,12 @@ export function TaskForm(props) {
|
|
|
126
131
|
if (!hasMatchingWorkstreamSelection && !manualWorkstreamEntry) {
|
|
127
132
|
setManualWorkstreamEntry(true);
|
|
128
133
|
}
|
|
129
|
-
}, [
|
|
134
|
+
}, [
|
|
135
|
+
editingTaskId,
|
|
136
|
+
hasMatchingWorkstreamSelection,
|
|
137
|
+
manualWorkstreamEntry,
|
|
138
|
+
workstreamInput,
|
|
139
|
+
]);
|
|
130
140
|
const isReadOnly = isArchivedReadOnly || isDeletedReadOnly;
|
|
131
141
|
const formRef = React.useRef(null);
|
|
132
142
|
const checklistSensors = useSensors(useSensor(PointerSensor, {
|
|
@@ -140,8 +150,8 @@ export function TaskForm(props) {
|
|
|
140
150
|
form.scrollTop = 0;
|
|
141
151
|
const container = form.parentElement;
|
|
142
152
|
if (container) {
|
|
143
|
-
if (typeof container.scrollTo ===
|
|
144
|
-
container.scrollTo({ top: 0, left: 0, behavior:
|
|
153
|
+
if (typeof container.scrollTo === "function") {
|
|
154
|
+
container.scrollTo({ top: 0, left: 0, behavior: "auto" });
|
|
145
155
|
}
|
|
146
156
|
else {
|
|
147
157
|
container.scrollTop = 0;
|
|
@@ -161,7 +171,7 @@ export function TaskForm(props) {
|
|
|
161
171
|
...checklistItems,
|
|
162
172
|
{
|
|
163
173
|
id: `checklist-draft-${now}-${checklistItems.length}`,
|
|
164
|
-
taskId: editingTaskId ||
|
|
174
|
+
taskId: editingTaskId || "",
|
|
165
175
|
title: text,
|
|
166
176
|
isCompleted: false,
|
|
167
177
|
order: checklistItems.length,
|
|
@@ -169,7 +179,7 @@ export function TaskForm(props) {
|
|
|
169
179
|
updatedAt: now,
|
|
170
180
|
},
|
|
171
181
|
]);
|
|
172
|
-
setNewChecklistItemText(
|
|
182
|
+
setNewChecklistItemText("");
|
|
173
183
|
};
|
|
174
184
|
const handleChecklistReorder = React.useCallback((event) => {
|
|
175
185
|
const { active, over } = event;
|
|
@@ -188,25 +198,27 @@ export function TaskForm(props) {
|
|
|
188
198
|
onChecklistItemsChange(reordered);
|
|
189
199
|
}, [checklistItems, onChecklistItemsChange]);
|
|
190
200
|
const formatAiAuthorLabel = (author) => {
|
|
191
|
-
const raw = String(author ||
|
|
201
|
+
const raw = String(author || "").trim();
|
|
192
202
|
const cleaned = author
|
|
193
203
|
.trim()
|
|
194
|
-
.replace(/^ai-profile-/i,
|
|
195
|
-
.replace(/[-_]+/g,
|
|
196
|
-
.replace(/\s+/g,
|
|
197
|
-
if (!cleaned ||
|
|
198
|
-
|
|
204
|
+
.replace(/^ai-profile-/i, "")
|
|
205
|
+
.replace(/[-_]+/g, " ")
|
|
206
|
+
.replace(/\s+/g, " ");
|
|
207
|
+
if (!cleaned ||
|
|
208
|
+
cleaned === "ai" ||
|
|
209
|
+
raw.toLowerCase().startsWith("ai-profile-"))
|
|
210
|
+
return "AI Agent";
|
|
199
211
|
const label = cleaned
|
|
200
|
-
.split(
|
|
212
|
+
.split(" ")
|
|
201
213
|
.map((part) => part.charAt(0).toUpperCase() + part.slice(1))
|
|
202
|
-
.join(
|
|
214
|
+
.join(" ");
|
|
203
215
|
return `AI Agent - ${label}`;
|
|
204
216
|
};
|
|
205
217
|
const resolvedAssigneeOptions = ensureAssigneeOptionPresent(assigneeOptions, assignee);
|
|
206
218
|
const currentAssigneeKind = getAssigneeKind(assignee);
|
|
207
219
|
const currentAssigneeLabel = getAssigneeLabel(assignee, resolvedAssigneeOptions);
|
|
208
220
|
const actorOptionByValue = React.useMemo(() => new Map(resolvedAssigneeOptions.map((option) => [String(option.value), option])), [resolvedAssigneeOptions]);
|
|
209
|
-
const currentAssigneeOption = actorOptionByValue.get(String(assignee ||
|
|
221
|
+
const currentAssigneeOption = actorOptionByValue.get(String(assignee || "unassigned"));
|
|
210
222
|
const currentAssigneeColor = currentTask?.assigneeActor?.color || currentAssigneeOption?.color;
|
|
211
223
|
const actorFallbackById = React.useMemo(() => {
|
|
212
224
|
const entries = [
|
|
@@ -223,28 +235,18 @@ export function TaskForm(props) {
|
|
|
223
235
|
initiative.id,
|
|
224
236
|
getInitiativeReferenceLabel(initiative) || initiative.id,
|
|
225
237
|
])), [initiatives]);
|
|
226
|
-
const taskActivityItems = React.useMemo(() => {
|
|
227
|
-
if (Array.isArray(currentTask?.activity) && currentTask.activity.length > 0) {
|
|
228
|
-
return currentTask.activity;
|
|
229
|
-
}
|
|
230
|
-
return comments.map((comment) => ({
|
|
231
|
-
id: `comment:${comment.id}`,
|
|
232
|
-
type: 'comment',
|
|
233
|
-
timestamp: comment.timestamp,
|
|
234
|
-
comment
|
|
235
|
-
}));
|
|
236
|
-
}, [comments, currentTask?.activity]);
|
|
238
|
+
const taskActivityItems = React.useMemo(() => getTaskActivityItems({ activity: currentTask?.activity, comments }), [comments, currentTask?.activity]);
|
|
237
239
|
const formatActivityFieldValue = (field, value) => {
|
|
238
|
-
const normalizedValue = String(value ||
|
|
240
|
+
const normalizedValue = String(value || "").trim();
|
|
239
241
|
if (!normalizedValue)
|
|
240
242
|
return undefined;
|
|
241
|
-
if (field ===
|
|
243
|
+
if (field === "assignee") {
|
|
242
244
|
return getAssigneeLabel(normalizedValue, resolvedAssigneeOptions);
|
|
243
245
|
}
|
|
244
|
-
if (field ===
|
|
246
|
+
if (field === "workstreamId") {
|
|
245
247
|
return workstreamLabelById.get(normalizedValue);
|
|
246
248
|
}
|
|
247
|
-
if (field ===
|
|
249
|
+
if (field === "initiativeId") {
|
|
248
250
|
return initiativeLabelById.get(normalizedValue);
|
|
249
251
|
}
|
|
250
252
|
return undefined;
|
|
@@ -252,26 +254,81 @@ export function TaskForm(props) {
|
|
|
252
254
|
const resolveActorLabel = React.useCallback((actorId, actorType, actorLabel) => {
|
|
253
255
|
if (actorLabel && actorLabel.trim())
|
|
254
256
|
return actorLabel.trim();
|
|
255
|
-
const raw = String(actorId ||
|
|
257
|
+
const raw = String(actorId || "").trim();
|
|
256
258
|
if (!raw)
|
|
257
|
-
return actorType ===
|
|
259
|
+
return actorType === "ai" ? "AI Agent" : t("taskForm.you");
|
|
258
260
|
const fallback = actorFallbackById.get(raw);
|
|
259
261
|
if (fallback?.label)
|
|
260
262
|
return fallback.label;
|
|
261
|
-
const option = actorOptionByValue.get(raw) ||
|
|
263
|
+
const option = actorOptionByValue.get(raw) ||
|
|
264
|
+
actorOptionByValue.get(raw.toLowerCase());
|
|
262
265
|
if (option?.label)
|
|
263
266
|
return option.label;
|
|
264
|
-
if (actorType ===
|
|
267
|
+
if (actorType === "ai")
|
|
265
268
|
return formatAiAuthorLabel(raw);
|
|
266
269
|
return getAssigneeLabel(raw, resolvedAssigneeOptions) || raw;
|
|
267
|
-
}, [
|
|
270
|
+
}, [
|
|
271
|
+
actorFallbackById,
|
|
272
|
+
actorOptionByValue,
|
|
273
|
+
formatAiAuthorLabel,
|
|
274
|
+
resolvedAssigneeOptions,
|
|
275
|
+
]);
|
|
276
|
+
const resolveActivityActorPresentation = React.useCallback(({ actorId, actorType, actorProfile, fallbackKind, }) => {
|
|
277
|
+
const actorRef = String(actorId || "").trim();
|
|
278
|
+
const normalizedActor = actorRef.toLowerCase();
|
|
279
|
+
const actorFallback = actorFallbackById.get(actorRef);
|
|
280
|
+
const actorOption = actorOptionByValue.get(actorRef) ||
|
|
281
|
+
actorOptionByValue.get(normalizedActor);
|
|
282
|
+
const resolvedActor = actorProfile || actorFallback || actorOption || null;
|
|
283
|
+
const resolvedKind = actorType === "system"
|
|
284
|
+
? "system"
|
|
285
|
+
: actorType === "ai" || resolvedActor?.kind === "ai"
|
|
286
|
+
? "agent"
|
|
287
|
+
: actorType === "human" || resolvedActor?.kind === "human"
|
|
288
|
+
? "member"
|
|
289
|
+
: actorOption?.kind === "agent"
|
|
290
|
+
? "agent"
|
|
291
|
+
: actorOption?.kind === "member"
|
|
292
|
+
? "member"
|
|
293
|
+
: fallbackKind ||
|
|
294
|
+
(normalizedActor === "" ||
|
|
295
|
+
normalizedActor === "user" ||
|
|
296
|
+
normalizedActor === "human"
|
|
297
|
+
? "member"
|
|
298
|
+
: "agent");
|
|
299
|
+
const label = resolvedKind === "system"
|
|
300
|
+
? "System"
|
|
301
|
+
: resolveActorLabel(actorRef, resolvedKind === "agent" ? "ai" : "human", resolvedActor?.label || null);
|
|
302
|
+
const color = resolvedActor?.color;
|
|
303
|
+
const iconName = String(resolvedActor?.icon ||
|
|
304
|
+
(resolvedKind === "agent"
|
|
305
|
+
? "Bot"
|
|
306
|
+
: resolvedKind === "member"
|
|
307
|
+
? "User"
|
|
308
|
+
: "ClipboardList"));
|
|
309
|
+
const ActorIcon = IconMap[iconName] ||
|
|
310
|
+
(resolvedKind === "agent"
|
|
311
|
+
? Bot
|
|
312
|
+
: resolvedKind === "member"
|
|
313
|
+
? User
|
|
314
|
+
: ClipboardList);
|
|
315
|
+
return { kind: resolvedKind, label, color, ActorIcon };
|
|
316
|
+
}, [
|
|
317
|
+
actorFallbackById,
|
|
318
|
+
actorOptionByValue,
|
|
319
|
+
resolveActorLabel,
|
|
320
|
+
]);
|
|
268
321
|
const latestActivityActorLabel = React.useMemo(() => {
|
|
269
|
-
const latestItem = taskActivityItems[
|
|
322
|
+
const latestItem = taskActivityItems[taskActivityItems.length - 1];
|
|
270
323
|
if (!latestItem)
|
|
271
324
|
return null;
|
|
272
|
-
if (latestItem.type ===
|
|
325
|
+
if (latestItem.type === "comment") {
|
|
273
326
|
const comment = latestItem.comment;
|
|
274
|
-
return resolveActorLabel(comment.author, comment.actor?.kind ===
|
|
327
|
+
return resolveActorLabel(comment.author, comment.actor?.kind === "ai"
|
|
328
|
+
? "ai"
|
|
329
|
+
: comment.actor?.kind === "human"
|
|
330
|
+
? "human"
|
|
331
|
+
: null, comment.actor?.label || null);
|
|
275
332
|
}
|
|
276
333
|
const event = latestItem.event;
|
|
277
334
|
return resolveActorLabel(event.actor, event.actorType, event.actorProfile?.label || null);
|
|
@@ -281,158 +338,233 @@ export function TaskForm(props) {
|
|
|
281
338
|
if (!completedAt)
|
|
282
339
|
return null;
|
|
283
340
|
for (const item of taskActivityItems) {
|
|
284
|
-
if (item.type !==
|
|
341
|
+
if (item.type !== "event")
|
|
285
342
|
continue;
|
|
286
343
|
const event = item.event;
|
|
287
344
|
const nextStatus = event.details?.changes?.status?.to;
|
|
288
|
-
if (nextStatus ===
|
|
345
|
+
if (nextStatus === "done" || nextStatus === "cancelled") {
|
|
289
346
|
return resolveActorLabel(event.actor, event.actorType, event.actorProfile?.label || null);
|
|
290
347
|
}
|
|
291
348
|
}
|
|
292
349
|
return latestActivityActorLabel;
|
|
293
|
-
}, [
|
|
294
|
-
|
|
295
|
-
|
|
350
|
+
}, [
|
|
351
|
+
currentTask?.completedAt,
|
|
352
|
+
latestActivityActorLabel,
|
|
353
|
+
resolveActorLabel,
|
|
354
|
+
taskActivityItems,
|
|
355
|
+
]);
|
|
356
|
+
const createdByLabel = currentTask?.createdByActor?.label ||
|
|
357
|
+
resolveActorLabel(currentTask?.createdBy || null, currentTask?.createdByActor?.kind === "ai"
|
|
358
|
+
? "ai"
|
|
359
|
+
: currentTask?.createdByActor?.kind === "human"
|
|
360
|
+
? "human"
|
|
361
|
+
: null, currentTask?.createdByActor?.label || null);
|
|
296
362
|
const updatedByLabel = latestActivityActorLabel;
|
|
297
363
|
const activityCount = taskActivityItems.length;
|
|
364
|
+
React.useLayoutEffect(() => {
|
|
365
|
+
if (!editingTaskId)
|
|
366
|
+
return;
|
|
367
|
+
const endNode = commentsEndRef.current;
|
|
368
|
+
if (!endNode)
|
|
369
|
+
return;
|
|
370
|
+
const thread = endNode.parentElement;
|
|
371
|
+
const scrollToLatest = () => {
|
|
372
|
+
if (typeof endNode.scrollIntoView === "function") {
|
|
373
|
+
endNode.scrollIntoView({ block: "end" });
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
if (thread) {
|
|
377
|
+
thread.scrollTop = thread.scrollHeight;
|
|
378
|
+
}
|
|
379
|
+
};
|
|
380
|
+
scrollToLatest();
|
|
381
|
+
const frame = window.requestAnimationFrame(scrollToLatest);
|
|
382
|
+
return () => window.cancelAnimationFrame(frame);
|
|
383
|
+
}, [activityCount, commentsEndRef, currentTask?.id, editingTaskId]);
|
|
384
|
+
const activitySidebar = editingTaskId ? (_jsxs("aside", { className: formStyles.activitySidebar, "aria-label": t("taskForm.commentsSectionTitle"), children: [_jsxs("div", { className: formStyles.activitySidebarHeader, children: [_jsx("h2", { className: formStyles.activitySidebarTitle, children: t("taskForm.commentsSectionTitle") }), _jsx("span", { className: formStyles.activitySidebarCount, children: activityCount })] }), _jsxs("div", { className: `${formStyles.commentPanel} ${formStyles.activitySidebarPanel}`.trim(), children: [_jsxs("div", { className: formStyles.commentThread, children: [activityCount === 0 && (_jsx("div", { className: formStyles.emptyComments, children: t("taskForm.noComments") })), taskActivityItems.map((item) => {
|
|
385
|
+
if (item.type === "comment") {
|
|
386
|
+
const c = item.comment;
|
|
387
|
+
const actorPresentation = resolveActivityActorPresentation({
|
|
388
|
+
actorId: c.author,
|
|
389
|
+
actorType: c.actor?.kind === "ai"
|
|
390
|
+
? "ai"
|
|
391
|
+
: c.actor?.kind === "human"
|
|
392
|
+
? "human"
|
|
393
|
+
: null,
|
|
394
|
+
actorProfile: c.actor,
|
|
395
|
+
});
|
|
396
|
+
const commentKind = actorPresentation.kind === "agent" ? "agent" : "member";
|
|
397
|
+
const { ActorIcon: CommentActorIcon } = actorPresentation;
|
|
398
|
+
const authorColor = actorPresentation.color;
|
|
399
|
+
const authorLabel = actorPresentation.label;
|
|
400
|
+
const commentAccentStyle = authorColor && hexToRgba(authorColor, 0.35)
|
|
401
|
+
? {
|
|
402
|
+
background: `linear-gradient(135deg, ${hexToRgba(authorColor, 0.18)} 0%, ${hexToRgba(authorColor, 0.1)} 100%)`,
|
|
403
|
+
borderColor: hexToRgba(authorColor, 0.35),
|
|
404
|
+
}
|
|
405
|
+
: undefined;
|
|
406
|
+
return (_jsxs("div", { className: `${formStyles.comment} ${commentKind === "agent" ? formStyles.commentAi : formStyles.commentUser}`, children: [_jsxs("div", { className: formStyles.commentHeader, children: [_jsx("span", { className: formStyles.commentAuthor, style: authorColor ? { color: authorColor } : undefined, children: _jsxs(_Fragment, { children: [_jsx(CommentActorIcon, { size: 12, strokeWidth: 1.5 }), " ", authorLabel] }) }), _jsx("span", { className: formStyles.commentTime, children: formatDate(c.timestamp, {
|
|
407
|
+
month: "short",
|
|
408
|
+
day: "numeric",
|
|
409
|
+
hour: "2-digit",
|
|
410
|
+
minute: "2-digit",
|
|
411
|
+
}) })] }), _jsx("div", { className: formStyles.commentText, style: commentAccentStyle, children: _jsx(Markdown, { onTaskIdClick: onOpenTaskById, children: c.text }) })] }, c.id));
|
|
412
|
+
}
|
|
413
|
+
const event = item.event;
|
|
414
|
+
const actorPresentation = resolveActivityActorPresentation({
|
|
415
|
+
actorId: event.actor,
|
|
416
|
+
actorType: event.actorType,
|
|
417
|
+
actorProfile: event.actorProfile,
|
|
418
|
+
fallbackKind: "system",
|
|
419
|
+
});
|
|
420
|
+
const { ActorIcon: EventActorIcon } = actorPresentation;
|
|
421
|
+
const authorColor = actorPresentation.color;
|
|
422
|
+
const authorLabel = actorPresentation.label;
|
|
423
|
+
const eventKind = actorPresentation.kind;
|
|
424
|
+
const changeEntries = Object.entries(event.details?.changes || {});
|
|
425
|
+
const primaryChangeField = getTaskEventPrimaryChangeField(event);
|
|
426
|
+
const displayedChangeEntries = changeEntries.length > 1 && primaryChangeField
|
|
427
|
+
? changeEntries.filter(([field]) => field !== primaryChangeField)
|
|
428
|
+
: changeEntries.length > 1
|
|
429
|
+
? changeEntries
|
|
430
|
+
: [];
|
|
431
|
+
return (_jsxs("div", { className: `${formStyles.comment} ${formStyles.activityEvent} ${eventKind === "system" ? formStyles.activityEventSystem : ""}`, children: [_jsxs("div", { className: `${formStyles.commentHeader} ${formStyles.activityEventHeader}`, children: [_jsx("span", { className: formStyles.commentAuthor, style: authorColor ? { color: authorColor } : undefined, children: _jsxs(_Fragment, { children: [_jsx(EventActorIcon, { size: 12, strokeWidth: 1.5 }), " ", authorLabel] }) }), _jsx("span", { className: formStyles.commentTime, children: formatDate(event.createdAt, {
|
|
432
|
+
month: "short",
|
|
433
|
+
day: "numeric",
|
|
434
|
+
hour: "2-digit",
|
|
435
|
+
minute: "2-digit",
|
|
436
|
+
}) })] }), _jsxs("div", { className: formStyles.activityEventText, children: [_jsx("div", { children: summarizeTaskEvent(event, formatActivityFieldValue) }), displayedChangeEntries.length > 0 && (_jsx("div", { className: formStyles.activityEventChanges, children: displayedChangeEntries.map(([field, change]) => (_jsx("div", { className: formStyles.activityEventChange, children: summarizeTaskChange(field, change, formatActivityFieldValue) }, field))) }))] })] }, item.id));
|
|
437
|
+
}), _jsx("div", { ref: commentsEndRef })] }), _jsxs("div", { className: formStyles.commentInputArea, children: [_jsx("textarea", { className: formStyles.commentInput, placeholder: t("taskForm.commentPlaceholder"), value: newCommentText, onChange: (e) => onNewCommentTextChange(e.target.value), onKeyDown: (e) => {
|
|
438
|
+
if (e.key === "Enter" && !e.shiftKey) {
|
|
439
|
+
e.preventDefault();
|
|
440
|
+
onAddComment();
|
|
441
|
+
}
|
|
442
|
+
}, rows: 1 }), _jsx("button", { type: "button", className: formStyles.sendCommentBtn, onClick: onAddComment, disabled: !newCommentText.trim(), "aria-label": "Send comment", title: "Send comment", children: _jsx(Send, { size: 16 }) })] })] })] })) : null;
|
|
298
443
|
return (_jsxs("form", { ref: formRef, onSubmit: onSubmit, className: `${styles.form} ${styles.appScrollbar} tf-scrollbar`, children: [error && _jsx("div", { className: formStyles.error, children: error }), isReadOnly && (_jsx("div", { className: `${formStyles.formNotice} tf-text-helper`, children: isDeletedReadOnly
|
|
299
|
-
?
|
|
300
|
-
:
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
}, title: isSelected ? 'Click to remove' : 'Click to add', children: [isSelected && _jsx(Check, { size: 12 }), opt.status === 'retired' ? `${opt.label} (Retired)` : opt.label] }, optionValue));
|
|
348
|
-
}) })) : (_jsxs("div", { className: styles.fieldIconWrapper, style: {
|
|
349
|
-
'--field-color': 'var(--text-primary)',
|
|
350
|
-
'--field-border': 'rgba(255, 255, 255, 0.1)',
|
|
351
|
-
'--field-bg': 'rgba(255, 255, 255, 0.02)'
|
|
352
|
-
}, children: [(() => {
|
|
353
|
-
const selectedOption = tax.options.find(o => String(o.value) === String(formTaxonomies[tax.id]));
|
|
354
|
-
const IconComp = selectedOption?.icon && Icons[selectedOption.icon] ? Icons[selectedOption.icon] : HelpCircle;
|
|
355
|
-
const color = selectedOption?.color || 'var(--text-secondary)';
|
|
356
|
-
return (_jsx("div", { className: styles.fieldIcon, style: { color: resolveColor(color) }, children: _jsx(IconComp, { size: 16 }) }));
|
|
357
|
-
})(), _jsxs("select", { value: formTaxonomies[tax.id] || '', onChange: (e) => onTaxonomyChange(tax.id, e.target.value), className: `${styles.select} ${styles.field_dynamic}`, required: tax.isRequired, children: [_jsxs("option", { value: "", children: ["Select ", tax.label, "..."] }), selectableOptions.map(opt => (_jsx("option", { value: opt.value, children: opt.status === 'retired' ? `${opt.label} (Retired)` : opt.label }, opt.value)))] })] })))] }, tax.id));
|
|
358
|
-
}) }), checklistEnabled && (_jsxs("div", { className: styles.specialistSection, children: [_jsxs("div", { className: styles.toggleHeading, onClick: () => onShowChecklistChange(!showChecklist), title: showChecklist ? 'Hide checklist' : 'Show checklist', children: [_jsxs("label", { className: styles.label, children: ["Checklist ", checklistItems.length > 0 && `(${completedChecklistCount}/${checklistItems.length})`] }), showChecklist ? _jsx(ChevronDown, { size: 14 }) : _jsx(ChevronRight, { size: 14 })] }), showChecklist && (_jsxs("div", { className: `${styles.dropdownList} ${formStyles.stackedList} ${formStyles.stackedListSpaced}`, children: [_jsxs("div", { className: styles.pathInputGroup, children: [_jsx("input", { type: "text", className: styles.input, placeholder: "Add checklist item", value: newChecklistItemText, onChange: (e) => setNewChecklistItemText(e.target.value), onKeyDown: (e) => {
|
|
359
|
-
if (e.key === 'Enter') {
|
|
360
|
-
e.preventDefault();
|
|
361
|
-
handleAddChecklistItem();
|
|
362
|
-
}
|
|
363
|
-
} }), _jsx("button", { type: "button", className: styles.secondaryHeaderBtn, onClick: handleAddChecklistItem, disabled: !newChecklistItemText.trim(), children: "Add" })] }), checklistItems.length > 0 ? (_jsx(DndContext, { sensors: checklistSensors, collisionDetection: closestCenter, onDragEnd: handleChecklistReorder, children: _jsx(SortableContext, { items: checklistItems.map((item) => item.id), strategy: verticalListSortingStrategy, children: checklistItems.map((item, index) => (_jsx(SortableChecklistRow, { id: item.id || `checklist-${index}`, item: item, onToggle: () => {
|
|
364
|
-
const now = new Date().toISOString();
|
|
365
|
-
onChecklistItemsChange(checklistItems.map((entry, entryIndex) => entryIndex === index
|
|
366
|
-
? { ...entry, isCompleted: !entry.isCompleted, updatedAt: now }
|
|
367
|
-
: entry));
|
|
368
|
-
}, onRemove: () => onChecklistItemsChange(checklistItems
|
|
369
|
-
.filter((_, entryIndex) => entryIndex !== index)
|
|
370
|
-
.map((entry, entryIndex) => ({ ...entry, order: entryIndex }))) }, item.id || `checklist-${index}`))) }) })) : (_jsx("div", { className: `${formStyles.settingsHint} tf-text-helper`, children: "No checklist items yet." }))] }))] }))] }), _jsxs("div", { children: [_jsx(TaskContextUpload, { taskId: editingTaskId, taskReferenceLabel: currentTaskReferenceLabel, workspaceId: currentWorkspaceId, apiBaseUrl: apiBaseUrl, contextFiles: contextFiles, onAddContextFile: onAddContextFile, onRemoveContextFile: onRemoveContextFile, onUpdateContextCaption: onUpdateContextCaption }), editingTaskId && (_jsxs("div", { className: styles.specialistSection, children: [_jsxs("div", { className: styles.toggleHeading, onClick: () => onShowCommentsChange(!showComments), title: showComments ? t('taskForm.hideComments') : t('taskForm.showComments'), children: [_jsxs("label", { className: styles.label, children: [t('taskForm.commentsSectionTitle'), " ", activityCount > 0 && `(${activityCount})`] }), showComments ? _jsx(ChevronDown, { size: 14 }) : _jsx(ChevronRight, { size: 14 })] }), showComments && (_jsxs("div", { className: formStyles.commentPanel, children: [_jsxs("div", { className: formStyles.commentThread, children: [activityCount === 0 && (_jsx("div", { className: formStyles.emptyComments, children: t('taskForm.noComments') })), taskActivityItems.map((item) => {
|
|
371
|
-
if (item.type === 'comment') {
|
|
372
|
-
const c = item.comment;
|
|
373
|
-
const authorRef = String(c.author || '').trim();
|
|
374
|
-
const normalizedAuthor = authorRef.toLowerCase();
|
|
375
|
-
const actorFallback = actorFallbackById.get(authorRef);
|
|
376
|
-
const actorOption = actorOptionByValue.get(authorRef) || actorOptionByValue.get(normalizedAuthor);
|
|
377
|
-
const resolvedActor = c.actor || actorFallback || actorOption || null;
|
|
378
|
-
const commentKind = c.actor?.kind === 'human'
|
|
379
|
-
? 'member'
|
|
380
|
-
: c.actor?.kind === 'ai'
|
|
381
|
-
? 'agent'
|
|
382
|
-
: actorFallback?.kind === 'human'
|
|
383
|
-
? 'member'
|
|
384
|
-
: actorFallback?.kind === 'ai'
|
|
385
|
-
? 'agent'
|
|
386
|
-
: actorOption?.kind || ((normalizedAuthor === '' || normalizedAuthor === 'user' || normalizedAuthor === 'human') ? 'member' : 'agent');
|
|
387
|
-
const authorLabel = c.actor?.label
|
|
388
|
-
|| actorFallback?.label
|
|
389
|
-
|| actorOption?.label
|
|
390
|
-
|| (authorRef ? getAssigneeLabel(authorRef, resolvedAssigneeOptions) : '')
|
|
391
|
-
|| (commentKind === 'agent' ? formatAiAuthorLabel(authorRef || 'ai') : t('taskForm.you'));
|
|
392
|
-
const authorColor = c.actor?.color || actorFallback?.color || actorOption?.color;
|
|
393
|
-
const iconName = String(resolvedActor?.icon || (commentKind === 'agent' ? 'Bot' : 'User'));
|
|
394
|
-
const CommentActorIcon = IconMap[iconName]
|
|
395
|
-
|| (commentKind === 'agent' ? Bot : User);
|
|
396
|
-
return (_jsxs("div", { className: `${formStyles.comment} ${commentKind === 'agent' ? formStyles.commentAi : formStyles.commentUser}`, children: [_jsxs("div", { className: formStyles.commentHeader, children: [_jsx("span", { className: formStyles.commentAuthor, style: authorColor ? { color: authorColor } : undefined, children: _jsxs(_Fragment, { children: [_jsx(CommentActorIcon, { size: 12, strokeWidth: 1.5 }), " ", authorLabel] }) }), _jsx("span", { className: formStyles.commentTime, children: formatDate(c.timestamp, {
|
|
397
|
-
month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit'
|
|
398
|
-
}) })] }), _jsx("div", { className: formStyles.commentText, style: authorColor ? {
|
|
399
|
-
background: `linear-gradient(135deg, ${hexToRgba(authorColor, 0.18)} 0%, ${hexToRgba(authorColor, 0.10)} 100%)`,
|
|
400
|
-
borderColor: hexToRgba(authorColor, 0.35),
|
|
401
|
-
} : undefined, children: _jsx(Markdown, { onTaskIdClick: onOpenTaskById, children: c.text }) })] }, c.id));
|
|
402
|
-
}
|
|
403
|
-
const event = item.event;
|
|
404
|
-
const actorRef = String(event.actor || '').trim();
|
|
405
|
-
const normalizedAuthor = actorRef.toLowerCase();
|
|
406
|
-
const actorFallback = actorFallbackById.get(actorRef);
|
|
407
|
-
const actorOption = actorOptionByValue.get(actorRef) || actorOptionByValue.get(normalizedAuthor);
|
|
408
|
-
const resolvedActor = event.actorProfile || actorFallback || actorOption || null;
|
|
409
|
-
const eventKind = event.actorType === 'ai'
|
|
410
|
-
? 'agent'
|
|
411
|
-
: event.actorType === 'human'
|
|
412
|
-
? 'member'
|
|
413
|
-
: 'system';
|
|
414
|
-
const authorLabel = event.actorProfile?.label
|
|
415
|
-
|| actorFallback?.label
|
|
416
|
-
|| actorOption?.label
|
|
417
|
-
|| (eventKind === 'system'
|
|
418
|
-
? 'System'
|
|
419
|
-
: actorRef
|
|
420
|
-
? getAssigneeLabel(actorRef, resolvedAssigneeOptions)
|
|
421
|
-
: eventKind === 'agent'
|
|
422
|
-
? formatAiAuthorLabel('ai')
|
|
423
|
-
: t('taskForm.you'));
|
|
424
|
-
const authorColor = event.actorProfile?.color || actorFallback?.color || actorOption?.color;
|
|
425
|
-
const iconName = String(resolvedActor?.icon || (eventKind === 'agent' ? 'Bot' : eventKind === 'member' ? 'User' : 'ClipboardList'));
|
|
426
|
-
const EventActorIcon = IconMap[iconName]
|
|
427
|
-
|| (eventKind === 'agent' ? Bot : eventKind === 'member' ? User : ClipboardList);
|
|
428
|
-
const changeEntries = Object.entries(event.details?.changes || {});
|
|
429
|
-
return (_jsxs("div", { className: `${formStyles.comment} ${formStyles.activityEvent} ${eventKind === 'system' ? formStyles.activityEventSystem : ''}`, children: [_jsxs("div", { className: `${formStyles.commentHeader} ${formStyles.activityEventHeader}`, children: [_jsx("span", { className: formStyles.commentAuthor, style: authorColor ? { color: authorColor } : undefined, children: _jsxs(_Fragment, { children: [_jsx(EventActorIcon, { size: 12, strokeWidth: 1.5 }), " ", authorLabel] }) }), _jsx("span", { className: formStyles.commentTime, children: formatDate(event.createdAt, {
|
|
430
|
-
month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit'
|
|
431
|
-
}) })] }), _jsxs("div", { className: formStyles.activityEventText, children: [_jsx("div", { children: summarizeTaskEvent(event, formatActivityFieldValue) }), changeEntries.length > 1 && (_jsx("div", { className: formStyles.activityEventChanges, children: changeEntries.map(([field, change]) => (_jsx("span", { className: formStyles.activityEventChange, children: summarizeTaskChange(field, change, formatActivityFieldValue) }, field))) }))] })] }, item.id));
|
|
432
|
-
}), _jsx("div", { ref: commentsEndRef })] }), _jsxs("div", { className: formStyles.commentInputArea, children: [_jsx("textarea", { className: formStyles.commentInput, placeholder: t('taskForm.commentPlaceholder'), value: newCommentText, onChange: (e) => onNewCommentTextChange(e.target.value), onKeyDown: (e) => {
|
|
433
|
-
if (e.key === 'Enter' && !e.shiftKey) {
|
|
434
|
-
e.preventDefault();
|
|
435
|
-
onAddComment();
|
|
444
|
+
? "Deleted tasks are read-only. Restore the task to make changes."
|
|
445
|
+
: "Archived tasks are read-only. Unarchive the task to make changes." })), _jsxs("fieldset", { disabled: isReadOnly, className: `${formStyles.formFieldsetReset} ${editingTaskId ? formStyles.formLayoutWithSidebar : formStyles.formLayout}`.trim(), children: [_jsxs("div", { className: formStyles.formMainColumn, children: [_jsxs("div", { className: styles.field, children: [_jsxs("div", { className: styles.labelRow, children: [_jsx("label", { className: styles.label, children: "Title" }), assignee && (_jsx("span", { title: `Assigned to: ${currentAssigneeLabel}`, className: [
|
|
446
|
+
formStyles.assigneeIndicator,
|
|
447
|
+
currentAssigneeKind === "agent"
|
|
448
|
+
? formStyles.assigneeIndicatorAgent
|
|
449
|
+
: currentAssigneeKind === "member"
|
|
450
|
+
? formStyles.assigneeIndicatorUser
|
|
451
|
+
: formStyles.assigneeIndicatorUnassigned,
|
|
452
|
+
].join(" "), style: currentAssigneeColor
|
|
453
|
+
? { color: currentAssigneeColor }
|
|
454
|
+
: undefined, children: currentAssigneeKind === "agent" ? (_jsx(Bot, { size: 20 })) : currentAssigneeKind === "member" ? (_jsx(User, { size: 20 })) : (_jsx(HelpCircle, { size: 20 })) }))] }), _jsx("input", { type: "text", value: title, onChange: (e) => onTitleChange(e.target.value), placeholder: "What needs to be done?", className: styles.input, required: true, maxLength: 255, autoFocus: true })] }), _jsxs("div", { className: styles.field, children: [_jsxs("div", { className: styles.labelRow, children: [_jsx("label", { className: styles.label, children: "Description" }), _jsx("button", { type: "button", className: styles.helpLink, onClick: () => onShowMarkdownHelpChange(!showMarkdownHelp), title: "Markdown Help", tabIndex: -1, children: _jsx(HelpCircle, { size: 14 }) })] }), showMarkdownHelp && (_jsxs("div", { className: formStyles.markdownHelp, children: [_jsxs("div", { className: formStyles.helpHeader, children: [_jsx("span", { children: "Markdown Guide" }), _jsx("button", { onClick: () => onShowMarkdownHelpChange(false), className: formStyles.helpClose, children: _jsx(X, { size: 12 }) })] }), _jsxs("div", { className: formStyles.helpGrid, children: [_jsx("div", { children: _jsx("code", { children: "**bold**" }) }), _jsx("div", { children: _jsx("code", { children: "_italic_" }) }), _jsx("div", { children: _jsx("code", { children: "- list" }) }), _jsx("div", { children: _jsx("code", { children: "1. list" }) }), _jsx("div", { className: formStyles.helpGridItem, children: _jsx("code", { children: "`inline code`" }) }), _jsxs("div", { className: formStyles.helpGridItem, children: [_jsx("code", { children: "```" }), _jsx("br", {}), _jsx("code", { children: "code block" }), _jsx("br", {}), _jsx("code", { children: "```" })] }), _jsx("div", { children: _jsx("code", { children: "[link](url)" }) })] })] })), descriptionFocused || !description ? (_jsx("textarea", { className: `${styles.textarea} ${formStyles.descriptionSurface}`, placeholder: "What needs to be done? (Markdown supported)", value: description, onChange: (e) => onDescriptionChange(e.target.value), onFocus: () => onDescriptionFocusedChange(true), onBlur: () => onDescriptionFocusedChange(false), rows: 9, autoFocus: descriptionFocused })) : (_jsx("div", { className: `${styles.textarea} ${formStyles.descriptionSurface} ${formStyles.markdownPreview} ${formStyles.markdownPreviewContainer}`, onClick: () => onDescriptionFocusedChange(true), children: _jsx(Markdown, { onTaskIdClick: onOpenTaskById, children: description }) }))] }), _jsxs("div", { className: formStyles.compactMetaSection, children: [_jsxs("div", { className: formStyles.compactMetaGrid, children: [_jsxs("div", { className: `${styles.field} ${formStyles.compactMetaField}`, children: [_jsx("label", { id: "category-label", className: styles.label, children: taxonomyDisplayLabels?.category || "Category" }), _jsx(TaxonomyDropdown, { value: category, options: categoryOptions, onChange: (value) => onCategoryChange(String(value)), required: true, ariaLabelledBy: "category-label" })] }), _jsxs("div", { className: `${styles.field} ${formStyles.compactMetaField}`, children: [_jsx("label", { id: "type-label", className: styles.label, children: taxonomyDisplayLabels?.type || "Type" }), _jsx(TaxonomyDropdown, { value: type, options: typeOptions, onChange: (value) => onTypeChange(String(value)), ariaLabelledBy: "type-label" })] }), _jsxs("div", { className: `${styles.field} ${formStyles.compactMetaField}`, children: [_jsx("label", { htmlFor: "task-form-scheduled-date", className: styles.label, children: t("taskForm.scheduledLabel") }), _jsx("input", { id: "task-form-scheduled-date", type: "date", value: scheduledDate, onChange: (e) => onScheduledDateChange(e.target.value), className: `${styles.input} ${styles.taskFormDateInput}` })] }), _jsxs("div", { className: `${styles.field} ${formStyles.compactMetaField}`, children: [_jsx("label", { id: "assignee-label", className: styles.label, children: "Assigned To" }), _jsx(TaxonomyDropdown, { value: assignee || "unassigned", options: resolvedAssigneeOptions, onChange: (value) => onAssigneeChange(String(value)), ariaLabelledBy: "assignee-label" })] }), _jsx("div", { className: formStyles.compactMetaField, children: _jsx(LevelSelector, { label: taxonomyDisplayLabels?.priority || "Priority", options: priorities, value: priority, onChange: onPriorityChange, type: "priority", showSelectedLabel: false }) }), _jsxs("div", { className: `${styles.field} ${formStyles.compactMetaField}`, children: [_jsx("label", { htmlFor: "task-form-due-date", className: styles.label, children: t("taskForm.dueLabel") }), _jsx("input", { id: "task-form-due-date", type: "date", value: dueDate, onChange: (e) => onDueDateChange(e.target.value), className: `${styles.input} ${styles.taskFormDateInput}` }), dueBeforeScheduledWarning && (_jsx("span", { className: `${formStyles.taskFormDateWarning} ${formStyles.compactMetaFieldHint}`, children: t("taskForm.dueBeforeScheduled") })), overdueWarning && (_jsx("span", { className: `${formStyles.taskFormDateError} ${formStyles.compactMetaFieldHint}`, children: t("taskForm.overdue") }))] }), manualComplexityEnabled && (_jsx("div", { className: formStyles.compactMetaField, children: _jsx(LevelSelector, { label: "Complexity", options: [
|
|
455
|
+
{
|
|
456
|
+
value: 1,
|
|
457
|
+
label: "Tiny",
|
|
458
|
+
color: "#10b981",
|
|
459
|
+
icon: "Gauge",
|
|
460
|
+
},
|
|
461
|
+
{
|
|
462
|
+
value: 2,
|
|
463
|
+
label: "Low",
|
|
464
|
+
color: "#14b8a6",
|
|
465
|
+
icon: "Gauge",
|
|
466
|
+
},
|
|
467
|
+
{
|
|
468
|
+
value: 3,
|
|
469
|
+
label: "Medium",
|
|
470
|
+
color: "#3b82f6",
|
|
471
|
+
icon: "Gauge",
|
|
472
|
+
},
|
|
473
|
+
{
|
|
474
|
+
value: 4,
|
|
475
|
+
label: "High",
|
|
476
|
+
color: "#8b5cf6",
|
|
477
|
+
icon: "Gauge",
|
|
478
|
+
},
|
|
479
|
+
{
|
|
480
|
+
value: 5,
|
|
481
|
+
label: "Epic",
|
|
482
|
+
color: "#d946ef",
|
|
483
|
+
icon: "Gauge",
|
|
484
|
+
},
|
|
485
|
+
], value: complexity, onChange: onComplexityChange, type: "general" }) }))] }), !editingTaskId && (_jsxs("div", { className: formStyles.createWorkstreamRow, children: [_jsx("label", { className: styles.label, children: "Attach to workstream" }), _jsxs("div", { className: styles.pathInputGroup, children: [manualWorkstreamEntry ? (_jsx("input", { type: "text", className: styles.input, value: workstreamInput, onChange: (e) => onWorkstreamInputChange(e.target.value), placeholder: "Type a workstream name or reference", "aria-label": "Attach to workstream" })) : (_jsx(TaxonomyDropdown, { value: workstreamInput, options: workstreamOptions, onChange: (value) => onWorkstreamInputChange(String(value)), placeholder: "Select workstream...", ariaLabel: "Attach to workstream", className: formStyles.createWorkstreamDropdown })), _jsx("button", { type: "button", className: styles.secondaryHeaderBtn, onClick: () => {
|
|
486
|
+
if (manualWorkstreamEntry) {
|
|
487
|
+
setManualWorkstreamEntry(false);
|
|
488
|
+
if (!hasMatchingWorkstreamSelection) {
|
|
489
|
+
onWorkstreamInputChange("");
|
|
490
|
+
}
|
|
491
|
+
return;
|
|
436
492
|
}
|
|
437
|
-
|
|
493
|
+
setManualWorkstreamEntry(true);
|
|
494
|
+
}, "aria-label": manualWorkstreamEntry
|
|
495
|
+
? "Use workstream dropdown"
|
|
496
|
+
: "Enter workstream by name", title: manualWorkstreamEntry
|
|
497
|
+
? "Use workstream dropdown"
|
|
498
|
+
: "Enter workstream by name", children: _jsx(Plus, { size: 14 }) })] })] }))] }), _jsxs("div", { className: formStyles.sectionBlock, children: [_jsx("div", { className: formStyles.taxonomyFieldsGrid, children: visibleTaxonomies.map((tax) => {
|
|
499
|
+
if (tax.id === "approach" && tax.isSystem)
|
|
500
|
+
return null; // Workflow stays hidden until the dedicated refactor lands.
|
|
501
|
+
const selectedRawValue = formTaxonomies[tax.id];
|
|
502
|
+
const selectedValues = Array.isArray(selectedRawValue)
|
|
503
|
+
? selectedRawValue.map((value) => String(value))
|
|
504
|
+
: selectedRawValue !== undefined &&
|
|
505
|
+
selectedRawValue !== null &&
|
|
506
|
+
selectedRawValue !== ""
|
|
507
|
+
? [String(selectedRawValue)]
|
|
508
|
+
: [];
|
|
509
|
+
const selectableOptions = tax.options.filter((option) => option.status !== "retired" ||
|
|
510
|
+
selectedValues.includes(String(option.value)));
|
|
511
|
+
return (_jsxs("div", { className: styles.field, children: [_jsx("label", { className: styles.label, children: tax.label }), tax.description && (_jsx("div", { className: `${formStyles.settingsHint} tf-text-helper`, children: tax.description })), tax.widgetType === "level" ? (_jsx(LevelSelector, { label: tax.label, options: selectableOptions.map((option) => ({
|
|
512
|
+
...option,
|
|
513
|
+
label: option.status === "retired"
|
|
514
|
+
? `${option.label} (Retired)`
|
|
515
|
+
: option.label,
|
|
516
|
+
})), value: formTaxonomies[tax.id] || "", onChange: (val) => onTaxonomyChange(tax.id, val), type: "general", hideLabel: true })) : tax.multiSelect ? (_jsx("div", { className: `${styles.dropdownList} ${formStyles.softSectionSurface}`, children: selectableOptions.map((opt) => {
|
|
517
|
+
const optionValue = String(opt.value);
|
|
518
|
+
const isSelected = selectedValues.includes(optionValue);
|
|
519
|
+
return (_jsxs("button", { type: "button", className: `${formStyles.specialistChip} ${isSelected ? formStyles.specialistChipActive : ""}`, onClick: () => {
|
|
520
|
+
const nextValues = isSelected
|
|
521
|
+
? selectedValues.filter((value) => value !== optionValue)
|
|
522
|
+
: [...selectedValues, optionValue];
|
|
523
|
+
onTaxonomyChange(tax.id, nextValues);
|
|
524
|
+
}, title: isSelected ? "Click to remove" : "Click to add", children: [isSelected && _jsx(Check, { size: 12 }), opt.status === "retired"
|
|
525
|
+
? `${opt.label} (Retired)`
|
|
526
|
+
: opt.label] }, optionValue));
|
|
527
|
+
}) })) : (_jsxs("div", { className: styles.fieldIconWrapper, style: {
|
|
528
|
+
"--field-color": "var(--text-primary)",
|
|
529
|
+
"--field-border": "rgba(255, 255, 255, 0.1)",
|
|
530
|
+
"--field-bg": "rgba(255, 255, 255, 0.02)",
|
|
531
|
+
}, children: [(() => {
|
|
532
|
+
const selectedOption = tax.options.find((o) => String(o.value) ===
|
|
533
|
+
String(formTaxonomies[tax.id]));
|
|
534
|
+
const IconComp = selectedOption?.icon &&
|
|
535
|
+
Icons[selectedOption.icon]
|
|
536
|
+
? Icons[selectedOption.icon]
|
|
537
|
+
: HelpCircle;
|
|
538
|
+
const color = selectedOption?.color || "var(--text-secondary)";
|
|
539
|
+
return (_jsx("div", { className: styles.fieldIcon, style: { color: resolveColor(color) }, children: _jsx(IconComp, { size: 16 }) }));
|
|
540
|
+
})(), _jsxs("select", { value: formTaxonomies[tax.id] || "", onChange: (e) => onTaxonomyChange(tax.id, e.target.value), className: `${styles.select} ${styles.field_dynamic}`, required: tax.isRequired, children: [_jsxs("option", { value: "", children: ["Select ", tax.label, "..."] }), selectableOptions.map((opt) => (_jsx("option", { value: opt.value, children: opt.status === "retired"
|
|
541
|
+
? `${opt.label} (Retired)`
|
|
542
|
+
: opt.label }, opt.value)))] })] }))] }, tax.id));
|
|
543
|
+
}) }), checklistEnabled && (_jsxs("div", { className: styles.specialistSection, children: [_jsxs("div", { className: styles.toggleHeading, onClick: () => onShowChecklistChange(!showChecklist), title: showChecklist ? "Hide checklist" : "Show checklist", children: [_jsxs("label", { className: styles.label, children: ["Checklist", " ", checklistItems.length > 0 &&
|
|
544
|
+
`(${completedChecklistCount}/${checklistItems.length})`] }), showChecklist ? (_jsx(ChevronDown, { size: 14 })) : (_jsx(ChevronRight, { size: 14 }))] }), showChecklist && (_jsxs("div", { className: `${styles.dropdownList} ${formStyles.stackedList} ${formStyles.stackedListSpaced}`, children: [_jsxs("div", { className: styles.pathInputGroup, children: [_jsx("input", { type: "text", className: styles.input, placeholder: "Add checklist item", value: newChecklistItemText, onChange: (e) => setNewChecklistItemText(e.target.value), onKeyDown: (e) => {
|
|
545
|
+
if (e.key === "Enter") {
|
|
546
|
+
e.preventDefault();
|
|
547
|
+
handleAddChecklistItem();
|
|
548
|
+
}
|
|
549
|
+
} }), _jsx("button", { type: "button", className: styles.secondaryHeaderBtn, onClick: handleAddChecklistItem, disabled: !newChecklistItemText.trim(), children: "Add" })] }), checklistItems.length > 0 ? (_jsx(DndContext, { sensors: checklistSensors, collisionDetection: closestCenter, onDragEnd: handleChecklistReorder, children: _jsx(SortableContext, { items: checklistItems.map((item) => item.id), strategy: verticalListSortingStrategy, children: checklistItems.map((item, index) => (_jsx(SortableChecklistRow, { id: item.id || `checklist-${index}`, item: item, onToggle: () => {
|
|
550
|
+
const now = new Date().toISOString();
|
|
551
|
+
onChecklistItemsChange(checklistItems.map((entry, entryIndex) => entryIndex === index
|
|
552
|
+
? {
|
|
553
|
+
...entry,
|
|
554
|
+
isCompleted: !entry.isCompleted,
|
|
555
|
+
updatedAt: now,
|
|
556
|
+
}
|
|
557
|
+
: entry));
|
|
558
|
+
}, onRemove: () => onChecklistItemsChange(checklistItems
|
|
559
|
+
.filter((_, entryIndex) => entryIndex !== index)
|
|
560
|
+
.map((entry, entryIndex) => ({
|
|
561
|
+
...entry,
|
|
562
|
+
order: entryIndex,
|
|
563
|
+
}))) }, item.id || `checklist-${index}`))) }) })) : (_jsx("div", { className: `${formStyles.settingsHint} tf-text-helper`, children: "No checklist items yet." }))] }))] }))] }), _jsxs("div", { children: [_jsx(TaskContextUpload, { taskId: editingTaskId, taskReferenceLabel: currentTaskReferenceLabel, workspaceId: currentWorkspaceId, apiBaseUrl: apiBaseUrl, contextFiles: contextFiles, onAddContextFile: onAddContextFile, onRemoveContextFile: onRemoveContextFile, onUpdateContextCaption: onUpdateContextCaption }), _jsx("div", { className: `${formStyles.taskFormLifecycle} ${formStyles.taskFormMetaDivider}`, children: _jsx("div", { className: formStyles.taskFormMetaGrid, children: currentTask && (_jsxs(_Fragment, { children: [_jsxs("div", { className: formStyles.taskFormDateRow, children: [_jsx("span", { className: formStyles.taskFormDateLabel, children: t("taskForm.createdLabel") }), _jsxs("span", { className: formStyles.taskFormMetaStack, children: [_jsx("span", { className: formStyles.taskFormDateValue, children: createdLabel || t("taskForm.emptyValue") }), _jsx("span", { className: formStyles.taskFormMetaActor, children: createdByLabel
|
|
564
|
+
? `by ${createdByLabel}`
|
|
565
|
+
: t("taskForm.emptyValue") })] })] }), _jsxs("div", { className: formStyles.taskFormDateRow, children: [_jsx("span", { className: formStyles.taskFormDateLabel, children: t("taskForm.updatedLabel") }), _jsxs("span", { className: formStyles.taskFormMetaStack, children: [_jsx("span", { className: formStyles.taskFormDateValue, children: updatedLabel || t("taskForm.emptyValue") }), _jsx("span", { className: formStyles.taskFormMetaActor, children: updatedByLabel
|
|
566
|
+
? `by ${updatedByLabel}`
|
|
567
|
+
: t("taskForm.emptyValue") })] })] }), _jsxs("div", { className: formStyles.taskFormDateRow, children: [_jsx("span", { className: formStyles.taskFormDateLabel, children: t("taskForm.completedLabel") }), _jsxs("span", { className: formStyles.taskFormMetaStack, children: [_jsx("span", { className: formStyles.taskFormDateValue, children: completedLabel || t("taskForm.emptyValue") }), _jsx("span", { className: formStyles.taskFormMetaActor, children: completedByLabel
|
|
568
|
+
? `by ${completedByLabel}`
|
|
569
|
+
: t("taskForm.emptyValue") })] })] })] })) }) })] })] }), activitySidebar, _jsxs("div", { className: formStyles.keyboardHint, children: ["Press ", _jsx("kbd", { children: navigator.platform.includes("Mac") ? "⌥" : "Alt" }), " to toggle"] })] })] }));
|
|
438
570
|
}
|