@taskforcehq/taskforce 0.3.302 → 0.3.303

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 (77) hide show
  1. package/dist/Taskforce.module.css +11 -0
  2. package/dist/TaskforceCore.js +59 -9
  3. package/dist/TaskforceCore.test.js +717 -14
  4. package/dist/components/task/TaskKanban.test.js +17 -0
  5. package/dist/components/views/PlansPage.d.ts +3 -1
  6. package/dist/components/views/PlansPage.js +110 -51
  7. package/dist/components/views/PlansPage.test.d.ts +1 -0
  8. package/dist/components/views/PlansPage.test.js +100 -0
  9. package/dist/components/views/StandaloneLayout.d.ts +1 -0
  10. package/dist/components/views/StandaloneLayout.js +106 -151
  11. package/dist/components/views/standalone/modals/AccountHubModal.d.ts +8 -3
  12. package/dist/components/views/standalone/modals/AccountHubModal.js +16 -2
  13. package/dist/components/views/standalone/modals/AccountHubModal.test.d.ts +1 -0
  14. package/dist/components/views/standalone/modals/AccountHubModal.test.js +26 -0
  15. package/dist/core/EntitlementsPolicy.test.js +2 -2
  16. package/dist/core/GlobalSettingsService.js +78 -18
  17. package/dist/core/PlanEntitlementService.d.ts +29 -5
  18. package/dist/core/PlanEntitlementService.js +297 -219
  19. package/dist/core/SignupMonetizationSettings.test.js +173 -49
  20. package/dist/core/SystemAdmin.test.js +186 -96
  21. package/dist/core/Taskforce.d.ts +51 -10
  22. package/dist/core/Taskforce.js +218 -49
  23. package/dist/core/Taskforce.mcpAuth.test.js +0 -2
  24. package/dist/core/UserProfileAvatarDrafts.test.js +12 -3
  25. package/dist/core/types.d.ts +11 -12
  26. package/dist/hooks/useSyncOrchestrator.js +17 -3
  27. package/dist/hooks/useSyncOrchestrator.retry-closure.test.js +97 -5
  28. package/dist/hooks/useTaskforce.d.ts +3 -0
  29. package/dist/hooks/useTaskforce.js +9 -4
  30. package/dist/mcp/runtime.js +90 -29
  31. package/dist/mcp/runtime.test.js +272 -81
  32. package/dist/migrations/billingSchemaParity.test.js +3 -0
  33. package/dist/migrations/taskSchemaMigrations.js +15 -0
  34. package/dist/server/index.cookieProxy.test.js +1 -0
  35. package/dist/server/index.js +2 -0
  36. package/dist/server/index.test.js +2 -1
  37. package/dist/server/mockStripe.d.ts +1 -0
  38. package/dist/server/mockStripe.js +2 -0
  39. package/dist/server/routes/admin.js +80 -23
  40. package/dist/server/routes/auth.d.ts +1 -0
  41. package/dist/server/routes/auth.js +194 -53
  42. package/dist/server/routes/billing.js +506 -23
  43. package/dist/server/routes/billing.test.js +937 -45
  44. package/dist/server/routes/shared.js +5 -3
  45. package/dist/server/routes/sync.integration.test.js +160 -0
  46. package/dist/server/routes/sync.js +15 -5
  47. package/dist/server/routes.js +3 -3
  48. package/dist/server/routes.test.js +287 -76
  49. package/dist/sync/cloudSyncApi.d.ts +1 -0
  50. package/dist/sync/contentSyncState.d.ts +10 -0
  51. package/dist/sync/contentSyncState.js +277 -16
  52. package/dist/sync/syncService.d.ts +1 -0
  53. package/dist/sync/syncService.js +21 -8
  54. package/dist/sync/workspacePullFeed.d.ts +10 -0
  55. package/dist/sync/workspacePullFeed.js +111 -63
  56. package/dist/sync/workspacePullFeed.test.js +112 -0
  57. package/dist/ui/assets/{AgentsModule-JG5jc3he.js → AgentsModule-CpsTmrIW.js} +1 -1
  58. package/dist/ui/assets/{AnnotatedAttachmentWorkspace-Dvi02Y7q.js → AnnotatedAttachmentWorkspace-C_TmXZwA.js} +1 -1
  59. package/dist/ui/assets/{DocumentWorkspace-iys3zfca.js → DocumentWorkspace-C3GyzrWm.js} +1 -1
  60. package/dist/ui/assets/{InitiativesModule-DJvLJI3A.js → InitiativesModule-4-Rh2K2n.js} +1 -1
  61. package/dist/ui/assets/PlansPage-BP7AYOqr.js +1 -0
  62. package/dist/ui/assets/{TaskSettings-Bqd4cCzW.js → TaskSettings-BOC5F6Ag.js} +1 -1
  63. package/dist/ui/assets/{WorkflowsModule-BguHeQaV.js → WorkflowsModule-CGk9s3NJ.js} +1 -1
  64. package/dist/ui/assets/index-CLIMgR6g.js +6 -0
  65. package/dist/ui/assets/index-DneETxcu.css +1 -0
  66. package/dist/ui/assets/{vendor-icons-C8EED3Oh.js → vendor-icons-pWocEipT.js} +1 -1
  67. package/dist/ui/index.html +3 -3
  68. package/dist/utils/accountProfileSummaryCache.d.ts +31 -0
  69. package/dist/utils/accountProfileSummaryCache.js +97 -0
  70. package/dist/utils/accountProfileSummaryCache.test.d.ts +1 -0
  71. package/dist/utils/accountProfileSummaryCache.test.js +63 -0
  72. package/package.json +3 -3
  73. package/dist/ui/assets/PlansPage-BMPlpFpw.js +0 -1
  74. package/dist/ui/assets/index-MXeWoebC.css +0 -1
  75. package/dist/ui/assets/index-YJ4k5QNa.js +0 -6
  76. package/dist/utils/billingStatusCache.d.ts +0 -21
  77. package/dist/utils/billingStatusCache.js +0 -70
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
2
2
  import React, { useMemo, useCallback, useState, useEffect, useRef, useLayoutEffect, Suspense } from 'react';
3
3
  import { createPortal } from 'react-dom';
4
4
  import { useLocation, useNavigate } from 'react-router-dom';
5
- import { Settings, Plus, Filter, Layers, User, Maximize2, Minimize2, Check, ChevronLeft, ArrowUp, ArrowDown, ArrowUpDown, AlertTriangle, Save, Loader2, Folder, FileText, LogOut, NotebookTabs, ClipboardCheck, Cloud, CloudOff, Users, ChevronsUp, ChevronsDown, EyeOff, Zap, HelpCircle, Bot, Image, Workflow } from 'lucide-react';
5
+ import { Settings, Plus, Filter, Layers, User, Maximize2, Minimize2, Check, ChevronLeft, ArrowUp, ArrowDown, ArrowUpDown, AlertTriangle, Save, Loader2, Folder, FileText, LogOut, NotebookTabs, ClipboardCheck, Cloud, CloudOff, Users, ClockAlert, ChevronsUp, ChevronsDown, EyeOff, Zap, HelpCircle, Bot, Image, Workflow } from 'lucide-react';
6
6
  import { STATUS_OPTIONS, getStatusDefinition } from '../../utils/constants';
7
7
  import styles from '../../Taskforce.module.css';
8
8
  import shellStyles from './WorkspaceShell.module.css';
@@ -33,7 +33,7 @@ import { getImageReferenceLabel } from '../../utils/imageReferences';
33
33
  import { buildCustomTaxonomySortOptions } from '../../utils/taxonomySorting.js';
34
34
  import { getLocale, t } from '../../localization';
35
35
  import { resolveAnnotatedAttachmentDetail } from '../../utils/annotatedAttachments';
36
- import { invalidateBillingStatusCache, loadBillingStatusCached } from '../../utils/billingStatusCache';
36
+ import { ACCOUNT_PROFILE_SUMMARY_PATH, invalidateAccountProfileSummaryCache, loadAccountProfileSummaryCached } from '../../utils/accountProfileSummaryCache';
37
37
  import { fetchTaskforceApi } from '../../utils/taskforceApiClient';
38
38
  import { AppShellHeader } from './AppShellHeader';
39
39
  import { getTaskforceCloudEnvironmentConfig, parseTaskforceCloudEnvironment, resolveTaskforceCloudEnvironmentFromBaseUrl } from '../../config/cloudEnvironment';
