@taskforcehq/taskforce 0.3.300 → 0.3.302

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.
Files changed (153) hide show
  1. package/README.md +19 -0
  2. package/dist/Taskforce.module.css +26 -2
  3. package/dist/TaskforceCore.filter-persistence.test.js +1 -1
  4. package/dist/TaskforceCore.js +13 -24
  5. package/dist/TaskforceCore.test.js +167 -152
  6. package/dist/components/features/TaskSettings.js +14 -82
  7. package/dist/components/features/TaskSettings.test.js +33 -70
  8. package/dist/components/features/settings/TaxonomyLibraryManager.js +8 -2
  9. package/dist/components/features/settings/TaxonomyLibraryManager.test.js +1 -1
  10. package/dist/components/task/TaskActionHeader.js +6 -3
  11. package/dist/components/task/TaskCard.js +6 -3
  12. package/dist/components/task/TaskForm.js +2 -2
  13. package/dist/components/task/TaskForm.test.js +41 -2
  14. package/dist/components/ui/ReferenceBadgeButton.js +1 -1
  15. package/dist/components/views/PlansPage.js +9 -8
  16. package/dist/components/views/StandaloneLayout.js +490 -207
  17. package/dist/components/views/panels/FilterToolbar.d.ts +2 -0
  18. package/dist/components/views/panels/FilterToolbarControls.d.ts +1 -0
  19. package/dist/components/views/panels/FilterToolbarControls.js +1 -1
  20. package/dist/components/views/panels/PlanningDrawer.js +19 -2
  21. package/dist/components/views/standalone/modals/AccountHubModal.d.ts +43 -0
  22. package/dist/components/views/standalone/modals/AccountHubModal.js +22 -0
  23. package/dist/components/views/standalone/modals/CreateWorkspaceConfirmModal.d.ts +9 -0
  24. package/dist/components/views/standalone/modals/CreateWorkspaceConfirmModal.js +8 -0
  25. package/dist/components/views/standalone/modals/EditProfileModal.d.ts +26 -0
  26. package/dist/components/views/standalone/modals/EditProfileModal.js +10 -0
  27. package/dist/components/views/standalone/modals/HelpModal.d.ts +9 -0
  28. package/dist/components/views/standalone/modals/HelpModal.js +8 -0
  29. package/dist/components/views/standalone/modals/ScheduleWarningModal.d.ts +13 -0
  30. package/dist/components/views/standalone/modals/ScheduleWarningModal.js +6 -0
  31. package/dist/components/views/standalone/modals/SyncStatusModal.d.ts +61 -0
  32. package/dist/components/views/standalone/modals/SyncStatusModal.js +38 -0
  33. package/dist/components/views/standalone/modals/TeamManagementModal.d.ts +64 -0
  34. package/dist/components/views/standalone/modals/TeamManagementModal.js +10 -0
  35. package/dist/components/views/standalone/modals/types.d.ts +2 -0
  36. package/dist/components/views/standalone/modals/types.js +1 -0
  37. package/dist/components/views/teamManagementAccess.d.ts +1 -1
  38. package/dist/components/views/teamManagementAccess.js +2 -2
  39. package/dist/components/views/teamManagementAccess.test.js +3 -3
  40. package/dist/config/deployment.js +2 -2
  41. package/dist/config/deployment.test.js +5 -3
  42. package/dist/core/AiProfileService.js +7 -2
  43. package/dist/core/AiProfiles.test.js +19 -0
  44. package/dist/core/SyncReconciliationService.d.ts +2 -0
  45. package/dist/core/SyncReconciliationService.js +3 -0
  46. package/dist/core/TaskAuditTimeline.test.js +40 -0
  47. package/dist/core/Taskforce.d.ts +49 -0
  48. package/dist/core/Taskforce.ids.test.js +133 -0
  49. package/dist/core/Taskforce.js +241 -78
  50. package/dist/core/UserProfileAvatarDrafts.test.d.ts +1 -0
  51. package/dist/core/UserProfileAvatarDrafts.test.js +102 -0
  52. package/dist/core/taskReferencePolicy.d.ts +28 -0
  53. package/dist/core/taskReferencePolicy.js +189 -0
  54. package/dist/core/types.d.ts +2 -0
  55. package/dist/hooks/tasks/useTaskFormActions.js +3 -0
  56. package/dist/hooks/useTaskData.js +16 -11
  57. package/dist/hooks/useTaskData.test.js +127 -0
  58. package/dist/hooks/useTaskforce.d.ts +5 -0
  59. package/dist/hooks/useTaskforce.js +63 -15
  60. package/dist/hooks/useTaskforce.sync-behavior.test.js +185 -6
  61. package/dist/mcp/aiProfileBootstrap.d.ts +29 -0
  62. package/dist/mcp/aiProfileBootstrap.js +80 -0
  63. package/dist/mcp/aiProfileBootstrap.test.d.ts +1 -0
  64. package/dist/mcp/aiProfileBootstrap.test.js +68 -0
  65. package/dist/mcp/clientIntegrations.d.ts +2 -8
  66. package/dist/mcp/clientIntegrations.js +21 -40
  67. package/dist/mcp/clientIntegrations.test.js +28 -2
  68. package/dist/mcp/clientProfileDefaults.d.ts +12 -0
  69. package/dist/mcp/clientProfileDefaults.js +37 -0
  70. package/dist/mcp/runtime.d.ts +10 -7
  71. package/dist/mcp/runtime.js +45 -23
  72. package/dist/mcp/runtime.test.js +56 -3
  73. package/dist/migrations/taskSchemaMigrations.js +53 -1
  74. package/dist/qaOpen.test.d.ts +1 -0
  75. package/dist/qaOpen.test.js +56 -0
  76. package/dist/server/index.cookieProxy.test.js +1 -0
  77. package/dist/server/index.d.ts +1 -0
  78. package/dist/server/index.js +15 -1
  79. package/dist/server/index.test.js +14 -1
  80. package/dist/server/routes/admin.js +25 -22
  81. package/dist/server/routes/auth.d.ts +5 -0
  82. package/dist/server/routes/auth.js +350 -16
  83. package/dist/server/routes/billing.js +11 -1
  84. package/dist/server/routes/billing.test.js +32 -2
  85. package/dist/server/routes/documents.js +6 -1
  86. package/dist/server/routes/shared.d.ts +1 -1
  87. package/dist/server/routes/shared.js +11 -3
  88. package/dist/server/routes/sync.integration.test.js +130 -0
  89. package/dist/server/routes/sync.js +226 -1924
  90. package/dist/server/routes/tasks.js +18 -4
  91. package/dist/server/routes.js +26 -19
  92. package/dist/server/routes.test.js +352 -6
  93. package/dist/shared/runtimeContract.d.ts +1 -1
  94. package/dist/sync/collaborationSyncPayload.d.ts +17 -0
  95. package/dist/sync/collaborationSyncPayload.js +131 -0
  96. package/dist/sync/collaborationSyncPayload.test.d.ts +1 -0
  97. package/dist/sync/collaborationSyncPayload.test.js +104 -0
  98. package/dist/sync/collaborationSyncState.d.ts +15 -0
  99. package/dist/sync/collaborationSyncState.js +60 -0
  100. package/dist/sync/contentSyncPayload.d.ts +45 -0
  101. package/dist/sync/contentSyncPayload.js +104 -0
  102. package/dist/sync/contentSyncPayload.test.d.ts +1 -0
  103. package/dist/sync/contentSyncPayload.test.js +104 -0
  104. package/dist/sync/contentSyncState.d.ts +88 -0
  105. package/dist/sync/contentSyncState.js +743 -0
  106. package/dist/sync/planningSyncPayload.d.ts +9 -0
  107. package/dist/sync/planningSyncPayload.js +82 -0
  108. package/dist/sync/planningSyncPayload.test.d.ts +1 -0
  109. package/dist/sync/planningSyncPayload.test.js +68 -0
  110. package/dist/sync/syncApplyHandlers.d.ts +83 -0
  111. package/dist/sync/syncApplyHandlers.js +445 -0
  112. package/dist/sync/taskSyncPayload.d.ts +33 -0
  113. package/dist/sync/taskSyncPayload.js +126 -0
  114. package/dist/sync/taskSyncPayload.test.d.ts +1 -0
  115. package/dist/sync/taskSyncPayload.test.js +54 -0
  116. package/dist/sync/workspacePullFeed.d.ts +28 -0
  117. package/dist/sync/workspacePullFeed.js +301 -0
  118. package/dist/sync/workspacePullFeed.test.d.ts +1 -0
  119. package/dist/sync/workspacePullFeed.test.js +166 -0
  120. package/dist/sync/workspaceSyncModel.d.ts +4 -1
  121. package/dist/sync/workspaceSyncModel.js +9 -2
  122. package/dist/sync/workspaceSyncModel.test.js +29 -0
  123. package/dist/test/setup.js +1 -1
  124. package/dist/types.d.ts +1 -0
  125. package/dist/ui/assets/{AgentsModule-TVmCgizS.js → AgentsModule-JG5jc3he.js} +1 -1
  126. package/dist/ui/assets/{AnnotatedAttachmentWorkspace-CAyhnPeR.js → AnnotatedAttachmentWorkspace-Dvi02Y7q.js} +1 -1
  127. package/dist/ui/assets/{DocumentWorkspace-BOCw5e4D.js → DocumentWorkspace-iys3zfca.js} +2 -2
  128. package/dist/ui/assets/{InitiativesModule-CN0r02Oi.js → InitiativesModule-DJvLJI3A.js} +1 -1
  129. package/dist/ui/assets/PlansPage-BMPlpFpw.js +1 -0
  130. package/dist/ui/assets/TaskSettings-Bqd4cCzW.js +8 -0
  131. package/dist/ui/assets/{TaskSettings-BwB9NtmJ.css → TaskSettings-CnIBL_Eb.css} +1 -1
  132. package/dist/ui/assets/{WorkflowsModule-CY9R2zPB.js → WorkflowsModule-BguHeQaV.js} +1 -1
  133. package/dist/ui/assets/index-MXeWoebC.css +1 -0
  134. package/dist/ui/assets/index-YJ4k5QNa.js +6 -0
  135. package/dist/ui/assets/{vendor-icons-QZyhEwgT.js → vendor-icons-C8EED3Oh.js} +1 -1
  136. package/dist/ui/index.html +3 -3
  137. package/dist/utils/billingStatusCache.d.ts +21 -0
  138. package/dist/utils/billingStatusCache.js +70 -0
  139. package/dist/utils/taskActivity.js +17 -4
  140. package/dist/utils/taskActivity.test.js +27 -0
  141. package/dist/utils/taskNormalization.js +10 -2
  142. package/dist/utils/taskReferences.d.ts +11 -1
  143. package/dist/utils/taskReferences.js +33 -1
  144. package/package.json +4 -1
  145. package/scripts/deploy-prod.sh +41 -0
  146. package/scripts/deploy-staging.sh +23 -0
  147. package/scripts/qa-open-lib.mjs +175 -0
  148. package/scripts/qa-open-lib.mjs.d.ts +49 -0
  149. package/scripts/qa-open.mjs +352 -0
  150. package/dist/ui/assets/PlansPage-FcQSNLZd.js +0 -1
  151. package/dist/ui/assets/TaskSettings-Cw5zYD1L.js +0 -8
  152. package/dist/ui/assets/index-Bzqy5QGm.js +0 -6
  153. package/dist/ui/assets/index-DxRiI3tY.css +0 -1
