@taskforcehq/taskforce 0.3.328 → 0.3.329
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Taskforce.module.css +233 -34
- package/dist/TaskforceCore.js +237 -230
- package/dist/annotatedAttachments/AnnotatedAttachmentSessionStore.js +1 -1
- package/dist/cli.js +34 -0
- package/dist/cliCloudTargetGuard.d.ts +5 -0
- package/dist/cliCloudTargetGuard.js +11 -0
- package/dist/components/features/AnnotatedAttachmentWorkspace.js +24 -8
- package/dist/components/features/TaskSettings.d.ts +12 -1
- package/dist/components/features/TaskSettings.js +147 -40
- package/dist/components/features/agentCapabilities/AgentCapabilitiesPrototype.d.ts +1 -1
- package/dist/components/features/planning/PlanningModule.js +2 -5
- package/dist/components/features/planning/PlanningTaskOrderList.d.ts +1 -1
- package/dist/components/features/planning/PlanningTaskOrderList.js +17 -6
- package/dist/components/features/planning/planningTaskArrangement.d.ts +1 -0
- package/dist/components/features/planning/planningTaskArrangement.js +5 -5
- package/dist/components/features/taskforceAgentEditorModel.js +3 -1
- package/dist/components/task/TaskCard.d.ts +5 -0
- package/dist/components/task/TaskCard.js +65 -10
- package/dist/components/task/TaskImageReviews.js +14 -5
- package/dist/components/task/TaskKanban.d.ts +6 -1
- package/dist/components/task/TaskKanban.js +152 -6
- package/dist/components/task/TaskList.d.ts +5 -0
- package/dist/components/task/TaskList.js +3 -3
- package/dist/components/ui/PasswordInput.d.ts +6 -0
- package/dist/components/ui/PasswordInput.js +11 -0
- package/dist/components/ui/TopNoticeLayer.js +1 -1
- package/dist/components/views/AppShellHeader.js +32 -11
- package/dist/components/views/PlanComparisonPage.js +12 -0
- package/dist/components/views/StandaloneLayout.js +7 -31
- package/dist/components/views/WidgetView.js +3 -3
- package/dist/components/views/panels/PlanningDrawer.js +5 -12
- package/dist/components/views/standalone/modals/AccountSettingsModal.js +26 -17
- package/dist/config/envSchema.js +12 -0
- package/dist/config/localServiceRuntimeContract.d.ts +13 -0
- package/dist/config/localServiceRuntimeContract.js +135 -0
- package/dist/core/AiProfileService.js +46 -8
- package/dist/core/DeletedTaskLifecycleService.d.ts +1 -0
- package/dist/core/DeletedTaskLifecycleService.js +37 -6
- package/dist/core/TaskAttachmentLinksDurableMutationService.d.ts +3 -2
- package/dist/core/TaskAttachmentLinksDurableMutationService.js +33 -9
- package/dist/core/Taskforce.d.ts +62 -2
- package/dist/core/Taskforce.js +635 -32
- package/dist/core/types.d.ts +6 -0
- package/dist/hooks/sync/useDurableLocalOutboxDrain.d.ts +1 -1
- package/dist/hooks/sync/useDurableTaskCreateMutation.js +1 -1
- package/dist/hooks/sync/useDurableTaskMetadataMutation.d.ts +1 -0
- package/dist/hooks/sync/useDurableTaskMetadataMutation.js +1 -0
- package/dist/hooks/sync/useDurableTaskStatusMutation.d.ts +1 -0
- package/dist/hooks/tasks/taskHttpMutation.js +1 -0
- package/dist/hooks/tasks/useCloudTaskSearch.d.ts +12 -0
- package/dist/hooks/tasks/useCloudTaskSearch.js +71 -0
- package/dist/hooks/tasks/useTaskDescriptionImageDraftRecovery.d.ts +16 -0
- package/dist/hooks/tasks/useTaskDescriptionImageDraftRecovery.js +197 -0
- package/dist/hooks/tasks/useTaskEditorNavigation.d.ts +16 -3
- package/dist/hooks/tasks/useTaskEditorNavigation.js +72 -13
- package/dist/hooks/tasks/useTaskFormActions.d.ts +6 -0
- package/dist/hooks/tasks/useTaskFormActions.js +65 -12
- package/dist/hooks/ui/useSettingsModel.d.ts +24 -2
- package/dist/hooks/ui/useSettingsModel.js +13 -2
- package/dist/hooks/ui/useSettingsPersistence.d.ts +12 -1
- package/dist/hooks/ui/useSettingsPersistence.js +74 -47
- package/dist/hooks/useFilterSort.d.ts +7 -3
- package/dist/hooks/useFilterSort.js +15 -5
- package/dist/hooks/useTaskData.d.ts +10 -1
- package/dist/hooks/useTaskData.js +216 -7
- package/dist/hooks/useTaskMutations.d.ts +10 -2
- package/dist/hooks/useTaskMutations.js +223 -43
- package/dist/hooks/useTaskforce.d.ts +37 -9
- package/dist/hooks/useTaskforce.js +349 -39
- package/dist/hooks/useUiNotice.d.ts +4 -0
- package/dist/hooks/useUiNotice.js +4 -0
- package/dist/mcp/adminWorkspaceRegistrar.js +3 -3
- package/dist/mcp/documentAssetRegistrar.js +44 -35
- package/dist/mcp/documentHelpers.d.ts +1 -0
- package/dist/mcp/documentHelpers.js +7 -3
- package/dist/mcp/runtime.js +172 -42
- package/dist/mcp/taskPlanningRegistrar.js +41 -38
- package/dist/mcp/toolCallCompatibility.js +38 -1
- package/dist/mcp/toolCatalog.d.ts +1 -0
- package/dist/migrations/taskSchemaMigrations.js +12 -0
- package/dist/runtime/appGateDefinitions.d.ts +30 -6
- package/dist/runtime/appGateDefinitions.js +6 -6
- package/dist/runtime/appGates.d.ts +12 -13
- package/dist/runtime/appGates.js +29 -13
- package/dist/runtime/nodeAppGates.d.ts +8 -0
- package/dist/runtime/nodeAppGates.js +27 -0
- package/dist/server/cloudCollectionRead.d.ts +1 -1
- package/dist/server/cloudCollectionRead.js +2 -2
- package/dist/server/index.d.ts +3 -0
- package/dist/server/index.js +15 -3
- package/dist/server/localServiceLease.d.ts +3 -0
- package/dist/server/localServiceLease.js +9 -1
- package/dist/server/localWorkspaceSyncServerRuntime.js +10 -3
- package/dist/server/rateLimit.d.ts +1 -0
- package/dist/server/rateLimit.js +4 -0
- package/dist/server/realtimeSyncWs.js +36 -12
- package/dist/server/routes/admin.js +1 -0
- package/dist/server/routes/agentRoles.js +3 -2
- package/dist/server/routes/agentSkills.js +3 -2
- package/dist/server/routes/agents.js +118 -16
- package/dist/server/routes/annotatedAttachments.js +3 -2
- package/dist/server/routes/auth.js +3 -1
- package/dist/server/routes/durableTaskHttpRouters.d.ts +9 -2
- package/dist/server/routes/localService.js +2 -0
- package/dist/server/routes/primitives.d.ts +2 -0
- package/dist/server/routes/syncAuxRoutes.js +5 -2
- package/dist/server/routes/syncPullApplyRoutes.js +23 -4
- package/dist/server/routes/syncV3FeedRoutes.js +18 -16
- package/dist/server/routes/syncV3LocalCaptureRoutes.js +28 -2
- package/dist/server/routes/syncV3LocalFeedApplyRoutes.d.ts +1 -0
- package/dist/server/routes/syncV3LocalFeedApplyRoutes.js +26 -0
- package/dist/server/routes/syncV3OperationRoutes.js +10 -0
- package/dist/server/routes/tasks.js +103 -11
- package/dist/server/routes/workspaces.js +43 -1
- package/dist/server/workspaceDeletionAdmission.d.ts +23 -0
- package/dist/server/workspaceDeletionAdmission.js +50 -0
- package/dist/service/localServiceCli.js +16 -0
- package/dist/service/localServiceClientLifecycle.js +36 -10
- package/dist/services/codexAppServerModelGateway.d.ts +112 -0
- package/dist/services/codexAppServerModelGateway.js +438 -0
- package/dist/services/taskforceAgentMcpBridge.d.ts +7 -1
- package/dist/services/taskforceAgentMcpBridge.js +23 -3
- package/dist/shared/taskforceAgentDefinition.js +7 -1
- package/dist/shared/taskforceAgentModels.d.ts +1 -0
- package/dist/shared/taskforceAgentModels.js +30 -1
- package/dist/storage/postgresAdapter.d.ts +20 -3
- package/dist/storage/postgresAdapter.js +106 -14
- package/dist/storage/postgresWorker.js +13 -4
- package/dist/storage/postgresWorkerProtocol.d.ts +29 -0
- package/dist/storage/postgresWorkerProtocol.js +30 -0
- package/dist/sync/cloudSyncApi.d.ts +4 -1
- package/dist/sync/cloudSyncApi.js +7 -2
- package/dist/sync/contentSyncState.d.ts +1 -1
- package/dist/sync/coordinator/localSyncExecutionPermitStore.d.ts +1 -0
- package/dist/sync/coordinator/localSyncExecutionPermitStore.js +7 -1
- package/dist/sync/coordinator/localWorkspaceSyncRunner.js +6 -2
- package/dist/sync/engine/workspaceSyncEngineBrowserGatewayRemoteServices.js +20 -2
- package/dist/sync/engine/workspaceSyncEngineLocalServices.d.ts +4 -0
- package/dist/sync/engine/workspaceSyncEngineLocalServices.js +11 -0
- package/dist/sync/engine/workspaceSyncEngineRemoteServices.d.ts +11 -0
- package/dist/sync/engine/workspaceSyncEngineRemoteServices.js +2 -1
- package/dist/sync/engine/workspaceSyncEngineServerRemoteServices.js +31 -4
- package/dist/sync/engine/workspaceSyncEngineServerRunnerOperations.d.ts +3 -1
- package/dist/sync/engine/workspaceSyncEngineServerRunnerOperations.js +25 -8
- package/dist/sync/engine/workspaceSyncEngineTransfers.d.ts +1 -1
- package/dist/sync/engine/workspaceSyncEngineTransfers.js +8 -3
- package/dist/sync/syncService.d.ts +2 -0
- package/dist/sync/syncService.js +4 -1
- package/dist/sync/v3/combinedFeedCapability.js +4 -0
- package/dist/sync/v3/durableTaskAttachmentLinksMutationRouter.d.ts +2 -0
- package/dist/sync/v3/durableTaskAttachmentLinksMutationRouter.js +11 -1
- package/dist/sync/v3/durableTaskChecklistMutationRouter.d.ts +2 -0
- package/dist/sync/v3/durableTaskChecklistMutationRouter.js +4 -0
- package/dist/sync/v3/durableTaskHttpBulkMutationRouter.d.ts +1 -0
- package/dist/sync/v3/durableTaskHttpBulkMutationRouter.js +30 -8
- package/dist/sync/v3/durableTaskHttpRootMutationRouter.d.ts +5 -3
- package/dist/sync/v3/durableTaskHttpRootMutationRouter.js +27 -2
- package/dist/sync/v3/localTaskAttachmentLinksOutboxService.d.ts +2 -0
- package/dist/sync/v3/localTaskAttachmentLinksOutboxService.js +47 -7
- package/dist/sync/v3/localTaskCreateOutboxService.js +15 -6
- package/dist/sync/v3/localTaskMetadataClient.d.ts +1 -0
- package/dist/sync/v3/localTaskMetadataClient.js +1 -0
- package/dist/sync/v3/localTaskMetadataOutboxHandler.js +5 -1
- package/dist/sync/v3/localTaskMetadataOutboxService.d.ts +2 -0
- package/dist/sync/v3/localTaskMetadataOutboxService.js +7 -0
- package/dist/sync/v3/localTaskRestoreOutboxService.js +14 -6
- package/dist/sync/v3/localTaskStatusClient.d.ts +1 -0
- package/dist/sync/v3/localTaskStatusClient.js +6 -1
- package/dist/sync/v3/localTaskStatusOutboxService.d.ts +2 -0
- package/dist/sync/v3/localTaskStatusOutboxService.js +54 -13
- package/dist/sync/v3/syncDurabilityStore.d.ts +7 -0
- package/dist/sync/v3/syncDurabilityStore.js +31 -11
- package/dist/sync/v3/taskAttachmentLinksMutationService.d.ts +6 -4
- package/dist/sync/v3/taskAttachmentLinksMutationService.js +19 -10
- package/dist/sync/v3/taskChecklistMutationService.d.ts +4 -2
- package/dist/sync/v3/taskChecklistMutationService.js +4 -0
- package/dist/sync/v3/taskCreateMutationService.d.ts +2 -2
- package/dist/sync/v3/taskCreateMutationService.js +7 -4
- package/dist/sync/v3/taskMetadataMutationService.d.ts +3 -0
- package/dist/sync/v3/taskMetadataMutationService.js +4 -0
- package/dist/sync/v3/taskRestoreMutationService.d.ts +2 -2
- package/dist/sync/v3/taskRestoreMutationService.js +7 -4
- package/dist/sync/v3/taskStatusMutationService.d.ts +2 -0
- package/dist/sync/v3/taskStatusMutationService.js +4 -0
- package/dist/sync/v3/workflowAssignmentSync.d.ts +1 -0
- package/dist/sync/v3/workflowAssignmentSync.js +3 -0
- package/dist/sync/v3/workspaceSyncV2ProjectionExclusions.d.ts +1 -1
- package/dist/sync/v3/workspaceSyncV2ProjectionExclusions.js +13 -3
- package/dist/sync/v3/workspaceSyncV3AttachmentApplyObligationStore.d.ts +11 -0
- package/dist/sync/v3/workspaceSyncV3AttachmentApplyObligationStore.js +27 -10
- package/dist/sync/v3/workspaceSyncV3AttachmentAssetHydration.d.ts +2 -1
- package/dist/sync/v3/workspaceSyncV3AttachmentAssetHydration.js +52 -10
- package/dist/sync/v3/workspaceSyncV3CloudOperationHandler.js +9 -1
- package/dist/sync/v3/workspaceSyncV3CombinedFeedOrchestrator.js +8 -3
- package/dist/sync/v3/workspaceSyncV3CombinedLocalApply.js +51 -10
- package/dist/sync/v3/workspaceSyncV3CombinedRepairBaseline.d.ts +7 -0
- package/dist/sync/v3/workspaceSyncV3CombinedRepairBaseline.js +30 -18
- package/dist/sync/v3/workspaceSyncV3CombinedRepairMaterialization.d.ts +9 -4
- package/dist/sync/v3/workspaceSyncV3CombinedRepairMaterialization.js +98 -47
- package/dist/sync/v3/workspaceSyncV3CombinedRepairSnapshot.d.ts +10 -0
- package/dist/sync/v3/workspaceSyncV3CombinedRepairSnapshot.js +58 -21
- package/dist/sync/v3/workspaceSyncV3CombinedWireContract.d.ts +3 -1
- package/dist/sync/v3/workspaceSyncV3CombinedWireContract.js +18 -0
- package/dist/sync/v3/workspaceSyncV3CoverageRegistry.js +6 -0
- package/dist/sync/v3/workspaceSyncV3FeedProfile.d.ts +1 -1
- package/dist/sync/v3/workspaceSyncV3LegacyBoundaryAttestation.d.ts +1 -0
- package/dist/sync/v3/workspaceSyncV3LegacyBoundaryAttestation.js +23 -10
- package/dist/sync/v3/workspaceSyncV3NestedTaskApply.d.ts +3 -0
- package/dist/sync/v3/workspaceSyncV3NestedTaskApply.js +64 -13
- package/dist/sync/v3/workspaceSyncV3NestedTaskCodecs.d.ts +9 -0
- package/dist/sync/v3/workspaceSyncV3NestedTaskCodecs.js +26 -0
- package/dist/sync/v3/workspaceSyncV3NestedTaskProjection.d.ts +15 -5
- package/dist/sync/v3/workspaceSyncV3NestedTaskProjection.js +50 -14
- package/dist/sync/v3/workspaceSyncV3NestedTaskRepairSnapshot.d.ts +2 -1
- package/dist/sync/v3/workspaceSyncV3NestedTaskRepairSnapshot.js +11 -3
- package/dist/sync/v3/workspaceSyncV3OperationProtocol.d.ts +2 -0
- package/dist/sync/v3/workspaceSyncV3OperatorDiagnostics.d.ts +2 -0
- package/dist/sync/v3/workspaceSyncV3OperatorDiagnostics.js +19 -7
- package/dist/sync/v3/workspaceSyncV3TaskCommentAudit.js +9 -2
- package/dist/sync/v3/workspaceSyncV3TaskCoverCoverage.d.ts +7 -0
- package/dist/sync/v3/workspaceSyncV3TaskCoverCoverage.js +37 -0
- package/dist/sync/v3/workspaceSyncV3TaskCoverWireContract.d.ts +6 -0
- package/dist/sync/v3/workspaceSyncV3TaskCoverWireContract.js +9 -0
- package/dist/sync/workspacePullFeed.js +2 -1
- package/dist/sync/workspaceRepair.d.ts +3 -0
- package/dist/sync/workspaceRepair.js +67 -6
- package/dist/types.d.ts +47 -1
- package/dist/types.js +6 -0
- package/dist/ui/assets/{AiIdentityRosterCard-MZs7lVED.js → AiIdentityRosterCard-BZM4aQnM.js} +1 -1
- package/dist/ui/assets/AiProfilesModule-BbAEWtHH.js +1 -0
- package/dist/ui/assets/AnnotatedAttachmentWorkspace-B68Wg-RV.js +3 -0
- package/dist/ui/assets/AnnotatedAttachmentWorkspace-TzYu7pkG.css +1 -0
- package/dist/ui/assets/{AssetTraySortControl-Kf1pelSy.js → AssetTraySortControl-C8Rztyal.js} +1 -1
- package/dist/ui/assets/{ContextAttachmentManager-B8boOs6Q.js → ContextAttachmentManager-Cj3c7X9y.js} +2 -2
- package/dist/ui/assets/{DocumentWorkspace-DXH4XUsB.js → DocumentWorkspace-B5uvzems.js} +1 -1
- package/dist/ui/assets/{EntityActivityTimeline-BtSTq0ON.js → EntityActivityTimeline-BVFvCB5R.js} +1 -1
- package/dist/ui/assets/PlanningModule-B5DWQOGz.js +1 -0
- package/dist/ui/assets/PlansPage-AY4cGWco.js +1 -0
- package/dist/ui/assets/TaskContextUpload-DZxTVesW.js +1 -0
- package/dist/ui/assets/TaskSettings-BeWAuyZj.js +12 -0
- package/dist/ui/assets/{TaskforceAgentsModule-BazdUwxZ.js → TaskforceAgentsModule-BifCYtjR.js} +6 -6
- package/dist/ui/assets/{WorkflowManagerModule-DWFspC-o.js → WorkflowManagerModule-DEr5di_r.js} +1 -1
- package/dist/ui/assets/index-I5zhdtgt.js +7 -0
- package/dist/ui/assets/index-VRFsx3wz.css +1 -0
- package/dist/ui/assets/{vendor-icons-BkFLXavV.js → vendor-icons-D9Lpw-j4.js} +1 -1
- package/dist/ui/index.html +3 -3
- package/dist/utils/taskActivity.d.ts +3 -2
- package/dist/utils/taskActivity.js +19 -2
- package/dist/utils/taskEditorReconciliation.d.ts +40 -0
- package/dist/utils/taskEditorReconciliation.js +217 -0
- package/dist/utils/taskNormalization.d.ts +1 -0
- package/dist/utils/taskNormalization.js +39 -0
- package/dist/utils/taskSearch.js +2 -2
- package/dist/utils/uiNotice.d.ts +4 -0
- package/package.json +16 -4
- package/dist/ui/assets/AiProfilesModule-C3i0LOxe.js +0 -1
- package/dist/ui/assets/AnnotatedAttachmentWorkspace-CBRZ8rca.css +0 -1
- package/dist/ui/assets/AnnotatedAttachmentWorkspace-DVZk5Tln.js +0 -3
- package/dist/ui/assets/PlanningModule-BPVyEa-f.js +0 -1
- package/dist/ui/assets/PlansPage-ChqgxALc.js +0 -1
- package/dist/ui/assets/TaskContextUpload-CB3ol0sN.js +0 -1
- package/dist/ui/assets/TaskSettings-g4ECKNOz.js +0 -12
- package/dist/ui/assets/index-BUplSsv_.js +0 -7
- package/dist/ui/assets/index-gz2EXuoK.css +0 -1
|
@@ -16,7 +16,7 @@ import { AssigneeIdentity } from '../ui/AssigneeIdentity';
|
|
|
16
16
|
const EMPTY_DELETED_RECORD_MAP = new Map();
|
|
17
17
|
const EMPTY_SELECTED_DELETED_IDS = new Set();
|
|
18
18
|
export const TaskList = forwardRef((props, ref) => {
|
|
19
|
-
const { tasks, archivedTasks, categories, types, priorities, taxonomyDisplayLabels, assigneeOptions = [], workstreams = [], initiatives = [], workspaceId = null, searchQuery, filterCategories, filterTypes, filterPriorities, filterStatus, filterAssignees = [], filterTaxonomies, sortBy, sortOrder, showArchive, taskScope, collapsedCategories, loadingTasks, filteredTasks, filteredArchive, groupedTasks, filteredDeletedTasks = [], groupedDeletedTasks = {}, copiedId, recentlyChangedTaskIds = [], showTaskCardStatusLabel = false, deletedTaskRecordByTaskId = EMPTY_DELETED_RECORD_MAP, selectedDeletedRecordIds = EMPTY_SELECTED_DELETED_IDS, onDeletedSelectionChange, trashControls, onSearchChange, onFilterCategoriesChange, onFilterTypesChange, onFilterPrioritiesChange, onFilterStatusChange, onFilterAssigneesChange, onTaxonomyFilterChange, onSortByChange, onSortOrderChange, onShowArchiveChange, onTaskScopeChange, onClearFilters, onToggleCategory, onEditTask, onUpdateTask, taskReferences, onCopyId, onToggleInProgress, onToggleReview, onToggleComplete, onToggleCancel, onSetStatus, onArchiveTask, onBulkArchive, onUnarchive, onDelete, onFetchArchive, onAddTaskToCategory, supplementalTasks = [], taxonomies, } = props;
|
|
19
|
+
const { tasks, archivedTasks, categories, types, priorities, taxonomyDisplayLabels, assigneeOptions = [], workstreams = [], initiatives = [], workspaceId = null, searchQuery, filterCategories, filterTypes, filterPriorities, filterStatus, filterAssignees = [], filterTaxonomies, sortBy, sortOrder, showArchive, taskScope, collapsedCategories, loadingTasks, filteredTasks, filteredArchive, groupedTasks, filteredDeletedTasks = [], groupedDeletedTasks = {}, copiedId, recentlyChangedTaskIds = [], showTaskCardStatusLabel = false, showTaskCardDescription = true, showTaskCardAttachments = true, showTaskCardChecklistProgress = true, showTaskCardLatestActivity = true, tintTaskCardActivityBackground = false, deletedTaskRecordByTaskId = EMPTY_DELETED_RECORD_MAP, selectedDeletedRecordIds = EMPTY_SELECTED_DELETED_IDS, onDeletedSelectionChange, trashControls, onSearchChange, onFilterCategoriesChange, onFilterTypesChange, onFilterPrioritiesChange, onFilterStatusChange, onFilterAssigneesChange, onTaxonomyFilterChange, onSortByChange, onSortOrderChange, onShowArchiveChange, onTaskScopeChange, onClearFilters, onToggleCategory, onEditTask, onUpdateTask, taskReferences, onCopyId, onToggleInProgress, onToggleReview, onToggleComplete, onToggleCancel, onSetStatus, onArchiveTask, onBulkArchive, onUnarchive, onDelete, onFetchArchive, onAddTaskToCategory, supplementalTasks = [], taxonomies, } = props;
|
|
20
20
|
const changedTaskIdSet = React.useMemo(() => new Set(recentlyChangedTaskIds), [recentlyChangedTaskIds]);
|
|
21
21
|
const workstreamById = React.useMemo(() => new Map(workstreams.map((workstream) => [workstream.id, workstream])), [workstreams]);
|
|
22
22
|
const initiativeById = React.useMemo(() => new Map(initiatives.map((initiative) => [initiative.id, initiative])), [initiatives]);
|
|
@@ -132,11 +132,11 @@ export const TaskList = forwardRef((props, ref) => {
|
|
|
132
132
|
: undefined;
|
|
133
133
|
return (_jsx(TaskCard, { task: task, taskWorkstream: taskWorkstream, taskInitiative: taskInitiative, workspaceId: workspaceId, searchQuery: searchQuery, copiedId: copiedId, taxonomies: taxonomies, types: types, priorities: priorities, assigneeOptions: assigneeOptions, workstreams: workstreams, onClick: onEditTask, onAssigneeChange: handleAssigneeChange, onWorkstreamChange: (nextTask, workstreamId) => (onUpdateTask(nextTask.id, { workstreamId })), taskReferences: taskReferences, onCopyId: onCopyId, onToggleInProgress: onToggleInProgress, onToggleReview: onToggleReview, onToggleComplete: onToggleComplete, onToggleCancel: onToggleCancel, onSetStatus: onSetStatus, onArchiveTask: onArchiveTask, onUnarchive: activeReadOnlyMode ? onUnarchive : undefined, onDelete: activeReadOnlyMode ? onDelete : undefined, categories: categories, isRecentlyChanged: changedTaskIdSet.has(task.id), readOnlyMode: activeReadOnlyMode, selectable: Boolean(deletedRecord && onDeletedSelectionChange), selected: Boolean(deletedRecord && selectedDeletedRecordIds.has(deletedRecord.id)), onSelectionChange: deletedRecord
|
|
134
134
|
? (selected) => onDeletedSelectionChange?.(deletedRecord.id, selected)
|
|
135
|
-
: undefined, showStatusLabel: showTaskCardStatusLabel }, task.id));
|
|
135
|
+
: undefined, showStatusLabel: showTaskCardStatusLabel, showDescription: showTaskCardDescription, showAttachments: showTaskCardAttachments, showChecklistProgress: showTaskCardChecklistProgress, showLatestActivity: showTaskCardLatestActivity, tintActivityBackground: tintTaskCardActivityBackground }, task.id));
|
|
136
136
|
})())) }))] }, category));
|
|
137
137
|
}) })), !useScopeToggle && showArchive && filteredArchive.length > 0 && (_jsxs("div", { className: styles.archiveList, children: [_jsx("div", { className: styles.archiveHeader, children: "Archived" }), filteredArchive.map((task) => ((() => {
|
|
138
138
|
const { taskWorkstream, taskInitiative } = resolveTaskHierarchy(task);
|
|
139
|
-
return (_jsx(TaskCard, { task: task, taskWorkstream: taskWorkstream, taskInitiative: taskInitiative, workspaceId: workspaceId, searchQuery: searchQuery, copiedId: copiedId, isArchived: true, types: types, assigneeOptions: assigneeOptions, workstreams: workstreams, onClick: onEditTask, onCopyId: onCopyId, onSetStatus: onSetStatus, onUnarchive: onUnarchive, onDelete: onDelete, deleteActionTitle: "Delete Permanently", taskReferences: taskReferences, categories: categories, showStatusLabel: showTaskCardStatusLabel }, task.id));
|
|
139
|
+
return (_jsx(TaskCard, { task: task, taskWorkstream: taskWorkstream, taskInitiative: taskInitiative, workspaceId: workspaceId, searchQuery: searchQuery, copiedId: copiedId, isArchived: true, types: types, assigneeOptions: assigneeOptions, workstreams: workstreams, onClick: onEditTask, onCopyId: onCopyId, onSetStatus: onSetStatus, onUnarchive: onUnarchive, onDelete: onDelete, deleteActionTitle: "Delete Permanently", taskReferences: taskReferences, categories: categories, showStatusLabel: showTaskCardStatusLabel, showDescription: showTaskCardDescription, showAttachments: showTaskCardAttachments, showChecklistProgress: showTaskCardChecklistProgress, showLatestActivity: showTaskCardLatestActivity, tintActivityBackground: tintTaskCardActivityBackground }, task.id));
|
|
140
140
|
})()))] }))] }));
|
|
141
141
|
});
|
|
142
142
|
TaskList.displayName = 'TaskList';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import React, { type InputHTMLAttributes } from 'react';
|
|
2
|
+
export interface PasswordInputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'type'> {
|
|
3
|
+
visibilityLabel?: string;
|
|
4
|
+
visibilityToggleTestId?: string;
|
|
5
|
+
}
|
|
6
|
+
export declare const PasswordInput: React.ForwardRefExoticComponent<PasswordInputProps & React.RefAttributes<HTMLInputElement>>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef, useState } from 'react';
|
|
3
|
+
import { Eye, EyeOff } from 'lucide-react';
|
|
4
|
+
import styles from './PasswordInput.module.css';
|
|
5
|
+
export const PasswordInput = forwardRef(function PasswordInput({ className, disabled, visibilityLabel = 'password', visibilityToggleTestId, ...inputProps }, ref) {
|
|
6
|
+
const [visible, setVisible] = useState(false);
|
|
7
|
+
const actionLabel = `${visible ? 'Hide' : 'Show'} ${visibilityLabel}`;
|
|
8
|
+
return (_jsxs("span", { className: styles.wrapper, children: [_jsx("input", { ...inputProps, ref: ref, className: `${className || ''} ${styles.input}`.trim(), type: visible ? 'text' : 'password', disabled: disabled }), _jsx("button", { type: "button", className: styles.toggle, "aria-label": actionLabel, title: actionLabel, "data-testid": visibilityToggleTestId, disabled: disabled, onClick: () => setVisible((current) => !current), children: visible
|
|
9
|
+
? _jsx(EyeOff, { size: 18, "aria-hidden": "true" })
|
|
10
|
+
: _jsx(Eye, { size: 18, "aria-hidden": "true" }) })] }));
|
|
11
|
+
});
|
|
@@ -13,5 +13,5 @@ export function TopNoticeLayer({ notice, onDismiss, placement = 'top' }) {
|
|
|
13
13
|
const layerClassName = placement === 'task'
|
|
14
14
|
? `${styles.topNoticeLayer} ${styles.taskNoticeLayer}`
|
|
15
15
|
: styles.topNoticeLayer;
|
|
16
|
-
return (_jsx("div", { className: layerClassName, children: _jsxs("div", { className: `${styles.topNotice} ${toneClass}`, role: statusRole, "aria-live": notice.tone === 'error' ? 'assertive' : 'polite', children: [notice.tone === 'error' ? _jsx(AlertTriangle, { size: 14 }) : notice.tone === 'success' ? _jsx(Check, { size: 14 }) : _jsx(Info, { size: 14 }), _jsx("span", { className: styles.topNoticeMessage, children: notice.message }), notice.actionLabel && notice.onAction && (_jsx("button", { type: "button", className: "tf-button-ghost tf-button-compact", onClick: notice.
|
|
16
|
+
return (_jsx("div", { className: layerClassName, children: _jsxs("div", { className: `${styles.topNotice} ${toneClass}`, role: statusRole, "aria-live": notice.tone === 'error' ? 'assertive' : 'polite', children: [notice.tone === 'error' ? _jsx(AlertTriangle, { size: 14 }) : notice.tone === 'success' ? _jsx(Check, { size: 14 }) : _jsx(Info, { size: 14 }), _jsx("span", { className: styles.topNoticeMessage, children: notice.message }), notice.actionLabel && notice.onAction && (_jsx("button", { type: "button", className: `${notice.actionPrimary ? 'tf-button-primary' : 'tf-button-ghost'} tf-button-compact`, onClick: notice.onAction, children: notice.actionLabel })), notice.secondaryActionLabel && notice.onSecondaryAction && (_jsx("button", { type: "button", className: "tf-button-ghost tf-button-compact", onClick: notice.onSecondaryAction, children: notice.secondaryActionLabel })), onDismiss && notice.dismissible !== false && (_jsx("button", { type: "button", className: styles.topNoticeDismiss, onClick: onDismiss, "aria-label": "Dismiss notice", title: "Dismiss notice", children: _jsx(X, { size: 14 }) }))] }) }));
|
|
17
17
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useCallback, useLayoutEffect, useRef, useState } from 'react';
|
|
2
|
+
import { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';
|
|
3
3
|
import styles from '../../Taskforce.module.css';
|
|
4
4
|
import shellStyles from './WorkspaceShell.module.css';
|
|
5
5
|
import logoLight from '../../assets/branding/logos/logo_light_no_bg.svg';
|
|
@@ -10,6 +10,7 @@ export function AppShellHeader({ projectName, currentWorkspaceId, brandLabel = '
|
|
|
10
10
|
const headerTitleRef = useRef(null);
|
|
11
11
|
const projectNameGroupRef = useRef(null);
|
|
12
12
|
const [projectNameFits, setProjectNameFits] = useState(true);
|
|
13
|
+
const [workspaceCopyStatus, setWorkspaceCopyStatus] = useState('idle');
|
|
13
14
|
const logo = isLightThemeFamily(theme) ? logoDark : logoLight;
|
|
14
15
|
const normalizedProjectName = String(projectName || '').trim();
|
|
15
16
|
const workspaceIdForCopy = String(currentWorkspaceId || '').trim();
|
|
@@ -56,20 +57,28 @@ export function AppShellHeader({ projectName, currentWorkspaceId, brandLabel = '
|
|
|
56
57
|
resizeObserver.observe(projectEl);
|
|
57
58
|
return () => resizeObserver.disconnect();
|
|
58
59
|
}, [measureProjectNameFit, normalizedProjectName, meta]);
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
if (workspaceCopyStatus === 'idle')
|
|
62
|
+
return;
|
|
63
|
+
const timer = window.setTimeout(() => setWorkspaceCopyStatus('idle'), 1600);
|
|
64
|
+
return () => window.clearTimeout(timer);
|
|
65
|
+
}, [workspaceCopyStatus]);
|
|
59
66
|
const showProjectName = canShowProjectName && projectNameFits;
|
|
60
|
-
const copyWorkspaceId = () => {
|
|
67
|
+
const copyWorkspaceId = async () => {
|
|
61
68
|
if (!workspaceIdForCopy || typeof navigator === 'undefined')
|
|
62
69
|
return;
|
|
63
70
|
const clipboard = navigator.clipboard;
|
|
64
|
-
if (!clipboard?.writeText)
|
|
65
|
-
|
|
66
|
-
void clipboard.writeText(workspaceIdForCopy).catch(() => undefined);
|
|
67
|
-
};
|
|
68
|
-
const handleProjectNameKeyDown = (event) => {
|
|
69
|
-
if (event.key !== 'Enter' && event.key !== ' ')
|
|
71
|
+
if (!clipboard?.writeText) {
|
|
72
|
+
setWorkspaceCopyStatus('failed');
|
|
70
73
|
return;
|
|
71
|
-
|
|
72
|
-
|
|
74
|
+
}
|
|
75
|
+
try {
|
|
76
|
+
await clipboard.writeText(workspaceIdForCopy);
|
|
77
|
+
setWorkspaceCopyStatus('copied');
|
|
78
|
+
}
|
|
79
|
+
catch {
|
|
80
|
+
setWorkspaceCopyStatus('failed');
|
|
81
|
+
}
|
|
73
82
|
};
|
|
74
83
|
const brandContent = (_jsxs(_Fragment, { children: [_jsx("img", { src: logo, alt: "Taskforce Logo", className: styles.brandIcon, onError: (event) => {
|
|
75
84
|
const target = event.currentTarget;
|
|
@@ -80,5 +89,17 @@ export function AppShellHeader({ projectName, currentWorkspaceId, brandLabel = '
|
|
|
80
89
|
target.style.display = 'none';
|
|
81
90
|
}
|
|
82
91
|
} }), _jsx("span", { children: brandLabel }), runtimeMode === 'cloud' && (_jsx("span", { className: styles.brandCloudSuffix, children: "HQ" }))] }));
|
|
83
|
-
return (_jsxs("div", { className: `${styles.header} ${shellStyles.standaloneHeader}`, children: [_jsxs("div", { ref: headerTitleRef, className: `${styles.headerTitle} ${shellStyles.standaloneTitle}`, children: [onBrandClick ? (_jsx("button", { type: "button", className: styles.brandHomeButton, onClick: onBrandClick, title: brandHomeLabel, "aria-label": brandHomeLabel, children: brandContent })) : (_jsx("span", { className: styles.brandHomeStatic, children: brandContent })), canShowProjectName && (_jsxs("span", { ref: projectNameGroupRef, className: `${styles.projectNameGroup} ${showProjectName ? '' : styles.projectNameGroupHidden}`.trim(), "aria-hidden": showProjectName ? undefined : true, children: [_jsx("span", { className: styles.projectSlash, children: "/" }), _jsx("
|
|
92
|
+
return (_jsxs("div", { className: `${styles.header} ${shellStyles.standaloneHeader}`, children: [_jsxs("div", { ref: headerTitleRef, className: `${styles.headerTitle} ${shellStyles.standaloneTitle}`, children: [onBrandClick ? (_jsx("button", { type: "button", className: styles.brandHomeButton, onClick: onBrandClick, title: brandHomeLabel, "aria-label": brandHomeLabel, children: brandContent })) : (_jsx("span", { className: styles.brandHomeStatic, children: brandContent })), canShowProjectName && (_jsxs("span", { ref: projectNameGroupRef, className: `${styles.projectNameGroup} ${showProjectName ? '' : styles.projectNameGroupHidden}`.trim(), "aria-hidden": showProjectName ? undefined : true, children: [_jsx("span", { className: styles.projectSlash, children: "/" }), workspaceIdForCopy ? (_jsx("button", { type: "button", className: `${styles.projectName} ${workspaceCopyStatus === 'copied' ? styles.projectNameCopied : ''}`.trim(), title: workspaceCopyStatus === 'copied'
|
|
93
|
+
? 'Workspace ID copied'
|
|
94
|
+
: workspaceCopyStatus === 'failed'
|
|
95
|
+
? 'Unable to copy workspace ID'
|
|
96
|
+
: `Workspace ID: ${workspaceIdForCopy}`, "aria-label": `${normalizedProjectName}. ${workspaceCopyStatus === 'copied'
|
|
97
|
+
? 'Workspace ID copied.'
|
|
98
|
+
: workspaceCopyStatus === 'failed'
|
|
99
|
+
? 'Unable to copy workspace ID.'
|
|
100
|
+
: 'Copy workspace ID.'}`, disabled: !showProjectName, onClick: () => { void copyWorkspaceId(); }, children: normalizedProjectName })) : (_jsx("span", { className: styles.projectName, children: normalizedProjectName })), _jsx("span", { className: styles.projectNameCopyFeedback, "aria-live": "polite", children: workspaceCopyStatus === 'copied'
|
|
101
|
+
? 'Workspace ID copied.'
|
|
102
|
+
: workspaceCopyStatus === 'failed'
|
|
103
|
+
? 'Unable to copy workspace ID.'
|
|
104
|
+
: '' })] })), meta] }), _jsx("div", { className: styles.headerActions, style: { gap: '16px' }, children: actions })] }));
|
|
84
105
|
}
|
|
@@ -147,6 +147,12 @@ function readConfiguredFeatureLimit(config, key) {
|
|
|
147
147
|
const normalized = Math.max(1, Math.floor(numericValue));
|
|
148
148
|
return normalized;
|
|
149
149
|
}
|
|
150
|
+
/**
|
|
151
|
+
* Shown for count-limited features that are enabled with no configured ceiling.
|
|
152
|
+
* Applies to workspaces and AI profiles only — storage keeps an explicit
|
|
153
|
+
* allowance, and team management renders its seat limit instead.
|
|
154
|
+
*/
|
|
155
|
+
const UNLIMITED_LIMIT_LABEL = 'Unlimited';
|
|
150
156
|
function formatLimitedFeatureLabel(baseLabel, limitValue) {
|
|
151
157
|
if (limitValue !== 1)
|
|
152
158
|
return baseLabel;
|
|
@@ -185,6 +191,9 @@ function getFeatureDisplayParts(feature, seatLimit) {
|
|
|
185
191
|
baseLabel: formatLimitedFeatureLabel(baseLabel, maxWorkspaces)
|
|
186
192
|
};
|
|
187
193
|
}
|
|
194
|
+
if (feature.access !== 'disabled') {
|
|
195
|
+
return { limitValue: UNLIMITED_LIMIT_LABEL, limitUnit: null, baseLabel };
|
|
196
|
+
}
|
|
188
197
|
}
|
|
189
198
|
if (feature.featureKey === 'collaboration.ai_profiles') {
|
|
190
199
|
const maxAiProfiles = readConfiguredFeatureLimit(config, 'maxAiProfiles');
|
|
@@ -195,6 +204,9 @@ function getFeatureDisplayParts(feature, seatLimit) {
|
|
|
195
204
|
baseLabel: formatLimitedFeatureLabel(baseLabel, maxAiProfiles)
|
|
196
205
|
};
|
|
197
206
|
}
|
|
207
|
+
if (feature.access !== 'disabled') {
|
|
208
|
+
return { limitValue: UNLIMITED_LIMIT_LABEL, limitUnit: null, baseLabel };
|
|
209
|
+
}
|
|
198
210
|
}
|
|
199
211
|
if (feature.featureKey === 'collaboration.team_management') {
|
|
200
212
|
const normalizedSeatLimit = Number(seatLimit);
|
|
@@ -74,7 +74,7 @@ import { useRecentSyncEvents } from '../../hooks/sync/useRecentSyncEvents';
|
|
|
74
74
|
import { useSyncStatusActions } from '../../hooks/sync/useSyncStatusActions';
|
|
75
75
|
import { useSyncStatusControls } from '../../hooks/sync/useSyncStatusControls';
|
|
76
76
|
import { toDateOnlyLocal, parseDateOnlyLocal, startOfWeek, getDayOffsetFromWeekStart, addDays, dateOnlyToIsoWeekKey, SCHEDULE_DAY_KEYS, SCHEDULE_DAY_LABELS, } from './panels/scheduleUtils';
|
|
77
|
-
import {
|
|
77
|
+
import { ANNOTATED_ATTACHMENTS_WORKSPACE_FEATURE_KEY, DOCUMENT_WORKSPACE_FEATURE_KEY, TASKFORCE_AGENTS_WORKSPACE_FEATURE_KEY, resolveAppGateAccessMap, } from '../../runtime/appGates';
|
|
78
78
|
// Heavy feature modules — lazy-loaded to reduce the initial app chunk size.
|
|
79
79
|
const TaskSettings = React.lazy(() => import('../features/TaskSettings').then(m => ({ default: m.TaskSettings })));
|
|
80
80
|
const AnnotatedAttachmentWorkspaceShell = React.lazy(() => import('../features/AnnotatedAttachmentWorkspace').then(m => ({ default: m.AnnotatedAttachmentWorkspaceShell })));
|
|
@@ -171,7 +171,7 @@ export function StandaloneLayout(props) {
|
|
|
171
171
|
// Data & Actions
|
|
172
172
|
tasks, handleEdit, handleSubmit, resetForm, discardDescriptionImageDraft, handleUpdateTask, handleSetStatus: handleSetStatusDurably,
|
|
173
173
|
// Form State (destructure all needed for TaskForm)
|
|
174
|
-
editingTaskId, loading, error, title, setTitle, setTitleDraft, description, setDescription, setDescriptionDraft, checklistItems, setChecklistItems, category, setCategory, type, setType, priority, setPriority, complexity, setComplexity, status, setStatus, assignee, setAssignee, scheduledDate, setScheduledDate, dueDate, setDueDate, workstreamInput, setWorkstreamInput, manualComplexityEnabled, checklistDropdownEnabled, showTaskCardStatusLabel, formTaxonomies, setFormTaxonomies, comments, newCommentText, setNewCommentText, attachments, setAttachments, attachmentsDirty, setAttachmentsDirty, descriptionImageUploadPending, descriptionImageDraftId, registerDescriptionImageUpload, descriptionFocused, setDescriptionFocused, showMarkdownHelp, setShowMarkdownHelp,
|
|
174
|
+
editingTaskId, loading, error, title, setTitle, setTitleDraft, description, setDescription, setDescriptionDraft, checklistItems, setChecklistItems, category, setCategory, type, setType, priority, setPriority, complexity, setComplexity, status, setStatus, assignee, setAssignee, scheduledDate, setScheduledDate, dueDate, setDueDate, workstreamInput, setWorkstreamInput, manualComplexityEnabled, checklistDropdownEnabled, showTaskCardStatusLabel, showTaskCardDescription, showTaskCardAttachments, showTaskCardChecklistProgress, showTaskCardLatestActivity, tintTaskCardActivityBackground, formTaxonomies, setFormTaxonomies, comments, newCommentText, setNewCommentText, attachments, setAttachments, attachmentsDirty, setAttachmentsDirty, descriptionImageUploadPending, descriptionImageDraftId, registerDescriptionImageUpload, descriptionFocused, setDescriptionFocused, showMarkdownHelp, setShowMarkdownHelp,
|
|
175
175
|
// Form Actions
|
|
176
176
|
handleAddComment, handleSetWorkstreamForCurrentTask, handleOpenTaskById,
|
|
177
177
|
// Other Props for Form
|
|
@@ -401,32 +401,7 @@ export function StandaloneLayout(props) {
|
|
|
401
401
|
setRequestedAnnotatedTarget(null);
|
|
402
402
|
setRequestedAnnotatedSessionId(null);
|
|
403
403
|
}, [layoutStateWorkspaceKey]);
|
|
404
|
-
const workspaceFeatureAccess = useMemo(() => (
|
|
405
|
-
[DOCUMENT_WORKSPACE_FEATURE_KEY]: resolveFeatureAccess({
|
|
406
|
-
featureKey: DOCUMENT_WORKSPACE_FEATURE_KEY,
|
|
407
|
-
runtimeMode,
|
|
408
|
-
}),
|
|
409
|
-
[ANNOTATED_ATTACHMENTS_WORKSPACE_FEATURE_KEY]: resolveFeatureAccess({
|
|
410
|
-
featureKey: ANNOTATED_ATTACHMENTS_WORKSPACE_FEATURE_KEY,
|
|
411
|
-
runtimeMode,
|
|
412
|
-
}),
|
|
413
|
-
[WORKFLOW_MANAGER_WORKSPACE_FEATURE_KEY]: resolveFeatureAccess({
|
|
414
|
-
featureKey: WORKFLOW_MANAGER_WORKSPACE_FEATURE_KEY,
|
|
415
|
-
runtimeMode,
|
|
416
|
-
}),
|
|
417
|
-
[TASKFORCE_AGENTS_WORKSPACE_FEATURE_KEY]: resolveFeatureAccess({
|
|
418
|
-
featureKey: TASKFORCE_AGENTS_WORKSPACE_FEATURE_KEY,
|
|
419
|
-
runtimeMode,
|
|
420
|
-
}),
|
|
421
|
-
[AI_PROFILES_WORKSPACE_FEATURE_KEY]: resolveFeatureAccess({
|
|
422
|
-
featureKey: AI_PROFILES_WORKSPACE_FEATURE_KEY,
|
|
423
|
-
runtimeMode,
|
|
424
|
-
}),
|
|
425
|
-
[PLANNING_WORKSPACE_FEATURE_KEY]: resolveFeatureAccess({
|
|
426
|
-
featureKey: PLANNING_WORKSPACE_FEATURE_KEY,
|
|
427
|
-
runtimeMode,
|
|
428
|
-
}),
|
|
429
|
-
}), [runtimeMode]);
|
|
404
|
+
const workspaceFeatureAccess = useMemo(() => props.config?.appGates || resolveAppGateAccessMap('production'), [props.config?.appGates]);
|
|
430
405
|
const workspaceModules = useMemo(() => getWorkspaceModuleDefinitions({ featureAccess: workspaceFeatureAccess }), [workspaceFeatureAccess]);
|
|
431
406
|
const resolvedWorkspaceModule = useMemo(() => resolveWorkspaceModule(activeWorkspaceModule, workspaceModules), [activeWorkspaceModule, workspaceModules]);
|
|
432
407
|
const planningSurfaceVisible = planningDrawerOpen || resolvedWorkspaceModule === 'planning';
|
|
@@ -1152,7 +1127,8 @@ export function StandaloneLayout(props) {
|
|
|
1152
1127
|
prerequisiteTaskIds: prerequisiteTaskIdsByTaskId.get(task.id) || [],
|
|
1153
1128
|
dependencyState: getDependencyState(task.id),
|
|
1154
1129
|
assignee: task.assignee || null,
|
|
1155
|
-
attachmentCount:
|
|
1130
|
+
attachmentCount: task.attachmentCount
|
|
1131
|
+
?? (Array.isArray(task.attachments) ? task.attachments.length : 0),
|
|
1156
1132
|
isArchived: Boolean(task.isArchived),
|
|
1157
1133
|
});
|
|
1158
1134
|
workstreamTaskPreviewsById.set(workstreamId, current);
|
|
@@ -4287,7 +4263,7 @@ export function StandaloneLayout(props) {
|
|
|
4287
4263
|
const taskforceAgentDrawerNode = taskforceAgentDrawerOpen ? (_jsx("div", { className: `${styles.taskforceAgentDrawerOverlay} ${taskforceAgentPanelCollapsed ? styles.taskforceAgentDrawerOverlayCollapsed : ''}`.trim(), style: { top: `${taskforceAgentDrawerTop}px` }, "data-theme": currentTheme, "aria-label": "Taskforce Agent chat", children: _jsx("aside", { className: taskforceAgentPanelCollapsed
|
|
4288
4264
|
? `${shellStyles.workspaceToolRail} ${shellStyles.workspaceToolRailRight}`
|
|
4289
4265
|
: styles.taskforceAgentDrawerPanel, children: taskforceAgentPanelCollapsed ? (_jsxs("div", { className: shellStyles.workspaceToolRailMain, children: [_jsx("button", { type: "button", className: `${shellStyles.workspaceToolButton} tf-control-icon tf-control-icon-quiet tf-control-icon-active`, onClick: () => setTaskforceAgentPanelCollapsed(false), title: "Expand agent chat", "aria-label": "Expand agent chat", children: _jsx(Bot, { size: 16 }) }), _jsx("button", { type: "button", className: `${shellStyles.workspaceToolButton} tf-control-icon tf-control-icon-quiet`, onClick: handleCloseTaskforceAgentDrawer, title: "Close agent chat", "aria-label": "Close agent chat", children: _jsx(X, { size: 16 }) })] })) : (_jsxs(_Fragment, { children: [_jsxs("div", { className: styles.taskforceAgentDrawerHeader, children: [taskforceAgentDrawerHeaderContent || (_jsxs("div", { className: styles.taskforceAgentDrawerHeaderFallback, children: [_jsx("span", { className: styles.taskforceAgentDrawerFallbackAvatar, "aria-hidden": "true", children: _jsx(Bot, { size: 16 }) }), _jsx("strong", { children: "Loading agent..." })] })), _jsxs("div", { className: styles.taskforceAgentDrawerHeaderActions, children: [_jsx("button", { type: "button", className: "tf-control-icon", onClick: () => setTaskforceAgentPanelCollapsed(true), title: "Collapse agent chat", "aria-label": "Collapse agent chat", children: _jsx(ChevronRight, { size: 16 }) }), _jsx("button", { type: "button", className: "tf-control-icon", onClick: handleCloseTaskforceAgentDrawer, title: "Close agent chat", "aria-label": "Close agent chat", children: _jsx(X, { size: 16 }) })] })] }), _jsx(Suspense, { fallback: _jsx("div", { className: styles.taskforceAgentDrawerLoading, children: _jsx(Loader2, { size: 18, className: styles.spinner }) }), children: _jsx(TaskforceAgentsModule, { workspaceId: currentWorkspaceId, launchContext: taskforceAgentsLaunchContext, onClearLaunchContext: handleCloseTaskforceAgentDrawer, onOpenTaskContext: handleOpenTaskforceAgentTaskContext, taskReferences: taskReferences, surface: "taskDrawer", runtimeMode: runtimeMode, cloudAuthConfigured: cloudAuthConfigured, authSessionResolved: authSessionResolved, isAuthenticated: isAuthenticated, resolveCloudAuthUrl: resolveCloudAuthUrl, theme: currentTheme, rememberedAgentId: taskforceAgentId, rememberedConversationId: taskforceAgentConversationId, rememberedConversationByAgentId: taskforceAgentConversationByAgentId, rememberedSelectionReady: uiStateReady, onRememberSelection: handleRememberTaskforceAgentSelection, onRenderDrawerHeader: handleRenderTaskforceAgentDrawerHeader }) })] })) }) })) : null;
|
|
4290
|
-
return (_jsxs("div", { className: `${shellStyles.standaloneWrapper} ${zenMode ? styles.zenModeEnabled : ''}`, "data-theme": currentTheme, children: [_jsx("div", { ref: appShellHeaderRef, style: { flexShrink: 0 }, children: _jsx(AppShellHeader, { projectName:
|
|
4266
|
+
return (_jsxs("div", { className: `${shellStyles.standaloneWrapper} ${zenMode ? styles.zenModeEnabled : ''}`, "data-theme": currentTheme, children: [_jsx("div", { ref: appShellHeaderRef, style: { flexShrink: 0 }, children: _jsx(AppShellHeader, { projectName: currentWorkspaceLabel, currentWorkspaceId: currentWorkspaceId, runtimeMode: runtimeMode, theme: currentTheme, onBrandClick: handleOpenKanbanFromBrand, meta: (_jsxs(_Fragment, { children: [_jsx("span", { className: styles.taskCountBadge, title: activeCountTitle, children: activeCountLabel }), normalizedAuthUserId && normalizedAuthUserId !== 'anonymous' && (_jsxs(_Fragment, { children: [_jsxs("button", { type: "button", className: `${styles.taskCountBadge} ${styles.taskCountBadgeButton} ${taskHeaderQuickFilter === 'assigned-to-me' ? styles.taskCountBadgeActive : ''}`.trim(), onClick: () => handleTaskHeaderQuickFilter('assigned-to-me'), title: taskHeaderQuickFilter === 'assigned-to-me' ? 'Clear assigned-to-me quick filter' : 'Filter to my open tasks', "aria-label": `${taskHeaderQuickFilter === 'assigned-to-me' ? 'Clear' : 'Apply'} assigned to me quick filter (${assignedTaskCount} task${assignedTaskCount === 1 ? '' : 's'})`, "aria-pressed": taskHeaderQuickFilter === 'assigned-to-me', children: [_jsx(User, { size: 13, className: styles.taskCountBadgeIcon, "aria-hidden": "true" }), assignedTaskCount] }), _jsxs("button", { type: "button", className: `${styles.taskCountBadge} ${styles.taskCountBadgeButton} ${assignedOverdueTaskCount > 0 ? styles.taskCountBadgeAlert : ''} ${taskHeaderQuickFilter === 'overdue' ? styles.taskCountBadgeActive : ''}`.trim(), onClick: () => handleTaskHeaderQuickFilter('overdue'), title: taskHeaderQuickFilter === 'overdue' ? 'Clear my-overdue-tasks quick filter' : 'Filter to my overdue tasks', "aria-label": `${taskHeaderQuickFilter === 'overdue' ? 'Clear' : 'Apply'} my overdue tasks quick filter (${assignedOverdueTaskCount} task${assignedOverdueTaskCount === 1 ? '' : 's'})`, "aria-pressed": taskHeaderQuickFilter === 'overdue', children: [_jsx(ClockAlert, { size: 13, className: styles.taskCountBadgeIcon, "aria-hidden": "true" }), assignedOverdueTaskCount] })] })), shouldShowSyncStatus && (_jsx("button", { className: "tf-control-icon", onClick: openSyncStatusModal, title: `Sync manager: ${headerSyncStatusMeta.label} | Last success: ${formattedLastSyncTime}`, style: {
|
|
4291
4267
|
marginLeft: '6px',
|
|
4292
4268
|
height: '24px',
|
|
4293
4269
|
width: '24px',
|
|
@@ -4358,7 +4334,7 @@ export function StandaloneLayout(props) {
|
|
|
4358
4334
|
if (count > 0 && window.confirm(`Permanently delete all ${count} tasks in Trash? Filters and selection do not limit this action. This cannot be undone.`)) {
|
|
4359
4335
|
void handleEmptyDeletedTasks();
|
|
4360
4336
|
}
|
|
4361
|
-
} })), _jsx(TaskKanban, { tasks: scopedBoardVisibleTasks, allTasks: scopedKanbanAllTasks, columns: kanbanColumns, groupBy: groupBy, scheduleDates: scheduleDates, searchQuery: searchQuery, filterCategories: filterCategories, filterTypes: filterTypes, filterPriorities: filterPriorities, filterStatus: filterStatus, filterAssignees: filterAssignees, filtersReady: hasInitedFilters, assigneeOptions: assigneeOptions, scheduleFilteredTaskIds: scheduleFilteredTaskIdList, copiedId: copiedId, taxonomies: taxonomies, types: activeTypes, priorities: priorities, workspaceId: currentWorkspaceId, deletedTaskRecordByTaskId: deletedTaskRecordByTaskId, selectedDeletedRecordIds: trashSelection.selectedIds, onDeletedSelectionChange: trashSelection.toggle, onUpdateTask: handleUpdateTask, onTaskClick: handleEdit, taskReferences: taskTextReferences, onCopyId: handleCopyId, onToggleInProgress: handleToggleInProgress, onToggleReview: handleToggleReview, onToggleComplete: handleToggleComplete, onToggleCancel: handleToggleCancel, onSetStatus: handleSetStatus, onArchiveTask: handleArchiveTask, onAddTaskToColumn: handleAddTaskToColumn, showTaskCardStatusLabel: showTaskCardStatusLabel, onScheduleDaySelected: handleBoardScheduleDaySelected, persistedScrollLeft: groupBy === 'schedule' ? scheduleScrollLeft : undefined, onScrollLeftChange: handleBoardScrollLeftChange, emptyColumnMode: emptyColumnMode, categories: activeCategories, compressed: compressedCards, readOnlyMode: taskScope === 'deleted' ? 'deleted' : taskScope === 'archived' ? 'archived' : null, recentlyChangedTaskIds: props.recentlyChangedTaskIds, sortBy: sortBy, sortOrder: props.sortOrder, planningDropTargets: planningDrawerPortalElement
|
|
4337
|
+
} })), _jsx(TaskKanban, { tasks: scopedBoardVisibleTasks, allTasks: scopedKanbanAllTasks, columns: kanbanColumns, groupBy: groupBy, scheduleDates: scheduleDates, searchQuery: searchQuery, filterCategories: filterCategories, filterTypes: filterTypes, filterPriorities: filterPriorities, filterStatus: filterStatus, filterAssignees: filterAssignees, filtersReady: hasInitedFilters, assigneeOptions: assigneeOptions, scheduleFilteredTaskIds: scheduleFilteredTaskIdList, copiedId: copiedId, taxonomies: taxonomies, types: activeTypes, priorities: priorities, workspaceId: currentWorkspaceId, deletedTaskRecordByTaskId: deletedTaskRecordByTaskId, selectedDeletedRecordIds: trashSelection.selectedIds, onDeletedSelectionChange: trashSelection.toggle, onUpdateTask: handleUpdateTask, onTaskClick: handleEdit, taskReferences: taskTextReferences, onCopyId: handleCopyId, onToggleInProgress: handleToggleInProgress, onToggleReview: handleToggleReview, onToggleComplete: handleToggleComplete, onToggleCancel: handleToggleCancel, onSetStatus: handleSetStatus, onArchiveTask: handleArchiveTask, onAddTaskToColumn: handleAddTaskToColumn, showTaskCardStatusLabel: showTaskCardStatusLabel, showTaskCardDescription: runtimeMode !== 'cloud' && showTaskCardDescription, showTaskCardAttachments: showTaskCardAttachments, showTaskCardChecklistProgress: showTaskCardChecklistProgress, showTaskCardLatestActivity: showTaskCardLatestActivity, tintTaskCardActivityBackground: tintTaskCardActivityBackground, onScheduleDaySelected: handleBoardScheduleDaySelected, persistedScrollLeft: groupBy === 'schedule' ? scheduleScrollLeft : undefined, onScrollLeftChange: handleBoardScrollLeftChange, emptyColumnMode: emptyColumnMode, categories: activeCategories, compressed: compressedCards, readOnlyMode: taskScope === 'deleted' ? 'deleted' : taskScope === 'archived' ? 'archived' : null, recentlyChangedTaskIds: props.recentlyChangedTaskIds, sortBy: sortBy, sortOrder: props.sortOrder, planningDropTargets: planningDrawerPortalElement
|
|
4362
4338
|
? createPortal(planningDrawerNode, planningDrawerPortalElement)
|
|
4363
4339
|
: null, onAssignTaskToWorkstream: handleAssignTaskToWorkstream, onAssignWorkstreamToInitiative: handleAssignInitiativeToWorkstream, workstreams: props.workstreams, initiatives: props.initiatives, onUnarchive: handleBoardUnarchive, onDelete: handleBoardDelete }, `${groupBy}-${kanbanColumns.map(c => String(c.value)).join('|')}-${scheduleDates.mon}`), groupBy === 'schedule' && !scheduleSidebarOpen && (_jsx("button", { type: "button", className: "tf-control-icon", onClick: () => {
|
|
4364
4340
|
setPlanningDrawerDetail(null);
|
|
@@ -42,7 +42,7 @@ export function WidgetView(props) {
|
|
|
42
42
|
// Actions
|
|
43
43
|
handleEdit, handleDelete, handleCopyId, handleUpdateTask, handleToggleComplete, handleToggleCancel, handleToggleInProgress, handleToggleReview, handleSetStatus, handleArchiveTask, handleBulkArchive, handleUnarchive, handleRestoreDeletedTask, handleRestoreSelectedDeletedTasks, handlePermanentlyDeleteDeletedTask, handleEmptyDeletedTasks, fetchArchive,
|
|
44
44
|
// Form State
|
|
45
|
-
editingTaskId, loading, error, clearTaskError, title, setTitle, setTitleDraft, description, setDescription, setDescriptionDraft, checklistItems, setChecklistItems, category, setCategory, type, setType, priority, setPriority, complexity, setComplexity, status, setStatus, manualComplexityEnabled, checklistDropdownEnabled, showTaskCardStatusLabel, assignee, setAssignee, scheduledDate, setScheduledDate, dueDate, setDueDate, workstreamInput, setWorkstreamInput, formTaxonomies, setFormTaxonomies, comments, newCommentText, setNewCommentText, attachments, setAttachments, setAttachmentsDirty, descriptionImageUploadPending, descriptionImageDraftId, registerDescriptionImageUpload, descriptionFocused, setDescriptionFocused, showMarkdownHelp, setShowMarkdownHelp,
|
|
45
|
+
editingTaskId, loading, error, clearTaskError, title, setTitle, setTitleDraft, description, setDescription, setDescriptionDraft, checklistItems, setChecklistItems, category, setCategory, type, setType, priority, setPriority, complexity, setComplexity, status, setStatus, manualComplexityEnabled, checklistDropdownEnabled, showTaskCardStatusLabel, showTaskCardDescription, showTaskCardAttachments, showTaskCardChecklistProgress, showTaskCardLatestActivity, tintTaskCardActivityBackground, assignee, setAssignee, scheduledDate, setScheduledDate, dueDate, setDueDate, workstreamInput, setWorkstreamInput, formTaxonomies, setFormTaxonomies, comments, newCommentText, setNewCommentText, attachments, setAttachments, setAttachmentsDirty, descriptionImageUploadPending, descriptionImageDraftId, registerDescriptionImageUpload, descriptionFocused, setDescriptionFocused, showMarkdownHelp, setShowMarkdownHelp,
|
|
46
46
|
// Form Actions
|
|
47
47
|
handleSubmit, resetForm, discardDescriptionImageDraft, handleAddComment, handleSetWorkstreamForCurrentTask, handleOpenTaskById,
|
|
48
48
|
// Modals
|
|
@@ -54,7 +54,7 @@ export function WidgetView(props) {
|
|
|
54
54
|
// Refs
|
|
55
55
|
commentsEndRef,
|
|
56
56
|
// Props from Core
|
|
57
|
-
currentWorkspaceId, authUserId, settingsModel, onHeaderMouseDown, isDragging } = props;
|
|
57
|
+
currentWorkspaceId, authUserId, runtimeMode, settingsModel, onHeaderMouseDown, isDragging } = props;
|
|
58
58
|
const taskReferences = useTaskReferenceContract(tasks, handleOpenTaskById);
|
|
59
59
|
const handleCanonicalEntityReference = useCallback((reference) => {
|
|
60
60
|
if (reference.kind === 'initiative' || reference.kind === 'workstream') {
|
|
@@ -346,7 +346,7 @@ export function WidgetView(props) {
|
|
|
346
346
|
if (confirm(`Permanently delete all ${deletedTasks.length} tasks in Trash? Filters and selection do not limit this action. This cannot be undone.`)) {
|
|
347
347
|
void handleEmptyDeletedTasks();
|
|
348
348
|
}
|
|
349
|
-
} })), copiedId: copiedId, recentlyChangedTaskIds: recentlyChangedTaskIds, showTaskCardStatusLabel: showTaskCardStatusLabel, workstreams: props.workstreams, initiatives: props.initiatives, onSearchChange: setSearchQuery, onFilterCategoriesChange: (vals) => setFilterCategories(vals), onFilterTypesChange: (vals) => setFilterTypes(vals), onFilterPrioritiesChange: setFilterPriorities, onFilterStatusChange: setFilterStatus, onFilterAssigneesChange: (vals) => setFilterAssignees(vals), onTaxonomyFilterChange: (id, values) => setFilterTaxonomies(prev => ({ ...prev, [id]: values })), onSortByChange: handleSortByChange, onSortOrderChange: toggleSortOrder, onShowArchiveChange: setShowArchive, onTaskScopeChange: setTaskScope, onClearFilters: clearFilters, onToggleCategory: (category) => setCollapsedCategories(prev => ({ ...prev, [category]: !prev[category] })), onEditTask: handleEdit, onUpdateTask: handleUpdateTask, taskReferences: taskTextReferences, onCopyId: handleCopyId, onToggleInProgress: handleToggleInProgress, onToggleReview: handleToggleReview, onToggleComplete: handleToggleComplete, onToggleCancel: handleToggleCancel, onSetStatus: handleSetStatus, onArchiveTask: handleArchiveTask, onBulkArchive: handleBulkArchive, onUnarchive: (taskId) => {
|
|
349
|
+
} })), copiedId: copiedId, recentlyChangedTaskIds: recentlyChangedTaskIds, showTaskCardStatusLabel: showTaskCardStatusLabel, showTaskCardDescription: runtimeMode !== 'cloud' && showTaskCardDescription, showTaskCardAttachments: showTaskCardAttachments, showTaskCardChecklistProgress: showTaskCardChecklistProgress, showTaskCardLatestActivity: showTaskCardLatestActivity, tintTaskCardActivityBackground: tintTaskCardActivityBackground, workstreams: props.workstreams, initiatives: props.initiatives, onSearchChange: setSearchQuery, onFilterCategoriesChange: (vals) => setFilterCategories(vals), onFilterTypesChange: (vals) => setFilterTypes(vals), onFilterPrioritiesChange: setFilterPriorities, onFilterStatusChange: setFilterStatus, onFilterAssigneesChange: (vals) => setFilterAssignees(vals), onTaxonomyFilterChange: (id, values) => setFilterTaxonomies(prev => ({ ...prev, [id]: values })), onSortByChange: handleSortByChange, onSortOrderChange: toggleSortOrder, onShowArchiveChange: setShowArchive, onTaskScopeChange: setTaskScope, onClearFilters: clearFilters, onToggleCategory: (category) => setCollapsedCategories(prev => ({ ...prev, [category]: !prev[category] })), onEditTask: handleEdit, onUpdateTask: handleUpdateTask, taskReferences: taskTextReferences, onCopyId: handleCopyId, onToggleInProgress: handleToggleInProgress, onToggleReview: handleToggleReview, onToggleComplete: handleToggleComplete, onToggleCancel: handleToggleCancel, onSetStatus: handleSetStatus, onArchiveTask: handleArchiveTask, onBulkArchive: handleBulkArchive, onUnarchive: (taskId) => {
|
|
350
350
|
const deletedRecord = deletedRecordByTaskId.get(taskId);
|
|
351
351
|
if (deletedRecord) {
|
|
352
352
|
void handleRestoreDeletedTask(deletedRecord.id, deletedRecord.taskId);
|
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import { useDndContext, useDraggable, useDroppable } from '@dnd-kit/core';
|
|
4
4
|
import { CSS } from '@dnd-kit/utilities';
|
|
5
|
-
import { ArrowUpDown, Archive, ChevronDown, ChevronLeft, ChevronRight, Eye, EyeOff, Filter, GripVertical, Kanban,
|
|
5
|
+
import { ArrowUpDown, Archive, ChevronDown, ChevronLeft, ChevronRight, Eye, EyeOff, Filter, GripVertical, Kanban, Pencil, Plus, RotateCcw, Search, Star, Unlink, X, } from 'lucide-react';
|
|
6
6
|
import styles from '../../../Taskforce.module.css';
|
|
7
7
|
import panelStyles from './PlanningDrawer.module.css';
|
|
8
8
|
import { HierarchyActionBadge } from '../../ui/HierarchyActionBadge';
|
|
@@ -500,7 +500,7 @@ function PlanningDrawerList({ initiatives, standaloneWorkstreams, planningLoadSt
|
|
|
500
500
|
: expandedInitiativeIds.has(initiative.id);
|
|
501
501
|
return (_jsxs("li", { children: [_jsx(PlanningRow, { rowId: initiative.id, rowType: "initiative", referenceLabel: formatInitiativeReference(initiative), item: initiative, ownerOption: initiative.ownerId ? assigneeOptionByValue.get(initiative.ownerId) : undefined, ownerOptions: assigneeOptions, onChangeOwner: (ownerId) => onChangePlanningOwner('initiative', initiative.id, ownerId === 'unassigned' ? '' : ownerId), onChangeIdentity: (patch) => onChangePlanningIdentity('initiative', initiative.id, patch), workstreamCount: treeWorkstreams.length, active: activeInitiativeId === initiative.id && !activeWorkstreamId, isArchived: Boolean(initiative.isArchived), detailOpen: detail?.type === 'initiative' && detail.item.id === initiative.id, canExpand: allWorkstreams.length > 0, expanded: expanded, controlsId: `planning-children-${initiative.id}`, onToggleExpand: () => onToggleInitiative(initiative.id), onToggleScope: () => onSelectInitiative(initiative.id), onOpenDetails: () => onOpenInitiativeDetails(initiative.id), favorite: planningFavoriteKeys.has(getPlanningFavoriteKey({ type: 'initiative', id: initiative.id })), favoriteReady: navigatorSortReady, onToggleFavorite: () => onTogglePlanningFavorite({ type: 'initiative', id: initiative.id }), onArchiveReady: onArchiveResolvedInitiative
|
|
502
502
|
? () => onArchiveResolvedInitiative(initiative.id)
|
|
503
|
-
: undefined }), expanded && displayedWorkstreams.length > 0 && (_jsx("ul", { id: `planning-children-${initiative.id}`, className: panelStyles.treeChildren, "aria-label": `Workstreams in ${initiative.title}`, children: displayedWorkstreams.map((workstream) => (_jsxs("li", { children: [_jsx(PlanningRow, { rowId: workstream.id, rowType: "workstream", referenceLabel: formatWorkstreamReference(workstream), item: workstream, ownerOption: workstream.ownerId ? assigneeOptionByValue.get(workstream.ownerId) : undefined, ownerOptions: assigneeOptions, onChangeOwner: (ownerId) => onChangePlanningOwner('workstream', workstream.id, ownerId === 'unassigned' ? '' : ownerId), onChangeIdentity: (patch) => onChangePlanningIdentity('workstream', workstream.id, patch), active: activeWorkstreamId === workstream.id, isArchived: Boolean(workstream.isArchived), detailOpen: (detail?.type === 'workstream' && detail.item.id === workstream.id)
|
|
503
|
+
: undefined }), expanded && displayedWorkstreams.length > 0 && (_jsx("ul", { id: `planning-children-${initiative.id}`, className: `${panelStyles.treeChildren} ${panelStyles.treeWorkstreamChildren}`.trim(), "aria-label": `Workstreams in ${initiative.title}`, children: displayedWorkstreams.map((workstream) => (_jsxs("li", { children: [_jsx(PlanningRow, { rowId: workstream.id, rowType: "workstream", referenceLabel: formatWorkstreamReference(workstream), item: workstream, ownerOption: workstream.ownerId ? assigneeOptionByValue.get(workstream.ownerId) : undefined, ownerOptions: assigneeOptions, onChangeOwner: (ownerId) => onChangePlanningOwner('workstream', workstream.id, ownerId === 'unassigned' ? '' : ownerId), onChangeIdentity: (patch) => onChangePlanningIdentity('workstream', workstream.id, patch), active: activeWorkstreamId === workstream.id, isArchived: Boolean(workstream.isArchived), detailOpen: (detail?.type === 'workstream' && detail.item.id === workstream.id)
|
|
504
504
|
|| (secondaryPane?.kind === 'detail' && secondaryPane.detail.item.id === workstream.id), canExpand: (workstream.tasks || []).length > 0, expanded: expandedWorkstreamIds.has(workstream.id), controlsId: `planning-tasks-${workstream.id}`, onToggleExpand: () => toggleWorkstreamTasks(workstream.id), onToggleScope: () => onSelectWorkstream(workstream.id, initiative.id), onOpenDetails: () => {
|
|
505
505
|
onOpenInitiativeDetails(initiative.id);
|
|
506
506
|
onOpenNestedWorkstreamDetails(workstream.id);
|
|
@@ -521,7 +521,6 @@ function PlanningFailureState({ title, message, retrying, onRetry, compact = fal
|
|
|
521
521
|
function PlanningDrawerDetailView({ detail, showArchivedChildren, inlineEditor, selectedChildWorkstreamId, currentWorkspaceId, currentActorId, theme, onOpenNestedWorkstreamDetails, onArchivePlanningTask, onArchiveResolvedWorkstream, onArchiveResolvedEntity, onEdit, onChangeOwner, onChangePlanningOwner, onChangePlanningIdentity, onArchive, onUnarchive, onCreateTaskInWorkstream, onCreateWorkstreamInInitiative, onOpenTaskById, onChangeTaskAssignee, taskReferences, onAssignTaskToWorkstream, onAssignInitiativeToWorkstream, onAttachTaskToWorkstreamByReference, onAttachWorkstreamToInitiativeByReference, onAddPlanningContextFile, onRemovePlanningContextFile, onUpdatePlanningContextCaption, parentInitiative = null, assigneeOptions, linkOptions, showTaskCardStatusLabel = false, taskArrangeMode = 'execution', onTaskArrangeModeChange = () => { }, onReorderWorkstreamTasks, onSetTaskStatus, hydrationState, onRetryHydration, }) {
|
|
522
522
|
const inlineEditorFormId = React.useId();
|
|
523
523
|
const [isAttachingReference, setIsAttachingReference] = React.useState(false);
|
|
524
|
-
const [showParentLinkPicker, setShowParentLinkPicker] = React.useState(false);
|
|
525
524
|
const [pendingUnlink, setPendingUnlink] = React.useState(null);
|
|
526
525
|
const [relationshipRecovery, setRelationshipRecovery] = React.useState(null);
|
|
527
526
|
const [isChangingRelationship, setIsChangingRelationship] = React.useState(false);
|
|
@@ -590,7 +589,6 @@ function PlanningDrawerDetailView({ detail, showArchivedChildren, inlineEditor,
|
|
|
590
589
|
: `Archive ${detailLabel.toLowerCase()}`;
|
|
591
590
|
React.useEffect(() => {
|
|
592
591
|
setIsAttachingReference(false);
|
|
593
|
-
setShowParentLinkPicker(false);
|
|
594
592
|
setPendingUnlink(null);
|
|
595
593
|
setRelationshipRecovery(null);
|
|
596
594
|
setIsChangingRelationship(false);
|
|
@@ -658,9 +656,7 @@ function PlanningDrawerDetailView({ detail, showArchivedChildren, inlineEditor,
|
|
|
658
656
|
return;
|
|
659
657
|
setIsChangingRelationship(true);
|
|
660
658
|
try {
|
|
661
|
-
|
|
662
|
-
if (result !== false)
|
|
663
|
-
setShowParentLinkPicker(false);
|
|
659
|
+
await onAssignInitiativeToWorkstream(detail.item.id, option.referenceLabel);
|
|
664
660
|
}
|
|
665
661
|
finally {
|
|
666
662
|
setIsChangingRelationship(false);
|
|
@@ -762,7 +758,7 @@ function PlanningDrawerDetailView({ detail, showArchivedChildren, inlineEditor,
|
|
|
762
758
|
});
|
|
763
759
|
}, title: "Unlink workstream from initiative", ariaLabel: "Unlink workstream from initiative", children: _jsx(Unlink, { size: 14, "aria-hidden": "true" }) })) : null] })) : null })) : null, detail.type === 'workstream'
|
|
764
760
|
&& !parentInitiativeReferenceLabel
|
|
765
|
-
&& !inlineEditor ? (_jsx(
|
|
761
|
+
&& !inlineEditor ? (_jsx(PlanningLinkPicker, { options: linkOptions.initiatives, entityLabel: "initiative", onSelect: handleLinkInitiative, disabled: !onAssignInitiativeToWorkstream || isChangingRelationship, iconTrigger: true, triggerTitle: "Link initiative" })) : null, _jsx(PlanningReferenceBadge, { label: detailReferenceLabel || 'Reference pending', entityType: detail.type, entityName: detail.item.title, pending: !detailReferenceLabel })] }), _jsx("div", { className: panelStyles.detailTopActions, children: inlineEditor ? (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", className: "tf-button-ghost tf-button-compact", onClick: inlineEditor.onCancel, disabled: inlineEditor.isSubmitting, children: "Cancel" }), _jsx("button", { type: "submit", form: inlineEditorFormId, className: "tf-button-primary tf-button-compact", disabled: inlineEditor.isSubmitting, children: inlineEditor.isSubmitting ? 'Saving…' : 'Save' })] })) : (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", className: "tf-control-icon", disabled: archiveDetailState.mode === 'blocked' || archiveDetailState.mode === 'checking', onClick: () => {
|
|
766
762
|
if (archiveDetailState.mode === 'archived') {
|
|
767
763
|
onUnarchive?.();
|
|
768
764
|
}
|
|
@@ -776,10 +772,7 @@ function PlanningDrawerDetailView({ detail, showArchivedChildren, inlineEditor,
|
|
|
776
772
|
? archiveEntityConfirmationVisible
|
|
777
773
|
: undefined, children: archiveDetailState.mode === 'archived'
|
|
778
774
|
? _jsx(RotateCcw, { size: 14, "aria-hidden": "true" })
|
|
779
|
-
: _jsx(Archive, { size: 14, "aria-hidden": "true" }) }), _jsx("button", { type: "button", className: "tf-control-icon", onClick: () => {
|
|
780
|
-
setShowParentLinkPicker(false);
|
|
781
|
-
onEdit();
|
|
782
|
-
}, title: `Edit ${detailLabel.toLowerCase()} details`, "aria-label": `Edit ${detailLabel.toLowerCase()} details`, children: _jsx(Pencil, { size: 14, "aria-hidden": "true" }) })] })) })] }), archiveEntityConfirmationVisible && archiveDetailState.mode === 'ready' ? (_jsx(PlanningArchiveConfirmation, { entityType: detail.type, title: detail.item.title, activeTaskCount: detail.item.archiveActiveTaskCount, activeWorkstreamCount: detail.item.archiveActiveWorkstreamCount, busy: archiveEntityBusy, onCancel: () => setArchiveEntityConfirmationVisible(false), onConfirm: () => void handleConfirmArchiveEntity() })) : null, detail.type === 'workstream' ? renderRelationshipFeedback('workstream', detail.item.id) : null, detail.type === 'workstream' && !parentInitiativeReferenceLabel && showParentLinkPicker ? (_jsxs("div", { className: panelStyles.relationshipPickerRow, role: "group", "aria-label": "Link workstream to initiative", children: [_jsx(PlanningLinkPicker, { options: linkOptions.initiatives, entityLabel: "initiative", onSelect: handleLinkInitiative, disabled: !onAssignInitiativeToWorkstream || isChangingRelationship }), _jsx("button", { type: "button", className: "tf-control-icon tf-control-icon-compact", disabled: isChangingRelationship, onClick: () => setShowParentLinkPicker(false), title: "Cancel initiative link", "aria-label": "Cancel initiative link", children: _jsx(X, { size: 14, "aria-hidden": "true" }) })] })) : null, inlineEditor ? (_jsx(PlanningDrawerEditor, { editor: inlineEditor.editor, draftTitle: inlineEditor.draftTitle, draftDescription: inlineEditor.draftDescription, draftOwner: inlineEditor.draftOwner, draftInitiativeId: inlineEditor.draftInitiativeId, assigneeOptions: assigneeOptions, initiativeLinkOptions: linkOptions.initiatives, draftInitiativeSummary: inlineEditor.draftInitiativeSummary, onChangeDraftTitle: inlineEditor.onChangeDraftTitle, onChangeDraftDescription: inlineEditor.onChangeDraftDescription, onChangeDraftOwner: inlineEditor.onChangeDraftOwner, onChangeDraftInitiativeId: inlineEditor.onChangeDraftInitiativeId, onCancel: inlineEditor.onCancel, onSubmit: inlineEditor.onSubmit, isSubmitting: inlineEditor.isSubmitting, inline: true, formId: inlineEditorFormId })) : (_jsxs(_Fragment, { children: [_jsxs("div", { className: panelStyles.detailTitleRow, children: [_jsx(PlanningVisualIdentity, { entityType: detail.type, entityId: detail.item.id, icon: detail.item.icon, color: detail.item.color, size: "detail", disabled: Boolean(detail.item.isArchived), onChange: onChangePlanningIdentity
|
|
775
|
+
: _jsx(Archive, { size: 14, "aria-hidden": "true" }) }), _jsx("button", { type: "button", className: "tf-control-icon", onClick: onEdit, title: `Edit ${detailLabel.toLowerCase()} details`, "aria-label": `Edit ${detailLabel.toLowerCase()} details`, children: _jsx(Pencil, { size: 14, "aria-hidden": "true" }) })] })) })] }), archiveEntityConfirmationVisible && archiveDetailState.mode === 'ready' ? (_jsx(PlanningArchiveConfirmation, { entityType: detail.type, title: detail.item.title, activeTaskCount: detail.item.archiveActiveTaskCount, activeWorkstreamCount: detail.item.archiveActiveWorkstreamCount, busy: archiveEntityBusy, onCancel: () => setArchiveEntityConfirmationVisible(false), onConfirm: () => void handleConfirmArchiveEntity() })) : null, detail.type === 'workstream' ? renderRelationshipFeedback('workstream', detail.item.id) : null, inlineEditor ? (_jsx(PlanningDrawerEditor, { editor: inlineEditor.editor, draftTitle: inlineEditor.draftTitle, draftDescription: inlineEditor.draftDescription, draftOwner: inlineEditor.draftOwner, draftInitiativeId: inlineEditor.draftInitiativeId, assigneeOptions: assigneeOptions, initiativeLinkOptions: linkOptions.initiatives, draftInitiativeSummary: inlineEditor.draftInitiativeSummary, onChangeDraftTitle: inlineEditor.onChangeDraftTitle, onChangeDraftDescription: inlineEditor.onChangeDraftDescription, onChangeDraftOwner: inlineEditor.onChangeDraftOwner, onChangeDraftInitiativeId: inlineEditor.onChangeDraftInitiativeId, onCancel: inlineEditor.onCancel, onSubmit: inlineEditor.onSubmit, isSubmitting: inlineEditor.isSubmitting, inline: true, formId: inlineEditorFormId })) : (_jsxs(_Fragment, { children: [_jsxs("div", { className: panelStyles.detailTitleRow, children: [_jsx(PlanningVisualIdentity, { entityType: detail.type, entityId: detail.item.id, icon: detail.item.icon, color: detail.item.color, size: "detail", disabled: Boolean(detail.item.isArchived), onChange: onChangePlanningIdentity
|
|
783
776
|
? (patch) => onChangePlanningIdentity(detail.type, detail.item.id, patch)
|
|
784
777
|
: undefined }), _jsx("h3", { className: panelStyles.detailTitle, children: detail.item.title })] }), _jsx(PlanningEntityDescription, { description: detail.item.description, label: detailLabel, taskReferences: taskReferences, className: `${panelStyles.detailDescription} ${styles.appScrollbar} tf-scrollbar`.trim(), markdownClassName: panelStyles.detailDescriptionMarkdown })] })), _jsxs("div", { className: panelStyles.detailOwnerField, children: [_jsx("span", { className: panelStyles.detailOwnerLabel, children: "Owner" }), _jsx(PlanningOwnerDropdown, { value: ownerValue, disabled: !onChangeOwner || isSavingOwner, onChange: (nextOwnerId) => void handleOwnerChange(nextOwnerId), ariaLabel: `${detailLabel} owner`, options: assigneeOptions, className: `${planningDropdownStyles.control} ${panelStyles.ownerSelect}` })] }), detail.item.taskCount > 0 ? (_jsx(PlanningProgress, { value: detail.item.progressPercent, label: `${detail.item.progressPercent}% resolved`, statusCounts: detail.item.statusCounts })) : (_jsx(PlanningNoTasks, {})), _jsx(PlanningSummaryMeta, { item: detail.item, workstreamCount: detail.type === 'initiative'
|
|
785
778
|
? detail.item.workstreamCount ?? detail.item.workstreams.length
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect, useState } from 'react';
|
|
2
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
3
3
|
import { CreditCard, KeyRound, Link2, RefreshCw, ShieldCheck, User } from 'lucide-react';
|
|
4
4
|
import { Modal } from '../../../ui/Modal';
|
|
5
5
|
import { EditableAvatarButton } from '../../../ui/EditableAvatarButton';
|
|
@@ -9,6 +9,7 @@ import styles from '../../../../Taskforce.module.css';
|
|
|
9
9
|
import settingsStyles from './AccountSettingsModal.module.css';
|
|
10
10
|
import { resolveCommercialLifecyclePresentation } from '../../../../utils/commercialLifecyclePresentation';
|
|
11
11
|
import { getPasswordPolicyGuidance, validatePasswordPolicy } from '../../../../shared/passwordPolicy';
|
|
12
|
+
import { PasswordInput } from '../../../ui/PasswordInput';
|
|
12
13
|
const PROVIDER_LABELS = {
|
|
13
14
|
password: 'Email & Password',
|
|
14
15
|
google: 'Google',
|
|
@@ -53,11 +54,7 @@ export function AccountSettingsModal({ isOpen, theme, displayName, email, avatar
|
|
|
53
54
|
const linkedProviders = new Set(loginMethods.map((method) => method.provider));
|
|
54
55
|
const hasPassword = linkedProviders.has('password');
|
|
55
56
|
const linkableProviders = availableAuthProviders.filter((provider) => provider !== 'password' && !linkedProviders.has(provider));
|
|
56
|
-
|
|
57
|
-
if (!isOpen)
|
|
58
|
-
return;
|
|
59
|
-
setActiveSection('profile');
|
|
60
|
-
setUnlinkError(null);
|
|
57
|
+
const clearCredentialState = useCallback(() => {
|
|
61
58
|
setShowAddPassword(false);
|
|
62
59
|
setAddPasswordValue('');
|
|
63
60
|
setAddPasswordError(null);
|
|
@@ -68,16 +65,28 @@ export function AccountSettingsModal({ isOpen, theme, displayName, email, avatar
|
|
|
68
65
|
setConfirmPasswordValue('');
|
|
69
66
|
setChangePasswordError(null);
|
|
70
67
|
setChangePasswordDone(false);
|
|
71
|
-
}, [
|
|
68
|
+
}, []);
|
|
69
|
+
const handleClose = () => {
|
|
70
|
+
clearCredentialState();
|
|
71
|
+
onClose();
|
|
72
|
+
};
|
|
73
|
+
const handleSectionChange = (section) => {
|
|
74
|
+
if (section !== 'security')
|
|
75
|
+
clearCredentialState();
|
|
76
|
+
setActiveSection(section);
|
|
77
|
+
};
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
setUnlinkError(null);
|
|
80
|
+
clearCredentialState();
|
|
81
|
+
if (!isOpen)
|
|
82
|
+
return;
|
|
83
|
+
setActiveSection('profile');
|
|
84
|
+
}, [clearCredentialState, isOpen]);
|
|
72
85
|
useEffect(() => {
|
|
73
86
|
if (activeSection === 'security')
|
|
74
87
|
return;
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
setNewPasswordValue('');
|
|
78
|
-
setConfirmPasswordValue('');
|
|
79
|
-
setChangePasswordError(null);
|
|
80
|
-
}, [activeSection]);
|
|
88
|
+
clearCredentialState();
|
|
89
|
+
}, [activeSection, clearCredentialState]);
|
|
81
90
|
useEffect(() => {
|
|
82
91
|
if (!isOpen || activeSection !== 'security' || !cloudAuthEnabled)
|
|
83
92
|
return;
|
|
@@ -173,7 +182,7 @@ export function AccountSettingsModal({ isOpen, theme, displayName, email, avatar
|
|
|
173
182
|
setShowChangePassword(true);
|
|
174
183
|
setChangePasswordDone(false);
|
|
175
184
|
setChangePasswordError(null);
|
|
176
|
-
}, children: "Change password" }) })), showChangePassword && (_jsxs("div", { className: settingsStyles.addPasswordForm, children: [_jsxs("
|
|
185
|
+
}, children: "Change password" }) })), showChangePassword && (_jsxs("div", { className: settingsStyles.addPasswordForm, children: [_jsxs("div", { className: settingsStyles.fieldLabel, children: [_jsx("label", { htmlFor: "account-current-password", children: "Current password" }), _jsx(PasswordInput, { id: "account-current-password", className: styles.input, value: currentPasswordValue, onChange: (event) => setCurrentPasswordValue(event.target.value), disabled: changePasswordBusy, autoComplete: "current-password", visibilityLabel: "current password" })] }), _jsxs("div", { className: settingsStyles.fieldLabel, children: [_jsx("label", { htmlFor: "account-new-password", children: "New password" }), _jsx(PasswordInput, { id: "account-new-password", className: styles.input, value: newPasswordValue, onChange: (event) => setNewPasswordValue(event.target.value), disabled: changePasswordBusy, autoComplete: "new-password", visibilityLabel: "new password" })] }), _jsxs("div", { className: settingsStyles.fieldLabel, children: [_jsx("label", { htmlFor: "account-confirm-password", children: "Confirm new password" }), _jsx(PasswordInput, { id: "account-confirm-password", className: styles.input, value: confirmPasswordValue, onChange: (event) => setConfirmPasswordValue(event.target.value), disabled: changePasswordBusy, autoComplete: "new-password", visibilityLabel: "confirm new password" })] }), _jsx("p", { className: "tf-text-helper", children: addPasswordGuidance.join(' ') }), changePasswordError && _jsx("p", { className: "tf-text-error", role: "alert", children: changePasswordError }), _jsxs("div", { className: settingsStyles.inlineActions, children: [_jsx("button", { type: "button", className: "tf-button-secondary tf-button-compact", onClick: () => {
|
|
177
186
|
setShowChangePassword(false);
|
|
178
187
|
setCurrentPasswordValue('');
|
|
179
188
|
setNewPasswordValue('');
|
|
@@ -183,15 +192,15 @@ export function AccountSettingsModal({ isOpen, theme, displayName, email, avatar
|
|
|
183
192
|
setShowAddPassword(true);
|
|
184
193
|
setAddPasswordDone(false);
|
|
185
194
|
setAddPasswordError(null);
|
|
186
|
-
}, children: "Add password login" }) })), showAddPassword && (_jsxs("div", { className: settingsStyles.addPasswordForm, children: [_jsxs("
|
|
195
|
+
}, children: "Add password login" }) })), showAddPassword && (_jsxs("div", { className: settingsStyles.addPasswordForm, children: [_jsxs("div", { className: settingsStyles.fieldLabel, children: [_jsx("label", { htmlFor: "account-add-password", children: "New password" }), _jsx(PasswordInput, { id: "account-add-password", className: styles.input, placeholder: "Use 12+ characters", value: addPasswordValue, onChange: (event) => setAddPasswordValue(event.target.value), disabled: addPasswordBusy, autoComplete: "new-password", visibilityLabel: "new password" })] }), _jsx("p", { className: "tf-text-helper", children: addPasswordGuidance.join(' ') }), addPasswordError && _jsx("p", { className: "tf-text-error", role: "alert", children: addPasswordError }), _jsxs("div", { className: settingsStyles.inlineActions, children: [_jsx("button", { type: "button", className: "tf-button-secondary tf-button-compact", onClick: () => {
|
|
187
196
|
setShowAddPassword(false);
|
|
188
197
|
setAddPasswordValue('');
|
|
189
198
|
setAddPasswordError(null);
|
|
190
199
|
}, disabled: addPasswordBusy, children: "Cancel" }), _jsx("button", { type: "button", className: "tf-button-primary tf-button-compact", onClick: () => { void handleAddPassword(); }, disabled: addPasswordBusy || !addPasswordValue, children: addPasswordBusy ? 'Saving…' : 'Save password' })] })] })), !loginMethodsLoading && !loginMethodsError && linkableProviders.length > 0 && (_jsxs("div", { className: settingsStyles.linkProviderBlock, children: [_jsx("p", { className: "tf-label-micro", children: "Link another account" }), _jsx("div", { className: settingsStyles.inlineActions, children: linkableProviders.map((provider) => (_jsxs("button", { type: "button", className: "tf-button-secondary tf-button-compact", onClick: () => onLinkProvider(provider), children: [_jsx(Link2, { size: 15 }), PROVIDER_LABELS[provider] || provider] }, provider))) })] }))] })] }));
|
|
191
200
|
const renderBillingSection = () => (_jsxs("section", { className: settingsStyles.section, "aria-labelledby": "account-billing-heading", children: [_jsxs("div", { className: settingsStyles.sectionHeader, children: [_jsxs("div", { children: [_jsx("h2", { className: "tf-heading-card", id: "account-billing-heading", children: "Plan & Billing" }), _jsx("p", { className: "tf-text-secondary", children: "Review your plan and open the secure billing portal." })] }), _jsxs("button", { type: "button", className: "tf-button-ghost tf-button-compact", onClick: onRefreshBilling, disabled: billingActionBusy || billingLoading, children: [_jsx(RefreshCw, { size: 14 }), "Refresh"] })] }), _jsxs("div", { className: `${settingsStyles.sectionCard} tf-surface-panel`, children: [billingLoading && _jsx("p", { className: "tf-text-secondary", children: "Loading billing status\u2026" }), billingError && _jsx("p", { className: "tf-text-error", role: "alert", children: billingError }), billingActionError && _jsx("p", { className: "tf-text-error", role: "alert", children: billingActionError }), billingNotice && _jsx("p", { className: "tf-text-secondary", role: "status", children: billingNotice }), _jsxs("div", { className: settingsStyles.planSummary, children: [_jsxs("div", { children: [_jsx("p", { className: "tf-label-micro", children: "Current plan" }), _jsx("strong", { className: settingsStyles.planName, children: resolvedPlanLabel })] }), _jsx("span", { className: `tf-chip ${lifecyclePresentation.isActive ? 'tf-chip-success' : 'tf-chip-neutral'}`, children: formatStatusLabel(accountProfileSummary?.entitlementState) })] }), lifecyclePresentation.message && (_jsx("p", { className: lifecyclePresentation.statusTone === 'error' ? 'tf-text-error' : 'tf-text-secondary', children: lifecyclePresentation.message })), accountProfileSummary?.stripeSubscriptionId && (_jsxs("div", { className: settingsStyles.intervalRow, children: [_jsxs("label", { className: settingsStyles.fieldLabel, children: [_jsx("span", { children: "Billing interval" }), _jsxs("select", { className: styles.input, value: billingIntervalChoice, onChange: (event) => onBillingIntervalChange(event.target.value === 'year' ? 'year' : 'month'), disabled: billingActionBusy, children: [_jsx("option", { value: "month", children: "Monthly" }), _jsx("option", { value: "year", children: "Yearly" })] })] }), _jsx("button", { type: "button", className: "tf-button-secondary tf-button-compact", onClick: onUpdateInterval, disabled: billingActionBusy, children: "Update interval" })] })), _jsxs("div", { className: settingsStyles.sectionActions, children: [(lifecyclePresentation.primaryAction === 'manage_billing' || lifecyclePresentation.primaryAction === 'none') && (_jsx("button", { type: "button", className: "tf-button-secondary tf-button-compact", onClick: onOpenPlans, disabled: billingActionBusy, children: "View plans" })), lifecyclePresentation.showManageBilling && lifecyclePresentation.primaryAction !== 'manage_billing' && (_jsx("button", { type: "button", className: "tf-button-secondary tf-button-compact", onClick: onManageBilling, disabled: billingActionBusy, children: "Manage billing" })), lifecyclePresentation.primaryLabel && (_jsx("button", { type: "button", className: "tf-button-primary tf-button-compact", onClick: handlePrimaryBillingAction, disabled: billingActionBusy, children: billingActionBusy ? 'Working…' : lifecyclePresentation.primaryLabel }))] })] })] }));
|
|
192
|
-
return (_jsx(Modal, { isOpen: isOpen, onClose:
|
|
201
|
+
return (_jsx(Modal, { isOpen: isOpen, onClose: handleClose, title: "Account Settings", size: "mdWide", theme: theme, draggable: true, children: _jsx("div", { className: `${modalStyles.form} ${settingsStyles.modalBody}`, children: _jsxs("div", { className: settingsStyles.layout, children: [_jsx("div", { className: settingsStyles.sectionTabs, role: "tablist", "aria-label": "Account settings sections", "aria-orientation": "vertical", children: SECTION_META.map((section) => {
|
|
193
202
|
const Icon = section.icon;
|
|
194
203
|
const selected = section.id === activeSection;
|
|
195
|
-
return (_jsxs("button", { type: "button", role: "tab", id: `account-settings-tab-${section.id}`, "aria-controls": `account-settings-panel-${section.id}`, "aria-selected": selected, className: settingsStyles.sectionTab, onClick: () =>
|
|
204
|
+
return (_jsxs("button", { type: "button", role: "tab", id: `account-settings-tab-${section.id}`, "aria-controls": `account-settings-panel-${section.id}`, "aria-selected": selected, className: settingsStyles.sectionTab, onClick: () => handleSectionChange(section.id), children: [_jsx(Icon, { size: 16 }), _jsx("span", { children: section.label })] }, section.id));
|
|
196
205
|
}) }), _jsxs("div", { className: settingsStyles.sectionPanel, role: "tabpanel", id: `account-settings-panel-${activeSection}`, "aria-labelledby": `account-settings-tab-${activeSection}`, children: [activeSection === 'profile' && renderProfileSection(), activeSection === 'security' && renderSecuritySection(), activeSection === 'billing' && renderBillingSection()] })] }) }) }));
|
|
197
206
|
}
|
package/dist/config/envSchema.js
CHANGED
|
@@ -143,10 +143,15 @@ const SPECS = [
|
|
|
143
143
|
{ key: 'TASKFORCE_BEDROCK_SMOKE_MAX_TOKENS', description: 'Maximum output tokens for live Bedrock smoke tests.', category: 'AI', defaultValue: '128', targets: ['server', 'all'] },
|
|
144
144
|
{ key: 'TASKFORCE_BEDROCK_MANTLE_MODEL_KEY', description: 'Optional model key for Bedrock Mantle smoke tests (openai.gpt-5-4|xai.grok-4-3).', category: 'AI', defaultValue: 'openai.gpt-5-4', targets: ['server', 'all'] },
|
|
145
145
|
{ key: 'TASKFORCE_BEDROCK_ENV_FILE', description: 'Optional env file path for the Bedrock smoke script.', category: 'AI', defaultValue: '.env.local', targets: ['server', 'all'] },
|
|
146
|
+
{ key: 'TASKFORCE_AGENT_SUBSCRIPTION_DEV_MODELS', description: 'Enable internal local-development-only Taskforce Agent subscription-auth model adapters such as OpenAI Codex app-server.', category: 'AI', defaultValue: 'false', targets: ['server', 'all'] },
|
|
147
|
+
{ key: 'TASKFORCE_CODEX_APP_SERVER_COMMAND', description: 'Optional Codex CLI command path for the internal OpenAI Codex app-server Taskforce Agent adapter.', category: 'AI', defaultValue: 'codex', targets: ['server', 'all'] },
|
|
148
|
+
{ key: 'TASKFORCE_CODEX_APP_SERVER_CWD', description: 'Optional working directory for the internal Codex app-server subprocess. Defaults to the system temp directory.', category: 'AI', targets: ['server', 'all'] },
|
|
146
149
|
{ key: 'TASKFORCE_ALLOWED_ORIGINS', description: 'Allowed CORS origins (comma-separated).', category: 'Security', targets: ['server', 'all'] },
|
|
147
150
|
{ key: 'TASKFORCE_API_RATE_LIMIT_ENABLED', description: 'Enable API rate limiter.', category: 'Security', defaultValue: 'true', targets: ['server', 'all'] },
|
|
148
151
|
{ key: 'TASKFORCE_API_RATE_LIMIT_MAX_REQUESTS', description: 'API rate limit max requests per window.', category: 'Security', defaultValue: '120', targets: ['server', 'all'] },
|
|
149
152
|
{ key: 'TASKFORCE_API_RATE_LIMIT_WINDOW_MS', description: 'API rate limit window size in ms.', category: 'Security', defaultValue: '60000', targets: ['server', 'all'] },
|
|
153
|
+
{ key: 'TASKFORCE_SYNC_FEED_RATE_LIMIT_MAX_REQUESTS', description: 'Workspace sync feed read limit per window.', category: 'Security', defaultValue: '600', targets: ['server', 'all'] },
|
|
154
|
+
{ key: 'TASKFORCE_SYNC_FEED_RATE_LIMIT_WINDOW_MS', description: 'Workspace sync feed read limit window size in ms.', category: 'Security', defaultValue: '60000', targets: ['server', 'all'] },
|
|
150
155
|
{ key: 'TASKFORCE_STRIPE_WEBHOOK_RATE_LIMIT_MAX_REQUESTS', description: 'Stripe webhook-specific rate limit max requests per window.', category: 'Security', defaultValue: '600', targets: ['server', 'all'] },
|
|
151
156
|
{ key: 'TASKFORCE_STRIPE_WEBHOOK_RATE_LIMIT_WINDOW_MS', description: 'Stripe webhook-specific rate limit window size in ms.', category: 'Security', defaultValue: '60000', targets: ['server', 'all'] },
|
|
152
157
|
{ key: 'TASKFORCE_MAX_PAYLOAD_BYTES', description: 'Maximum accepted API payload size in bytes.', category: 'Security', defaultValue: '1048576', targets: ['server', 'all'] },
|
|
@@ -168,6 +173,8 @@ const SPECS = [
|
|
|
168
173
|
{ key: 'TASKFORCE_BUILD_DEPLOY_ID', description: 'Build deploy ID metadata.', category: 'Build', targets: ['server', 'all'] },
|
|
169
174
|
{ key: 'TASKFORCE_UI_DIST_DIR', description: 'Override the standalone UI distribution directory for isolated local QA runtimes.', category: 'Build', targets: ['server', 'all'] },
|
|
170
175
|
{ key: 'TASKFORCE_REQUIRE_EXACT_PORT', description: 'Require the standalone server to use only its requested port.', category: 'Core', defaultValue: 'false', targets: ['server', 'all'] },
|
|
176
|
+
{ key: 'TASKFORCE_QA_EXPECTED_WORKSPACE_ID', description: 'Require isolated cloud QA to resolve the exact expected workspace ID before mutation.', category: 'Core', targets: ['server', 'all'] },
|
|
177
|
+
{ key: 'TASKFORCE_QA_EXPECTED_WORKSPACE_SLUG', description: 'Require isolated cloud QA to resolve the expected workspace slug before mutation.', category: 'Core', targets: ['server', 'all'] },
|
|
171
178
|
{ key: 'DEBUG_MODE', description: 'Expose debug-only client diagnostics such as pricing source metadata.', category: 'Build', defaultValue: 'false', targets: ['all'] },
|
|
172
179
|
{ key: 'VITE_TASKFORCE_BASE_URL', description: 'Client base URL for both API and cloud auth when specific overrides are not set.', category: 'Client', example: 'https://performance-app.taskforcehq.ai', targets: ['all'] },
|
|
173
180
|
{ key: 'VITE_TASKFORCE_API_BASE_URL', description: 'Client API base URL (overrides VITE_TASKFORCE_BASE_URL when set).', category: 'Client', example: 'https://performance-app.taskforcehq.ai', targets: ['all'] },
|
|
@@ -185,17 +192,20 @@ const SPECS = [
|
|
|
185
192
|
{ key: 'SYNC_SOAK_PROFILE', description: 'Soak profile (smoke|stress|overnight).', category: 'Soak', defaultValue: 'smoke', targets: ['soak', 'all'] },
|
|
186
193
|
{ key: 'SYNC_SOAK_DURATION_MIN', description: 'Soak duration in minutes.', category: 'Soak', defaultValue: '60', targets: ['soak', 'all'] },
|
|
187
194
|
{ key: 'SYNC_SOAK_SEED', description: 'Deterministic soak seed.', category: 'Soak', example: '4242', targets: ['soak', 'all'] },
|
|
195
|
+
{ key: 'SYNC_SOAK_TASK_ID_NAMESPACE', description: 'Optional per-run namespace for deterministic generated soak task IDs.', category: 'Soak', targets: ['soak', 'all'] },
|
|
188
196
|
{ key: 'SYNC_SOAK_ARTIFACT_DIR', description: 'Soak artifacts output directory.', category: 'Soak', defaultValue: '.taskforce/soak-artifacts', targets: ['soak', 'all'] },
|
|
189
197
|
{ key: 'SYNC_SOAK_ENABLE_SYNC_DEBUG', description: 'Enable sync debug diagnostics during soak.', category: 'Soak', defaultValue: 'false', targets: ['soak', 'all'] },
|
|
190
198
|
{ key: 'SYNC_SOAK_STRESS_ACTIONS', description: 'Stress action count.', category: 'Soak', defaultValue: '150', targets: ['soak', 'all'] },
|
|
191
199
|
{ key: 'SYNC_SOAK_STRESS_CHECKPOINT_INTERVAL', description: 'Stress checkpoint interval.', category: 'Soak', defaultValue: '50', targets: ['soak', 'all'] },
|
|
192
200
|
{ key: 'SYNC_SOAK_STRESS_SETTLE_RETRIES', description: 'Stress settle retry count.', category: 'Soak', defaultValue: '3', targets: ['soak', 'all'] },
|
|
193
201
|
{ key: 'SYNC_SOAK_STRESS_SETTLE_WAIT_MS', description: 'Stress settle wait time in ms.', category: 'Soak', defaultValue: '500', targets: ['soak', 'all'] },
|
|
202
|
+
{ key: 'SYNC_SOAK_STRESS_BOOTSTRAP_PUSH_SETTLE', description: 'Enable legacy bootstrap-push checkpoint settlement in stress and overnight profiles.', category: 'Soak', defaultValue: 'true', targets: ['soak', 'all'] },
|
|
194
203
|
{ key: 'SYNC_SOAK_STRESS_HARD_RESET_SETTLE', description: 'Enable hard reset settle path in stress.', category: 'Soak', defaultValue: 'true', targets: ['soak', 'all'] },
|
|
195
204
|
{ key: 'SYNC_SOAK_OVERNIGHT_ACTION_INTERVAL_MS', description: 'Overnight action pacing interval in ms.', category: 'Soak', defaultValue: '60000', targets: ['soak', 'all'] },
|
|
196
205
|
{ key: 'SYNC_SOAK_OVERNIGHT_CHECKPOINT_MIN', description: 'Overnight checkpoint interval in minutes.', category: 'Soak', defaultValue: '15', targets: ['soak', 'all'] },
|
|
197
206
|
{ key: 'SYNC_SOAK_OVERNIGHT_MAX_ACTIONS', description: 'Overnight hard cap on action count.', category: 'Soak', defaultValue: '10000', targets: ['soak', 'all'] },
|
|
198
207
|
{ key: 'SYNC_SOAK_OVERNIGHT_MIN_ACTIONS', description: 'Overnight minimum actions expected before pass.', category: 'Soak', defaultValue: '80', targets: ['soak', 'all'] },
|
|
208
|
+
{ key: 'SYNC_SOAK_OVERNIGHT_CLEAN_BASELINE_EVERY_ACTION', description: 'Require coordinator settlement before each accelerated overnight action.', category: 'Soak', defaultValue: 'false', targets: ['soak', 'all'] },
|
|
199
209
|
{ key: 'CF_ACCESS_CLIENT_ID', description: 'Cloudflare Access service token client id (soak).', category: 'Cloudflare', secret: true, targets: ['soak', 'all'] },
|
|
200
210
|
{ key: 'CF_ACCESS_CLIENT_SECRET', description: 'Cloudflare Access service token client secret (soak).', category: 'Cloudflare', secret: true, targets: ['soak', 'all'] }
|
|
201
211
|
];
|
|
@@ -255,6 +265,8 @@ function validateCommonNumbers(env, errors) {
|
|
|
255
265
|
'PORT',
|
|
256
266
|
'TASKFORCE_API_RATE_LIMIT_MAX_REQUESTS',
|
|
257
267
|
'TASKFORCE_API_RATE_LIMIT_WINDOW_MS',
|
|
268
|
+
'TASKFORCE_SYNC_FEED_RATE_LIMIT_MAX_REQUESTS',
|
|
269
|
+
'TASKFORCE_SYNC_FEED_RATE_LIMIT_WINDOW_MS',
|
|
258
270
|
'TASKFORCE_STRIPE_WEBHOOK_RATE_LIMIT_MAX_REQUESTS',
|
|
259
271
|
'TASKFORCE_STRIPE_WEBHOOK_RATE_LIMIT_WINDOW_MS',
|
|
260
272
|
'TASKFORCE_MAX_PAYLOAD_BYTES',
|