@@ -58,6 +58,7 @@ const InitiativesModule = React.lazy(() => import('../features/InitiativesModule
58
58
  const PlansPage = React.lazy(() => import('./PlansPage').then(m => ({ default: m.PlansPage })));
59
59
  const PROFILE_AVATAR_ACCEPT = 'image/png,image/jpeg,image/webp,image/gif';
60
60
  const PROFILE_AVATAR_MAX_BYTES = 5 * 1024 * 1024;
61
+ const resolveSameOriginTaskforcePath = (path) => path;
61
62
  function getSyncEventStableKey(event, index = 0) {
62
63
  const explicitId = String(event.id ?? '').trim();
63
64
  if (explicitId)
@@ -215,7 +216,7 @@ export function StandaloneLayout(props) {
215
216
  // clearFilters, // This was duplicated, removed one instance
216
217
  // activeTypes, // This was duplicated, removed one instance
217
218
  // Settings State & Props
218
- settingsModel, configLoaded, currentTheme, setCurrentTheme, pathSaved, saveSettings, keyShortcut, setKeyShortcut, globalWeekStartsOn, locale, jsonBackupEnabled, setJsonBackupEnabled, mcpHostRoot, setMcpHostRoot, settingsSection, setSettingsSection, runtimeMode, workspaceSwitchingEnabled, cloudAuthConfigured, authRequiredForApi, authBlocked, isAuthenticated, authUserId, authWorkspaceId, authUserEmail, authUserDisplayName, authUserAvatarUrl, authSessionResolved, realtimeSyncEnabled, realtimeSyncFlagSource, workspaceCloudSyncEnabled, workspaceSyncPhase, workspaceSyncStatus, workspaceSyncSummary, workspaceSyncRecommendedAction, workspaceSyncBusy, workspaceSyncPendingChanges, saveWorkspaceCloudSyncSettings, pushNotice, userGlobalSyncStatus, workspaceLastSuccessfulSyncAt, workspaceLastPullAt, workspaceLastPushAt, workspaceLastErrorAt, userGlobalSyncError, workspaceLastErrorMessage, retryWorkspaceCloudSync, resetWorkspaceSyncCursorAndPull, getWorkspaceSyncDiagnostics, currentWorkspaceId, currentWorkspaceRole, availableWorkspaces, switchWorkspace, updateCurrentUserProfile, logout, projectRoot, projectName, mcpScriptPath, serverHostRoot,
219
+ settingsModel, configLoaded, currentTheme, setCurrentTheme, pathSaved, saveSettings, keyShortcut, setKeyShortcut, globalWeekStartsOn, locale, jsonBackupEnabled, setJsonBackupEnabled, mcpHostRoot, setMcpHostRoot, settingsSection, setSettingsSection, runtimeMode, workspaceSwitchingEnabled, cloudAuthConfigured, authRequiredForApi, authBlocked, isAuthenticated, authUserId, authWorkspaceId, authUserEmail, authUserDisplayName, authUserAvatarUrl, authSessionResolved, realtimeSyncEnabled, realtimeSyncFlagSource, workspaceCloudSyncEnabled, workspaceSyncPhase, workspaceSyncStatus, workspaceSyncSummary, workspaceSyncRecommendedAction, workspaceSyncBusy, workspaceSyncPendingChanges, saveWorkspaceCloudSyncSettings, pushNotice, userGlobalSyncStatus, workspaceLastSuccessfulSyncAt, workspaceLastPullAt, workspaceLastPushAt, workspaceLastErrorAt, userGlobalSyncError, workspaceLastErrorMessage, retryWorkspaceCloudSync, resetWorkspaceSyncCursorAndPull, getWorkspaceSyncDiagnostics, currentWorkspaceId, currentWorkspaceRole, availableWorkspaces, switchWorkspace, updateCurrentUserProfile, resolveCloudAuthUrl = resolveSameOriginTaskforcePath, logout, projectRoot, projectName, mcpScriptPath, serverHostRoot,
219
220
  // Browser Props
220
221
  showFolderBrowser, setShowFolderBrowser, folders, files, currentBrowsePath, fetchFolders, browserTarget, setBrowserTarget, handleSelectPath, handleAddPath, handleRemovePath,
221
222
  // Settings Managers
@@ -804,9 +805,9 @@ export function StandaloneLayout(props) {
804
805
  if (taskScope === 'deleted')
805
806
  return deletedScopeAllTasks;
806
807
  if (!showArchive)
807
- return props.searchAgnosticTasks;
808
- return [...props.searchAgnosticTasks, ...archivedScopeAllTasks];
809
- }, [archivedScopeAllTasks, deletedScopeAllTasks, props.searchAgnosticTasks, showArchive, taskScope]);
808
+ return props.tasks;
809
+ return [...props.tasks, ...archivedScopeAllTasks];
810
+ }, [archivedScopeAllTasks, deletedScopeAllTasks, props.tasks, showArchive, taskScope]);
810
811
  const hierarchyTasks = useMemo(() => {
811
812
  const byId = new Map();
812
813
  for (const task of archivedScopeAllTasks)
@@ -1036,6 +1037,11 @@ export function StandaloneLayout(props) {
1036
1037
  return boardVisibleTasks;
1037
1038
  return boardVisibleTasks.filter((task) => selectedPlanningTaskIds.has(task.id));
1038
1039
  }, [boardVisibleTasks, selectedPlanningTaskIds]);
1040
+ const scopedKanbanAllTasks = useMemo(() => {
1041
+ if (!selectedPlanningTaskIds || selectedPlanningTaskIds.size === 0)
1042
+ return kanbanAllTasks;
1043
+ return kanbanAllTasks.filter((task) => selectedPlanningTaskIds.has(task.id));
1044
+ }, [kanbanAllTasks, selectedPlanningTaskIds]);
1039
1045
  const expiredRecoveryCandidates = useMemo(() => {
1040
1046
  if (groupBy !== 'schedule')
1041
1047
  return [];
@@ -1218,7 +1224,7 @@ export function StandaloneLayout(props) {
1218
1224
  const [teamAuditEvents, setTeamAuditEvents] = useState([]);
1219
1225
  const [teamAuditHasMore, setTeamAuditHasMore] = useState(false);
1220
1226
  const [teamAuditPages, setTeamAuditPages] = useState(1);
1221
- const [billingStatus, setBillingStatus] = useState(null);
1227
+ const [accountProfileSummary, setAccountProfileSummary] = useState(null);
1222
1228
  const [billingLoading, setBillingLoading] = useState(false);
1223
1229
  const [billingError, setBillingError] = useState(null);
1224
1230
  const [billingActionBusy, setBillingActionBusy] = useState(false);
@@ -1240,7 +1246,11 @@ export function StandaloneLayout(props) {
1240
1246
  billing: false,
1241
1247
  teamManagement: false
1242
1248
  });
1249
+ const accountProfileSummaryRef = useRef(null);
1243
1250
  // showSearch state removed - search always visible in actions area
1251
+ useEffect(() => {
1252
+ accountProfileSummaryRef.current = accountProfileSummary;
1253
+ }, [accountProfileSummary]);
1244
1254
  useEffect(() => {
1245
1255
  if (!showAccountMenu)
1246
1256
  return;
@@ -1291,33 +1301,17 @@ export function StandaloneLayout(props) {
1291
1301
  const authIdentityLabel = authDisplayName || authEmailLabel;
1292
1302
  const hasAuthIdentity = authIdentityLabel.length > 0;
1293
1303
  const hasAuthEmail = authEmailLabel.length > 0;
1294
- const accountBadgeLabel = (authIdentityLabel || authEmailLabel || 'U').trim();
1295
- const accountBadgeInitial = accountBadgeLabel.charAt(0).toUpperCase() || 'U';
1304
+ const accountBadgeLabel = (authIdentityLabel || authEmailLabel || '').trim();
1305
+ const shouldShowResolvedAccountInitial = isAuthenticated && authSessionResolved && accountBadgeLabel.length > 0;
1306
+ const accountBadgeInitial = shouldShowResolvedAccountInitial ? accountBadgeLabel.charAt(0).toUpperCase() : '';
1296
1307
  const accountAvatarUrl = authAvatarUrl;
1308
+ const shouldShowResolvedAccountAvatar = isAuthenticated && authSessionResolved && accountAvatarUrl.length > 0;
1297
1309
  const profileAvatarDisplayUrl = String(profileAvatarPreviewUrl || authAvatarUrl).trim();
1298
- const subscriptionLabel = String(billingStatus?.planName || billingStatus?.planId || '').trim();
1310
+ const subscriptionLabel = String(accountProfileSummary?.planName || accountProfileSummary?.planId || '').trim();
1299
1311
  const hasSubscription = subscriptionLabel.length > 0;
1300
- const teamManagementWorkspaceId = String(authWorkspaceId || currentWorkspaceId || '').trim();
1312
+ const teamManagementWorkspaceId = String(accountProfileSummary?.workspaceId || authWorkspaceId || currentWorkspaceId || '').trim();
1301
1313
  const runtimeLabel = runtimeMode === 'cloud' ? 'CLOUD' : 'LOCAL';
1302
1314
  const apiBaseUrl = props.config?.apiBaseUrl || '';
1303
- const accountCapabilityApiBaseUrl = useMemo(() => {
1304
- const preferredBase = runtimeMode === 'local'
1305
- ? (props.config?.cloudAuthBaseUrl || props.config?.apiBaseUrl || '')
1306
- : (props.config?.apiBaseUrl || props.config?.cloudAuthBaseUrl || '');
1307
- return String(preferredBase || '').trim().replace(/\/+$/, '');
1308
- }, [props.config?.apiBaseUrl, props.config?.cloudAuthBaseUrl, runtimeMode]);
1309
- const billingApiBaseUrl = useMemo(() => {
1310
- const preferredBase = runtimeMode === 'local'
1311
- ? (props.config?.cloudAuthBaseUrl || props.config?.apiBaseUrl || '')
1312
- : (props.config?.apiBaseUrl || props.config?.cloudAuthBaseUrl || '');
1313
- return String(preferredBase || '').trim().replace(/\/+$/, '');
1314
- }, [props.config?.apiBaseUrl, props.config?.cloudAuthBaseUrl, runtimeMode]);
1315
- const buildBillingApiUrl = useCallback((path) => {
1316
- const normalizedPath = String(path || '').trim();
1317
- if (!accountCapabilityApiBaseUrl)
1318
- return normalizedPath;
1319
- return `${accountCapabilityApiBaseUrl}${normalizedPath.startsWith('/') ? normalizedPath : `/${normalizedPath}`}`;
1320
- }, [accountCapabilityApiBaseUrl]);
1321
1315
  const connectedEnvironmentLabel = useMemo(() => resolveConnectedEnvironmentLabel(props.config?.cloudEnvironment, [
1322
1316
  String(props.config?.cloudBaseUrl || ''),
1323
1317
  String(props.config?.cloudAuthBaseUrl || ''),
@@ -1501,14 +1495,15 @@ export function StandaloneLayout(props) {
1501
1495
  const workspaceId = String(teamManagementWorkspaceId || '').trim();
1502
1496
  if (workspaceId)
1503
1497
  headers['x-taskforce-workspace-id'] = workspaceId;
1504
- if (currentWorkspaceRole === 'owner'
1505
- || currentWorkspaceRole === 'admin'
1506
- || currentWorkspaceRole === 'member'
1507
- || currentWorkspaceRole === 'read-only') {
1508
- headers['x-taskforce-workspace-role'] = currentWorkspaceRole;
1498
+ const workspaceRole = teamWorkspaceRole || currentWorkspaceRole;
1499
+ if (workspaceRole === 'owner'
1500
+ || workspaceRole === 'admin'
1501
+ || workspaceRole === 'member'
1502
+ || workspaceRole === 'read-only') {
1503
+ headers['x-taskforce-workspace-role'] = workspaceRole;
1509
1504
  }
1510
1505
  return headers;
1511
- }, [currentWorkspaceRole, teamManagementWorkspaceId]);
1506
+ }, [currentWorkspaceRole, teamManagementWorkspaceId, teamWorkspaceRole]);
1512
1507
  const startAccountSurfaceTiming = useCallback((surface) => {
1513
1508
  const expectsBilling = isAuthenticated;
1514
1509
  const expectsTeamManagement = canAccessCloudTeamManagement;
@@ -1544,59 +1539,96 @@ export function StandaloneLayout(props) {
1544
1539
  surface: showAccountHub ? 'account_hub' : (showAccountMenu ? 'account_menu' : 'closed'),
1545
1540
  durationMs: Math.round(getTimingNow() - startedAt),
1546
1541
  teamManagementVisible: canOpenTeamManagement,
1547
- billingPlanId: String(billingStatus?.planId || '').trim() || null
1542
+ billingPlanId: String(accountProfileSummary?.planId || '').trim() || null
1548
1543
  });
1549
1544
  accountSurfaceOpenStartedAtRef.current = null;
1550
- }, [billingStatus?.planId, canOpenTeamManagement, showAccountHub, showAccountMenu]);
1551
- const loadBillingStatus = useCallback(async () => {
1552
- const billingStatusPath = '/api/taskforce/billing/status';
1553
- const billingStatusUrl = buildBillingApiUrl(billingStatusPath);
1545
+ }, [accountProfileSummary?.planId, canOpenTeamManagement, showAccountHub, showAccountMenu]);
1546
+ const loadAccountProfileSummary = useCallback(async () => {
1547
+ const accountProfileSummaryUrl = resolveCloudAuthUrl(ACCOUNT_PROFILE_SUMMARY_PATH);
1554
1548
  if (!isAuthenticated) {
1555
- setBillingStatus(null);
1549
+ setAccountProfileSummary(null);
1556
1550
  setBillingError(null);
1557
- invalidateBillingStatusCache(billingStatusUrl);
1551
+ setTeamWorkspaceRole(null);
1552
+ setTeamPlanMode('unknown');
1553
+ setTeamManagementAccessLoading(false);
1554
+ setTeamManagementAccessError(null);
1555
+ invalidateAccountProfileSummaryCache(accountProfileSummaryUrl, { identityKey: authUserId });
1558
1556
  markAccountSurfaceLoadSettled('billing');
1557
+ markAccountSurfaceLoadSettled('teamManagement');
1559
1558
  return null;
1560
1559
  }
1561
1560
  const startedAt = getTimingNow();
1562
1561
  setBillingLoading(true);
1563
1562
  setBillingError(null);
1563
+ setTeamManagementAccessLoading(canAccessCloudTeamManagement);
1564
+ setTeamManagementAccessError(null);
1564
1565
  try {
1565
- const payload = await loadBillingStatusCached(billingStatusUrl);
1566
- setBillingStatus(payload);
1567
- logBootstrapDebug('account_billing_status_resolved', {
1566
+ const payload = await loadAccountProfileSummaryCached(accountProfileSummaryUrl, { identityKey: authUserId });
1567
+ const nextRole = payload?.workspaceRole === 'owner'
1568
+ || payload?.workspaceRole === 'admin'
1569
+ || payload?.workspaceRole === 'member'
1570
+ || payload?.workspaceRole === 'read-only'
1571
+ ? payload.workspaceRole
1572
+ : null;
1573
+ const nextMode = payload?.teamPlanMode === 'team' || payload?.teamPlanMode === 'personal'
1574
+ ? payload.teamPlanMode
1575
+ : 'unknown';
1576
+ setAccountProfileSummary(payload);
1577
+ setTeamWorkspaceRole(nextRole);
1578
+ setTeamPlanMode(nextMode);
1579
+ setTeamManagementAccessError(null);
1580
+ logBootstrapDebug('account_profile_summary_resolved', {
1568
1581
  durationMs: Math.round(getTimingNow() - startedAt),
1569
1582
  planId: String(payload?.planId || '').trim() || null,
1570
- gate: String(payload?.gate || '').trim() || null
1583
+ gate: String(payload?.gate || '').trim() || null,
1584
+ teamManagementAllowed: payload?.teamManagementAllowed === true
1571
1585
  });
1572
1586
  return payload;
1573
1587
  }
1574
1588
  catch (error) {
1575
- setBillingStatus(null);
1576
- setBillingError(error instanceof Error ? error.message : 'Failed to load billing status.');
1577
- logBootstrapDebug('account_billing_status_failed', {
1589
+ const message = error instanceof Error ? error.message : 'Failed to load account summary.';
1590
+ const hasPreviousSummary = Boolean(accountProfileSummaryRef.current);
1591
+ setBillingError(message);
1592
+ if (!hasPreviousSummary) {
1593
+ setTeamWorkspaceRole(null);
1594
+ setTeamPlanMode('unknown');
1595
+ setTeamManagementAccessError(message);
1596
+ }
1597
+ else {
1598
+ setTeamManagementAccessError(null);
1599
+ }
1600
+ logBootstrapDebug('account_profile_summary_failed', {
1578
1601
  durationMs: Math.round(getTimingNow() - startedAt),
1579
- error: error instanceof Error ? error.message : 'Failed to load billing status.'
1602
+ error: message,
1603
+ preservedSummary: hasPreviousSummary
1580
1604
  });
1581
- return null;
1605
+ return accountProfileSummaryRef.current;
1582
1606
  }
1583
1607
  finally {
1584
1608
  setBillingLoading(false);
1609
+ setTeamManagementAccessLoading(false);
1585
1610
  markAccountSurfaceLoadSettled('billing');
1611
+ markAccountSurfaceLoadSettled('teamManagement');
1586
1612
  }
1587
- }, [buildBillingApiUrl, isAuthenticated, markAccountSurfaceLoadSettled]);
1613
+ }, [authUserId, canAccessCloudTeamManagement, isAuthenticated, markAccountSurfaceLoadSettled, resolveCloudAuthUrl]);
1588
1614
  const handleStartCheckout = useCallback(async () => {
1589
1615
  const params = new URLSearchParams();
1590
1616
  params.set('screen', 'plans');
1591
1617
  params.set('interval', billingIntervalChoice);
1618
+ const planId = String(accountProfileSummary?.planId || '').trim();
1619
+ const planVersionId = String(accountProfileSummary?.planVersionId || '').trim();
1620
+ if (planId)
1621
+ params.set('planId', planId);
1622
+ if (planVersionId)
1623
+ params.set('planVersionId', planVersionId);
1592
1624
  navigate(`/?${params.toString()}`);
1593
- }, [billingIntervalChoice, navigate]);
1625
+ }, [accountProfileSummary?.planId, accountProfileSummary?.planVersionId, billingIntervalChoice, navigate]);
1594
1626
  const handleOpenBillingPortal = useCallback(async () => {
1595
1627
  setBillingActionError(null);
1596
1628
  setBillingNotice(null);
1597
1629
  setBillingActionBusy(true);
1598
1630
  try {
1599
- const res = await fetch(buildBillingApiUrl('/api/taskforce/billing/portal-session'), {
1631
+ const res = await fetch(resolveCloudAuthUrl('/api/taskforce/billing/portal-session'), {
1600
1632
  method: 'POST',
1601
1633
  credentials: 'include'
1602
1634
  });
@@ -1618,18 +1650,18 @@ export function StandaloneLayout(props) {
1618
1650
  finally {
1619
1651
  setBillingActionBusy(false);
1620
1652
  }
1621
- }, [buildBillingApiUrl]);
1653
+ }, [resolveCloudAuthUrl]);
1622
1654
  const handleSwitchSubscriptionInterval = useCallback(async () => {
1623
1655
  setBillingActionError(null);
1624
1656
  setBillingNotice(null);
1625
1657
  setBillingActionBusy(true);
1626
1658
  try {
1627
- const currentPlanVersionId = String(billingStatus?.planVersionId || '').trim();
1659
+ const currentPlanVersionId = String(accountProfileSummary?.planVersionId || '').trim();
1628
1660
  if (!currentPlanVersionId) {
1629
1661
  setBillingActionError('No active plan version is linked to this account.');
1630
1662
  return;
1631
1663
  }
1632
- const res = await fetch(buildBillingApiUrl('/api/taskforce/billing/subscription'), {
1664
+ const res = await fetch(resolveCloudAuthUrl('/api/taskforce/billing/subscription'), {
1633
1665
  method: 'POST',
1634
1666
  credentials: 'include',
1635
1667
  headers: { 'Content-Type': 'application/json' },
@@ -1644,8 +1676,8 @@ export function StandaloneLayout(props) {
1644
1676
  return;
1645
1677
  }
1646
1678
  setBillingNotice('Subscription updated.');
1647
- invalidateBillingStatusCache(buildBillingApiUrl('/api/taskforce/billing/status'));
1648
- await loadBillingStatus();
1679
+ invalidateAccountProfileSummaryCache(resolveCloudAuthUrl(ACCOUNT_PROFILE_SUMMARY_PATH), { identityKey: authUserId });
1680
+ await loadAccountProfileSummary();
1649
1681
  }
1650
1682
  catch {
1651
1683
  setBillingActionError('Failed to update subscription interval.');
@@ -1653,94 +1685,17 @@ export function StandaloneLayout(props) {
1653
1685
  finally {
1654
1686
  setBillingActionBusy(false);
1655
1687
  }
1656
- }, [billingIntervalChoice, billingStatus?.planVersionId, buildBillingApiUrl, loadBillingStatus]);
1657
- const loadTeamManagementAccess = useCallback(async () => {
1658
- if (!canAccessCloudTeamManagement) {
1659
- setTeamWorkspaceRole(null);
1660
- setTeamPlanMode('unknown');
1661
- setTeamManagementAccessLoading(false);
1662
- setTeamManagementAccessError(null);
1663
- markAccountSurfaceLoadSettled('teamManagement');
1664
- return { role: null, teamPlanMode: 'unknown', allowed: false };
1665
- }
1666
- const startedAt = getTimingNow();
1667
- setTeamManagementAccessLoading(true);
1668
- setTeamManagementAccessError(null);
1669
- try {
1670
- const res = await fetchTaskforceApi('/api/taskforce/account/team-management-access', {
1671
- method: 'GET',
1672
- credentials: 'include',
1673
- headers: teamAdminHeaders()
1674
- }, accountCapabilityApiBaseUrl);
1675
- const payload = await res.json().catch(() => ({}));
1676
- if (!res.ok) {
1677
- setTeamWorkspaceRole(null);
1678
- setTeamPlanMode('unknown');
1679
- setTeamManagementAccessError(String(payload?.error || 'Failed to load team management access.'));
1680
- logBootstrapDebug('team_management_access_failed', {
1681
- durationMs: Math.round(getTimingNow() - startedAt),
1682
- status: res.status
1683
- });
1684
- return { role: null, teamPlanMode: 'unknown', allowed: false };
1685
- }
1686
- const nextRole = payload?.role === 'owner' || payload?.role === 'admin' || payload?.role === 'member' || payload?.role === 'read-only'
1687
- ? payload.role
1688
- : null;
1689
- const nextMode = payload?.teamPlanMode === 'team' || payload?.teamPlanMode === 'personal'
1690
- ? payload.teamPlanMode
1691
- : 'unknown';
1692
- const allowed = payload?.allowed === true;
1693
- setTeamWorkspaceRole(nextRole);
1694
- setTeamPlanMode(nextMode);
1695
- setTeamManagementAccessError(null);
1696
- logBootstrapDebug('team_management_access_loaded', {
1697
- durationMs: Math.round(getTimingNow() - startedAt),
1698
- role: nextRole,
1699
- teamPlanMode: nextMode,
1700
- allowed
1701
- });
1702
- return { role: nextRole, teamPlanMode: nextMode, allowed };
1703
- }
1704
- catch (error) {
1705
- setTeamWorkspaceRole(null);
1706
- setTeamPlanMode('unknown');
1707
- setTeamManagementAccessError(error instanceof Error ? error.message : 'Failed to load team management access.');
1708
- logBootstrapDebug('team_management_access_failed', {
1709
- durationMs: Math.round(getTimingNow() - startedAt),
1710
- error: error instanceof Error ? error.message : 'Failed to load team management access.'
1711
- });
1712
- return { role: null, teamPlanMode: 'unknown', allowed: false };
1713
- }
1714
- finally {
1715
- setTeamManagementAccessLoading(false);
1716
- markAccountSurfaceLoadSettled('teamManagement');
1717
- }
1718
- }, [accountCapabilityApiBaseUrl, canAccessCloudTeamManagement, markAccountSurfaceLoadSettled, teamAdminHeaders]);
1719
- useEffect(() => {
1720
- if (!showAccountMenu && !showTeamManagementModal && !showAccountHub)
1721
- return;
1722
- void loadTeamManagementAccess();
1723
- }, [showAccountHub, showAccountMenu, showTeamManagementModal, loadTeamManagementAccess]);
1724
- useEffect(() => {
1725
- if (!isAuthenticated || !authSessionResolved)
1726
- return;
1727
- if (!canAccessCloudTeamManagement) {
1728
- setTeamWorkspaceRole(null);
1729
- setTeamPlanMode('unknown');
1730
- return;
1731
- }
1732
- void loadTeamManagementAccess();
1733
- }, [authSessionResolved, canAccessCloudTeamManagement, isAuthenticated, loadTeamManagementAccess]);
1688
+ }, [accountProfileSummary?.planVersionId, authUserId, billingIntervalChoice, loadAccountProfileSummary, resolveCloudAuthUrl]);
1734
1689
  useEffect(() => {
1735
1690
  if (!isAuthenticated || !authSessionResolved)
1736
1691
  return;
1737
- void loadBillingStatus();
1738
- }, [authSessionResolved, isAuthenticated, loadBillingStatus]);
1692
+ void loadAccountProfileSummary();
1693
+ }, [authSessionResolved, isAuthenticated, loadAccountProfileSummary]);
1739
1694
  useEffect(() => {
1740
1695
  if (!showAccountHub && !showAccountMenu)
1741
1696
  return;
1742
- void loadBillingStatus();
1743
- }, [loadBillingStatus, showAccountHub, showAccountMenu]);
1697
+ void loadAccountProfileSummary();
1698
+ }, [loadAccountProfileSummary, showAccountHub, showAccountMenu]);
1744
1699
  const loadTeamUsers = useCallback(async () => {
1745
1700
  if (!canManageTeamWorkspace)
1746
1701
  return;
@@ -1869,12 +1824,12 @@ export function StandaloneLayout(props) {
1869
1824
  setShowAccountMenu(false);
1870
1825
  setShowTeamManagementModal(true);
1871
1826
  setTeamManagementTab('members');
1872
- const access = await loadTeamManagementAccess();
1873
- if (!access.allowed)
1827
+ const summary = await loadAccountProfileSummary();
1828
+ if (!(summary?.teamManagementAllowed ?? canOpenTeamManagement))
1874
1829
  return;
1875
1830
  await loadTeamUsers();
1876
1831
  await loadTeamAuditPage(0);
1877
- }, [loadTeamAuditPage, loadTeamManagementAccess, loadTeamUsers]);
1832
+ }, [canOpenTeamManagement, loadAccountProfileSummary, loadTeamAuditPage, loadTeamUsers]);
1878
1833
  const executeTeamUserAction = useCallback(async (userId, fn) => {
1879
1834
  setTeamMgmtError(null);
1880
1835
  setTeamActionBusyUserId(userId);
@@ -2544,7 +2499,7 @@ export function StandaloneLayout(props) {
2544
2499
  }, [groupBy, activeCategories, activeTypes, priorities, approaches, assigneeOptions, scheduleColumns]);
2545
2500
  // === SECTION: AccountMenuControl ===
2546
2501
  // Not extracted: ~25 distinct props from local state would be needed.
2547
- // References: isAuthenticated, authIdentityLabel, billingStatus, runtimeLabel,
2502
+ // References: isAuthenticated, authIdentityLabel, accountProfileSummary, runtimeLabel,
2548
2503
  // connectedEnvironmentLabel, canManageCloudWorkspaces, workspaceActionBusy,
2549
2504
  // setWorkspacePanelOpen, canOpenTeamManagement, openTeamManagementModal, and more.
2550
2505
  const accountMenuControl = (_jsxs("div", { className: accountStyles.accountMenuWrap, ref: accountMenuRef, children: [_jsx("button", { className: `tf-control-icon ${accountStyles.avatarBtn}`, onClick: () => {
@@ -2559,7 +2514,7 @@ export function StandaloneLayout(props) {
2559
2514
  }
2560
2515
  return next;
2561
2516
  });
2562
- }, title: isAuthenticated ? 'Account' : 'Account (Not signed in)', "aria-label": isAuthenticated ? 'Account' : 'Account (Not signed in)', "aria-haspopup": "menu", "aria-expanded": showAccountMenu, children: _jsx("span", { className: accountStyles.avatarBadge, "aria-hidden": "true", children: isAuthenticated && accountAvatarUrl ? (_jsx("img", { src: accountAvatarUrl, alt: "", className: accountStyles.avatarImage })) : (isAuthenticated ? accountBadgeInitial : '?') }) }), showAccountMenu && (_jsxs("div", { className: accountStyles.accountMenu, role: "menu", "aria-label": "Account menu", children: [_jsxs("div", { className: accountStyles.accountMenuSection, children: [_jsx("div", { className: accountStyles.accountMenuSectionLabel, children: "Account" }), _jsx("div", { className: accountStyles.accountMenuHint, children: isAuthBootstrapPending
2517
+ }, title: isAuthenticated ? 'Account' : 'Account (Not signed in)', "aria-label": isAuthenticated ? 'Account' : 'Account (Not signed in)', "aria-haspopup": "menu", "aria-expanded": showAccountMenu, children: _jsx("span", { className: accountStyles.avatarBadge, "aria-hidden": "true", children: shouldShowResolvedAccountAvatar ? (_jsx("img", { src: accountAvatarUrl, alt: "", className: accountStyles.avatarImage })) : accountBadgeInitial ? (accountBadgeInitial) : (_jsx(User, { size: 14, className: accountStyles.avatarIcon })) }) }), showAccountMenu && (_jsxs("div", { className: accountStyles.accountMenu, role: "menu", "aria-label": "Account menu", children: [_jsxs("div", { className: accountStyles.accountMenuSection, children: [_jsx("div", { className: accountStyles.accountMenuSectionLabel, children: "Account" }), _jsx("div", { className: accountStyles.accountMenuHint, children: isAuthBootstrapPending
2563
2518
  ? 'Checking sign-in status...'
2564
2519
  : isAuthenticated
2565
2520
  ? (_jsx(_Fragment, { children: hasAuthIdentity
@@ -3213,7 +3168,7 @@ export function StandaloneLayout(props) {
3213
3168
  const workspacePrimaryAction = workspaceChromeDefinition.primaryAction
3214
3169
  ? primaryActionRegistry[workspaceChromeDefinition.primaryAction]
3215
3170
  : null;
3216
- return (_jsxs("div", { className: `${shellStyles.standaloneWrapper} ${zenMode ? styles.zenModeEnabled : ''}`, "data-theme": currentTheme, children: [_jsx(AppShellHeader, { projectName: projectName, currentWorkspaceId: currentWorkspaceId, runtimeMode: runtimeMode, theme: currentTheme, meta: isPlansScreen ? null : (_jsxs(_Fragment, { children: [_jsx("span", { className: styles.taskCountBadge, title: activeCountTitle, children: activeCountLabel }), normalizedAuthUserId && normalizedAuthUserId !== 'anonymous' && (_jsxs(_Fragment, { children: [_jsxs("span", { className: styles.taskCountBadge, title: "Open tasks assigned to you", children: ["Mine ", assignedTaskCount] }), _jsxs("span", { className: styles.taskCountBadge, title: "Your open tasks with overdue due dates", children: ["Overdue ", assignedOverdueTaskCount] })] })), shouldShowSyncStatus && (_jsx("button", { className: "tf-control-icon", onClick: () => setShowSyncStatusModal(true), title: `Sync manager: ${syncStatusMeta.label} | Last success: ${formattedLastSyncTime}`, style: {
3171
+ return (_jsxs("div", { className: `${shellStyles.standaloneWrapper} ${zenMode ? styles.zenModeEnabled : ''}`, "data-theme": currentTheme, children: [_jsx(AppShellHeader, { projectName: projectName, currentWorkspaceId: currentWorkspaceId, runtimeMode: runtimeMode, theme: currentTheme, meta: isPlansScreen ? null : (_jsxs(_Fragment, { children: [_jsx("span", { className: styles.taskCountBadge, title: activeCountTitle, children: activeCountLabel }), normalizedAuthUserId && normalizedAuthUserId !== 'anonymous' && (_jsxs(_Fragment, { children: [_jsxs("span", { className: styles.taskCountBadge, title: "Tasks assigned to me", children: [_jsx(User, { size: 13, className: styles.taskCountBadgeIcon, "aria-hidden": "true" }), assignedTaskCount] }), _jsxs("span", { className: `${styles.taskCountBadge} ${assignedOverdueTaskCount > 0 ? styles.taskCountBadgeAlert : ''}`.trim(), title: "Tasks overdue", children: [_jsx(ClockAlert, { size: 13, className: styles.taskCountBadgeIcon, "aria-hidden": "true" }), assignedOverdueTaskCount] })] })), shouldShowSyncStatus && (_jsx("button", { className: "tf-control-icon", onClick: () => setShowSyncStatusModal(true), title: `Sync manager: ${syncStatusMeta.label} | Last success: ${formattedLastSyncTime}`, style: {
3217
3172
  marginLeft: '6px',
3218
3173
  height: '24px',
3219
3174
  width: '24px',
@@ -3225,7 +3180,7 @@ export function StandaloneLayout(props) {
3225
3180
  display: 'inline-flex',
3226
3181
  alignItems: 'center',
3227
3182
  justifyContent: 'center'
3228
- }, children: syncStatusMeta.icon === 'off' ? (_jsx(CloudOff, { size: 16 })) : (_jsx(Cloud, { size: 16 })) }))] })), actions: (_jsxs(_Fragment, { children: [isPlansScreen && (_jsx("button", { className: "tf-control-icon", onClick: handlePlansBack, title: "Back", children: "Back" })), !isPlansScreen && (_jsxs(_Fragment, { children: [workspaceHeaderSections, workspacePrimaryAction] })), !isPlansScreen && _jsx(WorkspaceHeaderActions, { actions: universalDisplayActions }), !isPlansScreen && _jsx(WorkspaceHeaderActions, { actions: universalFilterActions }), accountMenuControl] })) }), _jsx(TopNoticeLayer, { notice: uiNotice, onDismiss: clearNotice }), authBlocked && (_jsx("div", { className: authStyles.authBlockedBanner, children: "Authentication required for this environment. Use the Account menu to sign in." })), !isPlansScreen && showFilters && workspaceFilterBar, isPlansScreen ? (_jsx("div", { className: `${shellStyles.standalonePage} ${shellStyles.standaloneContent} ${styles.appScrollbar} tf-scrollbar`, style: { position: 'relative', display: 'flex', flex: 1, minHeight: 0, overflowY: 'auto', overflowX: 'hidden', scrollbarGutter: 'stable' }, children: _jsx(Suspense, { fallback: _jsx("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'center', flex: 1 }, children: _jsx(Loader2, { size: 20, className: styles.spinner }) }), children: _jsx(PlansPage, { ...props, currentTheme: currentTheme, projectName: projectName, currentWorkspaceId: currentWorkspaceId, apiBaseUrl: billingApiBaseUrl, connectedEnvironmentSource: props.config?.cloudAuthBaseUrl || props.config?.apiBaseUrl || '', embedded: true, shellOwnsScroll: true }) }) })) : resolvedWorkspaceModule === 'docs' && documentsWorkspaceEnabled ? (_jsxs("div", { className: `${shellStyles.standalonePage} ${shellStyles.standaloneContent}`, style: { position: 'relative', display: 'flex', flex: 1, minHeight: 0, overflow: 'hidden', paddingLeft: `${WORKSPACE_TOOL_RAIL_WIDTH}px` }, children: [workspaceToolRail, _jsx(Suspense, { fallback: _jsx("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'center', flex: 1 }, children: _jsx(Loader2, { size: 20, className: styles.spinner }) }), children: _jsx(DocumentWorkspaceShell, { tasks: tasks, runtimeMode: runtimeMode, apiBaseUrl: props.config?.apiBaseUrl || '', cloudAuthBaseUrl: props.config?.cloudAuthBaseUrl || '', typeFilters: documentTypeFilters, attachmentFilters: documentAttachmentFilters, onTypeFiltersChange: setDocumentTypeFilters, onAttachmentFiltersChange: setDocumentAttachmentFilters, requestedDocPath: requestedDocPath, requestedDocAssetId: requestedDocAssetId, onRequestedDocHandled: () => {
3183
+ }, children: syncStatusMeta.icon === 'off' ? (_jsx(CloudOff, { size: 16 })) : (_jsx(Cloud, { size: 16 })) }))] })), actions: (_jsxs(_Fragment, { children: [isPlansScreen && (_jsx("button", { className: "tf-control-icon", onClick: handlePlansBack, title: "Back", children: "Back" })), !isPlansScreen && (_jsxs(_Fragment, { children: [workspaceHeaderSections, workspacePrimaryAction] })), !isPlansScreen && _jsx(WorkspaceHeaderActions, { actions: universalDisplayActions }), !isPlansScreen && _jsx(WorkspaceHeaderActions, { actions: universalFilterActions }), accountMenuControl] })) }), _jsx(TopNoticeLayer, { notice: uiNotice, onDismiss: clearNotice }), authBlocked && (_jsx("div", { className: authStyles.authBlockedBanner, children: "Authentication required for this environment. Use the Account menu to sign in." })), !isPlansScreen && showFilters && workspaceFilterBar, isPlansScreen ? (_jsx("div", { className: `${shellStyles.standalonePage} ${shellStyles.standaloneContent} ${styles.appScrollbar} tf-scrollbar`, style: { position: 'relative', display: 'flex', flex: 1, minHeight: 0, overflowY: 'auto', overflowX: 'hidden', scrollbarGutter: 'stable' }, children: _jsx(Suspense, { fallback: _jsx("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'center', flex: 1 }, children: _jsx(Loader2, { size: 20, className: styles.spinner }) }), children: _jsx(PlansPage, { ...props, currentTheme: currentTheme, projectName: projectName, currentWorkspaceId: currentWorkspaceId, authUserId: authUserId, apiBaseUrl: apiBaseUrl, connectedEnvironmentSource: props.config?.cloudAuthBaseUrl || props.config?.apiBaseUrl || '', resolveCloudAuthUrl: resolveCloudAuthUrl, embedded: true, shellOwnsScroll: true }) }) })) : resolvedWorkspaceModule === 'docs' && documentsWorkspaceEnabled ? (_jsxs("div", { className: `${shellStyles.standalonePage} ${shellStyles.standaloneContent}`, style: { position: 'relative', display: 'flex', flex: 1, minHeight: 0, overflow: 'hidden', paddingLeft: `${WORKSPACE_TOOL_RAIL_WIDTH}px` }, children: [workspaceToolRail, _jsx(Suspense, { fallback: _jsx("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'center', flex: 1 }, children: _jsx(Loader2, { size: 20, className: styles.spinner }) }), children: _jsx(DocumentWorkspaceShell, { tasks: tasks, runtimeMode: runtimeMode, apiBaseUrl: props.config?.apiBaseUrl || '', cloudAuthBaseUrl: props.config?.cloudAuthBaseUrl || '', typeFilters: documentTypeFilters, attachmentFilters: documentAttachmentFilters, onTypeFiltersChange: setDocumentTypeFilters, onAttachmentFiltersChange: setDocumentAttachmentFilters, requestedDocPath: requestedDocPath, requestedDocAssetId: requestedDocAssetId, onRequestedDocHandled: () => {
3229
3184
  setRequestedDocPath(null);
3230
3185
  setRequestedDocAssetId(null);
3231
3186
  }, enableTaskGeneration: true }) })] })) : resolvedWorkspaceModule === 'annotate' && annotatedAttachmentsWorkspaceEnabled ? (_jsxs("div", { className: `${shellStyles.standalonePage} ${shellStyles.standaloneContent}`, style: { position: 'relative', display: 'flex', flex: 1, minHeight: 0, overflow: 'hidden', paddingLeft: `${WORKSPACE_TOOL_RAIL_WIDTH}px` }, children: [workspaceToolRail, _jsx(Suspense, { fallback: _jsx("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'center', flex: 1 }, children: _jsx(Loader2, { size: 20, className: styles.spinner }) }), children: _jsx(AnnotatedAttachmentWorkspaceShell, { runtimeMode: runtimeMode, apiBaseUrl: props.config?.apiBaseUrl || '', cloudAuthBaseUrl: props.config?.cloudAuthBaseUrl || '', workspaceId: currentWorkspaceId, sessionLoadReady: annotatedWorkspaceReady, requestedTarget: requestedAnnotatedTarget, requestedSessionId: requestedAnnotatedSessionId, requestedOpenVersion: requestedAnnotatedOpenVersion, resolveTaskReferenceLabel: resolveAnnotatedTaskReferenceLabel, resolveImageReferenceLabel: resolveAnnotatedImageReferenceLabel, onOpenTarget: handleAnnotatedAttachmentOpenTarget, onContextChange: handleAnnotatedAttachmentContextChange, onBackToTask: handleBackToTaskFromAnnotatedAttachment }) })] })) : resolvedWorkspaceModule === 'workflows' ? (_jsxs("div", { className: `${shellStyles.standalonePage} ${shellStyles.standaloneContent}`, style: { position: 'relative', display: 'flex', flex: 1, minHeight: 0, overflow: 'hidden', paddingLeft: `${WORKSPACE_TOOL_RAIL_WIDTH}px` }, children: [workspaceToolRail, _jsx(Suspense, { fallback: _jsx("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'center', flex: 1 }, children: _jsx(Loader2, { size: 20, className: styles.spinner }) }), children: _jsx(WorkflowsModule, { availableWorkflows: availableWorkflows, availableEnvironments: availableEnvironments, exportEnvironment: exportEnvironment, exportWorkflowsPath: props.exportWorkflowsPath, exportingResource: exportingResource, exportResult: exportResult, onExportEnvironmentChange: props.setExportEnvironment, onExportWorkflows: onExportWorkflows, onRefreshWorkflows: fetchWorkflows, onFetchWorkflowTemplate: props.fetchWorkflowTemplate, onFetchWorkflowOverrideNames: props.fetchWorkflowOverrideNames, onSaveWorkflowTemplateDraft: props.saveWorkflowTemplateDraft, onResetWorkflowTemplateDraft: props.resetWorkflowTemplateDraft }) })] })) : resolvedWorkspaceModule === 'agents' ? (_jsxs("div", { className: `${shellStyles.standalonePage} ${shellStyles.standaloneContent}`, style: { position: 'relative', display: 'flex', flex: 1, minHeight: 0, overflow: 'hidden', paddingLeft: `${WORKSPACE_TOOL_RAIL_WIDTH}px` }, children: [workspaceToolRail, _jsx(Suspense, { fallback: _jsx("div", { style: { display: 'flex', alignItems: 'center', justifyContent: 'center', flex: 1 }, children: _jsx(Loader2, { size: 20, className: styles.spinner }) }), children: _jsx(AgentsModule, { workspaceId: currentWorkspaceId }) })] })) : (_jsxs("div", { className: `${shellStyles.standalonePage} ${shellStyles.standaloneContent} `, style: {
@@ -3244,7 +3199,7 @@ export function StandaloneLayout(props) {
3244
3199
  inset: 0,
3245
3200
  zIndex: 35,
3246
3201
  pointerEvents: 'none',
3247
- } }), _jsxs("div", { style: { position: 'relative', width: '100%', minWidth: 0, flex: 1, minHeight: 0, display: 'flex', flexDirection: 'column', overflow: 'hidden' }, children: [props.loadingTasks && boardVisibleTasks.length > 0 && (_jsx("div", { className: styles.boardRefreshIndicator, "aria-live": "polite", "aria-label": "Refreshing tasks", children: _jsx(Loader2, { size: 14, className: styles.spinner }) })), _jsx(TaskKanban, { tasks: scopedBoardVisibleTasks, allTasks: [], columns: kanbanColumns, groupBy: groupBy, scheduleDates: scheduleDates, searchQuery: searchQuery, filterCategories: filterCategories, filterTypes: filterTypes, filterPriorities: filterPriorities, filterStatus: filterStatus, filterAssignees: filterAssignees, assigneeOptions: assigneeOptions, scheduleFilteredTaskIds: Array.from(scheduleFilteredTaskIds), copiedId: copiedId, taxonomies: taxonomies, types: activeTypes, priorities: priorities, onUpdateTask: handleUpdateTask, onTaskClick: (task) => handleEdit(task), onOpenTaskById: handleOpenTaskById, onCopyId: handleCopyId, onToggleInProgress: handleToggleInProgress, onToggleReview: handleToggleReview, onToggleComplete: handleToggleComplete, onToggleCancel: handleToggleCancel, onSetStatus: handleSetStatus, onArchiveTask: handleArchiveTask, onAddTaskToColumn: handleAddTaskToColumn, showTaskCardStatusLabel: showTaskCardStatusLabel, onScheduleDaySelected: (dateOnly) => {
3202
+ } }), _jsxs("div", { style: { position: 'relative', width: '100%', minWidth: 0, flex: 1, minHeight: 0, display: 'flex', flexDirection: 'column', overflow: 'hidden' }, children: [props.loadingTasks && boardVisibleTasks.length > 0 && (_jsx("div", { className: styles.boardRefreshIndicator, "aria-live": "polite", "aria-label": "Refreshing tasks", children: _jsx(Loader2, { size: 14, className: styles.spinner }) })), _jsx(TaskKanban, { tasks: scopedBoardVisibleTasks, allTasks: scopedKanbanAllTasks, columns: kanbanColumns, groupBy: groupBy, scheduleDates: scheduleDates, searchQuery: searchQuery, filterCategories: filterCategories, filterTypes: filterTypes, filterPriorities: filterPriorities, filterStatus: filterStatus, filterAssignees: filterAssignees, assigneeOptions: assigneeOptions, scheduleFilteredTaskIds: Array.from(scheduleFilteredTaskIds), copiedId: copiedId, taxonomies: taxonomies, types: activeTypes, priorities: priorities, onUpdateTask: handleUpdateTask, onTaskClick: (task) => handleEdit(task), onOpenTaskById: handleOpenTaskById, onCopyId: handleCopyId, onToggleInProgress: handleToggleInProgress, onToggleReview: handleToggleReview, onToggleComplete: handleToggleComplete, onToggleCancel: handleToggleCancel, onSetStatus: handleSetStatus, onArchiveTask: handleArchiveTask, onAddTaskToColumn: handleAddTaskToColumn, showTaskCardStatusLabel: showTaskCardStatusLabel, onScheduleDaySelected: (dateOnly) => {
3248
3203
  setScheduleSelectedDate(dateOnly);
3249
3204
  const parsed = parseDateOnlyLocal(dateOnly);
3250
3205
  if (parsed) {
@@ -3350,11 +3305,11 @@ export function StandaloneLayout(props) {
3350
3305
  setShowHelpModal(false);
3351
3306
  setActiveTab('settings');
3352
3307
  setSettingsSection('general');
3353
- } }), _jsx(EditProfileModal, { isOpen: showEditProfileModal, theme: currentTheme, onClose: closeEditProfileModal, onSave: () => { void handleSaveProfile(); }, displayName: profileDisplayNameDraft, email: authEmailLabel, avatarDisplayUrl: profileAvatarDisplayUrl, accountBadgeInitial: accountBadgeInitial, avatarInputRef: profileAvatarInputRef, avatarAccept: PROFILE_AVATAR_ACCEPT, avatarDraftId: profileAvatarDraftId, saveBusy: profileSaveBusy, avatarBusy: profileAvatarBusy, saveError: profileSaveError, saveNotice: profileSaveNotice, onDisplayNameChange: setProfileDisplayNameDraft, onAvatarInputChange: (file) => { void handleProfileAvatarSelected(file); }, onStartAvatarUpload: () => profileAvatarInputRef.current?.click(), onDiscardUpload: handleDiscardPendingProfileAvatar, onRemovePhoto: handleRemoveProfileAvatar }), _jsx(AccountHubModal, { isOpen: showAccountHub, theme: currentTheme, runtimeMode: runtimeMode, authRequiredForApi: authRequiredForApi, isAuthenticated: isAuthenticated, hasAuthIdentity: hasAuthIdentity, authIdentityLabel: authIdentityLabel, billingLoading: billingLoading, billingError: billingError, billingActionError: billingActionError, billingNotice: billingNotice, billingActionBusy: billingActionBusy, billingIntervalChoice: billingIntervalChoice, billingStatus: billingStatus, currentWorkspaceId: currentWorkspaceId, canOpenTeamManagement: canOpenTeamManagement, canManageWorkspaceSync: canManageWorkspaceSync, workspaceCloudSyncEnabled: workspaceCloudSyncEnabled, syncStatusLabel: syncStatusMeta.label, workspaceSyncError: workspaceSyncError, syncControlBusy: syncControlBusy, onClose: () => setShowAccountHub(false), onOpenWorkspaceAudit: () => {
3308
+ } }), _jsx(EditProfileModal, { isOpen: showEditProfileModal, theme: currentTheme, onClose: closeEditProfileModal, onSave: () => { void handleSaveProfile(); }, displayName: profileDisplayNameDraft, email: authEmailLabel, avatarDisplayUrl: profileAvatarDisplayUrl, accountBadgeInitial: accountBadgeInitial, avatarInputRef: profileAvatarInputRef, avatarAccept: PROFILE_AVATAR_ACCEPT, avatarDraftId: profileAvatarDraftId, saveBusy: profileSaveBusy, avatarBusy: profileAvatarBusy, saveError: profileSaveError, saveNotice: profileSaveNotice, onDisplayNameChange: setProfileDisplayNameDraft, onAvatarInputChange: (file) => { void handleProfileAvatarSelected(file); }, onStartAvatarUpload: () => profileAvatarInputRef.current?.click(), onDiscardUpload: handleDiscardPendingProfileAvatar, onRemovePhoto: handleRemoveProfileAvatar }), _jsx(AccountHubModal, { isOpen: showAccountHub, theme: currentTheme, runtimeMode: runtimeMode, authRequiredForApi: authRequiredForApi, isAuthenticated: isAuthenticated, hasAuthIdentity: hasAuthIdentity, authIdentityLabel: authIdentityLabel, billingLoading: billingLoading, billingError: billingError, billingActionError: billingActionError, billingNotice: billingNotice, billingActionBusy: billingActionBusy, billingIntervalChoice: billingIntervalChoice, accountProfileSummary: accountProfileSummary, currentWorkspaceId: currentWorkspaceId, canOpenTeamManagement: canOpenTeamManagement, canManageWorkspaceSync: canManageWorkspaceSync, workspaceCloudSyncEnabled: workspaceCloudSyncEnabled, syncStatusLabel: syncStatusMeta.label, workspaceSyncError: workspaceSyncError, syncControlBusy: syncControlBusy, onClose: () => setShowAccountHub(false), onOpenWorkspaceAudit: () => {
3354
3309
  setShowAccountHub(false);
3355
3310
  void openTeamManagementModal();
3356
3311
  setTeamManagementTab('audit');
3357
- }, onBillingIntervalChange: setBillingIntervalChoice, onRefreshBilling: () => { void loadBillingStatus(); }, onUpdateInterval: () => { void handleSwitchSubscriptionInterval(); }, onManageBilling: () => { void handleOpenBillingPortal(); }, onStartCheckout: () => { void handleStartCheckout(); }, onToggleWorkspaceSync: (enabled) => { void handleWorkspaceSyncToggle(enabled); }, onOpenHelp: () => {
3312
+ }, onBillingIntervalChange: setBillingIntervalChoice, onRefreshBilling: () => { void loadAccountProfileSummary(); }, onUpdateInterval: () => { void handleSwitchSubscriptionInterval(); }, onManageBilling: () => { void handleOpenBillingPortal(); }, onStartCheckout: () => { void handleStartCheckout(); }, onToggleWorkspaceSync: (enabled) => { void handleWorkspaceSyncToggle(enabled); }, onOpenHelp: () => {
3358
3313
  setShowAccountHub(false);
3359
3314
  setShowHelpModal(true);
3360
3315
  } }), _jsx(TeamManagementModal, { isOpen: showTeamManagementModal, theme: currentTheme, teamPlanMode: teamPlanMode, teamMgmtError: teamMgmtError, teamManagementTab: teamManagementTab, teamUsersLoading: teamUsersLoading, teamUsers: teamUsers, teamActionBusyUserId: teamActionBusyUserId, teamInviteFeedback: teamInviteFeedback, teamInviteEmail: teamInviteEmail, teamInviteRole: teamInviteRole, teamInvitePermissionMode: teamInvitePermissionMode, teamInviteBusy: teamInviteBusy, pendingInvites: pendingInvites, teamAuditLoading: teamAuditLoading, teamAuditEvents: teamAuditEvents, teamAuditPage: teamAuditPage, teamAuditPages: teamAuditPages, teamAuditHasMore: teamAuditHasMore, onClose: () => setShowTeamManagementModal(false), onOpenMembersTab: () => {
@@ -1,11 +1,16 @@
1
1
  import type { ModalRuntimeMode, ModalThemeName } from './types';
2
- interface BillingStatusLike {
2
+ interface AccountProfileSummaryLike {
3
3
  planId?: string | null;
4
+ planName?: string | null;
4
5
  entitlementState?: string | null;
5
6
  stripeStatus?: string | null;
6
7
  effectiveUntil?: string | null;
7
8
  stripeSubscriptionId?: string | null;
8
9
  stripeCustomerId?: string | null;
10
+ gate?: string | null;
11
+ message?: string | null;
12
+ billingConflictCode?: 'CHECKOUT_SESSION_EXPIRED' | 'CHECKOUT_SESSION_SUPERSEDED' | null;
13
+ planSelectionRequired?: boolean;
9
14
  }
10
15
  interface AccountHubModalProps {
11
16
  isOpen: boolean;
@@ -21,7 +26,7 @@ interface AccountHubModalProps {
21
26
  billingNotice: string | null;
22
27
  billingActionBusy: boolean;
23
28
  billingIntervalChoice: 'month' | 'year';
24
- billingStatus: BillingStatusLike | null;
29
+ accountProfileSummary: AccountProfileSummaryLike | null;
25
30
  currentWorkspaceId: string;
26
31
  canOpenTeamManagement: boolean;
27
32
  canManageWorkspaceSync: boolean;
@@ -39,5 +44,5 @@ interface AccountHubModalProps {
39
44
  onToggleWorkspaceSync: (enabled: boolean) => void;
40
45
  onOpenHelp: () => void;
41
46
  }
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;
47
+ export declare function AccountHubModal({ isOpen, theme, runtimeMode, authRequiredForApi, isAuthenticated, hasAuthIdentity, authIdentityLabel, billingLoading, billingError, billingActionError, billingNotice, billingActionBusy, billingIntervalChoice, accountProfileSummary, currentWorkspaceId, canOpenTeamManagement, canManageWorkspaceSync, workspaceCloudSyncEnabled, syncStatusLabel, workspaceSyncError, syncControlBusy, onClose, onOpenWorkspaceAudit, onBillingIntervalChange, onRefreshBilling, onUpdateInterval, onManageBilling, onStartCheckout, onToggleWorkspaceSync, onOpenHelp, }: AccountHubModalProps): import("react/jsx-runtime").JSX.Element;
43
48
  export {};
@@ -5,7 +5,21 @@ import accountStyles from '../../StandaloneAccount.module.css';
5
5
  import authStyles from '../../AuthShell.module.css';
6
6
  import managementStyles from '../../StandaloneManagement.module.css';
7
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, }) {
8
+ export function AccountHubModal({ isOpen, theme, runtimeMode, authRequiredForApi, isAuthenticated, hasAuthIdentity, authIdentityLabel, billingLoading, billingError, billingActionError, billingNotice, billingActionBusy, billingIntervalChoice, accountProfileSummary, currentWorkspaceId, canOpenTeamManagement, canManageWorkspaceSync, workspaceCloudSyncEnabled, syncStatusLabel, workspaceSyncError, syncControlBusy, onClose, onOpenWorkspaceAudit, onBillingIntervalChange, onRefreshBilling, onUpdateInterval, onManageBilling, onStartCheckout, onToggleWorkspaceSync, onOpenHelp, }) {
9
+ const billingGate = String(accountProfileSummary?.gate || '').trim().toLowerCase();
10
+ const billingConflictCode = String(accountProfileSummary?.billingConflictCode || '').trim().toUpperCase();
11
+ const billingRecoveryMessage = String(accountProfileSummary?.message || '').trim()
12
+ || (billingConflictCode === 'CHECKOUT_SESSION_EXPIRED'
13
+ ? 'Your latest checkout expired. Choose a plan to continue.'
14
+ : (billingConflictCode === 'CHECKOUT_SESSION_SUPERSEDED'
15
+ ? 'A newer checkout is already in progress for this account. Open plans to continue.'
16
+ : ''));
17
+ const startCheckoutLabel = billingConflictCode === 'CHECKOUT_SESSION_EXPIRED'
18
+ || accountProfileSummary?.planSelectionRequired === true
19
+ || billingGate === 'plan_selection_required'
20
+ ? 'Choose Plan'
21
+ : (billingGate === 'checkout_pending' ? 'Open Plans' : 'Start Checkout');
22
+ const resolvedPlanLabel = String(accountProfileSummary?.planName || accountProfileSummary?.planId || 'n/a').trim() || 'n/a';
9
23
  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
24
  ? (authRequiredForApi
11
25
  ? (isAuthenticated ? 'Signed in. API access is enabled.' : 'Cloud mode requires sign in before app usage.')
@@ -14,7 +28,7 @@ export function AccountHubModal({ isOpen, theme, runtimeMode, authRequiredForApi
14
28
  ? (hasAuthIdentity
15
29
  ? _jsxs(_Fragment, { children: ["Signed in as ", _jsx("span", { className: accountStyles.accountIdentityEmail, children: authIdentityLabel })] })
16
30
  : '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
31
+ : '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 })), billingRecoveryMessage && (_jsx("p", { className: managementStyles.sectionText, children: billingRecoveryMessage })), _jsxs("p", { className: managementStyles.sectionTextSpaced, children: ["Plan: ", _jsx("code", { children: resolvedPlanLabel }), ' · ', "Entitlement: ", _jsx("code", { children: String(accountProfileSummary?.entitlementState || 'n/a') })] }), _jsxs("p", { className: managementStyles.sectionText, children: ["Stripe status: ", _jsx("code", { children: String(accountProfileSummary?.stripeStatus || 'n/a') }), accountProfileSummary?.effectiveUntil ? ` · Effective until ${new Date(accountProfileSummary.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 || !accountProfileSummary?.stripeSubscriptionId, children: "Update Interval" }), _jsx("button", { className: styles.cancelBtn, onClick: onManageBilling, disabled: billingActionBusy || !accountProfileSummary?.stripeCustomerId, children: "Manage Billing" }), _jsx("button", { className: styles.submitBtn, onClick: onStartCheckout, disabled: billingActionBusy, children: billingActionBusy ? 'Working...' : startCheckoutLabel })] })] }))] }), _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
32
  ? (workspaceCloudSyncEnabled
19
33
  ? `Enabled · ${syncStatusLabel}`
20
34
  : 'Disabled')