@@ -45,12 +45,14 @@ export interface FilterToolbarProps {
45
45
  initiativeFilterOptions?: Array<{
46
46
  value: string;
47
47
  label: string;
48
+ selectedLabel?: string;
48
49
  }>;
49
50
  selectedInitiativeId?: string;
50
51
  setSelectedInitiativeId?: (value: string) => void;
51
52
  workstreamFilterOptions?: Array<{
52
53
  value: string;
53
54
  label: string;
55
+ selectedLabel?: string;
54
56
  }>;
55
57
  selectedWorkstreamId?: string;
56
58
  setSelectedWorkstreamId?: (value: string) => void;
@@ -18,6 +18,7 @@ type FilterToolbarSingleSelectProps = {
18
18
  options: {
19
19
  value: string;
20
20
  label: string;
21
+ selectedLabel?: string;
21
22
  }[];
22
23
  selected: string;
23
24
  onChange: (value: string) => void;
@@ -27,7 +27,7 @@ export function FilterToolbarSingleSelect({ label, options, selected, onChange,
27
27
  const [isOpen, setIsOpen] = React.useState(false);
28
28
  const containerRef = React.useRef(null);
29
29
  const selectedOption = options.find((option) => option.value === selected);
30
- const displayText = selectedOption?.label || allLabel;
30
+ const displayText = selectedOption?.selectedLabel || selectedOption?.label || allLabel;
31
31
  React.useEffect(() => {
32
32
  const handleClickOutside = (event) => {
33
33
  if (containerRef.current && !containerRef.current.contains(event.target)) {
@@ -6,6 +6,7 @@ import { ChevronLeft, ChevronRight, ChevronsDown, ChevronsRight, Filter, Plus, }
6
6
  import styles from '../../../Taskforce.module.css';
7
7
  import panelStyles from './PlanningDrawer.module.css';
8
8
  import { ReferenceBadgeButton } from '../../ui/ReferenceBadgeButton';
9
+ import { IconMap, getStatusDefinition, resolveColor } from '../../../utils/constants';
9
10
  import { formatInitiativeReference, formatWorkstreamReference, } from '../../../utils/planningReferences.js';
10
11
  export const PLANNING_DRAWER_TREE_WIDTH_PX = 332;
11
12
  export const PLANNING_DRAWER_DETAIL_WIDTH_PX = 392;
@@ -64,7 +65,7 @@ function PlanningRow({ rowId, rowType, referenceLabel, title, meta, active, deta
64
65
  ? { transform: CSS.Translate.toString(transform) }
65
66
  : undefined;
66
67
  const showDropReady = canDropTask || canDropWorkstream;
67
- return (_jsxs("div", { ref: setDropRef, style: dragStyle, className: `${panelStyles.treeRow} ${detailOpen ? panelStyles.treeRowDetailOpen : ''} ${showDropReady ? panelStyles.treeRowDropReady : ''} ${isOver ? panelStyles.treeRowDropActive : ''} ${isDragging ? panelStyles.treeRowDragging : ''} ${showDropReady ? panelStyles.treeRowDropMode : ''}`, children: [canExpand ? (_jsx("button", { type: "button", className: panelStyles.treeChevron, onClick: onToggleExpand, title: expanded ? 'Collapse workstreams' : 'Expand workstreams', children: expanded ? _jsx(ChevronsDown, { size: 15 }) : _jsx(ChevronsRight, { size: 15 }) })) : (_jsx("span", { className: panelStyles.treeChevron, "aria-hidden": "true", children: _jsx(ChevronRight, { size: 14 }) })), _jsxs("div", { className: panelStyles.rowContent, children: [referenceLabel ? (_jsx(PlanningReferenceBadge, { label: referenceLabel, entityType: rowType })) : null, _jsxs("button", { type: "button", ref: draggable ? setDragRef : undefined, className: panelStyles.rowButton, onClick: onToggleDetails, ...(draggable ? attributes : {}), ...(draggable ? listeners : {}), children: [_jsx("span", { className: panelStyles.rowTitle, children: title }), _jsx("span", { className: panelStyles.rowMeta, children: meta })] })] }), _jsx("button", { type: "button", className: `${panelStyles.detailsButton} ${active ? panelStyles.detailsButtonActive : ''}`, onClick: onToggleScope, title: active ? 'Clear board scope' : 'Scope board to this item', children: _jsx(Filter, { size: 14 }) })] }));
68
+ return (_jsxs("div", { ref: setDropRef, style: dragStyle, className: `${panelStyles.treeRow} ${detailOpen ? panelStyles.treeRowDetailOpen : ''} ${showDropReady ? panelStyles.treeRowDropReady : ''} ${isOver ? panelStyles.treeRowDropActive : ''} ${isDragging ? panelStyles.treeRowDragging : ''} ${showDropReady ? panelStyles.treeRowDropMode : ''}`, children: [canExpand ? (_jsx("button", { type: "button", className: panelStyles.treeChevron, onClick: onToggleExpand, title: expanded ? 'Collapse workstreams' : 'Expand workstreams', children: expanded ? _jsx(ChevronsDown, { size: 15 }) : _jsx(ChevronsRight, { size: 15 }) })) : (_jsx("span", { className: panelStyles.treeChevron, "aria-hidden": "true", children: _jsx(ChevronRight, { size: 14 }) })), _jsxs("div", { className: panelStyles.rowContent, children: [_jsxs("div", { className: panelStyles.rowTopRow, children: [referenceLabel ? (_jsx(PlanningReferenceBadge, { label: referenceLabel, entityType: rowType })) : _jsx("span", { "aria-hidden": "true" }), _jsx("button", { type: "button", className: `${panelStyles.detailsButton} ${active ? panelStyles.detailsButtonActive : ''}`, onClick: onToggleScope, title: active ? 'Clear board scope' : 'Scope board to this item', children: _jsx(Filter, { size: 14 }) })] }), _jsxs("button", { type: "button", ref: draggable ? setDragRef : undefined, className: panelStyles.rowButton, onClick: onToggleDetails, ...(draggable ? attributes : {}), ...(draggable ? listeners : {}), children: [_jsx("span", { className: panelStyles.rowTitle, children: title }), _jsx("span", { className: panelStyles.rowMeta, children: meta })] })] })] }));
68
69
  }
69
70
  function CollapsedPane({ label, onExpand, }) {
70
71
  return (_jsx("div", { className: `${panelStyles.paneShell} ${panelStyles.collapsedPaneShell}`.trim(), children: _jsxs("div", { className: panelStyles.collapsedPaneHeader, children: [_jsx("button", { type: "button", className: "tf-control-icon", onClick: onExpand, title: `Expand ${label}`, children: _jsx(ChevronRight, { size: 16 }) }), _jsx("span", { className: panelStyles.collapsedPaneLabel, children: label })] }) }));
@@ -157,7 +158,23 @@ function PlanningDrawerDetailView({ detail, onOpenNestedWorkstreamDetails, onEdi
157
158
  onAttachTaskToWorkstreamByReference?.(detail.item.id, value);
158
159
  }
159
160
  setAttachReference('');
160
- }, children: "Attach" })] }), detail.type === 'initiative' ? (childItems.length > 0 ? childItems.map((workstream) => (_jsx("button", { type: "button", className: panelStyles.listItemButton, onClick: () => onOpenNestedWorkstreamDetails?.(workstream.id), children: _jsx("div", { className: panelStyles.listItem, children: _jsxs("div", { className: panelStyles.listItemText, children: [_jsx("span", { className: panelStyles.listItemTitle, children: workstream.title }), _jsxs("span", { className: panelStyles.listItemMeta, children: [workstream.taskCount, " tasks \u2022 ", workstream.progressPercent, "% complete"] })] }) }) }, workstream.id))) : (_jsxs("div", { className: panelStyles.emptyPanel, children: [_jsx("div", { className: panelStyles.emptyPanelTitle, children: "No workstreams yet" }), _jsx("div", { className: panelStyles.emptyPanelText, children: "Add the first workstream to break the initiative into clearer lanes of execution." })] }))) : (detail.item.tasks && detail.item.tasks.length > 0 ? detail.item.tasks.map((task) => (_jsx("button", { type: "button", className: panelStyles.listItemButton, onClick: () => onOpenTaskById?.(task.id), children: _jsx("div", { className: panelStyles.listItem, children: _jsxs("div", { className: panelStyles.listItemText, children: [_jsx("span", { className: panelStyles.listItemTitle, children: task.title }), _jsxs("span", { className: panelStyles.listItemMeta, children: [task.referenceNumber ? `T-${task.referenceNumber} • ` : '', formatTaskStatusLabel(task.status)] })] }) }) }, task.id))) : (_jsxs("div", { className: panelStyles.sectionNote, children: ["No tasks are linked to this workstream yet. Use the ", _jsx("code", { children: "+" }), " action above to add the first one."] })))] }), _jsxs("div", { className: panelStyles.listCard, children: [_jsx("div", { className: panelStyles.sectionTitle, children: "Context" }), _jsx("div", { className: panelStyles.sectionNote, children: "Comments, attachments, and planning notes can live here without pushing that context down into task cards." })] })] }));
161
+ }, children: "Attach" })] }), detail.type === 'initiative' ? (childItems.length > 0 ? childItems.map((workstream) => (_jsx("button", { type: "button", className: `${panelStyles.listItemButton} ${panelStyles.detailCardButton}`.trim(), onClick: () => onOpenNestedWorkstreamDetails?.(workstream.id), children: _jsxs("div", { className: `${panelStyles.listItem} ${panelStyles.detailChildCard}`.trim(), children: [_jsx("div", { className: panelStyles.detailChildCardTopRow, children: workstream.referenceNumber ? (_jsx(PlanningReferenceBadge, { label: formatWorkstreamReference(workstream), entityType: "workstream" })) : _jsx("span", { "aria-hidden": "true" }) }), _jsxs("div", { className: panelStyles.listItemText, children: [_jsx("span", { className: panelStyles.listItemTitle, children: workstream.title }), _jsxs("span", { className: panelStyles.listItemMeta, children: [workstream.taskCount, " tasks \u2022 ", workstream.progressPercent, "% complete"] })] })] }) }, workstream.id))) : (_jsxs("div", { className: panelStyles.emptyPanel, children: [_jsx("div", { className: panelStyles.emptyPanelTitle, children: "No workstreams yet" }), _jsx("div", { className: panelStyles.emptyPanelText, children: "Add the first workstream to break the initiative into clearer lanes of execution." })] }))) : (detail.item.tasks && detail.item.tasks.length > 0 ? [...detail.item.tasks]
162
+ .sort((left, right) => {
163
+ const leftDone = getStatusDefinition(left.status).value === 'done';
164
+ const rightDone = getStatusDefinition(right.status).value === 'done';
165
+ if (leftDone === rightDone)
166
+ return 0;
167
+ return leftDone ? 1 : -1;
168
+ })
169
+ .map((task) => {
170
+ const referenceLabel = task.referenceNumber ? `T-${task.referenceNumber}` : '';
171
+ const statusDefinition = getStatusDefinition(task.status);
172
+ const StatusIcon = IconMap[statusDefinition.icon || 'Square'];
173
+ const statusColor = resolveColor(statusDefinition.color) || 'var(--text-secondary)';
174
+ return (_jsx("button", { type: "button", className: `${panelStyles.listItemButton} ${panelStyles.detailCardButton}`.trim(), onClick: () => onOpenTaskById?.(task.id), children: _jsxs("div", { className: `${panelStyles.listItem} ${panelStyles.detailChildCard}`.trim(), children: [_jsxs("div", { className: panelStyles.detailChildCardTopRow, children: [referenceLabel ? (_jsx(PlanningReferenceBadge, { label: referenceLabel, entityType: "task" })) : _jsx("span", { "aria-hidden": "true" }), _jsx("span", { className: panelStyles.taskStatusIcon, style: {
175
+ color: statusColor,
176
+ }, "aria-label": statusDefinition.shortLabel || formatTaskStatusLabel(task.status), title: statusDefinition.shortLabel || formatTaskStatusLabel(task.status), children: StatusIcon ? _jsx(StatusIcon, { size: 18, strokeWidth: 2.4 }) : null })] }), _jsxs("div", { className: panelStyles.listItemText, children: [_jsx("span", { className: panelStyles.listItemTitle, children: task.title }), _jsx("span", { className: panelStyles.listItemMeta, children: formatTaskStatusLabel(task.status) })] })] }) }, task.id));
177
+ }) : (_jsxs("div", { className: panelStyles.sectionNote, children: ["No tasks are linked to this workstream yet. Use the ", _jsx("code", { children: "+" }), " action above to add the first one."] })))] }), _jsxs("div", { className: panelStyles.listCard, children: [_jsx("div", { className: panelStyles.sectionTitle, children: "Context" }), _jsx("div", { className: panelStyles.sectionNote, children: "Comments, attachments, and planning notes can live here without pushing that context down into task cards." })] })] }));
161
178
  }
