@taskforcehq/taskforce 0.3.327 → 0.3.328
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 +24 -53
- package/dist/components/activity/EntityActivityTimeline.js +15 -5
- package/dist/components/context/AttachmentPreviewHost.js +1 -1
- package/dist/components/context/ContextAttachmentManager.d.ts +3 -0
- package/dist/components/context/ContextAttachmentManager.js +116 -20
- package/dist/components/features/planning/PlanningEntitySummary.d.ts +3 -1
- package/dist/components/features/planning/PlanningEntitySummary.js +3 -2
- package/dist/components/features/planning/PlanningLinkPicker.d.ts +3 -1
- package/dist/components/features/planning/PlanningLinkPicker.js +5 -3
- package/dist/components/features/planning/PlanningModule.js +26 -9
- package/dist/components/features/planning/PlanningVisualIdentity.d.ts +14 -0
- package/dist/components/features/planning/PlanningVisualIdentity.js +106 -0
- package/dist/components/features/planning/planningWorkspaceModel.d.ts +11 -0
- package/dist/components/task/TaskActionHeader.d.ts +2 -1
- package/dist/components/task/TaskActionHeader.js +2 -2
- package/dist/components/task/TaskCard.js +37 -4
- package/dist/components/task/TaskContextUpload.d.ts +3 -0
- package/dist/components/task/TaskContextUpload.js +2 -2
- package/dist/components/task/TaskForm.d.ts +4 -0
- package/dist/components/task/TaskForm.js +32 -7
- package/dist/components/task/TaskImageReviews.js +16 -16
- package/dist/components/task/TaskStatusActions.js +11 -2
- package/dist/components/task/TaskWorkstreamPicker.js +2 -1
- package/dist/components/ui/DocumentTypeIcon.d.ts +7 -0
- package/dist/components/ui/DocumentTypeIcon.js +8 -0
- package/dist/components/ui/TaxonomyDropdown.d.ts +9 -1
- package/dist/components/ui/TaxonomyDropdown.js +30 -3
- package/dist/components/views/StandaloneLayout.js +62 -9
- package/dist/components/views/WidgetView.js +8 -5
- package/dist/components/views/panels/PlanningDrawer.d.ts +7 -1
- package/dist/components/views/panels/PlanningDrawer.js +48 -44
- package/dist/config/envSchema.js +1 -0
- package/dist/config/localServiceCloudTarget.d.ts +10 -0
- package/dist/config/localServiceCloudTarget.js +76 -0
- package/dist/core/AiProfileService.d.ts +1 -0
- package/dist/core/AiProfileService.js +2 -1
- package/dist/core/AttachmentLinkService.js +7 -4
- package/dist/core/DeletedTaskLifecycleService.d.ts +4 -0
- package/dist/core/DeletedTaskLifecycleService.js +21 -0
- package/dist/core/McpTokenService.d.ts +4 -0
- package/dist/core/McpTokenService.js +52 -0
- package/dist/core/TaskAttachmentLinksDurableMutationService.d.ts +5 -0
- package/dist/core/TaskAttachmentLinksDurableMutationService.js +20 -11
- package/dist/core/Taskforce.d.ts +63 -1
- package/dist/core/Taskforce.js +558 -73
- package/dist/core/types.d.ts +2 -1
- package/dist/documentReviews/DocumentReviewCommentStore.d.ts +11 -0
- package/dist/documentReviews/DocumentReviewCommentStore.js +35 -2
- package/dist/documentReviews/service.d.ts +4 -0
- package/dist/documentReviews/service.js +141 -46
- package/dist/hooks/tasks/useTaskFormActions.d.ts +3 -0
- package/dist/hooks/tasks/useTaskFormActions.js +33 -7
- package/dist/hooks/useTaskforce.d.ts +59 -50
- package/dist/hooks/useTaskforce.js +103 -1
- package/dist/mcp/canonicalAssetHelpers.d.ts +13 -0
- package/dist/mcp/canonicalAssetHelpers.js +39 -5
- package/dist/mcp/collaborationRegistrar.js +15 -13
- package/dist/mcp/documentAssetRegistrar.js +16 -9
- package/dist/mcp/localServiceProxy.js +4 -1
- package/dist/mcp/runtime.d.ts +16 -0
- package/dist/mcp/runtime.js +566 -171
- package/dist/mcp/taskAttachmentCommandAdapter.d.ts +2 -0
- package/dist/mcp/taskAttachmentCommandAdapter.js +14 -8
- package/dist/mcp/taskPlanningRegistrar.js +42 -34
- package/dist/mcp/toolRegistry.js +2 -2
- package/dist/migrations/taskSchemaMigrations.js +35 -0
- package/dist/runtime/appGateDefinitions.d.ts +2 -2
- package/dist/runtime/appGateDefinitions.js +2 -2
- package/dist/server/appAccess.d.ts +12 -0
- package/dist/server/appAccess.js +21 -0
- package/dist/server/authorizedWorkspaceAccess.d.ts +12 -0
- package/dist/server/authorizedWorkspaceAccess.js +40 -0
- package/dist/server/cloudCollectionRead.d.ts +25 -0
- package/dist/server/cloudCollectionRead.js +120 -0
- package/dist/server/cloudCollectionReadPool.d.ts +52 -0
- package/dist/server/cloudCollectionReadPool.js +140 -0
- package/dist/server/cloudCollectionReadWorker.d.ts +1 -0
- package/dist/server/cloudCollectionReadWorker.js +36 -0
- package/dist/server/index.d.ts +3 -11
- package/dist/server/index.js +53 -40
- package/dist/server/localServiceLease.d.ts +3 -0
- package/dist/server/localServiceLease.js +9 -1
- package/dist/server/routes/documents.d.ts +5 -2
- package/dist/server/routes/documents.js +160 -13
- package/dist/server/routes/durableTaskHttpRouters.d.ts +2 -0
- package/dist/server/routes/durableTaskHttpRouters.js +2 -2
- package/dist/server/routes/localService.js +2 -0
- package/dist/server/routes/primitives.d.ts +2 -0
- package/dist/server/routes/shared.d.ts +24 -0
- package/dist/server/routes/shared.js +114 -18
- package/dist/server/routes/sync.js +22 -0
- package/dist/server/routes/syncV3FeedRoutes.js +4 -2
- package/dist/server/routes/syncV3LocalCaptureRoutes.js +20 -2
- package/dist/server/routes/syncV3OperationRoutes.js +17 -2
- package/dist/server/routes/tasks.js +199 -89
- package/dist/server/routes.js +35 -7
- package/dist/service/localServiceCli.js +36 -25
- package/dist/service/localServiceClientLifecycle.js +67 -6
- package/dist/shared/planningIdentity.d.ts +20 -0
- package/dist/shared/planningIdentity.js +79 -0
- package/dist/storage/postgresAdapter.js +4 -0
- package/dist/storage/postgresRequestDiagnostics.d.ts +8 -0
- package/dist/storage/postgresRequestDiagnostics.js +24 -0
- package/dist/storage/workspaceAssetStore.d.ts +32 -0
- package/dist/storage/workspaceAssetStore.js +70 -0
- package/dist/sync/coordinator/localSyncExecutionPermitStore.d.ts +16 -0
- package/dist/sync/coordinator/localSyncExecutionPermitStore.js +70 -22
- package/dist/sync/coordinator/workspaceSyncTransportGateway.js +4 -2
- package/dist/sync/engine/workspaceSyncEngineRemoteServices.d.ts +5 -0
- package/dist/sync/engine/workspaceSyncEngineServerRemoteServices.d.ts +1 -1
- package/dist/sync/engine/workspaceSyncEngineServerRemoteServices.js +15 -0
- package/dist/sync/engine/workspaceSyncEngineServerRunnerOperations.js +26 -3
- package/dist/sync/engine/workspaceSyncV2ChangeDispatcher.js +2 -0
- package/dist/sync/syncApplyHandlers.d.ts +3 -1
- package/dist/sync/syncApplyHandlers.js +29 -0
- package/dist/sync/syncService.js +9 -0
- package/dist/sync/taskSyncPayload.d.ts +1 -0
- package/dist/sync/taskSyncPayload.js +6 -0
- package/dist/sync/v3/durableTaskAttachmentLinksMutationRouter.d.ts +1 -0
- package/dist/sync/v3/durableTaskAttachmentLinksMutationRouter.js +21 -3
- package/dist/sync/v3/durableTaskHttpBulkMutationRouter.js +17 -4
- package/dist/sync/v3/localInitiativeOutboxService.js +4 -2
- package/dist/sync/v3/localOutboxDispatcher.js +5 -1
- package/dist/sync/v3/localTaskAttachmentLinksOutboxHandler.js +12 -2
- package/dist/sync/v3/localTaskAttachmentLinksOutboxService.d.ts +1 -0
- package/dist/sync/v3/localTaskAttachmentLinksOutboxService.js +15 -3
- package/dist/sync/v3/localWorkstreamOutboxService.js +4 -2
- package/dist/sync/v3/syncDurabilityStore.d.ts +8 -0
- package/dist/sync/v3/syncDurabilityStore.js +171 -33
- package/dist/sync/v3/taskAttachmentLinksMutationService.d.ts +1 -0
- package/dist/sync/v3/taskAttachmentLinksMutationService.js +18 -2
- package/dist/sync/v3/taskMetadataCoexistence.js +4 -0
- package/dist/sync/v3/workspaceSyncV2ProjectionExclusions.js +1 -9
- package/dist/sync/v3/workspaceSyncV3CloudOperationHandler.js +1 -0
- package/dist/sync/v3/workspaceSyncV3CombinedFeedOrchestrator.js +7 -1
- package/dist/sync/v3/workspaceSyncV3CombinedLocalApply.js +15 -3
- package/dist/sync/v3/workspaceSyncV3CombinedRepairBaseline.d.ts +4 -0
- package/dist/sync/v3/workspaceSyncV3CombinedRepairBaseline.js +29 -6
- package/dist/sync/v3/workspaceSyncV3CombinedRepairMaterialization.d.ts +1 -0
- package/dist/sync/v3/workspaceSyncV3CombinedRepairMaterialization.js +29 -7
- package/dist/sync/v3/workspaceSyncV3CombinedRepairSnapshot.js +62 -7
- package/dist/sync/v3/workspaceSyncV3OperationProtocol.d.ts +1 -0
- package/dist/sync/v3/workspaceSyncV3RepairSqlBatching.d.ts +2 -0
- package/dist/sync/v3/workspaceSyncV3RepairSqlBatching.js +10 -0
- package/dist/sync/v3/workspaceSyncV3TaskRootProjection.d.ts +1 -1
- package/dist/sync/v3/workspaceSyncV3TaskRootProjection.js +1 -1
- package/dist/sync/workspacePullFeed.d.ts +6 -1
- package/dist/sync/workspacePullFeed.js +172 -161
- package/dist/sync/workspaceSyncModel.d.ts +11 -0
- package/dist/sync/workspaceSyncModel.js +27 -2
- package/dist/types.d.ts +8 -0
- package/dist/ui/assets/{AiIdentityRosterCard-DB1BLO_j.js → AiIdentityRosterCard-MZs7lVED.js} +1 -1
- package/dist/ui/assets/{AiProfilesModule-DPxQeF9N.js → AiProfilesModule-C3i0LOxe.js} +1 -1
- package/dist/ui/assets/{AnnotatedAttachmentWorkspace-DHNYaNg6.js → AnnotatedAttachmentWorkspace-DVZk5Tln.js} +1 -1
- package/dist/ui/assets/AssetTraySortControl-Kf1pelSy.js +1 -0
- package/dist/ui/assets/ContextAttachmentManager-B8boOs6Q.js +3 -0
- package/dist/ui/assets/{DocumentWorkspace-BC7sy5TG.js → DocumentWorkspace-DXH4XUsB.js} +4 -4
- package/dist/ui/assets/EntityActivityTimeline-BtSTq0ON.js +1 -0
- package/dist/ui/assets/PlanningModule-BPVyEa-f.js +1 -0
- package/dist/ui/assets/PlanningModule-CoIR9hbV.css +1 -0
- package/dist/ui/assets/{PlansPage-BVH_NfgT.js → PlansPage-ChqgxALc.js} +1 -1
- package/dist/ui/assets/TaskContextUpload-CB3ol0sN.js +1 -0
- package/dist/ui/assets/TaskContextUpload-qmBeUw3u.css +1 -0
- package/dist/ui/assets/{TaskSettings-xVsxSa0g.js → TaskSettings-g4ECKNOz.js} +1 -1
- package/dist/ui/assets/{TaskforceAgentsModule-CkAxW19K.js → TaskforceAgentsModule-BazdUwxZ.js} +1 -1
- package/dist/ui/assets/WorkflowManagerModule-DWFspC-o.js +1 -0
- package/dist/ui/assets/index-BUplSsv_.js +7 -0
- package/dist/ui/assets/index-gz2EXuoK.css +1 -0
- package/dist/ui/assets/{vendor-icons-B8ZNsH1g.js → vendor-icons-BkFLXavV.js} +1 -1
- package/dist/ui/index.html +3 -3
- package/dist/utils/constants.d.ts +2 -2
- package/dist/utils/constants.js +1 -1
- package/dist/utils/contextAttachmentUpload.d.ts +2 -0
- package/dist/utils/contextAttachmentUpload.js +7 -0
- package/dist/utils/contextFiles.d.ts +1 -1
- package/dist/utils/contextFiles.js +4 -4
- package/package.json +6 -1
- package/dist/ui/assets/AssetTraySortControl-DphbyHVc.js +0 -1
- package/dist/ui/assets/ContextAttachmentManager-CMxSzHqc.js +0 -3
- package/dist/ui/assets/EntityActivityTimeline-pJ2ZjZ-k.js +0 -1
- package/dist/ui/assets/PlanningModule-CeTqXMMz.css +0 -1
- package/dist/ui/assets/PlanningModule-t5GbvVR9.js +0 -1
- package/dist/ui/assets/TaskContextUpload-Ds4LnDzV.css +0 -1
- package/dist/ui/assets/TaskContextUpload-bl4fOOUz.js +0 -1
- package/dist/ui/assets/WorkflowManagerModule-BKqIGVnF.js +0 -1
- package/dist/ui/assets/index-BzJlVnNS.css +0 -1
- package/dist/ui/assets/index-Cx7vZJHL.js +0 -7
|
@@ -16,6 +16,7 @@ import { getPlanningFavoriteKey, } from '../../../utils/planningFavorites';
|
|
|
16
16
|
import { TASK_ASSIGNEE_UNASSIGNED_COLOR, TASK_ASSIGNEE_UNASSIGNED_ICON, } from '../../../utils/assignees';
|
|
17
17
|
import { PlanningEntityActivity, PlanningEntityContext, PlanningEntityDescription, } from '../../features/planning/PlanningEntityDetails';
|
|
18
18
|
import { PlanningEntitySummary, PlanningNoTasks, PlanningProgress, PlanningReferenceBadge, PlanningSummaryMeta, } from '../../features/planning/PlanningEntitySummary';
|
|
19
|
+
import { PlanningVisualIdentity } from '../../features/planning/PlanningVisualIdentity';
|
|
19
20
|
import { PlanningTaskCard } from '../../features/planning/PlanningTaskCard';
|
|
20
21
|
import { PlanningTaskArrangeControl } from '../../features/planning/PlanningTaskArrangeControl';
|
|
21
22
|
import planningDropdownStyles from '../../features/planning/PlanningDropdownControl.module.css';
|
|
@@ -72,11 +73,10 @@ function countArchivedDetailChildren(detail) {
|
|
|
72
73
|
}
|
|
73
74
|
return (detail.item.tasks || []).filter((task) => Boolean(task.isArchived)).length;
|
|
74
75
|
}
|
|
75
|
-
function PlanningRow({ rowId, rowType, referenceLabel, item, ownerOption, ownerOptions, workstreamCount, active, isArchived = false, detailOpen = false, canExpand = false, expanded = false, controlsId, onToggleExpand, onToggleScope, onOpenDetails, onChangeOwner, initiativeLinkOptions = [], onLinkInitiative, favorite, favoriteReady, onToggleFavorite, onArchiveReady, hierarchyInteractions = true, }) {
|
|
76
|
+
function PlanningRow({ rowId, rowType, referenceLabel, item, ownerOption, ownerOptions, workstreamCount, active, isArchived = false, detailOpen = false, canExpand = false, expanded = false, controlsId, onToggleExpand, onToggleScope, onOpenDetails, onChangeOwner, onChangeIdentity, initiativeLinkOptions = [], onLinkInitiative, favorite, favoriteReady, onToggleFavorite, onArchiveReady, hierarchyInteractions = true, }) {
|
|
76
77
|
const { title } = item;
|
|
77
78
|
const [archiveConfirmationVisible, setArchiveConfirmationVisible] = React.useState(false);
|
|
78
79
|
const [archiveBusy, setArchiveBusy] = React.useState(false);
|
|
79
|
-
const [initiativePickerVisible, setInitiativePickerVisible] = React.useState(false);
|
|
80
80
|
const [initiativeLinkBusy, setInitiativeLinkBusy] = React.useState(false);
|
|
81
81
|
const { active: dndActive } = useDndContext();
|
|
82
82
|
const dragType = String(dndActive?.data?.current?.type || '');
|
|
@@ -91,7 +91,7 @@ function PlanningRow({ rowId, rowType, referenceLabel, item, ownerOption, ownerO
|
|
|
91
91
|
disabled: !hierarchyInteractions,
|
|
92
92
|
});
|
|
93
93
|
const draggable = hierarchyInteractions && rowType === 'workstream';
|
|
94
|
-
const { attributes, listeners, setNodeRef: setDragRef, transform, isDragging, } = useDraggable({
|
|
94
|
+
const { attributes, listeners, setNodeRef: setDragRef, setActivatorNodeRef, transform, isDragging, } = useDraggable({
|
|
95
95
|
id: `planning-workstream:${rowId}`,
|
|
96
96
|
data: { type: 'planning-workstream', workstreamId: rowId },
|
|
97
97
|
disabled: !draggable,
|
|
@@ -163,9 +163,7 @@ function PlanningRow({ rowId, rowType, referenceLabel, item, ownerOption, ownerO
|
|
|
163
163
|
return;
|
|
164
164
|
setInitiativeLinkBusy(true);
|
|
165
165
|
try {
|
|
166
|
-
|
|
167
|
-
if (result !== false)
|
|
168
|
-
setInitiativePickerVisible(false);
|
|
166
|
+
await onLinkInitiative(option);
|
|
169
167
|
}
|
|
170
168
|
finally {
|
|
171
169
|
setInitiativeLinkBusy(false);
|
|
@@ -178,29 +176,26 @@ function PlanningRow({ rowId, rowType, referenceLabel, item, ownerOption, ownerO
|
|
|
178
176
|
: initiativeLinkOptions.length === 0
|
|
179
177
|
? 'No initiatives available'
|
|
180
178
|
: 'Link initiative';
|
|
181
|
-
return (
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
event.preventDefault();
|
|
202
|
-
onOpenDetails();
|
|
203
|
-
}, "aria-label": `View ${rowType} details for ${title}`, "aria-pressed": detailOpen, children: _jsx(PlanningEntitySummary, { item: item, entityType: rowType, workstreamCount: workstreamCount, ownerOption: ownerOption ? toPlanningAssigneeOption(ownerOption) : undefined, ownerOptions: ownerOptions.map(toPlanningAssigneeOption), onChangeOwner: onChangeOwner }) })] }), archiveConfirmationVisible ? (_jsx(PlanningArchiveConfirmation, { entityType: rowType, title: title, activeTaskCount: item.archiveActiveTaskCount, activeWorkstreamCount: item.archiveActiveWorkstreamCount, busy: archiveBusy, onCancel: () => setArchiveConfirmationVisible(false), onConfirm: () => void handleConfirmArchive() })) : null] }) }));
|
|
179
|
+
return (_jsxs("div", { ref: setDragRef, style: dragStyle, className: `${panelStyles.treeRowShell} ${draggable ? panelStyles.treeRowShellDraggable : ''} ${isDragging ? panelStyles.treeRowDragging : ''} ${showDropReady ? panelStyles.treeRowShellDropMode : ''}`.trim(), "data-planning-draggable-row": draggable || undefined, children: [draggable ? (_jsx("div", { className: panelStyles.rowDragGutter, "data-planning-drag-gutter": "true", children: _jsx("button", { type: "button", ref: setActivatorNodeRef, className: panelStyles.dragHandle, title: "Drag onto an initiative to move this workstream", "aria-label": `Move workstream ${title} to an initiative`, "data-planning-drag-handle": "true", ...attributes, ...listeners, children: _jsx(GripVertical, { size: 15, "aria-hidden": "true" }) }) })) : null, _jsx("div", { ref: setDropRef, className: `${panelStyles.treeRow} ${isArchived ? panelStyles.treeRowArchived : ''} ${detailOpen ? panelStyles.treeRowDetailOpen : ''} ${showDropReady ? panelStyles.treeRowDropReady : ''} ${isOver ? panelStyles.treeRowDropActive : ''} ${showDropReady ? panelStyles.treeRowDropMode : ''}`, "data-board-scoped": active || undefined, children: _jsxs("div", { className: panelStyles.rowContent, children: [_jsxs("div", { className: panelStyles.rowTopRow, children: [_jsxs("div", { className: panelStyles.rowReferenceActions, children: [canLinkInitiative ? (_jsx(PlanningLinkPicker, { options: initiativeLinkOptions, entityLabel: "initiative", onSelect: (option) => void handleLinkInitiative(option), disabled: initiativeLinkDisabled, iconTrigger: true, triggerTitle: initiativeLinkTitle })) : null, _jsx(PlanningReferenceBadge, { label: referenceLabel || 'Reference pending', entityType: rowType, entityName: title, pending: !referenceLabel })] }), _jsxs("div", { className: panelStyles.rowActions, role: "toolbar", "aria-label": `${rowType === 'initiative' ? 'Initiative' : 'Workstream'} actions for ${title}`, "aria-orientation": "horizontal", onKeyDown: handleRowActionKeyDown, children: [_jsx("button", { type: "button", ref: (node) => {
|
|
180
|
+
rowActionRefs.current.favorite = node;
|
|
181
|
+
}, className: `tf-control-icon tf-control-icon-compact ${panelStyles.favoriteButton} ${favorite ? 'tf-control-icon-active' : ''}`.trim(), onClick: onToggleFavorite, onFocus: () => handleRowActionFocus('favorite'), disabled: !favoriteReady, tabIndex: effectiveActiveRowActionId === 'favorite' ? 0 : -1, "data-planning-row-action": "favorite", title: !favoriteReady
|
|
182
|
+
? 'Loading Favorites'
|
|
183
|
+
: favorite ? `Remove ${rowType} from Favorites` : `Add ${rowType} to Favorites`, "aria-label": !favoriteReady
|
|
184
|
+
? `Loading Favorites for ${rowType} ${title}`
|
|
185
|
+
: favorite ? `Remove ${rowType} ${title} from Favorites` : `Add ${rowType} ${title} to Favorites`, "aria-pressed": favorite, children: _jsx(Star, { size: 14, fill: favorite ? 'currentColor' : 'none', "aria-hidden": "true" }) }), archiveActionVisible ? (_jsx("button", { type: "button", ref: (node) => {
|
|
186
|
+
rowActionRefs.current.archive = node;
|
|
187
|
+
}, className: "tf-control-icon tf-control-icon-compact", onClick: () => setArchiveConfirmationVisible(true), onFocus: () => handleRowActionFocus('archive'), tabIndex: effectiveActiveRowActionId === 'archive' ? 0 : -1, "data-planning-row-action": "archive", title: `Archive completed ${rowType}`, "aria-label": `Archive completed ${rowType} ${title}`, "aria-expanded": archiveConfirmationVisible, children: _jsx(Archive, { size: 14, "aria-hidden": "true" }) })) : null, _jsx("button", { type: "button", ref: (node) => {
|
|
188
|
+
rowActionRefs.current.scope = node;
|
|
189
|
+
}, className: `tf-control-icon tf-control-icon-compact ${panelStyles.scopeButton} ${active ? 'tf-control-icon-active' : ''}`.trim(), onClick: onToggleScope, onFocus: () => handleRowActionFocus('scope'), tabIndex: effectiveActiveRowActionId === 'scope' ? 0 : -1, "data-planning-row-action": "scope", title: active ? 'Clear board scope' : 'Scope board to this item', "aria-label": active ? `Clear board scope for ${title}` : `Scope board to ${title}`, "aria-pressed": active, children: _jsx(Kanban, { size: 14, "aria-hidden": "true" }) }), _jsx("button", { type: "button", ref: (node) => {
|
|
190
|
+
rowActionRefs.current.disclosure = node;
|
|
191
|
+
}, className: `tf-control-icon tf-control-icon-compact ${panelStyles.treeChevron}`.trim(), onClick: expansionEnabled ? onToggleExpand : undefined, onFocus: () => handleRowActionFocus('disclosure'), tabIndex: effectiveActiveRowActionId === 'disclosure' ? 0 : -1, "data-planning-row-action": "disclosure", title: expansionLabel, "aria-label": expansionLabel, "aria-expanded": expansionEnabled ? expanded : false, "aria-controls": expansionEnabled && expanded ? controlsId : undefined, disabled: !expansionEnabled, children: expanded && expansionEnabled
|
|
192
|
+
? _jsx(ChevronDown, { size: 14, "aria-hidden": "true" })
|
|
193
|
+
: _jsx(ChevronRight, { size: 14, "aria-hidden": "true" }) })] })] }), _jsx("div", { className: panelStyles.rowBody, children: _jsx("div", { role: "button", tabIndex: 0, className: panelStyles.rowButton, onClick: onOpenDetails, onKeyDown: (event) => {
|
|
194
|
+
if (event.key !== 'Enter' && event.key !== ' ')
|
|
195
|
+
return;
|
|
196
|
+
event.preventDefault();
|
|
197
|
+
onOpenDetails();
|
|
198
|
+
}, "aria-label": `View ${rowType} details for ${title}`, "aria-pressed": detailOpen, children: _jsx(PlanningEntitySummary, { item: item, entityType: rowType, workstreamCount: workstreamCount, ownerOption: ownerOption ? toPlanningAssigneeOption(ownerOption) : undefined, ownerOptions: ownerOptions.map(toPlanningAssigneeOption), onChangeOwner: onChangeOwner, onChangeIdentity: onChangeIdentity }) }) }), archiveConfirmationVisible ? (_jsx(PlanningArchiveConfirmation, { entityType: rowType, title: title, activeTaskCount: item.archiveActiveTaskCount, activeWorkstreamCount: item.archiveActiveWorkstreamCount, busy: archiveBusy, onCancel: () => setArchiveConfirmationVisible(false), onConfirm: () => void handleConfirmArchive() })) : null] }) })] }));
|
|
204
199
|
}
|
|
205
200
|
function PlanningTreeTaskCard({ task, assigneeOption, assigneeOptions, showTaskCardStatusLabel, onOpenTaskById, onChangeTaskAssignee, onSetTaskStatus, onArchivePlanningTask, }) {
|
|
206
201
|
const [archiveBusy, setArchiveBusy] = React.useState(false);
|
|
@@ -233,7 +228,7 @@ function RelationshipPane({ label, title, description, onCollapse, action, paneR
|
|
|
233
228
|
maxWidth: `${PLANNING_DRAWER_DETAIL_WIDTH_PX}px`,
|
|
234
229
|
}, children: [_jsxs("div", { className: panelStyles.paneHeader, children: [_jsx("h2", { className: panelStyles.paneHeaderLabel, tabIndex: -1, "data-planning-pane-heading": true, children: label }), _jsx("button", { type: "button", className: "tf-control-icon", onClick: onCollapse, "aria-label": `Collapse ${label} pane`, title: `Collapse ${label} pane`, children: _jsx(ChevronLeft, { size: 16, "aria-hidden": "true" }) })] }), _jsx("div", { className: `${panelStyles.paneContent} ${isEmptyState ? panelStyles.relationshipPaneContentEmpty : ''}`.trim(), children: _jsxs("div", { className: `${panelStyles.relationshipState} ${isEmptyState ? panelStyles.relationshipStateEmpty : ''}`.trim(), "data-relationship-display": isEmptyState ? 'empty' : 'detail', children: [_jsx("span", { className: panelStyles.relationshipStateTitle, children: title }), description ? _jsx("p", { className: panelStyles.relationshipStateText, children: description }) : null, action ? (_jsx("button", { type: "button", className: "tf-button-secondary tf-button-compact", onClick: action.onClick, children: action.label })) : null] }) })] }));
|
|
235
230
|
}
|
|
236
|
-
function PlanningDrawerList({ initiatives, standaloneWorkstreams, planningLoadState, taskScope, showArchivedPlanning, currentOwnerId, assigneeOptions, showTaskCardStatusLabel = false, taskArrangeMode = 'execution', activeInitiativeId, activeWorkstreamId, navigatorSort, navigatorSortReady, onNavigatorSortChange, planningFavorites, onTogglePlanningFavorite, favoritesSectionCollapsed, initiativesSectionCollapsed, workstreamsSectionCollapsed, onToggleFavoritesSection, onToggleInitiativesSection, onToggleWorkstreamsSection, expandedInitiativeIds, detail, secondaryPane, onCreateInitiative, onCreateWorkstream, onToggleInitiative, onSelectInitiative, onSelectWorkstream, onOpenInitiativeDetails, onOpenWorkstreamDetails, onOpenNestedWorkstreamDetails, onOpenTaskById, onSetTaskStatus, onChangePlanningOwner, onChangeTaskAssignee, onArchivePlanningTask, onArchiveResolvedWorkstream, onArchiveResolvedInitiative, linkOptions, onAssignInitiativeToWorkstream, onRetryPlanning, }) {
|
|
231
|
+
function PlanningDrawerList({ initiatives, standaloneWorkstreams, planningLoadState, taskScope, showArchivedPlanning, currentOwnerId, assigneeOptions, showTaskCardStatusLabel = false, taskArrangeMode = 'execution', activeInitiativeId, activeWorkstreamId, navigatorSort, navigatorSortReady, onNavigatorSortChange, planningFavorites, onTogglePlanningFavorite, favoritesSectionCollapsed, initiativesSectionCollapsed, workstreamsSectionCollapsed, onToggleFavoritesSection, onToggleInitiativesSection, onToggleWorkstreamsSection, expandedInitiativeIds, detail, secondaryPane, onCreateInitiative, onCreateWorkstream, onToggleInitiative, onSelectInitiative, onSelectWorkstream, onOpenInitiativeDetails, onOpenWorkstreamDetails, onOpenNestedWorkstreamDetails, onOpenTaskById, onSetTaskStatus, onChangePlanningOwner, onChangePlanningIdentity = () => { }, onChangeTaskAssignee, onArchivePlanningTask, onArchiveResolvedWorkstream, onArchiveResolvedInitiative, linkOptions, onAssignInitiativeToWorkstream, onRetryPlanning, }) {
|
|
237
232
|
const [searchQuery, setSearchQuery] = React.useState('');
|
|
238
233
|
const [ownerFilter, setOwnerFilter] = React.useState('all');
|
|
239
234
|
const [filtersExpanded, setFiltersExpanded] = React.useState(false);
|
|
@@ -435,7 +430,7 @@ function PlanningDrawerList({ initiatives, standaloneWorkstreams, planningLoadSt
|
|
|
435
430
|
const favoriteControlsId = `planning-${favoriteBranchKey.replaceAll(':', '-')}-children`;
|
|
436
431
|
return (_jsxs("li", { children: [_jsx(PlanningRow, { rowId: `favorite:${favorite.type}:${item.id}`, rowType: favorite.type, referenceLabel: favorite.type === 'initiative'
|
|
437
432
|
? formatInitiativeReference(item)
|
|
438
|
-
: formatWorkstreamReference(item), item: item, ownerOption: item.ownerId ? assigneeOptionByValue.get(item.ownerId) : undefined, ownerOptions: assigneeOptions, onChangeOwner: (ownerId) => onChangePlanningOwner(favorite.type, item.id, ownerId === 'unassigned' ? '' : ownerId), workstreamCount: favoriteInitiative
|
|
433
|
+
: formatWorkstreamReference(item), item: item, ownerOption: item.ownerId ? assigneeOptionByValue.get(item.ownerId) : undefined, ownerOptions: assigneeOptions, onChangeOwner: (ownerId) => onChangePlanningOwner(favorite.type, item.id, ownerId === 'unassigned' ? '' : ownerId), onChangeIdentity: (patch) => onChangePlanningIdentity(favorite.type, item.id, patch), workstreamCount: favoriteInitiative
|
|
439
434
|
? visibleInitiativeWorkstreams.length
|
|
440
435
|
: undefined, active: favorite.type === 'initiative'
|
|
441
436
|
? activeInitiativeId === item.id && !activeWorkstreamId
|
|
@@ -476,7 +471,7 @@ function PlanningDrawerList({ initiatives, standaloneWorkstreams, planningLoadSt
|
|
|
476
471
|
const childControlsId = `planning-${childBranchKey.replaceAll(':', '-')}-children`;
|
|
477
472
|
return (_jsxs("li", { children: [_jsx(PlanningRow, { rowId: childBranchKey, rowType: "workstream", referenceLabel: formatWorkstreamReference(workstream), item: workstream, ownerOption: workstream.ownerId
|
|
478
473
|
? assigneeOptionByValue.get(workstream.ownerId)
|
|
479
|
-
: undefined, ownerOptions: assigneeOptions, onChangeOwner: (ownerId) => onChangePlanningOwner('workstream', workstream.id, ownerId === 'unassigned' ? '' : ownerId), active: activeWorkstreamId === workstream.id, isArchived: Boolean(workstream.isArchived), detailOpen: (detail?.type === 'workstream' && detail.item.id === workstream.id)
|
|
474
|
+
: 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)
|
|
480
475
|
|| (secondaryPane?.kind === 'detail'
|
|
481
476
|
&& secondaryPane.detail.item.id === workstream.id), canExpand: Boolean(workstream.tasks?.length), expanded: childExpanded, controlsId: childControlsId, onToggleExpand: () => toggleFavoriteBranch(childBranchKey), onToggleScope: () => onSelectWorkstream(workstream.id, favoriteInitiative.id), onOpenDetails: () => {
|
|
482
477
|
onOpenInitiativeDetails(favoriteInitiative.id);
|
|
@@ -503,16 +498,16 @@ function PlanningDrawerList({ initiatives, standaloneWorkstreams, planningLoadSt
|
|
|
503
498
|
const expanded = navigatorResults.hasCriteria
|
|
504
499
|
? displayedWorkstreams.length > 0
|
|
505
500
|
: expandedInitiativeIds.has(initiative.id);
|
|
506
|
-
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), 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
|
|
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
|
|
507
502
|
? () => onArchiveResolvedInitiative(initiative.id)
|
|
508
|
-
: 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), 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, "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)
|
|
509
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: () => {
|
|
510
505
|
onOpenInitiativeDetails(initiative.id);
|
|
511
506
|
onOpenNestedWorkstreamDetails(workstream.id);
|
|
512
507
|
}, favorite: planningFavoriteKeys.has(getPlanningFavoriteKey({ type: 'workstream', id: workstream.id })), favoriteReady: navigatorSortReady, onToggleFavorite: () => onTogglePlanningFavorite({ type: 'workstream', id: workstream.id }), onArchiveReady: onArchiveResolvedWorkstream
|
|
513
508
|
? () => onArchiveResolvedWorkstream(workstream.id)
|
|
514
509
|
: undefined }), renderWorkstreamTasks(workstream)] }, workstream.id))) }))] }, initiative.id));
|
|
515
|
-
}) })) : null] }), _jsxs("div", { className: panelStyles.sectionGroup, children: [_jsx(DisclosureSectionHeader, { label: "Workstreams", count: navigatorResults.standaloneWorkstreams.length, showZeroCount: true, actions: (planningLoadState.collections.workstreams === 'ready' ? (_jsx("button", { type: "button", className: "tf-control-icon", title: "Create workstream", "aria-label": "Create workstream", onClick: onCreateWorkstream, children: _jsx(Plus, { size: 15 }) })) : null), expanded: !workstreamsSectionCollapsed, expandedTitle: "Collapse workstreams", collapsedTitle: "Expand workstreams", onToggle: onToggleWorkstreamsSection, controlsId: !workstreamsSectionCollapsed ? 'planning-workstreams-section' : undefined }), !workstreamsSectionCollapsed ? (_jsx("ul", { id: "planning-workstreams-section", className: panelStyles.treeList, "aria-label": "Standalone workstreams", children: planningLoadState.collections.workstreams === 'error' ? (_jsx("li", { children: _jsx(PlanningFailureState, { compact: true, title: "Workstreams unavailable", message: "Workstreams could not be loaded.", retrying: planningLoadState.isRefreshing, onRetry: onRetryPlanning }) })) : navigatorResults.standaloneWorkstreams.length === 0 ? (_jsxs("li", { className: panelStyles.emptyPanel, children: [_jsx("div", { className: panelStyles.emptyPanelTitle, children: workstreamEmptyTitle }), _jsx("div", { className: panelStyles.emptyPanelText, children: workstreamEmptyText })] })) : navigatorResults.standaloneWorkstreams.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), active: activeWorkstreamId === workstream.id, isArchived: Boolean(workstream.isArchived), detailOpen: (detail?.type === 'workstream' && detail.item.id === workstream.id)
|
|
510
|
+
}) })) : null] }), _jsxs("div", { className: panelStyles.sectionGroup, children: [_jsx(DisclosureSectionHeader, { label: "Workstreams", count: navigatorResults.standaloneWorkstreams.length, showZeroCount: true, actions: (planningLoadState.collections.workstreams === 'ready' ? (_jsx("button", { type: "button", className: "tf-control-icon", title: "Create workstream", "aria-label": "Create workstream", onClick: onCreateWorkstream, children: _jsx(Plus, { size: 15 }) })) : null), expanded: !workstreamsSectionCollapsed, expandedTitle: "Collapse workstreams", collapsedTitle: "Expand workstreams", onToggle: onToggleWorkstreamsSection, controlsId: !workstreamsSectionCollapsed ? 'planning-workstreams-section' : undefined }), !workstreamsSectionCollapsed ? (_jsx("ul", { id: "planning-workstreams-section", className: panelStyles.treeList, "aria-label": "Standalone workstreams", children: planningLoadState.collections.workstreams === 'error' ? (_jsx("li", { children: _jsx(PlanningFailureState, { compact: true, title: "Workstreams unavailable", message: "Workstreams could not be loaded.", retrying: planningLoadState.isRefreshing, onRetry: onRetryPlanning }) })) : navigatorResults.standaloneWorkstreams.length === 0 ? (_jsxs("li", { className: panelStyles.emptyPanel, children: [_jsx("div", { className: panelStyles.emptyPanelTitle, children: workstreamEmptyTitle }), _jsx("div", { className: panelStyles.emptyPanelText, children: workstreamEmptyText })] })) : navigatorResults.standaloneWorkstreams.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)
|
|
516
511
|
|| (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, null), onOpenDetails: () => onOpenWorkstreamDetails(workstream.id), favorite: planningFavoriteKeys.has(getPlanningFavoriteKey({ type: 'workstream', id: workstream.id })), favoriteReady: navigatorSortReady, onToggleFavorite: () => onTogglePlanningFavorite({ type: 'workstream', id: workstream.id }), onArchiveReady: onArchiveResolvedWorkstream
|
|
517
512
|
? () => onArchiveResolvedWorkstream(workstream.id)
|
|
518
513
|
: undefined, initiativeLinkOptions: linkOptions.initiatives, onLinkInitiative: (option) => onAssignInitiativeToWorkstream(workstream.id, option.referenceLabel) }), renderWorkstreamTasks(workstream)] }, workstream.id))) })) : null] })] }))] }));
|
|
@@ -523,7 +518,7 @@ function PlanningLoadingState() {
|
|
|
523
518
|
function PlanningFailureState({ title, message, retrying, onRetry, compact = false, }) {
|
|
524
519
|
return (_jsxs("div", { className: `${panelStyles.failureState} ${compact ? panelStyles.failureStateCompact : ''} tf-surface-inset`.trim(), role: "alert", children: [_jsxs("div", { className: panelStyles.failureCopy, children: [_jsx("strong", { children: title }), _jsx("span", { children: message })] }), _jsx("button", { type: "button", className: "tf-button-ghost tf-button-compact", disabled: retrying, onClick: onRetry, children: retrying ? 'Retrying…' : 'Retry' })] }));
|
|
525
520
|
}
|
|
526
|
-
function PlanningDrawerDetailView({ detail, showArchivedChildren, inlineEditor, selectedChildWorkstreamId, currentWorkspaceId, currentActorId, theme, onOpenNestedWorkstreamDetails, onArchivePlanningTask, onArchiveResolvedWorkstream, onArchiveResolvedEntity, onEdit, onChangeOwner, onChangePlanningOwner, 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, }) {
|
|
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, }) {
|
|
527
522
|
const inlineEditorFormId = React.useId();
|
|
528
523
|
const [isAttachingReference, setIsAttachingReference] = React.useState(false);
|
|
529
524
|
const [showParentLinkPicker, setShowParentLinkPicker] = React.useState(false);
|
|
@@ -784,7 +779,9 @@ function PlanningDrawerDetailView({ detail, showArchivedChildren, inlineEditor,
|
|
|
784
779
|
: _jsx(Archive, { size: 14, "aria-hidden": "true" }) }), _jsx("button", { type: "button", className: "tf-control-icon", onClick: () => {
|
|
785
780
|
setShowParentLinkPicker(false);
|
|
786
781
|
onEdit();
|
|
787
|
-
}, 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: [
|
|
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
|
|
783
|
+
? (patch) => onChangePlanningIdentity(detail.type, detail.item.id, patch)
|
|
784
|
+
: 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'
|
|
788
785
|
? detail.item.workstreamCount ?? detail.item.workstreams.length
|
|
789
786
|
: undefined, className: panelStyles.detailSummaryMeta }), detail.item.taskCount > 0 ? (_jsx("span", { className: panelStyles.passiveGuidance, children: "Lifetime progress includes archived tasks." })) : null] }), _jsxs("div", { ref: setChildDropRef, className: `${panelStyles.listCard} ${canDropTask || canDropWorkstream ? panelStyles.sectionDropReady : ''} ${isChildDropOver ? panelStyles.sectionDropActive : ''} ${canDropTask || canDropWorkstream ? panelStyles.sectionDropMode : ''}`.trim(), children: [_jsxs("div", { className: panelStyles.sectionHeaderRow, children: [_jsxs("div", { className: panelStyles.sectionTitle, children: [childLabel, " (", visibleChildCount, ")"] }), _jsx("div", { className: panelStyles.headerActionGroup, children: detail.type === 'initiative' ? (_jsx("button", { type: "button", className: "tf-control-icon", title: "Create workstream in this initiative", "aria-label": "Create workstream in this initiative", onClick: () => onCreateWorkstreamInInitiative?.(detail.item.id), children: _jsx(Plus, { size: 15 }) })) : (_jsx("button", { type: "button", className: "tf-control-icon", title: "Create task in this workstream", "aria-label": "Create task in this workstream", onClick: () => onCreateTaskInWorkstream?.(detail.item.id), children: _jsx(Plus, { size: 15 }) })) })] }), _jsx("div", { className: panelStyles.linkPickerRow, children: _jsx(PlanningLinkPicker, { options: availableLinkOptions, entityLabel: detail.type === 'initiative' ? 'workstream' : 'task', onSelect: handleAttachReference, disabled: !hasAttachReferenceHandler || isAttachingReference }) }), detail.type === 'workstream' ? (_jsx(PlanningTaskArrangeControl, { value: taskArrangeMode, onChange: onTaskArrangeModeChange })) : null, detail.type === 'initiative' ? (visibleChildItems.length > 0 ? visibleChildItems.map((workstream) => {
|
|
790
787
|
const workstreamReferenceLabel = formatWorkstreamReference(workstream);
|
|
@@ -822,6 +819,8 @@ function PlanningDrawerDetailView({ detail, showArchivedChildren, inlineEditor,
|
|
|
822
819
|
? toPlanningAssigneeOption(assigneeOptionByValue.get(workstream.ownerId))
|
|
823
820
|
: undefined, ownerOptions: assigneeOptions.map(toPlanningAssigneeOption), onChangeOwner: onChangePlanningOwner
|
|
824
821
|
? (ownerId) => onChangePlanningOwner('workstream', workstream.id, ownerId === 'unassigned' ? '' : ownerId)
|
|
822
|
+
: undefined, onChangeIdentity: onChangePlanningIdentity
|
|
823
|
+
? (patch) => onChangePlanningIdentity('workstream', workstream.id, patch)
|
|
825
824
|
: undefined }) })] }) }, workstream.id));
|
|
826
825
|
}) : (_jsx("div", { className: panelStyles.passiveGuidance, children: "No workstreams are linked to this initiative." }))) : (visibleTaskItems.length > 0 ? (_jsx(PlanningTaskOrderList, { tasks: visibleTaskItems, mode: taskArrangeMode, onReorder: onReorderWorkstreamTasks
|
|
827
826
|
? (taskIds) => onReorderWorkstreamTasks(detail.item.id, mergeVisiblePlanningTaskOrder(arrangePlanningTasks(taskItems, 'execution'), taskIds))
|
|
@@ -855,7 +854,7 @@ function PlanningDrawerDetailView({ detail, showArchivedChildren, inlineEditor,
|
|
|
855
854
|
}, title: "Archive task", "aria-label": `Archive task ${referenceLabel || task.title}`, children: _jsx(Archive, { size: 14, "aria-hidden": "true" }) })) : undefined }) }, task.id));
|
|
856
855
|
} })) : (_jsx("div", { className: panelStyles.passiveGuidance, children: "No tasks are linked to this workstream." }))), detachedRelationshipRecovery] }), _jsx("div", { className: panelStyles.listCard, children: _jsx(PlanningEntityContext, { entityType: detail.type, entityId: detail.item.id, referenceLabel: detailReferenceLabel, workspaceId: currentWorkspaceId, attachments: detail.item.attachments, readOnly: Boolean(detail.item.isArchived), onAddAttachment: (file) => onAddPlanningContextFile?.(detail.type, detail.item.id, file), onRemoveAttachment: (index) => onRemovePlanningContextFile?.(detail.type, detail.item.id, index), onUpdateAttachmentCaption: (index, caption) => (onUpdatePlanningContextCaption?.(detail.type, detail.item.id, index, caption)) }) }), _jsx("div", { className: panelStyles.listCard, children: _jsx(PlanningEntityActivity, { entityType: detail.type, item: detail.item, assigneeOptions: assigneeOptions.map(toPlanningAssigneeOption), currentActorId: currentActorId, taskReferences: taskReferences, parentInitiative: parentInitiative, listClassName: panelStyles.activityTimelineList, emptyClassName: panelStyles.activityTimelineEmpty, hydrationState: hydrationState, onRetryHydration: onRetryHydration }) }), _jsx(PlanningUnlinkConfirmation, { item: pendingUnlink, busy: isChangingRelationship, theme: theme, onCancel: () => setPendingUnlink(null), onConfirm: () => void handleConfirmUnlink() })] }));
|
|
857
856
|
}
|
|
858
|
-
function PlanningDrawerEditor({ editor, draftTitle, draftDescription, draftOwner, draftInitiativeId, assigneeOptions, initiativeLinkOptions, draftInitiativeSummary, onChangeDraftTitle, onChangeDraftDescription, onChangeDraftOwner, onChangeDraftInitiativeId, onCancel, onSubmit, isSubmitting, inline = false, formId, }) {
|
|
857
|
+
function PlanningDrawerEditor({ editor, draftTitle, draftDescription, draftOwner, draftInitiativeId, draftIcon, draftColor, assigneeOptions, initiativeLinkOptions, draftInitiativeSummary, onChangeDraftTitle, onChangeDraftDescription, onChangeDraftOwner, onChangeDraftInitiativeId, onChangeDraftIcon, onChangeDraftColor, onCancel, onSubmit, isSubmitting, inline = false, formId, }) {
|
|
859
858
|
const entityLabel = editor.entityType === 'initiative' ? 'Initiative' : 'Workstream';
|
|
860
859
|
const titleInputId = React.useId();
|
|
861
860
|
const titleErrorId = React.useId();
|
|
@@ -881,7 +880,12 @@ function PlanningDrawerEditor({ editor, draftTitle, draftDescription, draftOwner
|
|
|
881
880
|
event.preventDefault();
|
|
882
881
|
onCancel();
|
|
883
882
|
}, [inline, isSubmitting, onCancel]);
|
|
884
|
-
return (_jsxs("form", { id: formId, className: `${panelStyles.editorCard} ${inline ? panelStyles.inlineEditorCard : ''}`.trim(), onSubmit: handleSubmit, onKeyDown: handleKeyDown, noValidate: true, children: [
|
|
883
|
+
return (_jsxs("form", { id: formId, className: `${panelStyles.editorCard} ${inline ? panelStyles.inlineEditorCard : ''}`.trim(), onSubmit: handleSubmit, onKeyDown: handleKeyDown, noValidate: true, children: [editor.mode === 'create' && onChangeDraftIcon && onChangeDraftColor ? (_jsxs("div", { className: panelStyles.editorIdentityField, children: [_jsx(PlanningVisualIdentity, { entityType: editor.entityType, entityId: "create-preview", icon: draftIcon, color: draftColor || 'blue', size: "detail", onChange: (patch) => {
|
|
884
|
+
if (Object.prototype.hasOwnProperty.call(patch, 'icon'))
|
|
885
|
+
onChangeDraftIcon(patch.icon ?? null);
|
|
886
|
+
if (Object.prototype.hasOwnProperty.call(patch, 'color'))
|
|
887
|
+
onChangeDraftColor(patch.color ?? null);
|
|
888
|
+
} }), _jsx("span", { children: "Choose an icon and color" })] })) : null, _jsxs("div", { className: panelStyles.editorField, children: [_jsxs("label", { className: panelStyles.editorLabel, htmlFor: titleInputId, children: [entityLabel, " title"] }), _jsx("input", { ref: titleInputRef, id: titleInputId, "data-planning-editor-title": true, className: styles.input, disabled: isSubmitting, required: true, "aria-invalid": titleError ? 'true' : undefined, "aria-describedby": titleError ? titleErrorId : undefined, value: draftTitle, onChange: (event) => {
|
|
885
889
|
if (titleError)
|
|
886
890
|
setTitleError(null);
|
|
887
891
|
onChangeDraftTitle(event.target.value);
|
|
@@ -889,7 +893,7 @@ function PlanningDrawerEditor({ editor, draftTitle, draftDescription, draftOwner
|
|
|
889
893
|
? 'Describe the broader outcome this initiative is meant to achieve.'
|
|
890
894
|
: 'Describe the lane of work this workstream will coordinate.', rows: 5 })] }), editor.entityType === 'workstream' && (_jsxs("div", { className: panelStyles.editorField, children: [_jsx("span", { className: panelStyles.editorLabel, children: "Initiative" }), _jsxs("div", { className: panelStyles.editorRelationshipRow, children: [_jsx(PlanningLinkPicker, { options: initiativeLinkOptions, selectedId: draftInitiativeSummary?.id || '', entityLabel: "initiative", disabled: isSubmitting, onSelect: (option) => onChangeDraftInitiativeId(option.referenceLabel) }), draftInitiativeId.trim() ? (_jsx("button", { type: "button", className: "tf-button-ghost tf-button-compact", disabled: isSubmitting, onClick: () => onChangeDraftInitiativeId(''), children: "Remove" })) : null] })] })), !inline ? _jsxs("div", { className: panelStyles.editorField, children: [_jsxs("span", { className: panelStyles.editorLabel, children: [entityLabel, " owner"] }), _jsx(PlanningOwnerDropdown, { disabled: isSubmitting, value: draftOwner, onChange: onChangeDraftOwner, ariaLabel: `${entityLabel} owner`, options: assigneeOptions, className: panelStyles.editorSelect })] }) : null, !inline ? _jsxs("div", { className: panelStyles.editorActions, children: [_jsx("button", { type: "button", className: styles.secondaryHeaderBtn, onClick: onCancel, disabled: isSubmitting, children: "Cancel" }), _jsx("button", { type: "submit", className: styles.primaryUpdateBtn, disabled: isSubmitting, children: submitLabel })] }) : null] }));
|
|
891
895
|
}
|
|
892
|
-
export function PlanningDrawer({ open, leftOffset = 0, returnFocusRef, onWidthChange, initiatives, standaloneWorkstreams, planningLoadState, onRetryPlanning, taskScope, activeInitiativeId, activeWorkstreamId, navigatorSort, navigatorSortReady, onNavigatorSortChange, taskArrangeMode = 'execution', onTaskArrangeModeChange = () => { }, onReorderWorkstreamTasks, planningFavorites, onTogglePlanningFavorite, favoritesSectionCollapsed, initiativesSectionCollapsed, workstreamsSectionCollapsed, onToggleFavoritesSection, onToggleInitiativesSection, onToggleWorkstreamsSection, expandedInitiativeIds, detail, editor, secondaryEditor = null, secondaryPane, primaryHydrationState, secondaryHydrationState, onRetryPrimaryHydration, onRetrySecondaryHydration, currentWorkspaceId, currentOwnerId, theme, assigneeOptions, linkOptions, showTaskCardStatusLabel = false, draftInitiativeSummary, draftTitle, draftDescription, draftOwner, draftInitiativeId, onChangeDraftTitle, onChangeDraftDescription, onChangeDraftOwner, onChangeDraftInitiativeId, onCollapseTreePane, onExpandTreePane, onCollapsePrimaryPane, onExpandPrimaryPane, onCollapseSecondaryPane, onExpandSecondaryPane, onBackFromSecondary, treeCollapsed, primaryCollapsed, secondaryCollapsed, onCancelEditor, onCancelSecondaryEditor, onSubmitEditor, isSubmittingEditor, onCreateInitiative, onCreateWorkstream, onToggleInitiative, onSelectInitiative, onSelectWorkstream, onOpenInitiativeDetails, onOpenWorkstreamDetails, onOpenNestedWorkstreamDetails, onEditInitiative, onEditWorkstream, onChangePlanningOwner, onChangeTaskAssignee, onArchiveInitiative, onUnarchiveInitiative, onArchiveWorkstream, onUnarchiveWorkstream, onArchivePlanningTask, onArchiveResolvedWorkstream, onArchiveResolvedInitiative, onCreateTaskInWorkstream, onCreateWorkstreamInInitiative, onAssignInitiativeToWorkstream, onOpenTaskById, onSetTaskStatus, taskReferences, onAssignTaskToWorkstream, onAttachTaskToWorkstreamByReference, onAttachWorkstreamToInitiativeByReference, onAddPlanningContextFile, onRemovePlanningContextFile, onUpdatePlanningContextCaption, }) {
|
|
896
|
+
export function PlanningDrawer({ open, leftOffset = 0, returnFocusRef, onWidthChange, initiatives, standaloneWorkstreams, planningLoadState, onRetryPlanning, taskScope, activeInitiativeId, activeWorkstreamId, navigatorSort, navigatorSortReady, onNavigatorSortChange, taskArrangeMode = 'execution', onTaskArrangeModeChange = () => { }, onReorderWorkstreamTasks, planningFavorites, onTogglePlanningFavorite, favoritesSectionCollapsed, initiativesSectionCollapsed, workstreamsSectionCollapsed, onToggleFavoritesSection, onToggleInitiativesSection, onToggleWorkstreamsSection, expandedInitiativeIds, detail, editor, secondaryEditor = null, secondaryPane, primaryHydrationState, secondaryHydrationState, onRetryPrimaryHydration, onRetrySecondaryHydration, currentWorkspaceId, currentOwnerId, theme, assigneeOptions, linkOptions, showTaskCardStatusLabel = false, draftInitiativeSummary, draftTitle, draftDescription, draftOwner, draftInitiativeId, draftIcon = null, draftColor = null, onChangeDraftTitle, onChangeDraftDescription, onChangeDraftOwner, onChangeDraftInitiativeId, onChangeDraftIcon = () => { }, onChangeDraftColor = () => { }, onCollapseTreePane, onExpandTreePane, onCollapsePrimaryPane, onExpandPrimaryPane, onCollapseSecondaryPane, onExpandSecondaryPane, onBackFromSecondary, treeCollapsed, primaryCollapsed, secondaryCollapsed, onCancelEditor, onCancelSecondaryEditor, onSubmitEditor, isSubmittingEditor, onCreateInitiative, onCreateWorkstream, onToggleInitiative, onSelectInitiative, onSelectWorkstream, onOpenInitiativeDetails, onOpenWorkstreamDetails, onOpenNestedWorkstreamDetails, onEditInitiative, onEditWorkstream, onChangePlanningOwner, onChangePlanningIdentity = () => { }, onChangeTaskAssignee, onArchiveInitiative, onUnarchiveInitiative, onArchiveWorkstream, onUnarchiveWorkstream, onArchivePlanningTask, onArchiveResolvedWorkstream, onArchiveResolvedInitiative, onCreateTaskInWorkstream, onCreateWorkstreamInInitiative, onAssignInitiativeToWorkstream, onOpenTaskById, onSetTaskStatus, taskReferences, onAssignTaskToWorkstream, onAttachTaskToWorkstreamByReference, onAttachWorkstreamToInitiativeByReference, onAddPlanningContextFile, onRemovePlanningContextFile, onUpdatePlanningContextCaption, }) {
|
|
893
897
|
const primaryInlineEditor = editor?.mode === 'edit'
|
|
894
898
|
&& detail
|
|
895
899
|
&& editor.entityType === detail.type
|
|
@@ -1088,7 +1092,7 @@ export function PlanningDrawer({ open, leftOffset = 0, returnFocusRef, onWidthCh
|
|
|
1088
1092
|
pointerEvents: open ? 'auto' : 'none',
|
|
1089
1093
|
transition: 'left 220ms cubic-bezier(0.4, 0, 0.2, 1)',
|
|
1090
1094
|
willChange: 'left',
|
|
1091
|
-
}, children: _jsxs("div", { className: panelStyles.drawerBody, children: [showTreePane && (effectiveTreeCollapsed ? (_jsx(CollapsedPane, { label: "Planning", onExpand: onExpandTreePane })) : (_jsxs("div", { ref: treePaneRef, "data-planning-pane": "tree", className: `${panelStyles.paneShell} ${styles.appScrollbar} tf-scrollbar ${panelStyles.treePane} ${hasPrimaryPane && !primaryCollapsed ? panelStyles.treePaneSplit : ''}`.trim(), style: paneWidthStyle(layoutMode === 'narrow' ? drawerWidth : PLANNING_DRAWER_TREE_WIDTH_PX), children: [_jsxs("div", { className: panelStyles.paneHeader, children: [_jsx("h2", { className: panelStyles.paneHeaderLabel, tabIndex: -1, "data-planning-pane-heading": true, children: "Planning" }), _jsxs("div", { className: panelStyles.headerActionGroup, children: [_jsx(ArchiveVisibilityToggle, { visible: showArchivedPlanning, disabled: taskScope === 'deleted' || archivedPlanningItemCount === 0, disabledLabel: taskScope === 'deleted' ? 'Archive visibility unavailable in Trash' : undefined, itemLabel: "planning items", onToggle: () => setShowArchivedPlanning((current) => !current) }), wideLayout ? (_jsx("button", { type: "button", className: "tf-control-icon", onClick: onCollapseTreePane, "aria-label": "Collapse Planning pane", title: "Collapse Planning pane", children: _jsx(ChevronLeft, { size: 16, "aria-hidden": "true" }) })) : null] })] }), _jsx("div", { className: panelStyles.paneContent, "aria-busy": planningLoadState.status === 'loading' || planningLoadState.isRefreshing, children: _jsx(PlanningDrawerList, { initiatives: initiatives, standaloneWorkstreams: standaloneWorkstreams, planningLoadState: planningLoadState, taskScope: taskScope, showArchivedPlanning: showArchivedPlanning, currentOwnerId: currentOwnerId, assigneeOptions: assigneeOptions, showTaskCardStatusLabel: showTaskCardStatusLabel, taskArrangeMode: taskArrangeMode, activeInitiativeId: activeInitiativeId, activeWorkstreamId: activeWorkstreamId, navigatorSort: navigatorSort, navigatorSortReady: navigatorSortReady, onNavigatorSortChange: onNavigatorSortChange, planningFavorites: planningFavorites, onTogglePlanningFavorite: onTogglePlanningFavorite, favoritesSectionCollapsed: favoritesSectionCollapsed, initiativesSectionCollapsed: initiativesSectionCollapsed, workstreamsSectionCollapsed: workstreamsSectionCollapsed, onToggleFavoritesSection: onToggleFavoritesSection, onToggleInitiativesSection: onToggleInitiativesSection, onToggleWorkstreamsSection: onToggleWorkstreamsSection, expandedInitiativeIds: expandedInitiativeIds, detail: detail, secondaryPane: secondaryPane, onCreateInitiative: onCreateInitiative, onCreateWorkstream: onCreateWorkstream, onToggleInitiative: onToggleInitiative, onSelectInitiative: onSelectInitiative, onSelectWorkstream: onSelectWorkstream, onOpenInitiativeDetails: onOpenInitiativeDetails, onOpenWorkstreamDetails: onOpenWorkstreamDetails, onOpenNestedWorkstreamDetails: onOpenNestedWorkstreamDetails, onOpenTaskById: onOpenTaskById, onSetTaskStatus: onSetTaskStatus, onChangePlanningOwner: onChangePlanningOwner, onChangeTaskAssignee: onChangeTaskAssignee, onArchivePlanningTask: onArchivePlanningTask, onArchiveResolvedWorkstream: onArchiveResolvedWorkstream, onArchiveResolvedInitiative: onArchiveResolvedInitiative, linkOptions: linkOptions, onAssignInitiativeToWorkstream: onAssignInitiativeToWorkstream, onRetryPlanning: onRetryPlanning }) })] }))), wideLayout && !primaryIsInitiative && (initiativeRelationshipExpanded ? (_jsx(RelationshipPane, { label: "Initiative", title: initiativeRelationship.title, description: initiativeRelationship.description, action: initiativeRelationship.action, onCollapse: () => setInitiativeRelationshipExpanded(false), paneRef: initiativeRelationshipPaneRef })) : (_jsx(CollapsedPane, { label: "Initiative", muted: !hasInitiativeRelationship, onExpand: () => setInitiativeRelationshipExpanded(true) }))), showPrimaryPane && (effectivePrimaryCollapsed ? (_jsx(CollapsedPane, { label: primaryEntityType === 'initiative' ? 'Initiative' : 'Workstream', onExpand: onExpandPrimaryPane })) : (_jsxs("div", { ref: primaryPaneRef, "data-planning-pane": "primary", className: `${panelStyles.paneShell} ${styles.appScrollbar} tf-scrollbar ${panelStyles.detailPane}`.trim(), style: paneWidthStyle(wideLayout ? PLANNING_DRAWER_DETAIL_WIDTH_PX : adaptiveDetailWidth), children: [_jsxs("div", { className: panelStyles.paneHeader, children: [layoutMode === 'narrow' ? (_jsx("button", { type: "button", className: "tf-control-icon", onClick: handleBackFromPrimary, "aria-label": "Back to Planning", title: "Back to Planning", children: _jsx(ChevronLeft, { size: 16, "aria-hidden": "true" }) })) : null, _jsx("h2", { className: panelStyles.paneHeaderLabel, tabIndex: -1, "data-planning-pane-heading": true, children: primaryPaneLabel }), _jsxs("div", { className: panelStyles.headerActionGroup, children: [primaryArchiveDetail ? (_jsx(ArchiveVisibilityToggle, { visible: archivedDetailVisibility[primaryArchiveDetail.type], disabled: countArchivedDetailChildren(primaryArchiveDetail) === 0, itemLabel: primaryArchiveDetail.type === 'initiative' ? 'workstreams' : 'tasks', onToggle: () => toggleArchivedDetailVisibility(primaryArchiveDetail.type) })) : null, wideLayout ? (_jsx("button", { type: "button", className: "tf-control-icon", onClick: onCollapsePrimaryPane, "aria-label": `Collapse ${primaryPaneLabel} pane`, title: `Collapse ${primaryPaneLabel} pane`, children: _jsx(ChevronLeft, { size: 16, "aria-hidden": "true" }) })) : null] })] }), _jsx("div", { className: panelStyles.paneContent, "aria-busy": primaryHydrationState?.status === 'loading', children: dedicatedPrimaryEditor ? (_jsx(PlanningDrawerEditor, { editor: dedicatedPrimaryEditor, draftTitle: draftTitle, draftDescription: draftDescription, draftOwner: draftOwner, draftInitiativeId: draftInitiativeId, assigneeOptions: assigneeOptions, initiativeLinkOptions: linkOptions.initiatives, draftInitiativeSummary: draftInitiativeSummary, onChangeDraftTitle: onChangeDraftTitle, onChangeDraftDescription: onChangeDraftDescription, onChangeDraftOwner: onChangeDraftOwner, onChangeDraftInitiativeId: onChangeDraftInitiativeId, onCancel: onCancelEditor, onSubmit: onSubmitEditor, isSubmitting: isSubmittingEditor })) : detail ? (_jsx(PlanningDrawerDetailView, { detail: detail, showArchivedChildren: archivedDetailVisibility[detail.type], inlineEditor: primaryInlineEditor ? {
|
|
1095
|
+
}, children: _jsxs("div", { className: panelStyles.drawerBody, children: [showTreePane && (effectiveTreeCollapsed ? (_jsx(CollapsedPane, { label: "Planning", onExpand: onExpandTreePane })) : (_jsxs("div", { ref: treePaneRef, "data-planning-pane": "tree", className: `${panelStyles.paneShell} ${styles.appScrollbar} tf-scrollbar ${panelStyles.treePane} ${hasPrimaryPane && !primaryCollapsed ? panelStyles.treePaneSplit : ''}`.trim(), style: paneWidthStyle(layoutMode === 'narrow' ? drawerWidth : PLANNING_DRAWER_TREE_WIDTH_PX), children: [_jsxs("div", { className: panelStyles.paneHeader, children: [_jsx("h2", { className: panelStyles.paneHeaderLabel, tabIndex: -1, "data-planning-pane-heading": true, children: "Planning" }), _jsxs("div", { className: panelStyles.headerActionGroup, children: [_jsx(ArchiveVisibilityToggle, { visible: showArchivedPlanning, disabled: taskScope === 'deleted' || archivedPlanningItemCount === 0, disabledLabel: taskScope === 'deleted' ? 'Archive visibility unavailable in Trash' : undefined, itemLabel: "planning items", onToggle: () => setShowArchivedPlanning((current) => !current) }), wideLayout ? (_jsx("button", { type: "button", className: "tf-control-icon", onClick: onCollapseTreePane, "aria-label": "Collapse Planning pane", title: "Collapse Planning pane", children: _jsx(ChevronLeft, { size: 16, "aria-hidden": "true" }) })) : null] })] }), _jsx("div", { className: panelStyles.paneContent, "aria-busy": planningLoadState.status === 'loading' || planningLoadState.isRefreshing, children: _jsx(PlanningDrawerList, { initiatives: initiatives, standaloneWorkstreams: standaloneWorkstreams, planningLoadState: planningLoadState, taskScope: taskScope, showArchivedPlanning: showArchivedPlanning, currentOwnerId: currentOwnerId, assigneeOptions: assigneeOptions, showTaskCardStatusLabel: showTaskCardStatusLabel, taskArrangeMode: taskArrangeMode, activeInitiativeId: activeInitiativeId, activeWorkstreamId: activeWorkstreamId, navigatorSort: navigatorSort, navigatorSortReady: navigatorSortReady, onNavigatorSortChange: onNavigatorSortChange, planningFavorites: planningFavorites, onTogglePlanningFavorite: onTogglePlanningFavorite, favoritesSectionCollapsed: favoritesSectionCollapsed, initiativesSectionCollapsed: initiativesSectionCollapsed, workstreamsSectionCollapsed: workstreamsSectionCollapsed, onToggleFavoritesSection: onToggleFavoritesSection, onToggleInitiativesSection: onToggleInitiativesSection, onToggleWorkstreamsSection: onToggleWorkstreamsSection, expandedInitiativeIds: expandedInitiativeIds, detail: detail, secondaryPane: secondaryPane, onCreateInitiative: onCreateInitiative, onCreateWorkstream: onCreateWorkstream, onToggleInitiative: onToggleInitiative, onSelectInitiative: onSelectInitiative, onSelectWorkstream: onSelectWorkstream, onOpenInitiativeDetails: onOpenInitiativeDetails, onOpenWorkstreamDetails: onOpenWorkstreamDetails, onOpenNestedWorkstreamDetails: onOpenNestedWorkstreamDetails, onOpenTaskById: onOpenTaskById, onSetTaskStatus: onSetTaskStatus, onChangePlanningOwner: onChangePlanningOwner, onChangePlanningIdentity: onChangePlanningIdentity, onChangeTaskAssignee: onChangeTaskAssignee, onArchivePlanningTask: onArchivePlanningTask, onArchiveResolvedWorkstream: onArchiveResolvedWorkstream, onArchiveResolvedInitiative: onArchiveResolvedInitiative, linkOptions: linkOptions, onAssignInitiativeToWorkstream: onAssignInitiativeToWorkstream, onRetryPlanning: onRetryPlanning }) })] }))), wideLayout && !primaryIsInitiative && (initiativeRelationshipExpanded ? (_jsx(RelationshipPane, { label: "Initiative", title: initiativeRelationship.title, description: initiativeRelationship.description, action: initiativeRelationship.action, onCollapse: () => setInitiativeRelationshipExpanded(false), paneRef: initiativeRelationshipPaneRef })) : (_jsx(CollapsedPane, { label: "Initiative", muted: !hasInitiativeRelationship, onExpand: () => setInitiativeRelationshipExpanded(true) }))), showPrimaryPane && (effectivePrimaryCollapsed ? (_jsx(CollapsedPane, { label: primaryEntityType === 'initiative' ? 'Initiative' : 'Workstream', onExpand: onExpandPrimaryPane })) : (_jsxs("div", { ref: primaryPaneRef, "data-planning-pane": "primary", className: `${panelStyles.paneShell} ${styles.appScrollbar} tf-scrollbar ${panelStyles.detailPane}`.trim(), style: paneWidthStyle(wideLayout ? PLANNING_DRAWER_DETAIL_WIDTH_PX : adaptiveDetailWidth), children: [_jsxs("div", { className: panelStyles.paneHeader, children: [layoutMode === 'narrow' ? (_jsx("button", { type: "button", className: "tf-control-icon", onClick: handleBackFromPrimary, "aria-label": "Back to Planning", title: "Back to Planning", children: _jsx(ChevronLeft, { size: 16, "aria-hidden": "true" }) })) : null, _jsx("h2", { className: panelStyles.paneHeaderLabel, tabIndex: -1, "data-planning-pane-heading": true, children: primaryPaneLabel }), _jsxs("div", { className: panelStyles.headerActionGroup, children: [primaryArchiveDetail ? (_jsx(ArchiveVisibilityToggle, { visible: archivedDetailVisibility[primaryArchiveDetail.type], disabled: countArchivedDetailChildren(primaryArchiveDetail) === 0, itemLabel: primaryArchiveDetail.type === 'initiative' ? 'workstreams' : 'tasks', onToggle: () => toggleArchivedDetailVisibility(primaryArchiveDetail.type) })) : null, wideLayout ? (_jsx("button", { type: "button", className: "tf-control-icon", onClick: onCollapsePrimaryPane, "aria-label": `Collapse ${primaryPaneLabel} pane`, title: `Collapse ${primaryPaneLabel} pane`, children: _jsx(ChevronLeft, { size: 16, "aria-hidden": "true" }) })) : null] })] }), _jsx("div", { className: panelStyles.paneContent, "aria-busy": primaryHydrationState?.status === 'loading', children: dedicatedPrimaryEditor ? (_jsx(PlanningDrawerEditor, { editor: dedicatedPrimaryEditor, draftTitle: draftTitle, draftDescription: draftDescription, draftOwner: draftOwner, draftInitiativeId: draftInitiativeId, draftIcon: draftIcon, draftColor: draftColor, assigneeOptions: assigneeOptions, initiativeLinkOptions: linkOptions.initiatives, draftInitiativeSummary: draftInitiativeSummary, onChangeDraftTitle: onChangeDraftTitle, onChangeDraftDescription: onChangeDraftDescription, onChangeDraftOwner: onChangeDraftOwner, onChangeDraftInitiativeId: onChangeDraftInitiativeId, onChangeDraftIcon: onChangeDraftIcon, onChangeDraftColor: onChangeDraftColor, onCancel: onCancelEditor, onSubmit: onSubmitEditor, isSubmitting: isSubmittingEditor })) : detail ? (_jsx(PlanningDrawerDetailView, { detail: detail, showArchivedChildren: archivedDetailVisibility[detail.type], inlineEditor: primaryInlineEditor ? {
|
|
1092
1096
|
editor: primaryInlineEditor,
|
|
1093
1097
|
draftTitle,
|
|
1094
1098
|
draftDescription,
|
|
@@ -1108,13 +1112,13 @@ export function PlanningDrawer({ open, leftOffset = 0, returnFocusRef, onWidthCh
|
|
|
1108
1112
|
? onArchiveResolvedInitiative
|
|
1109
1113
|
: onArchiveResolvedWorkstream, onOpenNestedWorkstreamDetails: detail.type === 'initiative' ? onOpenNestedWorkstreamDetails : undefined, onCreateTaskInWorkstream: detail.type === 'workstream' ? onCreateTaskInWorkstream : undefined, onCreateWorkstreamInInitiative: detail.type === 'initiative' ? onCreateWorkstreamInInitiative : undefined, onOpenTaskById: detail.type === 'workstream' ? onOpenTaskById : undefined, onChangeTaskAssignee: detail.type === 'workstream' ? onChangeTaskAssignee : undefined, taskReferences: taskReferences, onAssignTaskToWorkstream: detail.type === 'workstream' ? onAssignTaskToWorkstream : undefined, onAssignInitiativeToWorkstream: onAssignInitiativeToWorkstream, onAttachTaskToWorkstreamByReference: detail.type === 'workstream' ? onAttachTaskToWorkstreamByReference : undefined, onAttachWorkstreamToInitiativeByReference: detail.type === 'initiative' ? onAttachWorkstreamToInitiativeByReference : undefined, parentInitiative: detail.type === 'workstream' ? resolveParentInitiative(detail.item) : null, onEdit: () => detail.type === 'initiative'
|
|
1110
1114
|
? onEditInitiative(detail.item.id)
|
|
1111
|
-
: onEditWorkstream(detail.item.id), onChangeOwner: (ownerId) => onChangePlanningOwner(detail.type, detail.item.id, ownerId), onChangePlanningOwner: onChangePlanningOwner, onArchive: () => detail.type === 'initiative'
|
|
1115
|
+
: onEditWorkstream(detail.item.id), onChangeOwner: (ownerId) => onChangePlanningOwner(detail.type, detail.item.id, ownerId), onChangePlanningOwner: onChangePlanningOwner, onChangePlanningIdentity: onChangePlanningIdentity, onArchive: () => detail.type === 'initiative'
|
|
1112
1116
|
? onArchiveInitiative(detail.item.id)
|
|
1113
1117
|
: onArchiveWorkstream(detail.item.id), onUnarchive: () => detail.type === 'initiative'
|
|
1114
1118
|
? onUnarchiveInitiative(detail.item.id)
|
|
1115
1119
|
: onUnarchiveWorkstream(detail.item.id), onAddPlanningContextFile: onAddPlanningContextFile, onRemovePlanningContextFile: onRemovePlanningContextFile, onUpdatePlanningContextCaption: onUpdatePlanningContextCaption })) : null })] }))), wideLayout && !primaryIsWorkstream && !hasSecondaryPane && (workstreamRelationshipExpanded ? (_jsx(RelationshipPane, { label: "Workstream", title: workstreamRelationship.title, description: workstreamRelationship.description, action: workstreamRelationship.action, onCollapse: () => setWorkstreamRelationshipExpanded(false), paneRef: workstreamRelationshipPaneRef })) : (_jsx(CollapsedPane, { label: "Workstream", muted: true, onExpand: () => setWorkstreamRelationshipExpanded(true) }))), showSecondaryPane && (effectiveSecondaryCollapsed ? (_jsx(CollapsedPane, { label: "Workstream", onExpand: onExpandSecondaryPane })) : (_jsxs("div", { ref: secondaryPaneRef, "data-planning-pane": "secondary", className: `${panelStyles.paneShell} ${styles.appScrollbar} tf-scrollbar ${panelStyles.detailPane}`.trim(), style: paneWidthStyle(wideLayout ? PLANNING_DRAWER_DETAIL_WIDTH_PX : adaptiveDetailWidth), children: [_jsxs("div", { className: panelStyles.paneHeader, children: [!wideLayout ? (_jsx("button", { type: "button", className: "tf-control-icon", onClick: secondaryInlineEditor
|
|
1116
1120
|
? (onCancelSecondaryEditor || onBackFromSecondary)
|
|
1117
|
-
: onBackFromSecondary, "aria-label": secondaryInlineEditor ? 'Cancel workstream editing' : `Back to ${primaryPaneLabel}`, title: secondaryInlineEditor ? 'Cancel workstream editing' : `Back to ${primaryPaneLabel}`, children: _jsx(ChevronLeft, { size: 16, "aria-hidden": "true" }) })) : null, _jsx("h2", { className: panelStyles.paneHeaderLabel, tabIndex: -1, "data-planning-pane-heading": true, children: secondaryPaneLabel }), _jsxs("div", { className: panelStyles.headerActionGroup, children: [secondaryArchiveDetail ? (_jsx(ArchiveVisibilityToggle, { visible: archivedDetailVisibility.workstream, disabled: countArchivedDetailChildren(secondaryArchiveDetail) === 0, itemLabel: "tasks", onToggle: () => toggleArchivedDetailVisibility('workstream') })) : null, wideLayout ? (_jsx("button", { type: "button", className: "tf-control-icon", onClick: onCollapseSecondaryPane, "aria-label": `Collapse ${secondaryPaneLabel} pane`, title: `Collapse ${secondaryPaneLabel} pane`, children: _jsx(ChevronLeft, { size: 16, "aria-hidden": "true" }) })) : null] })] }), _jsx("div", { className: panelStyles.paneContent, "aria-busy": secondaryHydrationState?.status === 'loading', children: secondaryPane?.kind === 'editor' ? (_jsx(PlanningDrawerEditor, { editor: secondaryPane.editor, draftTitle: draftTitle, draftDescription: draftDescription, draftOwner: draftOwner, draftInitiativeId: draftInitiativeId, assigneeOptions: assigneeOptions, initiativeLinkOptions: linkOptions.initiatives, draftInitiativeSummary: draftInitiativeSummary, onChangeDraftTitle: onChangeDraftTitle, onChangeDraftDescription: onChangeDraftDescription, onChangeDraftOwner: onChangeDraftOwner, onChangeDraftInitiativeId: onChangeDraftInitiativeId, onCancel: onBackFromSecondary, onSubmit: onSubmitEditor, isSubmitting: isSubmittingEditor })) : secondaryPane ? (_jsx(PlanningDrawerDetailView, { detail: secondaryPane.detail, showArchivedChildren: archivedDetailVisibility.workstream, inlineEditor: secondaryInlineEditor ? {
|
|
1121
|
+
: onBackFromSecondary, "aria-label": secondaryInlineEditor ? 'Cancel workstream editing' : `Back to ${primaryPaneLabel}`, title: secondaryInlineEditor ? 'Cancel workstream editing' : `Back to ${primaryPaneLabel}`, children: _jsx(ChevronLeft, { size: 16, "aria-hidden": "true" }) })) : null, _jsx("h2", { className: panelStyles.paneHeaderLabel, tabIndex: -1, "data-planning-pane-heading": true, children: secondaryPaneLabel }), _jsxs("div", { className: panelStyles.headerActionGroup, children: [secondaryArchiveDetail ? (_jsx(ArchiveVisibilityToggle, { visible: archivedDetailVisibility.workstream, disabled: countArchivedDetailChildren(secondaryArchiveDetail) === 0, itemLabel: "tasks", onToggle: () => toggleArchivedDetailVisibility('workstream') })) : null, wideLayout ? (_jsx("button", { type: "button", className: "tf-control-icon", onClick: onCollapseSecondaryPane, "aria-label": `Collapse ${secondaryPaneLabel} pane`, title: `Collapse ${secondaryPaneLabel} pane`, children: _jsx(ChevronLeft, { size: 16, "aria-hidden": "true" }) })) : null] })] }), _jsx("div", { className: panelStyles.paneContent, "aria-busy": secondaryHydrationState?.status === 'loading', children: secondaryPane?.kind === 'editor' ? (_jsx(PlanningDrawerEditor, { editor: secondaryPane.editor, draftTitle: draftTitle, draftDescription: draftDescription, draftOwner: draftOwner, draftInitiativeId: draftInitiativeId, draftIcon: draftIcon, draftColor: draftColor, assigneeOptions: assigneeOptions, initiativeLinkOptions: linkOptions.initiatives, draftInitiativeSummary: draftInitiativeSummary, onChangeDraftTitle: onChangeDraftTitle, onChangeDraftDescription: onChangeDraftDescription, onChangeDraftOwner: onChangeDraftOwner, onChangeDraftInitiativeId: onChangeDraftInitiativeId, onChangeDraftIcon: onChangeDraftIcon, onChangeDraftColor: onChangeDraftColor, onCancel: onBackFromSecondary, onSubmit: onSubmitEditor, isSubmitting: isSubmittingEditor })) : secondaryPane ? (_jsx(PlanningDrawerDetailView, { detail: secondaryPane.detail, showArchivedChildren: archivedDetailVisibility.workstream, inlineEditor: secondaryInlineEditor ? {
|
|
1118
1122
|
editor: secondaryInlineEditor,
|
|
1119
1123
|
draftTitle,
|
|
1120
1124
|
draftDescription,
|
|
@@ -1128,5 +1132,5 @@ export function PlanningDrawer({ open, leftOffset = 0, returnFocusRef, onWidthCh
|
|
|
1128
1132
|
onCancel: onCancelSecondaryEditor || onBackFromSecondary,
|
|
1129
1133
|
onSubmit: onSubmitEditor,
|
|
1130
1134
|
isSubmitting: isSubmittingEditor,
|
|
1131
|
-
} : null, currentWorkspaceId: currentWorkspaceId, currentActorId: currentOwnerId, theme: theme, assigneeOptions: assigneeOptions, linkOptions: linkOptions, showTaskCardStatusLabel: showTaskCardStatusLabel, taskArrangeMode: taskArrangeMode, onTaskArrangeModeChange: onTaskArrangeModeChange, onReorderWorkstreamTasks: onReorderWorkstreamTasks, hydrationState: secondaryHydrationState, onRetryHydration: onRetrySecondaryHydration, onSetTaskStatus: onSetTaskStatus, onArchivePlanningTask: onArchivePlanningTask, onArchiveResolvedEntity: onArchiveResolvedWorkstream, onCreateTaskInWorkstream: onCreateTaskInWorkstream, onOpenTaskById: onOpenTaskById, onChangeTaskAssignee: onChangeTaskAssignee, taskReferences: taskReferences, onAssignTaskToWorkstream: onAssignTaskToWorkstream, onAssignInitiativeToWorkstream: onAssignInitiativeToWorkstream, onAttachTaskToWorkstreamByReference: onAttachTaskToWorkstreamByReference, parentInitiative: resolveParentInitiative(secondaryPane.detail.item), onEdit: () => onEditWorkstream(secondaryPane.detail.item.id), onChangeOwner: (ownerId) => onChangePlanningOwner('workstream', secondaryPane.detail.item.id, ownerId), onChangePlanningOwner: onChangePlanningOwner, onArchive: () => onArchiveWorkstream(secondaryPane.detail.item.id), onUnarchive: () => onUnarchiveWorkstream(secondaryPane.detail.item.id), onAddPlanningContextFile: onAddPlanningContextFile, onRemovePlanningContextFile: onRemovePlanningContextFile, onUpdatePlanningContextCaption: onUpdatePlanningContextCaption })) : null })] })))] }) }));
|
|
1135
|
+
} : null, currentWorkspaceId: currentWorkspaceId, currentActorId: currentOwnerId, theme: theme, assigneeOptions: assigneeOptions, linkOptions: linkOptions, showTaskCardStatusLabel: showTaskCardStatusLabel, taskArrangeMode: taskArrangeMode, onTaskArrangeModeChange: onTaskArrangeModeChange, onReorderWorkstreamTasks: onReorderWorkstreamTasks, hydrationState: secondaryHydrationState, onRetryHydration: onRetrySecondaryHydration, onSetTaskStatus: onSetTaskStatus, onArchivePlanningTask: onArchivePlanningTask, onArchiveResolvedEntity: onArchiveResolvedWorkstream, onCreateTaskInWorkstream: onCreateTaskInWorkstream, onOpenTaskById: onOpenTaskById, onChangeTaskAssignee: onChangeTaskAssignee, taskReferences: taskReferences, onAssignTaskToWorkstream: onAssignTaskToWorkstream, onAssignInitiativeToWorkstream: onAssignInitiativeToWorkstream, onAttachTaskToWorkstreamByReference: onAttachTaskToWorkstreamByReference, parentInitiative: resolveParentInitiative(secondaryPane.detail.item), onEdit: () => onEditWorkstream(secondaryPane.detail.item.id), onChangeOwner: (ownerId) => onChangePlanningOwner('workstream', secondaryPane.detail.item.id, ownerId), onChangePlanningOwner: onChangePlanningOwner, onChangePlanningIdentity: onChangePlanningIdentity, onArchive: () => onArchiveWorkstream(secondaryPane.detail.item.id), onUnarchive: () => onUnarchiveWorkstream(secondaryPane.detail.item.id), onAddPlanningContextFile: onAddPlanningContextFile, onRemovePlanningContextFile: onRemovePlanningContextFile, onUpdatePlanningContextCaption: onUpdatePlanningContextCaption })) : null })] })))] }) }));
|
|
1132
1136
|
}
|
package/dist/config/envSchema.js
CHANGED
|
@@ -11,6 +11,7 @@ const SPECS = [
|
|
|
11
11
|
{ key: 'TASKFORCE_DB_PROVIDER', description: 'Database provider (sqlite|postgres).', category: 'Core', defaultValue: 'sqlite', example: 'postgres', targets: ['server', 'all'] },
|
|
12
12
|
{ key: 'TASKFORCE_BILLING_ENABLED', description: 'Enable Stripe billing routes and webhook processing.', category: 'Core', defaultValue: 'false', targets: ['server', 'all'] },
|
|
13
13
|
{ key: 'TASKFORCE_DATABASE_URL', description: 'Postgres connection string when provider=postgres.', category: 'Core', example: 'postgresql://user:pass@host:5432/db', secret: true, targets: ['server', 'all'] },
|
|
14
|
+
{ key: 'TASKFORCE_CLOUD_READ_WORKERS', description: 'Bounded cloud collection-read worker count (1-8, or 0 to disable).', category: 'Core', defaultValue: '2', targets: ['server', 'all'] },
|
|
14
15
|
{ key: 'TASKFORCE_ALLOW_SQLITE_IN_CLOUD', description: 'Emergency override to allow sqlite in cloud runtime.', category: 'Core', defaultValue: 'false', targets: ['server', 'all'] },
|
|
15
16
|
{ key: 'TASKFORCE_FORCE_SETUP', description: 'Force local setup gate for development.', category: 'Core', defaultValue: 'false', targets: ['server', 'all'] },
|
|
16
17
|
{ key: 'TASKFORCE_FORCE_GLOBAL_SETUP', description: 'Force global setup step for development.', category: 'Core', defaultValue: 'false', targets: ['server', 'all'] },
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type TaskforceCloudEnvironment } from './cloudEnvironment.js';
|
|
2
|
+
export interface LocalServiceCloudTarget {
|
|
3
|
+
environment: TaskforceCloudEnvironment | null;
|
|
4
|
+
cloudProxyBaseUrl: string;
|
|
5
|
+
cloudAuthBaseUrl: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function resolveLocalServiceCloudTarget(env: NodeJS.ProcessEnv): LocalServiceCloudTarget;
|
|
8
|
+
export declare function applyLocalServiceCloudTarget(env: NodeJS.ProcessEnv, target: LocalServiceCloudTarget): void;
|
|
9
|
+
export declare function parseLocalServiceCloudTarget(value: unknown): LocalServiceCloudTarget | null;
|
|
10
|
+
export declare function localServiceCloudTargetsEqual(left: LocalServiceCloudTarget | null | undefined, right: LocalServiceCloudTarget | null | undefined): boolean;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { TASKFORCE_CLOUD_ENVIRONMENTS, parseTaskforceCloudEnvironment, resolveTaskforceCloudEnvironmentFromBaseUrl, } from './cloudEnvironment.js';
|
|
2
|
+
import { resolveDeploymentConfig } from './deployment.js';
|
|
3
|
+
function normalizeCloudBaseUrl(raw, label) {
|
|
4
|
+
let parsed;
|
|
5
|
+
try {
|
|
6
|
+
parsed = new URL(String(raw || '').trim());
|
|
7
|
+
}
|
|
8
|
+
catch {
|
|
9
|
+
throw new Error(`${label} must be a valid HTTP(S) URL.`);
|
|
10
|
+
}
|
|
11
|
+
if (!['http:', 'https:'].includes(parsed.protocol) || parsed.username || parsed.password) {
|
|
12
|
+
throw new Error(`${label} must be an HTTP(S) URL without embedded credentials.`);
|
|
13
|
+
}
|
|
14
|
+
if (parsed.search || parsed.hash) {
|
|
15
|
+
throw new Error(`${label} must not include a query string or fragment.`);
|
|
16
|
+
}
|
|
17
|
+
const pathname = parsed.pathname.replace(/\/+$/, '');
|
|
18
|
+
return pathname ? `${parsed.origin}${pathname}` : parsed.origin;
|
|
19
|
+
}
|
|
20
|
+
export function resolveLocalServiceCloudTarget(env) {
|
|
21
|
+
const rawEnvironment = String(env.TASKFORCE_CLOUD_ENVIRONMENT || '').trim();
|
|
22
|
+
const explicitEnvironment = parseTaskforceCloudEnvironment(rawEnvironment);
|
|
23
|
+
if (rawEnvironment && !explicitEnvironment) {
|
|
24
|
+
throw new Error(`Unknown Taskforce cloud environment: ${rawEnvironment}.`);
|
|
25
|
+
}
|
|
26
|
+
const deployment = resolveDeploymentConfig(env);
|
|
27
|
+
const productionOrigin = TASKFORCE_CLOUD_ENVIRONMENTS.production.appBaseUrl;
|
|
28
|
+
const cloudProxyBaseUrl = normalizeCloudBaseUrl(deployment.cloudBaseUrl || productionOrigin, 'Taskforce cloud proxy URL');
|
|
29
|
+
const cloudAuthBaseUrl = normalizeCloudBaseUrl(deployment.cloudAuthBaseUrl || cloudProxyBaseUrl, 'Taskforce cloud auth URL');
|
|
30
|
+
const proxyEnvironment = resolveTaskforceCloudEnvironmentFromBaseUrl(cloudProxyBaseUrl);
|
|
31
|
+
const authEnvironment = resolveTaskforceCloudEnvironmentFromBaseUrl(cloudAuthBaseUrl);
|
|
32
|
+
const knownEnvironments = [explicitEnvironment, proxyEnvironment, authEnvironment]
|
|
33
|
+
.filter((value) => Boolean(value));
|
|
34
|
+
if (new Set(knownEnvironments).size > 1) {
|
|
35
|
+
throw new Error('Taskforce cloud environment, proxy URL, and auth URL must identify the same cloud target.');
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
environment: explicitEnvironment || proxyEnvironment || authEnvironment,
|
|
39
|
+
cloudProxyBaseUrl,
|
|
40
|
+
cloudAuthBaseUrl,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
export function applyLocalServiceCloudTarget(env, target) {
|
|
44
|
+
if (target.environment)
|
|
45
|
+
env.TASKFORCE_CLOUD_ENVIRONMENT = target.environment;
|
|
46
|
+
env.TASKFORCE_CLOUD_PROXY_BASE_URL ||= target.cloudProxyBaseUrl;
|
|
47
|
+
env.VITE_TASKFORCE_CLOUD_AUTH_BASE_URL ||= target.cloudAuthBaseUrl;
|
|
48
|
+
}
|
|
49
|
+
export function parseLocalServiceCloudTarget(value) {
|
|
50
|
+
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
51
|
+
return null;
|
|
52
|
+
const record = value;
|
|
53
|
+
const environment = record.environment === null
|
|
54
|
+
? null
|
|
55
|
+
: parseTaskforceCloudEnvironment(record.environment);
|
|
56
|
+
if (record.environment !== null && !environment)
|
|
57
|
+
return null;
|
|
58
|
+
if (typeof record.cloudProxyBaseUrl !== 'string' || typeof record.cloudAuthBaseUrl !== 'string')
|
|
59
|
+
return null;
|
|
60
|
+
try {
|
|
61
|
+
const cloudProxyBaseUrl = normalizeCloudBaseUrl(record.cloudProxyBaseUrl, 'Taskforce cloud proxy base URL');
|
|
62
|
+
const cloudAuthBaseUrl = normalizeCloudBaseUrl(record.cloudAuthBaseUrl, 'Taskforce cloud auth base URL');
|
|
63
|
+
if (cloudProxyBaseUrl !== record.cloudProxyBaseUrl || cloudAuthBaseUrl !== record.cloudAuthBaseUrl)
|
|
64
|
+
return null;
|
|
65
|
+
return { environment, cloudProxyBaseUrl, cloudAuthBaseUrl };
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
export function localServiceCloudTargetsEqual(left, right) {
|
|
72
|
+
return Boolean(left && right
|
|
73
|
+
&& left.environment === right.environment
|
|
74
|
+
&& left.cloudProxyBaseUrl === right.cloudProxyBaseUrl
|
|
75
|
+
&& left.cloudAuthBaseUrl === right.cloudAuthBaseUrl);
|
|
76
|
+
}
|
|
@@ -138,6 +138,7 @@ export declare function getAiProfileByConnectionService(deps: AiProfileServiceDe
|
|
|
138
138
|
workspaceId: string;
|
|
139
139
|
connectionId: string;
|
|
140
140
|
seatScope?: AiProfileSeatScope | null;
|
|
141
|
+
repairLegacyBinding?: boolean;
|
|
141
142
|
}): AiProfileRecordLike | null;
|
|
142
143
|
export declare function archiveAiProfileService(deps: AiProfileServiceDeps, input: {
|
|
143
144
|
workspaceId: string;
|
|
@@ -1367,8 +1367,9 @@ export function getAiProfileByConnectionService(deps, input) {
|
|
|
1367
1367
|
if (legacyRows.length === 0)
|
|
1368
1368
|
return null;
|
|
1369
1369
|
const record = hydrateAiProfileRecord(deps, legacyRows[0]);
|
|
1370
|
-
if (!record.archivedAt)
|
|
1370
|
+
if (!record.archivedAt && input.repairLegacyBinding !== false) {
|
|
1371
1371
|
bindAiProfileConnectionService(deps, { workspaceId, profileId: record.id, connectionId });
|
|
1372
|
+
}
|
|
1372
1373
|
return record;
|
|
1373
1374
|
}
|
|
1374
1375
|
export function archiveAiProfileService(deps, input) {
|
|
@@ -262,11 +262,14 @@ export function syncCanonicalAttachmentLinksForTargetService(deps, targetType, t
|
|
|
262
262
|
: String(row?.kind || '').trim() === 'image'
|
|
263
263
|
? 'image'
|
|
264
264
|
: 'attachment';
|
|
265
|
-
const displayName =
|
|
265
|
+
const displayName = typeof attachment === 'string'
|
|
266
266
|
? null
|
|
267
|
-
:
|
|
268
|
-
?
|
|
269
|
-
|
|
267
|
+
: safeTargetType === 'task'
|
|
268
|
+
? String(attachment.displayName || attachment.originalFilename || attachment.caption
|
|
269
|
+
|| (isExternalStorageKey || !storageKey ? '' : path.basename(storageKey))).trim() || null
|
|
270
|
+
: String(row?.kind || '').trim() === 'document' && !isExternalStorageKey
|
|
271
|
+
? null
|
|
272
|
+
: buildAttachmentDisplayName(attachment, isExternalStorageKey || !storageKey ? null : path.basename(storageKey));
|
|
270
273
|
const timestamp = typeof attachment === 'string' ? new Date().toISOString() : (attachment.timestamp || new Date().toISOString());
|
|
271
274
|
upsertLink.run(deps.tenantId, normalizedWorkspaceId, assetId, safeTargetType, normalizedTargetId, role, displayName, index, timestamp, timestamp);
|
|
272
275
|
});
|
|
@@ -16,6 +16,7 @@ export interface DeletedTaskLifecycleServiceDeps {
|
|
|
16
16
|
actorProfile: TaskActor | null;
|
|
17
17
|
};
|
|
18
18
|
runRetentionPurgeTransaction: <T>(workspaceId: string | null, apply: (durableDeleteOwnedKeys: ReadonlySet<string>) => T) => T;
|
|
19
|
+
listDurableDeleteOwnedTaskIds: (workspaceId: string) => ReadonlySet<string>;
|
|
19
20
|
}
|
|
20
21
|
export declare function normalizeTaskSnapshotForRestoreService(deps: DeletedTaskLifecycleServiceDeps, task: TaskItem): TaskItem;
|
|
21
22
|
export declare function normalizeDeletedTaskRestoreLifecycleService(deps: DeletedTaskLifecycleServiceDeps, task: TaskItem): Pick<TaskItem, 'status' | 'isArchived' | 'completedAt' | 'canceledReason'>;
|
|
@@ -33,4 +34,7 @@ export declare function addDeletedTaskRecordService(deps: DeletedTaskLifecycleSe
|
|
|
33
34
|
export declare function listDeletedTasksService(deps: DeletedTaskLifecycleServiceDeps, workspaceId?: string): {
|
|
34
35
|
deleted: DeletedTaskRecord[];
|
|
35
36
|
};
|
|
37
|
+
export declare function listDeletedTasksReadOnlyService(deps: DeletedTaskLifecycleServiceDeps, workspaceId?: string): {
|
|
38
|
+
deleted: DeletedTaskRecord[];
|
|
39
|
+
};
|
|
36
40
|
export declare function getDeletedTaskService(deps: DeletedTaskLifecycleServiceDeps, id: string, workspaceId?: string): DeletedTaskRecord | null;
|
|
@@ -225,6 +225,27 @@ export function listDeletedTasksService(deps, workspaceId) {
|
|
|
225
225
|
.filter((row) => Boolean(row))
|
|
226
226
|
};
|
|
227
227
|
}
|
|
228
|
+
export function listDeletedTasksReadOnlyService(deps, workspaceId) {
|
|
229
|
+
const normalizedWorkspaceId = deps.normalizeWorkspaceId(workspaceId);
|
|
230
|
+
const cutoff = new Date(Date.now() - (DELETED_TASK_RETENTION_DAYS * 24 * 60 * 60 * 1000)).toISOString();
|
|
231
|
+
const durableDeleteOwnedTaskIds = deps.listDurableDeleteOwnedTaskIds(normalizedWorkspaceId);
|
|
232
|
+
const listSql = deps.db.provider === 'postgres'
|
|
233
|
+
? deletedTaskListSnapshotSelect()
|
|
234
|
+
: `
|
|
235
|
+
SELECT *
|
|
236
|
+
FROM deleted_tasks
|
|
237
|
+
WHERE tenant_id = ?
|
|
238
|
+
AND workspace_id = ?
|
|
239
|
+
ORDER BY deleted_at DESC, id DESC
|
|
240
|
+
`;
|
|
241
|
+
const rows = deps.db.prepare(listSql).all(deps.tenantId, normalizedWorkspaceId);
|
|
242
|
+
return {
|
|
243
|
+
deleted: rows
|
|
244
|
+
.map((row) => mapDeletedTaskRowService(deps, row))
|
|
245
|
+
.filter((row) => Boolean(row))
|
|
246
|
+
.filter((row) => row.deletedAt >= cutoff || durableDeleteOwnedTaskIds.has(row.taskId))
|
|
247
|
+
};
|
|
248
|
+
}
|
|
228
249
|
export function getDeletedTaskService(deps, id, workspaceId) {
|
|
229
250
|
const normalizedWorkspaceId = deps.normalizeWorkspaceId(workspaceId);
|
|
230
251
|
const normalizedId = String(id || '').trim();
|
|
@@ -124,6 +124,10 @@ export declare class McpTokenService {
|
|
|
124
124
|
profileToken: string;
|
|
125
125
|
profileName: string;
|
|
126
126
|
}): boolean;
|
|
127
|
+
listMcpOAuthRestorableAiProfileIds(input: {
|
|
128
|
+
grantId: string;
|
|
129
|
+
workspaceId: string;
|
|
130
|
+
}): string[];
|
|
127
131
|
recordMcpOAuthConnectorGrantUsage(input: {
|
|
128
132
|
grantId: string;
|
|
129
133
|
workspaceId: string;
|