162
179
  function PlanningDrawerEditor({ editor, draftTitle, draftDescription, draftOwner, draftInitiativeId, assigneeOptions, draftInitiativeSummary, onChangeDraftTitle, onChangeDraftDescription, onChangeDraftOwner, onChangeDraftInitiativeId, onCancel, onSubmit, onAssignInitiativeToWorkstream, }) {
163
180
  const entityLabel = editor.entityType === 'initiative' ? 'Initiative' : 'Workstream';
@@ -0,0 +1,43 @@
1
+ import type { ModalRuntimeMode, ModalThemeName } from './types';
2
+ interface BillingStatusLike {
3
+ planId?: string | null;
4
+ entitlementState?: string | null;
5
+ stripeStatus?: string | null;
6
+ effectiveUntil?: string | null;
7
+ stripeSubscriptionId?: string | null;
8
+ stripeCustomerId?: string | null;
9
+ }
10
+ interface AccountHubModalProps {
11
+ isOpen: boolean;
12
+ theme: ModalThemeName;
13
+ runtimeMode: ModalRuntimeMode;
14
+ authRequiredForApi: boolean;
15
+ isAuthenticated: boolean;
16
+ hasAuthIdentity: boolean;
17
+ authIdentityLabel: string;
18
+ billingLoading: boolean;
19
+ billingError: string | null;
20
+ billingActionError: string | null;
21
+ billingNotice: string | null;
22
+ billingActionBusy: boolean;
23
+ billingIntervalChoice: 'month' | 'year';
24
+ billingStatus: BillingStatusLike | null;
25
+ currentWorkspaceId: string;
26
+ canOpenTeamManagement: boolean;
27
+ canManageWorkspaceSync: boolean;
28
+ workspaceCloudSyncEnabled: boolean;
29
+ syncStatusLabel: string;
30
+ workspaceSyncError: string | null;
31
+ syncControlBusy: boolean;
32
+ onClose: () => void;
33
+ onOpenWorkspaceAudit: () => void;
34
+ onBillingIntervalChange: (value: 'month' | 'year') => void;
35
+ onRefreshBilling: () => void;
36
+ onUpdateInterval: () => void;
37
+ onManageBilling: () => void;
38
+ onStartCheckout: () => void;
39
+ onToggleWorkspaceSync: (enabled: boolean) => void;
40
+ onOpenHelp: () => void;
41
+ }
42
+ export declare function AccountHubModal({ isOpen, theme, runtimeMode, authRequiredForApi, isAuthenticated, hasAuthIdentity, authIdentityLabel, billingLoading, billingError, billingActionError, billingNotice, billingActionBusy, billingIntervalChoice, billingStatus, currentWorkspaceId, canOpenTeamManagement, canManageWorkspaceSync, workspaceCloudSyncEnabled, syncStatusLabel, workspaceSyncError, syncControlBusy, onClose, onOpenWorkspaceAudit, onBillingIntervalChange, onRefreshBilling, onUpdateInterval, onManageBilling, onStartCheckout, onToggleWorkspaceSync, onOpenHelp, }: AccountHubModalProps): import("react/jsx-runtime").JSX.Element;
43
+ export {};
@@ -0,0 +1,22 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Modal } from '../../../ui/Modal';
3
+ import modalStyles from '../../../ui/Modal.module.css';
4
+ import accountStyles from '../../StandaloneAccount.module.css';
5
+ import authStyles from '../../AuthShell.module.css';
6
+ import managementStyles from '../../StandaloneManagement.module.css';
7
+ import styles from '../../../../Taskforce.module.css';
8
+ export function AccountHubModal({ isOpen, theme, runtimeMode, authRequiredForApi, isAuthenticated, hasAuthIdentity, authIdentityLabel, billingLoading, billingError, billingActionError, billingNotice, billingActionBusy, billingIntervalChoice, billingStatus, currentWorkspaceId, canOpenTeamManagement, canManageWorkspaceSync, workspaceCloudSyncEnabled, syncStatusLabel, workspaceSyncError, syncControlBusy, onClose, onOpenWorkspaceAudit, onBillingIntervalChange, onRefreshBilling, onUpdateInterval, onManageBilling, onStartCheckout, onToggleWorkspaceSync, onOpenHelp, }) {
9
+ return (_jsx(Modal, { isOpen: isOpen, onClose: onClose, title: "Account Hub", size: "md", theme: theme, draggable: true, children: _jsxs("div", { className: `${modalStyles.form} ${managementStyles.modalBody}`, children: [_jsx("p", { className: managementStyles.mutedText, children: "Manage sign in, profile, and workspace controls from one place." }), _jsxs("div", { className: accountStyles.accountHubCard, children: [_jsx("strong", { children: "Authentication" }), _jsx("p", { className: managementStyles.sectionText, children: runtimeMode === 'cloud'
10
+ ? (authRequiredForApi
11
+ ? (isAuthenticated ? 'Signed in. API access is enabled.' : 'Cloud mode requires sign in before app usage.')
12
+ : 'Cloud runtime with optional authentication.')
13
+ : 'Local runtime allows guest usage without sign in.' }), _jsx("p", { className: managementStyles.sectionTextSpaced, children: isAuthenticated
14
+ ? (hasAuthIdentity
15
+ ? _jsxs(_Fragment, { children: ["Signed in as ", _jsx("span", { className: accountStyles.accountIdentityEmail, children: authIdentityLabel })] })
16
+ : 'Signed in')
17
+ : 'Signed in as: not signed in' }), runtimeMode === 'cloud' && authRequiredForApi && !isAuthenticated && (_jsx("p", { className: managementStyles.sectionTextTop, children: "Use the `/login` screen to sign in." }))] }), _jsxs("div", { className: accountStyles.accountHubCard, children: [_jsx("strong", { children: "Profile & Account" }), _jsx("p", { className: managementStyles.sectionText, children: "Profile editing and account preferences will live here." })] }), _jsxs("div", { className: accountStyles.accountHubCard, children: [_jsx("strong", { children: "Billing" }), !isAuthenticated ? (_jsx("p", { className: managementStyles.sectionText, children: "Sign in to manage subscription billing." })) : runtimeMode !== 'cloud' ? (_jsx("p", { className: managementStyles.sectionText, children: "Billing remains cloud-backed in local runtime and uses your connected cloud account." })) : (_jsxs(_Fragment, { children: [billingLoading && (_jsx("p", { className: managementStyles.sectionTextSpaced, children: "Loading billing status..." })), billingError && (_jsx("p", { className: `${authStyles.loginError} ${managementStyles.errorText}`, children: billingError })), billingActionError && (_jsx("p", { className: `${authStyles.loginError} ${managementStyles.errorText}`, children: billingActionError })), billingNotice && (_jsx("p", { className: managementStyles.errorText, children: billingNotice })), _jsxs("p", { className: managementStyles.sectionTextSpaced, children: ["Plan: ", _jsx("code", { children: String(billingStatus?.planId || 'n/a') }), ' · ', "Entitlement: ", _jsx("code", { children: String(billingStatus?.entitlementState || 'n/a') })] }), _jsxs("p", { className: managementStyles.sectionText, children: ["Stripe status: ", _jsx("code", { children: String(billingStatus?.stripeStatus || 'n/a') }), billingStatus?.effectiveUntil ? ` · Effective until ${new Date(billingStatus.effectiveUntil).toLocaleString()}` : ''] }), _jsx("div", { className: managementStyles.wrapRow, children: _jsxs("label", { className: `${accountStyles.accountMenuMeta} ${managementStyles.inlineFieldLabel}`, children: ["Interval", _jsxs("select", { className: styles.input, value: billingIntervalChoice, onChange: (event) => onBillingIntervalChange(event.target.value === 'year' ? 'year' : 'month'), disabled: billingActionBusy, children: [_jsx("option", { value: "month", children: "Monthly" }), _jsx("option", { value: "year", children: "Yearly" })] })] }) }), _jsxs("div", { className: managementStyles.actionRowEnd, children: [_jsx("button", { className: styles.cancelBtn, onClick: onRefreshBilling, disabled: billingActionBusy || billingLoading, children: "Refresh Billing" }), _jsx("button", { className: styles.cancelBtn, onClick: onUpdateInterval, disabled: billingActionBusy || !billingStatus?.stripeSubscriptionId, children: "Update Interval" }), _jsx("button", { className: styles.cancelBtn, onClick: onManageBilling, disabled: billingActionBusy || !billingStatus?.stripeCustomerId, children: "Manage Billing" }), _jsx("button", { className: styles.submitBtn, onClick: onStartCheckout, disabled: billingActionBusy, children: billingActionBusy ? 'Working...' : 'Start Checkout' })] })] }))] }), _jsxs("div", { className: accountStyles.accountHubCard, children: [_jsx("strong", { children: "Workspace Audit Log" }), _jsx("p", { className: managementStyles.sectionText, children: "Audit entries are workspace-scoped for the active workspace, not global account history." }), _jsxs("p", { className: managementStyles.sectionText, children: ["Active workspace: ", _jsx("code", { children: currentWorkspaceId })] }), _jsx("div", { className: managementStyles.actionRowEnd, children: _jsx("button", { className: styles.cancelBtn, onClick: onOpenWorkspaceAudit, disabled: !canOpenTeamManagement, children: "Open Workspace Audit" }) })] }), _jsxs("div", { className: accountStyles.accountHubCard, children: [_jsx("strong", { children: "Workspace Cloud Sync" }), _jsx("p", { className: managementStyles.sectionText, children: canManageWorkspaceSync
18
+ ? (workspaceCloudSyncEnabled
19
+ ? `Enabled · ${syncStatusLabel}`
20
+ : 'Disabled')
21
+ : 'Sign in to cloud account to enable workspace sync.' }), workspaceSyncError && (_jsx("p", { className: `${authStyles.loginError} ${managementStyles.errorText}`, children: workspaceSyncError })), _jsx("div", { className: managementStyles.actionRowEnd, children: _jsx("button", { className: styles.cancelBtn, disabled: !canManageWorkspaceSync || syncControlBusy, onClick: () => onToggleWorkspaceSync(!workspaceCloudSyncEnabled), children: workspaceCloudSyncEnabled ? 'Disable Sync' : 'Enable Sync' }) })] }), _jsxs("div", { className: `${modalStyles.formActions} ${managementStyles.modalActionsEnd}`, children: [_jsx("button", { className: styles.cancelBtn, onClick: onOpenHelp, children: "Help & Tutorial" }), _jsx("button", { className: styles.submitBtn, onClick: onClose, children: "Close" })] })] }) }));
22
+ }
@@ -0,0 +1,9 @@
1
+ import type { ModalThemeName } from './types';
2
+ interface CreateWorkspaceConfirmModalProps {
3
+ isOpen: boolean;
4
+ theme: ModalThemeName;
5
+ onClose: () => void;
6
+ onConfirm: () => void;
7
+ }
8
+ export declare function CreateWorkspaceConfirmModal({ isOpen, theme, onClose, onConfirm, }: CreateWorkspaceConfirmModalProps): import("react/jsx-runtime").JSX.Element;
9
+ export {};
@@ -0,0 +1,8 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Modal } from '../../../ui/Modal';
3
+ import modalStyles from '../../../ui/Modal.module.css';
4
+ import managementStyles from '../../StandaloneManagement.module.css';
5
+ import styles from '../../../../Taskforce.module.css';
6
+ export function CreateWorkspaceConfirmModal({ isOpen, theme, onClose, onConfirm, }) {
7
+ return (_jsx(Modal, { isOpen: isOpen, onClose: onClose, title: "New Workspace", size: "sm", theme: theme, draggable: true, footer: (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", className: styles.cancelBtn, onClick: onClose, children: "Cancel" }), _jsx("button", { type: "button", className: styles.submitBtn, onClick: onConfirm, children: "Start Setup" })] })), children: _jsx("div", { className: `${modalStyles.form} ${managementStyles.modalBody}`, children: _jsx("p", { className: managementStyles.sectionText, children: "Create and configure a new workspace." }) }) }));
8
+ }
@@ -0,0 +1,26 @@
1
+ import React from 'react';
2
+ import type { ModalThemeName } from './types';
3
+ interface EditProfileModalProps {
4
+ isOpen: boolean;
5
+ theme: ModalThemeName;
6
+ onClose: () => void;
7
+ onSave: () => void;
8
+ displayName: string;
9
+ email: string;
10
+ avatarDisplayUrl: string;
11
+ accountBadgeInitial: string;
12
+ avatarInputRef: React.RefObject<HTMLInputElement | null>;
13
+ avatarAccept: string;
14
+ avatarDraftId: string | null;
15
+ saveBusy: boolean;
16
+ avatarBusy: boolean;
17
+ saveError: string | null;
18
+ saveNotice: string | null;
19
+ onDisplayNameChange: (value: string) => void;
20
+ onAvatarInputChange: (file: File | null) => void;
21
+ onStartAvatarUpload: () => void;
22
+ onDiscardUpload: () => void;
23
+ onRemovePhoto: () => void;
24
+ }
25
+ export declare function EditProfileModal({ isOpen, theme, onClose, onSave, displayName, email, avatarDisplayUrl, accountBadgeInitial, avatarInputRef, avatarAccept, avatarDraftId, saveBusy, avatarBusy, saveError, saveNotice, onDisplayNameChange, onAvatarInputChange, onStartAvatarUpload, onDiscardUpload, onRemovePhoto, }: EditProfileModalProps): import("react/jsx-runtime").JSX.Element;
26
+ export {};
@@ -0,0 +1,10 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { Modal } from '../../../ui/Modal';
3
+ import modalStyles from '../../../ui/Modal.module.css';
4
+ import accountStyles from '../../StandaloneAccount.module.css';
5
+ import authStyles from '../../AuthShell.module.css';
6
+ import managementStyles from '../../StandaloneManagement.module.css';
7
+ import styles from '../../../../Taskforce.module.css';
8
+ export function EditProfileModal({ isOpen, theme, onClose, onSave, displayName, email, avatarDisplayUrl, accountBadgeInitial, avatarInputRef, avatarAccept, avatarDraftId, saveBusy, avatarBusy, saveError, saveNotice, onDisplayNameChange, onAvatarInputChange, onStartAvatarUpload, onDiscardUpload, onRemovePhoto, }) {
9
+ return (_jsx(Modal, { isOpen: isOpen, onClose: onClose, title: "Edit Profile", size: "sm", theme: theme, draggable: true, footer: (_jsxs(_Fragment, { children: [_jsx("button", { type: "button", className: styles.secondaryButton, onClick: onClose, disabled: saveBusy || avatarBusy, children: "Cancel" }), _jsx("button", { type: "button", className: styles.primaryUpdateBtn, onClick: onSave, disabled: saveBusy || avatarBusy || !displayName.trim(), children: saveBusy ? 'Saving...' : 'Save Profile' })] })), children: _jsxs("div", { className: `${modalStyles.form} ${managementStyles.modalBody}`, children: [_jsx("p", { className: managementStyles.sectionText, children: "Update the name shown around your account and shared workspace surfaces." }), _jsxs("div", { className: accountStyles.profileAvatarEditor, children: [_jsx("div", { className: accountStyles.profileAvatarPreview, "aria-label": "Profile photo preview", children: avatarDisplayUrl ? (_jsx("img", { src: avatarDisplayUrl, alt: "", className: accountStyles.profileAvatarImage })) : (_jsx("span", { children: accountBadgeInitial })) }), _jsxs("div", { className: accountStyles.profileAvatarActions, children: [_jsx("input", { ref: avatarInputRef, type: "file", "aria-label": "Upload profile photo", accept: avatarAccept, className: accountStyles.profileAvatarInput, onChange: (event) => onAvatarInputChange(event.target.files?.[0] || null), disabled: saveBusy || avatarBusy }), _jsx("button", { type: "button", className: styles.secondaryButton, onClick: onStartAvatarUpload, disabled: saveBusy || avatarBusy, children: avatarBusy ? 'Uploading...' : 'Upload Photo' }), avatarDraftId && (_jsx("button", { type: "button", className: styles.secondaryButton, onClick: onDiscardUpload, disabled: saveBusy || avatarBusy, children: "Discard Upload" })), avatarDisplayUrl && (_jsx("button", { type: "button", className: styles.secondaryButton, onClick: onRemovePhoto, disabled: saveBusy || avatarBusy, children: "Remove Photo" }))] })] }), _jsxs("label", { className: managementStyles.inlineFieldLabel, children: ["Display name", _jsx("input", { className: styles.input, type: "text", value: displayName, onChange: (event) => onDisplayNameChange(event.target.value), placeholder: "Display name", disabled: saveBusy })] }), _jsxs("label", { className: managementStyles.inlineFieldLabel, children: ["Email", _jsx("input", { className: styles.input, type: "email", value: email, readOnly: true, disabled: true })] }), saveError && (_jsx("p", { className: `${authStyles.loginError} ${managementStyles.errorText}`, children: saveError })), saveNotice && !saveError && (_jsx("p", { className: managementStyles.errorText, children: saveNotice }))] }) }));
10
+ }
@@ -0,0 +1,9 @@
1
+ import type { ModalThemeName } from './types';
2
+ interface HelpModalProps {
3
+ isOpen: boolean;
4
+ theme: ModalThemeName;
5
+ onClose: () => void;
6
+ onOpenSettings: () => void;
7
+ }
8
+ export declare function HelpModal({ isOpen, theme, onClose, onOpenSettings, }: HelpModalProps): import("react/jsx-runtime").JSX.Element;
9
+ export {};
@@ -0,0 +1,8 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Modal } from '../../../ui/Modal';
3
+ import modalStyles from '../../../ui/Modal.module.css';
4
+ import managementStyles from '../../StandaloneManagement.module.css';
5
+ import styles from '../../../../Taskforce.module.css';
6
+ export function HelpModal({ isOpen, theme, onClose, onOpenSettings, }) {
7
+ return (_jsx(Modal, { isOpen: isOpen, onClose: onClose, title: "Quick Start", size: "md", theme: theme, draggable: true, children: _jsxs("div", { className: `${modalStyles.form} ${managementStyles.modalBody}`, children: [_jsx("p", { className: managementStyles.mutedText, children: "Use this quick guide to get productive in Taskforce dashboard mode." }), _jsxs("div", { children: [_jsx("strong", { children: "1. Capture work" }), _jsxs("p", { className: managementStyles.sectionText, children: ["Click ", _jsx("code", { children: "Add Task" }), ", write a short title, and save."] })] }), _jsxs("div", { children: [_jsx("strong", { children: "2. Plan the board" }), _jsxs("p", { className: managementStyles.sectionText, children: ["Use ", _jsx("code", { children: "Sort" }), ", ", _jsx("code", { children: "Group" }), ", and ", _jsx("code", { children: "Filters" }), " in the header."] })] }), _jsxs("div", { children: [_jsx("strong", { children: "3. Execute and stage" }), _jsxs("p", { className: managementStyles.sectionText, children: ["Move tasks through ", _jsx("code", { children: "Task" }), ", ", _jsx("code", { children: "In Progress" }), ", ", _jsx("code", { children: "Review" }), ", and ", _jsx("code", { children: "Done" }), "."] })] }), _jsxs("div", { children: [_jsx("strong", { children: "4. Archive finished work" }), _jsx("p", { className: managementStyles.sectionText, children: "Archive completed/cancelled tasks to keep active board signal high." })] }), _jsxs("div", { className: `${modalStyles.formActions} ${managementStyles.modalActionsEnd}`, children: [_jsx("button", { className: styles.cancelBtn, onClick: onOpenSettings, children: "Open Settings" }), _jsx("button", { className: styles.submitBtn, onClick: onClose, children: "Close Tutorial" })] })] }) }));
8
+ }
@@ -0,0 +1,13 @@
1
+ import type { ModalThemeName } from './types';
2
+ interface ScheduleWarningPrompt {
3
+ dueDate: string;
4
+ scheduledDate: string;
5
+ }
6
+ interface ScheduleWarningModalProps {
7
+ prompt: ScheduleWarningPrompt | null;
8
+ theme: ModalThemeName;
9
+ onClose: () => void;
10
+ onConfirm: () => void;
11
+ }
12
+ export declare function ScheduleWarningModal({ prompt, theme, onClose, onConfirm, }: ScheduleWarningModalProps): import("react/jsx-runtime").JSX.Element;
13
+ export {};
@@ -0,0 +1,6 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Modal } from '../../../ui/Modal';
3
+ import styles from '../../../../Taskforce.module.css';
4
+ export function ScheduleWarningModal({ prompt, theme, onClose, onConfirm, }) {
5
+ return (_jsx(Modal, { isOpen: !!prompt, onClose: onClose, title: "Date Warning", size: "sm", theme: theme, draggable: false, closeOnOverlayClick: false, children: _jsxs("div", { className: `${styles.form} tf-inline-stack-sm`, style: { padding: '16px', gap: '12px' }, children: [_jsx("p", { className: "tf-text-body", children: "Due date is before scheduled date." }), prompt && (_jsxs("p", { className: "tf-text-helper", children: ["Due: ", prompt.dueDate, " \u00B7 Scheduled: ", prompt.scheduledDate] })), _jsxs("div", { className: styles.formActions, children: [_jsx("button", { className: styles.cancelBtn, onClick: onClose, children: "Go Back" }), _jsx("button", { className: styles.submitBtn, onClick: onConfirm, children: "Save Anyway" })] })] }) }));
6
+ }
@@ -0,0 +1,61 @@
1
+ import React from 'react';
2
+ import type { ModalRuntimeMode, ModalThemeName } from './types';
3
+ interface SyncStatusMeta {
4
+ label: string;
5
+ color: string;
6
+ border: string;
7
+ background: string;
8
+ }
9
+ interface ReferenceMismatchSummary {
10
+ key: string;
11
+ pathLabel: string;
12
+ refsLabel?: string | null;
13
+ }
14
+ interface SyncSummaryItem {
15
+ label: string;
16
+ value: string;
17
+ }
18
+ interface SyncEventRow {
19
+ key: string;
20
+ text: string;
21
+ tone?: 'error' | 'muted' | 'default';
22
+ }
23
+ interface WorkspaceSyncDiagnostics {
24
+ lastErrorAt?: string | null;
25
+ }
26
+ interface SyncStatusModalProps {
27
+ isOpen: boolean;
28
+ theme: ModalThemeName;
29
+ currentWorkspaceLabel: string;
30
+ syncStatusMeta: SyncStatusMeta;
31
+ workspaceCloudSyncEnabled: boolean;
32
+ syncControlBusy: boolean;
33
+ canManageWorkspaceSync: boolean;
34
+ workspaceSyncSummary: string;
35
+ workspaceSyncRepairBusy: boolean;
36
+ referenceMismatchCount: number;
37
+ syncStageLabel: string;
38
+ workspaceSyncPendingChanges: string | number;
39
+ formattedLastSyncTime: string;
40
+ formattedLastPullTime: string;
41
+ formattedLastPushTime: string;
42
+ syncLastError: string;
43
+ workspaceSyncDiagnostics: WorkspaceSyncDiagnostics;
44
+ activeReferenceMismatchSummaries: ReferenceMismatchSummary[];
45
+ syncDiagnosticsSummary: SyncSummaryItem[];
46
+ syncEventRows: SyncEventRow[];
47
+ syncEventsListRef: React.RefObject<HTMLDivElement | null>;
48
+ workspaceSyncRepairQueued: boolean;
49
+ workspaceSyncBusy: boolean;
50
+ workspaceSyncCopied: boolean;
51
+ runtimeMode: ModalRuntimeMode;
52
+ isAuthenticated: boolean;
53
+ onClose: () => void;
54
+ onToggleWorkspaceSync: (enabled: boolean) => void;
55
+ onRepairSync: () => void;
56
+ onCopyReport: () => void;
57
+ onOpenLogin: () => void;
58
+ onRetrySync: () => void;
59
+ }
60
+ export declare function SyncStatusModal({ isOpen, theme, currentWorkspaceLabel, syncStatusMeta, workspaceCloudSyncEnabled, syncControlBusy, canManageWorkspaceSync, workspaceSyncSummary, workspaceSyncRepairBusy, referenceMismatchCount, syncStageLabel, workspaceSyncPendingChanges, formattedLastSyncTime, formattedLastPullTime, formattedLastPushTime, syncLastError, workspaceSyncDiagnostics, activeReferenceMismatchSummaries, syncDiagnosticsSummary, syncEventRows, syncEventsListRef, workspaceSyncRepairQueued, workspaceSyncBusy, workspaceSyncCopied, runtimeMode, isAuthenticated, onClose, onToggleWorkspaceSync, onRepairSync, onCopyReport, onOpenLogin, onRetrySync, }: SyncStatusModalProps): import("react/jsx-runtime").JSX.Element;
61
+ export {};
@@ -0,0 +1,38 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { AlertTriangle, Copy, Loader2, RefreshCw } from 'lucide-react';
3
+ import { Modal } from '../../../ui/Modal';
4
+ import modalStyles from '../../../ui/Modal.module.css';
5
+ import syncStatusStyles from '../../SyncStatusModal.module.css';
6
+ import styles from '../../../../Taskforce.module.css';
7
+ export function SyncStatusModal({ isOpen, theme, currentWorkspaceLabel, syncStatusMeta, workspaceCloudSyncEnabled, syncControlBusy, canManageWorkspaceSync, workspaceSyncSummary, workspaceSyncRepairBusy, referenceMismatchCount, syncStageLabel, workspaceSyncPendingChanges, formattedLastSyncTime, formattedLastPullTime, formattedLastPushTime, syncLastError, workspaceSyncDiagnostics, activeReferenceMismatchSummaries, syncDiagnosticsSummary, syncEventRows, syncEventsListRef, workspaceSyncRepairQueued, workspaceSyncBusy, workspaceSyncCopied, runtimeMode, isAuthenticated, onClose, onToggleWorkspaceSync, onRepairSync, onCopyReport, onOpenLogin, onRetrySync, }) {
8
+ return (_jsx(Modal, { isOpen: isOpen, onClose: onClose, title: "Sync Manager", size: "md", theme: theme, draggable: true, children: _jsxs("div", { className: `${modalStyles.form} ${syncStatusStyles.syncStatusModal}`, style: { gap: '10px' }, children: [_jsxs("div", { className: syncStatusStyles.syncStatusTop, children: [_jsx("div", { className: syncStatusStyles.syncStatusHeaderInfo, children: _jsxs("div", { className: syncStatusStyles.syncStatusMetaHeader, children: [_jsx("div", { className: syncStatusStyles.syncStatusBadge, style: {
9
+ borderColor: syncStatusMeta.border,
10
+ background: syncStatusMeta.background,
11
+ color: syncStatusMeta.color
12
+ }, children: syncStatusMeta.label }), _jsxs("span", { className: syncStatusStyles.syncStatusLabel, children: ["Workspace: ", _jsx("code", { children: currentWorkspaceLabel })] })] }) }), _jsx("label", { className: syncStatusStyles.syncToggle, children: _jsxs("span", { className: [
13
+ syncStatusStyles.syncToggleControl,
14
+ workspaceCloudSyncEnabled ? syncStatusStyles.syncToggleControlEnabled : syncStatusStyles.syncToggleControlDisabled,
15
+ syncControlBusy ? syncStatusStyles.syncToggleControlBusy : '',
16
+ !canManageWorkspaceSync ? syncStatusStyles.syncToggleControlBlocked : ''
17
+ ].filter(Boolean).join(' '), children: [_jsx("input", { className: syncStatusStyles.syncToggleInput, type: "checkbox", role: "switch", "aria-label": "Enable Sync", checked: workspaceCloudSyncEnabled, disabled: !canManageWorkspaceSync || syncControlBusy, onChange: (event) => onToggleWorkspaceSync(event.target.checked) }), _jsx("span", { className: `${syncStatusStyles.syncToggleState} ${syncStatusStyles.syncToggleStateOn}`, children: "On" }), _jsx("span", { className: `${syncStatusStyles.syncToggleState} ${syncStatusStyles.syncToggleStateOff}`, children: "Off" }), _jsx("span", { className: syncStatusStyles.syncToggleThumb })] }) })] }), _jsxs("div", { className: syncStatusStyles.syncStatusSummaryCompact, children: [_jsx("span", { className: syncStatusStyles.syncStatusLabel, children: "Health" }), _jsx("span", { className: syncStatusStyles.syncStatusSummaryValue, children: workspaceSyncSummary })] }), workspaceSyncRepairBusy && (_jsxs("div", { className: syncStatusStyles.syncStatusRepairBanner, children: [_jsxs("div", { className: syncStatusStyles.syncStatusRepairHeader, children: [_jsxs("div", { className: syncStatusStyles.syncStatusRepairBadge, children: [_jsx(Loader2, { size: 13, className: styles.spinner }), _jsx("span", { children: "Repair In Progress" })] }), _jsx("span", { className: syncStatusStyles.syncStatusRepairMeta, children: "Advanced recovery mode" })] }), _jsx("span", { className: syncStatusStyles.syncStatusRepairText, children: referenceMismatchCount > 0
18
+ ? `Taskforce is clearing the saved sync cursor and reconciling workspace state from the cloud again, including ${referenceMismatchCount} detected reference mismatch${referenceMismatchCount === 1 ? '' : 'es'}. The panel may stay busy for a while during large repairs.`
19
+ : 'Taskforce is clearing the saved sync cursor and reconciling workspace state from the cloud again. The panel may stay busy for a while during large repairs.' })] })), _jsx("div", { className: syncStatusStyles.syncStatusSectionHeader, children: _jsx("span", { className: syncStatusStyles.syncStatusLabel, children: "Current Session" }) }), _jsxs("div", { className: syncStatusStyles.syncStatusCompactGrid, children: [_jsxs("div", { className: syncStatusStyles.syncStatusCompactItem, children: [_jsx("span", { className: syncStatusStyles.syncStatusLabel, children: "Stage" }), _jsx("span", { className: syncStatusStyles.syncStatusValue, children: syncStageLabel })] }), _jsxs("div", { className: syncStatusStyles.syncStatusCompactItem, children: [_jsx("span", { className: syncStatusStyles.syncStatusLabel, children: "Local Changes" }), _jsx("span", { className: syncStatusStyles.syncStatusValue, children: workspaceSyncPendingChanges })] }), _jsxs("div", { className: syncStatusStyles.syncStatusCompactItem, children: [_jsx("span", { className: syncStatusStyles.syncStatusLabel, children: "Last Successful" }), _jsx("span", { className: syncStatusStyles.syncStatusValue, children: formattedLastSyncTime })] })] }), _jsx("div", { className: syncStatusStyles.syncStatusSectionHeader, children: _jsx("span", { className: syncStatusStyles.syncStatusLabel, children: "Activity" }) }), _jsxs("div", { className: syncStatusStyles.syncStatusCompactGrid, children: [_jsxs("div", { className: syncStatusStyles.syncStatusCompactItem, children: [_jsx("span", { className: syncStatusStyles.syncStatusLabel, children: "Last Pull" }), _jsx("span", { className: syncStatusStyles.syncStatusValue, children: formattedLastPullTime })] }), _jsxs("div", { className: syncStatusStyles.syncStatusCompactItem, children: [_jsx("span", { className: syncStatusStyles.syncStatusLabel, children: "Last Push" }), _jsx("span", { className: syncStatusStyles.syncStatusValue, children: formattedLastPushTime })] })] }), syncLastError !== 'None' && (_jsxs("div", { className: syncStatusStyles.syncStatusPanel, style: { background: 'rgba(239, 68, 68, 0.05)', borderColor: 'rgba(239, 68, 68, 0.2)' }, children: [_jsxs("div", { className: syncStatusStyles.syncStatusSectionHeader, style: { marginTop: 0 }, children: [_jsx(AlertTriangle, { size: 12, color: "#fda4af" }), _jsxs("span", { className: syncStatusStyles.syncStatusLabel, style: { color: '#fda4af' }, children: ["Last Error (", workspaceSyncDiagnostics.lastErrorAt ? new Date(workspaceSyncDiagnostics.lastErrorAt).toLocaleTimeString() : 'Recent', ")"] })] }), _jsx("span", { className: `${syncStatusStyles.syncStatusValue} ${syncStatusStyles.syncStatusValueError}`, children: syncLastError })] })), referenceMismatchCount > 0 && (_jsxs("div", { className: syncStatusStyles.syncStatusPanel, style: { background: 'rgba(250, 204, 21, 0.08)', borderColor: 'rgba(250, 204, 21, 0.22)' }, children: [_jsxs("div", { className: syncStatusStyles.syncStatusPanelHeader, children: [_jsx("span", { className: syncStatusStyles.syncStatusLabel, style: { color: '#fde68a' }, children: "Identifier Integrity" }), _jsxs("span", { className: syncStatusStyles.syncStatusPanelMeta, children: [referenceMismatchCount, " mismatch", referenceMismatchCount === 1 ? '' : 'es', " detected"] })] }), _jsx("span", { className: syncStatusStyles.syncStatusValue, children: "Task, document, or image reference numbers disagreed during normal sync. Cloud-backed sync preserves the incoming cloud reference and renumbers the displaced local item to the next available reference." }), activeReferenceMismatchSummaries.length > 0 && (_jsx("div", { style: { marginTop: '10px', display: 'flex', flexDirection: 'column', gap: '8px' }, children: activeReferenceMismatchSummaries.map((item) => (_jsxs("div", { style: {
20
+ display: 'flex',
21
+ flexDirection: 'column',
22
+ gap: '2px',
23
+ padding: '8px 10px',
24
+ borderRadius: '8px',
25
+ background: 'rgba(15, 23, 42, 0.18)',
26
+ border: '1px solid rgba(250, 204, 21, 0.16)'
27
+ }, children: [_jsx("span", { className: syncStatusStyles.syncStatusValue, style: { fontSize: '12px', wordBreak: 'break-all' }, children: item.pathLabel }), item.refsLabel ? (_jsx("span", { className: syncStatusStyles.syncStatusPanelMeta, children: item.refsLabel })) : null] }, item.key))) }))] })), _jsxs("div", { className: syncStatusStyles.syncStatusPanel, children: [_jsxs("div", { className: syncStatusStyles.syncStatusPanelHeader, children: [_jsx("span", { className: syncStatusStyles.syncStatusLabel, children: "Local Sync Diagnostics" }), _jsx("span", { className: syncStatusStyles.syncStatusPanelMeta, style: { fontSize: '10px' }, children: "AI profiles & metadata" })] }), _jsx("div", { className: syncStatusStyles.syncStatusCompactGrid, style: { gridTemplateColumns: 'repeat(2, 1fr)' }, children: syncDiagnosticsSummary.map((item) => (_jsxs("div", { style: { display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', gap: '8px' }, children: [_jsx("span", { className: syncStatusStyles.syncStatusLabel, style: { fontSize: '9px', textTransform: 'capitalize' }, children: item.label }), _jsx("span", { className: syncStatusStyles.syncStatusValue, style: { fontSize: '11px', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }, children: item.value })] }, item.label))) })] }), _jsxs("div", { className: syncStatusStyles.syncStatusPanel, children: [_jsx("div", { className: syncStatusStyles.syncStatusPanelHeader, children: _jsx("span", { className: syncStatusStyles.syncStatusLabel, children: "Recent Events" }) }), _jsx("div", { ref: syncEventsListRef, className: syncStatusStyles.syncStatusEventsList, style: { height: '120px' }, children: syncEventRows.map((row) => (_jsx("div", { className: [
28
+ syncStatusStyles.syncStatusEventItem,
29
+ row.tone === 'error' ? syncStatusStyles.syncStatusEventItemError : '',
30
+ row.tone === 'muted' ? syncStatusStyles.syncStatusEventItemMuted : ''
31
+ ].filter(Boolean).join(' '), style: { padding: '6px 8px', borderRadius: '6px' }, children: _jsx("span", { className: syncStatusStyles.syncStatusEventLine, style: { fontSize: '11px' }, children: row.text }) }, row.key))) })] }), _jsxs("div", { className: `${modalStyles.formActions} ${syncStatusStyles.syncStatusActions}`, children: [_jsxs("div", { className: syncStatusStyles.syncStatusSecondaryActions, children: [_jsx("button", { className: styles.cancelBtn, onClick: onRepairSync, disabled: workspaceSyncRepairBusy, title: workspaceSyncRepairBusy
32
+ ? 'Repair is running now.'
33
+ : workspaceSyncRepairQueued
34
+ ? 'Repair is queued and will start when the current sync finishes.'
35
+ : workspaceSyncBusy
36
+ ? 'Queue a repair to start automatically when the current sync finishes.'
37
+ : 'Advanced recovery: clear the saved pull cursor and re-fetch cloud state from the beginning.', children: workspaceSyncRepairBusy ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { size: 14, className: styles.spinner }), _jsx("span", { style: { marginLeft: '6px' }, children: "Repairing..." })] })) : workspaceSyncRepairQueued ? ('Repair Queued') : ('Repair') }), _jsxs("button", { className: styles.cancelBtn, onClick: onCopyReport, title: "Copy the current sync manager report for support or AI troubleshooting.", children: [_jsx(Copy, { size: 14 }), _jsx("span", { style: { marginLeft: '6px' }, children: workspaceSyncCopied ? 'Copied' : 'Copy Report' })] })] }), _jsxs("div", { className: syncStatusStyles.syncStatusPrimaryActions, children: [_jsx("div", { className: syncStatusStyles.syncStatusPrimaryActionSlot, children: runtimeMode === 'local' && !isAuthenticated ? (_jsx("button", { className: styles.submitBtn, onClick: onOpenLogin, children: "Sign In / Register" })) : (_jsx("div", { className: syncStatusStyles.syncStatusActionPlaceholder, "aria-hidden": "true" })) }), _jsx("div", { className: syncStatusStyles.syncStatusPrimaryActionSlot, children: _jsx("button", { className: styles.submitBtn, onClick: onRetrySync, disabled: workspaceSyncBusy, children: workspaceSyncBusy ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { size: 14, className: styles.spinner }), "Syncing..."] })) : (_jsxs(_Fragment, { children: [_jsx(RefreshCw, { size: 14 }), "Sync"] })) }) })] })] })] }) }));
38
+ }
@@ -0,0 +1,64 @@
1
+ import type { ModalThemeName } from './types';
2
+ type TeamManagementTab = 'members' | 'invites' | 'audit';
3
+ type TeamPlanMode = 'unknown' | 'personal' | 'team';
4
+ type PermissionMode = 'read-only' | 'read-write';
5
+ interface TeamWorkspaceUser {
6
+ userId: string;
7
+ email: string;
8
+ displayName?: string | null;
9
+ role: string;
10
+ permissionMode: PermissionMode;
11
+ status: string;
12
+ disabled: boolean;
13
+ }
14
+ interface TeamAuditEvent {
15
+ id: string;
16
+ action: string;
17
+ actorUserId: string;
18
+ actorRole: string;
19
+ createdAt: string;
20
+ }
21
+ interface TeamInviteSummary {
22
+ userId: string;
23
+ email: string;
24
+ displayName?: string | null;
25
+ role: string;
26
+ }
27
+ interface TeamManagementModalProps {
28
+ isOpen: boolean;
29
+ theme: ModalThemeName;
30
+ teamPlanMode: TeamPlanMode;
31
+ teamMgmtError: string | null;
32
+ teamManagementTab: TeamManagementTab;
33
+ teamUsersLoading: boolean;
34
+ teamUsers: TeamWorkspaceUser[];
35
+ teamActionBusyUserId: string | null;
36
+ teamInviteFeedback: string | null;
37
+ teamInviteEmail: string;
38
+ teamInviteRole: 'admin' | 'member';
39
+ teamInvitePermissionMode: PermissionMode;
40
+ teamInviteBusy: boolean;
41
+ pendingInvites: TeamInviteSummary[];
42
+ teamAuditLoading: boolean;
43
+ teamAuditEvents: TeamAuditEvent[];
44
+ teamAuditPage: number;
45
+ teamAuditPages: number;
46
+ teamAuditHasMore: boolean;
47
+ onClose: () => void;
48
+ onOpenMembersTab: () => void;
49
+ onOpenInvitesTab: () => void;
50
+ onOpenAuditTab: () => void;
51
+ onMemberRoleChange: (userId: string, role: string) => void;
52
+ onMemberPermissionChange: (userId: string, mode: string) => void;
53
+ onToggleMemberDisabled: (userId: string, disabled: boolean) => void;
54
+ onRevokeInvite: (userId: string) => void;
55
+ onRemoveMember: (userId: string) => void;
56
+ onInviteEmailChange: (value: string) => void;
57
+ onInviteRoleChange: (value: 'admin' | 'member') => void;
58
+ onInvitePermissionModeChange: (value: PermissionMode) => void;
59
+ onSubmitInvite: () => void;
60
+ onLoadAuditPrevious: () => void;
61
+ onLoadAuditNext: () => void;
62
+ }
63
+ export declare function TeamManagementModal({ isOpen, theme, teamPlanMode, teamMgmtError, teamManagementTab, teamUsersLoading, teamUsers, teamActionBusyUserId, teamInviteFeedback, teamInviteEmail, teamInviteRole, teamInvitePermissionMode, teamInviteBusy, pendingInvites, teamAuditLoading, teamAuditEvents, teamAuditPage, teamAuditPages, teamAuditHasMore, onClose, onOpenMembersTab, onOpenInvitesTab, onOpenAuditTab, onMemberRoleChange, onMemberPermissionChange, onToggleMemberDisabled, onRevokeInvite, onRemoveMember, onInviteEmailChange, onInviteRoleChange, onInvitePermissionModeChange, onSubmitInvite, onLoadAuditPrevious, onLoadAuditNext, }: TeamManagementModalProps): import("react/jsx-runtime").JSX.Element;
64
+ export {};
@@ -0,0 +1,10 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Modal } from '../../../ui/Modal';
3
+ import modalStyles from '../../../ui/Modal.module.css';
4
+ import accountStyles from '../../StandaloneAccount.module.css';
5
+ import authStyles from '../../AuthShell.module.css';
6
+ import managementStyles from '../../StandaloneManagement.module.css';
7
+ import styles from '../../../../Taskforce.module.css';
8
+ export function TeamManagementModal({ isOpen, theme, teamPlanMode, teamMgmtError, teamManagementTab, teamUsersLoading, teamUsers, teamActionBusyUserId, teamInviteFeedback, teamInviteEmail, teamInviteRole, teamInvitePermissionMode, teamInviteBusy, pendingInvites, teamAuditLoading, teamAuditEvents, teamAuditPage, teamAuditPages, teamAuditHasMore, onClose, onOpenMembersTab, onOpenInvitesTab, onOpenAuditTab, onMemberRoleChange, onMemberPermissionChange, onToggleMemberDisabled, onRevokeInvite, onRemoveMember, onInviteEmailChange, onInviteRoleChange, onInvitePermissionModeChange, onSubmitInvite, onLoadAuditPrevious, onLoadAuditNext, }) {
9
+ return (_jsx(Modal, { isOpen: isOpen, onClose: onClose, title: "Team Management", size: "md", theme: theme, draggable: true, children: _jsxs("div", { className: `${modalStyles.form} ${managementStyles.modalBody}`, children: [teamPlanMode === 'personal' && (_jsx("p", { className: authStyles.loginError, children: "Team management is only available in TEAM accounts." })), teamMgmtError && (_jsx("p", { className: authStyles.loginError, children: teamMgmtError })), _jsxs("div", { className: `${styles.settingsTabs} tf-scrollbar tf-scrollbar--track-transparent tf-scrollbar--compact`, children: [_jsx("button", { className: `${styles.settingsTabBtn} ${teamManagementTab === 'members' ? styles.settingsTabBtnActive : ''}`, onClick: onOpenMembersTab, children: "Members" }), _jsx("button", { className: `${styles.settingsTabBtn} ${teamManagementTab === 'invites' ? styles.settingsTabBtnActive : ''}`, onClick: onOpenInvitesTab, children: "Invites" }), _jsx("button", { className: `${styles.settingsTabBtn} ${teamManagementTab === 'audit' ? styles.settingsTabBtnActive : ''}`, onClick: onOpenAuditTab, children: "Audit Log" })] }), teamManagementTab === 'members' && (_jsxs("div", { className: accountStyles.accountHubCard, children: [teamUsersLoading && _jsx("p", { className: managementStyles.mutedText, children: "Loading members..." }), !teamUsersLoading && teamUsers.length === 0 && (_jsx("p", { className: managementStyles.mutedText, children: "No users found." })), !teamUsersLoading && teamUsers.map((member) => (_jsxs("div", { className: managementStyles.memberRow, children: [_jsx("div", { className: managementStyles.memberTitle, children: member.displayName || member.email }), _jsxs("div", { className: accountStyles.accountMenuMeta, children: [member.email, " \u00B7 ", member.status, member.disabled ? ' · deactivated' : ''] }), _jsxs("div", { className: managementStyles.memberActions, children: [_jsxs("select", { className: `${styles.input} ${managementStyles.selectRole}`, value: member.role, onChange: (event) => onMemberRoleChange(member.userId, event.target.value), disabled: teamActionBusyUserId === member.userId, children: [_jsx("option", { value: "owner", children: "Owner" }), _jsx("option", { value: "admin", children: "Admin" }), _jsx("option", { value: "member", children: "Member" })] }), member.role === 'member' && (_jsxs("select", { className: `${styles.input} ${managementStyles.selectPermission}`, value: member.permissionMode, onChange: (event) => onMemberPermissionChange(member.userId, event.target.value), disabled: teamActionBusyUserId === member.userId, children: [_jsx("option", { value: "read-write", children: "Read / Write" }), _jsx("option", { value: "read-only", children: "Read Only" })] })), _jsx("button", { className: styles.cancelBtn, disabled: teamActionBusyUserId === member.userId, onClick: () => onToggleMemberDisabled(member.userId, !member.disabled), children: member.disabled ? 'Reactivate' : 'Deactivate' }), member.status === 'invited' && (_jsx("button", { className: styles.cancelBtn, disabled: teamActionBusyUserId === member.userId, onClick: () => onRevokeInvite(member.userId), children: "Revoke Invite" })), _jsx("button", { className: styles.cancelBtn, disabled: teamActionBusyUserId === member.userId, onClick: () => onRemoveMember(member.userId), children: "Remove" })] })] }, member.userId)))] })), teamManagementTab === 'invites' && (_jsxs("div", { className: accountStyles.accountHubCard, children: [teamInviteFeedback && (_jsx("p", { className: managementStyles.sectionText, children: teamInviteFeedback })), _jsxs("div", { className: styles.pathInputGroup, children: [_jsx("label", { className: `${styles.label} ${managementStyles.labelFixed}`, children: "Email" }), _jsx("input", { className: styles.input, value: teamInviteEmail, onChange: (event) => onInviteEmailChange(event.target.value), placeholder: "name@company.com" })] }), _jsxs("div", { className: styles.pathInputGroup, children: [_jsx("label", { className: `${styles.label} ${managementStyles.labelFixed}`, children: "Role" }), _jsxs("select", { className: `${styles.input} ${managementStyles.selectRole}`, value: teamInviteRole, onChange: (event) => onInviteRoleChange(event.target.value === 'admin' ? 'admin' : 'member'), children: [_jsx("option", { value: "member", children: "Member" }), _jsx("option", { value: "admin", children: "Admin" })] }), teamInviteRole === 'member' && (_jsxs("select", { className: `${styles.input} ${managementStyles.selectPermission}`, value: teamInvitePermissionMode, onChange: (event) => onInvitePermissionModeChange(event.target.value === 'read-only' ? 'read-only' : 'read-write'), children: [_jsx("option", { value: "read-write", children: "Read / Write" }), _jsx("option", { value: "read-only", children: "Read Only" })] })), _jsx("button", { className: styles.submitBtn, disabled: teamInviteBusy || !teamInviteEmail.trim() || teamPlanMode !== 'team', onClick: onSubmitInvite, children: teamInviteBusy ? 'Sending...' : 'Send Invite' })] }), _jsxs("div", { className: managementStyles.inviteRow, children: [_jsx("div", { className: managementStyles.listHeading, children: "Pending Invites" }), pendingInvites.length === 0 && (_jsx("p", { className: managementStyles.mutedText, children: "No pending invites." })), pendingInvites.map((invite) => (_jsxs("div", { className: managementStyles.memberRow, children: [_jsx("div", { className: managementStyles.memberTitle, children: invite.displayName || invite.email }), _jsxs("div", { className: accountStyles.accountMenuMeta, children: [invite.email, " \u00B7 ", invite.role] })] }, invite.userId)))] })] })), teamManagementTab === 'audit' && (_jsxs("div", { className: accountStyles.accountHubCard, children: [teamAuditLoading && _jsx("p", { className: managementStyles.mutedText, children: "Loading audit entries..." }), !teamAuditLoading && teamAuditEvents.length === 0 && (_jsx("p", { className: managementStyles.mutedText, children: "No audit events for this workspace." })), !teamAuditLoading && (_jsx("div", { className: managementStyles.auditList, children: teamAuditEvents.map((event) => (_jsxs("div", { className: managementStyles.memberRow, children: [_jsx("div", { className: accountStyles.accountMenuMeta, children: new Date(event.createdAt).toLocaleString() }), _jsx("div", { className: managementStyles.memberTitle, children: event.action }), _jsxs("div", { className: accountStyles.accountMenuMeta, children: [event.actorUserId, " (", event.actorRole, ")"] })] }, event.id))) })), _jsxs("div", { className: managementStyles.auditPager, children: [_jsx("button", { className: styles.cancelBtn, disabled: teamAuditLoading || teamAuditPage === 0, onClick: onLoadAuditPrevious, children: "Previous" }), _jsxs("span", { className: accountStyles.accountMenuMeta, children: ["Page ", teamAuditPage + 1, " of ", teamAuditPages] }), _jsx("button", { className: styles.cancelBtn, disabled: teamAuditLoading || !teamAuditHasMore || teamAuditPage + 1 >= teamAuditPages, onClick: onLoadAuditNext, children: "Next" })] })] })), _jsx("div", { className: `${modalStyles.formActions} ${managementStyles.modalActionsEnd}`, children: _jsx("button", { className: styles.cancelBtn, onClick: onClose, children: "Close" }) })] }) }));
10
+ }
@@ -0,0 +1,2 @@
1
+ export type ModalThemeName = 'light' | 'dawn' | 'dark' | 'midnight';
2
+ export type ModalRuntimeMode = 'cloud' | 'local';
@@ -0,0 +1 @@
1
+ export {};
@@ -3,4 +3,4 @@ export type TeamWorkspaceRole = 'owner' | 'admin' | 'member' | 'read-only';
3
3
  export declare function normalizeTeamWorkspaceRole(value: unknown): TeamWorkspaceRole | null;
4
4
  export declare function canManageTeamWorkspace(canManageCloudWorkspaces: boolean, currentWorkspaceRole: TeamWorkspaceRole | null): boolean;
5
5
  export declare function resolveTeamPlanModeFromEntitlements(payload: unknown): Exclude<TeamPlanMode, 'unknown'>;
6
- export declare function canOpenTeamManagement(canManageCloudWorkspaces: boolean, currentWorkspaceRole: TeamWorkspaceRole | null, _teamPlanMode: TeamPlanMode): boolean;
6
+ export declare function canOpenTeamManagement(canManageCloudWorkspaces: boolean, currentWorkspaceRole: TeamWorkspaceRole | null, teamPlanMode: TeamPlanMode): boolean;
@@ -13,6 +13,6 @@ export function resolveTeamPlanModeFromEntitlements(payload) {
13
13
  const stateAllows = state === 'active' || state === 'trialing' || state === 'grace';
14
14
  return teamAllowed && stateAllows ? 'team' : 'personal';
15
15
  }
16
- export function canOpenTeamManagement(canManageCloudWorkspaces, currentWorkspaceRole, _teamPlanMode) {
17
- return canManageTeamWorkspace(canManageCloudWorkspaces, currentWorkspaceRole);
16
+ export function canOpenTeamManagement(canManageCloudWorkspaces, currentWorkspaceRole, teamPlanMode) {
17
+ return canManageTeamWorkspace(canManageCloudWorkspaces, currentWorkspaceRole) && teamPlanMode === 'team';
18
18
  }
@@ -33,11 +33,11 @@ describe('teamManagementAccess', () => {
33
33
  }
34
34
  })).toBe('personal');
35
35
  });
36
- it('opens Team Management whenever the workspace admin role is present', () => {
36
+ it('opens Team Management only when the workspace admin role and team entitlement are both present', () => {
37
37
  expect(canOpenTeamManagement(true, 'owner', 'team')).toBe(true);
38
38
  expect(canOpenTeamManagement(true, 'admin', 'team')).toBe(true);
39
- expect(canOpenTeamManagement(true, 'owner', 'personal')).toBe(true);
40
- expect(canOpenTeamManagement(true, 'owner', 'unknown')).toBe(true);
39
+ expect(canOpenTeamManagement(true, 'owner', 'personal')).toBe(false);
40
+ expect(canOpenTeamManagement(true, 'owner', 'unknown')).toBe(false);
41
41
  expect(canOpenTeamManagement(true, 'member', 'team')).toBe(false);
42
42
  expect(canOpenTeamManagement(false, 'owner', 'team')).toBe(false);
43
43
  });
@@ -48,7 +48,7 @@ export function resolveDeploymentConfig(env, options = {}) {
48
48
  const cloudMcpBaseUrl = firstNonEmpty(explicitCloudMcpBaseUrl, canonicalCloudMcpBaseUrl, cloudBaseUrl);
49
49
  const baseUrl = firstNonEmpty(requestBaseUrl, explicitBaseUrl, explicitApiBaseUrl, explicitCloudAuthBaseUrl, cloudBaseUrl);
50
50
  const apiBaseUrl = firstNonEmpty(requestBaseUrl, explicitApiBaseUrl, explicitBaseUrl, cloudBaseUrl);
51
- const cloudAuthBaseUrl = firstNonEmpty(requestBaseUrl, explicitCloudAuthBaseUrl, explicitBaseUrl, explicitApiBaseUrl, cloudBaseUrl);
51
+ const cloudAuthBaseUrl = firstNonEmpty(explicitCloudAuthBaseUrl, explicitBaseUrl, explicitApiBaseUrl, cloudBaseUrl);
52
52
  const wsBaseUrl = runtimeMode === 'local'
53
53
  ? firstNonEmpty(explicitWsBaseUrl, requestBaseUrl, explicitApiBaseUrl, explicitBaseUrl, explicitCloudProxyBaseUrl, explicitCloudAuthBaseUrl, canonicalCloudBaseUrl, cloudBaseUrl)
54
54
  : firstNonEmpty(requestBaseUrl, explicitWsBaseUrl, apiBaseUrl, baseUrl, cloudBaseUrl);
@@ -61,7 +61,7 @@ export function resolveDeploymentConfig(env, options = {}) {
61
61
  apiBaseUrl,
62
62
  cloudAuthBaseUrl,
63
63
  wsBaseUrl,
64
- cloudAuthViaLocalProxy: Boolean(explicitCloudProxyBaseUrl),
64
+ cloudAuthViaLocalProxy: runtimeMode === 'local' || Boolean(explicitCloudProxyBaseUrl),
65
65
  warnings
66
66
  };
67
67
  }
@@ -21,7 +21,7 @@ describe('config/deployment', () => {
21
21
  expect(deployment.cloudAuthBaseUrl).toBe('https://staging-app.taskforcehq.ai');
22
22
  expect(deployment.wsBaseUrl).toBe('https://staging-app.taskforcehq.ai');
23
23
  });
24
- it('keeps request-origin precedence for base/api urls and local-runtime websockets', () => {
24
+ it('keeps request-origin precedence for base/api urls and local-runtime websockets while preserving cloud auth origin', () => {
25
25
  const deployment = resolveDeploymentConfig({
26
26
  TASKFORCE_RUNTIME_MODE: 'local',
27
27
  TASKFORCE_CLOUD_ENVIRONMENT: 'production',
@@ -31,9 +31,10 @@ describe('config/deployment', () => {
31
31
  });
32
32
  expect(deployment.baseUrl).toBe('http://127.0.0.1:5174');
33
33
  expect(deployment.apiBaseUrl).toBe('http://127.0.0.1:5174');
34
- expect(deployment.cloudAuthBaseUrl).toBe('http://127.0.0.1:5174');
34
+ expect(deployment.cloudAuthBaseUrl).toBe('https://app.taskforcehq.ai');
35
35
  expect(deployment.wsBaseUrl).toBe('http://127.0.0.1:5174');
36
36
  expect(deployment.cloudMcpBaseUrl).toBe('https://mcp.taskforcehq.ai');
37
+ expect(deployment.cloudAuthViaLocalProxy).toBe(true);
37
38
  });
38
39
  it('keeps local-runtime websockets on the request origin even when cloud proxying is configured', () => {
39
40
  const deployment = resolveDeploymentConfig({
@@ -45,8 +46,9 @@ describe('config/deployment', () => {
45
46
  });
46
47
  expect(deployment.baseUrl).toBe('http://127.0.0.1:5174');
47
48
  expect(deployment.apiBaseUrl).toBe('http://127.0.0.1:5174');
48
- expect(deployment.cloudAuthBaseUrl).toBe('http://127.0.0.1:5174');
49
+ expect(deployment.cloudAuthBaseUrl).toBe('https://staging-app.taskforcehq.ai');
49
50
  expect(deployment.wsBaseUrl).toBe('http://127.0.0.1:5174');
51
+ expect(deployment.cloudAuthViaLocalProxy).toBe(true);
50
52
  });
51
53
  it('still honors an explicit websocket override in local runtime', () => {
52
54
  const deployment = resolveDeploymentConfig({