@taskforcehq/taskforce 0.3.301 → 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 (171) hide show
  1. package/dist/Taskforce.module.css +37 -2
  2. package/dist/TaskforceCore.filter-persistence.test.js +1 -1
  3. package/dist/TaskforceCore.js +72 -33
  4. package/dist/TaskforceCore.test.js +848 -130
  5. package/dist/components/features/TaskSettings.js +14 -82
  6. package/dist/components/features/TaskSettings.test.js +33 -70
  7. package/dist/components/features/settings/TaxonomyLibraryManager.js +8 -2
  8. package/dist/components/features/settings/TaxonomyLibraryManager.test.js +1 -1
  9. package/dist/components/task/TaskActionHeader.js +6 -3
  10. package/dist/components/task/TaskCard.js +6 -3
  11. package/dist/components/task/TaskForm.js +2 -2
  12. package/dist/components/task/TaskForm.test.js +41 -2
  13. package/dist/components/task/TaskKanban.test.js +17 -0
  14. package/dist/components/ui/ReferenceBadgeButton.js +1 -1
  15. package/dist/components/views/PlansPage.d.ts +3 -1
  16. package/dist/components/views/PlansPage.js +109 -49
  17. package/dist/components/views/PlansPage.test.d.ts +1 -0
  18. package/dist/components/views/PlansPage.test.js +100 -0
  19. package/dist/components/views/StandaloneLayout.d.ts +1 -0
  20. package/dist/components/views/StandaloneLayout.js +473 -256
  21. package/dist/components/views/panels/PlanningDrawer.js +18 -1
  22. package/dist/components/views/standalone/modals/AccountHubModal.d.ts +48 -0
  23. package/dist/components/views/standalone/modals/AccountHubModal.js +36 -0
  24. package/dist/components/views/standalone/modals/AccountHubModal.test.d.ts +1 -0
  25. package/dist/components/views/standalone/modals/AccountHubModal.test.js +26 -0
  26. package/dist/components/views/standalone/modals/CreateWorkspaceConfirmModal.d.ts +9 -0
  27. package/dist/components/views/standalone/modals/CreateWorkspaceConfirmModal.js +8 -0
  28. package/dist/components/views/standalone/modals/EditProfileModal.d.ts +26 -0
  29. package/dist/components/views/standalone/modals/EditProfileModal.js +10 -0
  30. package/dist/components/views/standalone/modals/HelpModal.d.ts +9 -0
  31. package/dist/components/views/standalone/modals/HelpModal.js +8 -0
  32. package/dist/components/views/standalone/modals/ScheduleWarningModal.d.ts +13 -0
  33. package/dist/components/views/standalone/modals/ScheduleWarningModal.js +6 -0
  34. package/dist/components/views/standalone/modals/SyncStatusModal.d.ts +61 -0
  35. package/dist/components/views/standalone/modals/SyncStatusModal.js +38 -0
  36. package/dist/components/views/standalone/modals/TeamManagementModal.d.ts +64 -0
  37. package/dist/components/views/standalone/modals/TeamManagementModal.js +10 -0
  38. package/dist/components/views/standalone/modals/types.d.ts +2 -0
  39. package/dist/components/views/standalone/modals/types.js +1 -0
  40. package/dist/components/views/teamManagementAccess.d.ts +1 -1
  41. package/dist/components/views/teamManagementAccess.js +2 -2
  42. package/dist/components/views/teamManagementAccess.test.js +3 -3
  43. package/dist/config/deployment.js +2 -2
  44. package/dist/config/deployment.test.js +5 -3
  45. package/dist/core/AiProfileService.js +7 -2
  46. package/dist/core/AiProfiles.test.js +19 -0
  47. package/dist/core/EntitlementsPolicy.test.js +2 -2
  48. package/dist/core/GlobalSettingsService.js +78 -18
  49. package/dist/core/PlanEntitlementService.d.ts +29 -5
  50. package/dist/core/PlanEntitlementService.js +297 -219
  51. package/dist/core/SignupMonetizationSettings.test.js +173 -49
  52. package/dist/core/SyncReconciliationService.d.ts +2 -0
  53. package/dist/core/SyncReconciliationService.js +3 -0
  54. package/dist/core/SystemAdmin.test.js +186 -96
  55. package/dist/core/TaskAuditTimeline.test.js +40 -0
  56. package/dist/core/Taskforce.d.ts +100 -10
  57. package/dist/core/Taskforce.ids.test.js +133 -0
  58. package/dist/core/Taskforce.js +459 -127
  59. package/dist/core/Taskforce.mcpAuth.test.js +0 -2
  60. package/dist/core/UserProfileAvatarDrafts.test.d.ts +1 -0
  61. package/dist/core/UserProfileAvatarDrafts.test.js +111 -0
  62. package/dist/core/taskReferencePolicy.d.ts +28 -0
  63. package/dist/core/taskReferencePolicy.js +189 -0
  64. package/dist/core/types.d.ts +13 -12
  65. package/dist/hooks/tasks/useTaskFormActions.js +3 -0
  66. package/dist/hooks/useSyncOrchestrator.js +17 -3
  67. package/dist/hooks/useSyncOrchestrator.retry-closure.test.js +97 -5
  68. package/dist/hooks/useTaskData.js +3 -2
  69. package/dist/hooks/useTaskData.test.js +66 -0
  70. package/dist/hooks/useTaskforce.d.ts +8 -0
  71. package/dist/hooks/useTaskforce.js +72 -19
  72. package/dist/hooks/useTaskforce.sync-behavior.test.js +185 -6
  73. package/dist/mcp/aiProfileBootstrap.d.ts +29 -0
  74. package/dist/mcp/aiProfileBootstrap.js +80 -0
  75. package/dist/mcp/aiProfileBootstrap.test.d.ts +1 -0
  76. package/dist/mcp/aiProfileBootstrap.test.js +68 -0
  77. package/dist/mcp/clientIntegrations.d.ts +2 -8
  78. package/dist/mcp/clientIntegrations.js +21 -40
  79. package/dist/mcp/clientIntegrations.test.js +28 -2
  80. package/dist/mcp/clientProfileDefaults.d.ts +12 -0
  81. package/dist/mcp/clientProfileDefaults.js +37 -0
  82. package/dist/mcp/runtime.d.ts +10 -7
  83. package/dist/mcp/runtime.js +131 -48
  84. package/dist/mcp/runtime.test.js +321 -83
  85. package/dist/migrations/billingSchemaParity.test.js +3 -0
  86. package/dist/migrations/taskSchemaMigrations.js +68 -1
  87. package/dist/qaOpen.test.d.ts +1 -0
  88. package/dist/qaOpen.test.js +56 -0
  89. package/dist/server/index.cookieProxy.test.js +2 -0
  90. package/dist/server/index.d.ts +1 -0
  91. package/dist/server/index.js +17 -1
  92. package/dist/server/index.test.js +15 -1
  93. package/dist/server/mockStripe.d.ts +1 -0
  94. package/dist/server/mockStripe.js +2 -0
  95. package/dist/server/routes/admin.js +105 -45
  96. package/dist/server/routes/auth.d.ts +6 -0
  97. package/dist/server/routes/auth.js +505 -30
  98. package/dist/server/routes/billing.js +517 -24
  99. package/dist/server/routes/billing.test.js +967 -45
  100. package/dist/server/routes/documents.js +6 -1
  101. package/dist/server/routes/shared.d.ts +1 -1
  102. package/dist/server/routes/shared.js +16 -6
  103. package/dist/server/routes/sync.integration.test.js +290 -0
  104. package/dist/server/routes/sync.js +234 -1922
  105. package/dist/server/routes/tasks.js +18 -4
  106. package/dist/server/routes.js +29 -22
  107. package/dist/server/routes.test.js +639 -82
  108. package/dist/shared/runtimeContract.d.ts +1 -1
  109. package/dist/sync/cloudSyncApi.d.ts +1 -0
  110. package/dist/sync/collaborationSyncPayload.d.ts +17 -0
  111. package/dist/sync/collaborationSyncPayload.js +131 -0
  112. package/dist/sync/collaborationSyncPayload.test.d.ts +1 -0
  113. package/dist/sync/collaborationSyncPayload.test.js +104 -0
  114. package/dist/sync/collaborationSyncState.d.ts +15 -0
  115. package/dist/sync/collaborationSyncState.js +60 -0
  116. package/dist/sync/contentSyncPayload.d.ts +45 -0
  117. package/dist/sync/contentSyncPayload.js +104 -0
  118. package/dist/sync/contentSyncPayload.test.d.ts +1 -0
  119. package/dist/sync/contentSyncPayload.test.js +104 -0
  120. package/dist/sync/contentSyncState.d.ts +98 -0
  121. package/dist/sync/contentSyncState.js +1004 -0
  122. package/dist/sync/planningSyncPayload.d.ts +9 -0
  123. package/dist/sync/planningSyncPayload.js +82 -0
  124. package/dist/sync/planningSyncPayload.test.d.ts +1 -0
  125. package/dist/sync/planningSyncPayload.test.js +68 -0
  126. package/dist/sync/syncApplyHandlers.d.ts +83 -0
  127. package/dist/sync/syncApplyHandlers.js +445 -0
  128. package/dist/sync/syncService.d.ts +1 -0
  129. package/dist/sync/syncService.js +21 -8
  130. package/dist/sync/taskSyncPayload.d.ts +33 -0
  131. package/dist/sync/taskSyncPayload.js +126 -0
  132. package/dist/sync/taskSyncPayload.test.d.ts +1 -0
  133. package/dist/sync/taskSyncPayload.test.js +54 -0
  134. package/dist/sync/workspacePullFeed.d.ts +38 -0
  135. package/dist/sync/workspacePullFeed.js +349 -0
  136. package/dist/sync/workspacePullFeed.test.d.ts +1 -0
  137. package/dist/sync/workspacePullFeed.test.js +278 -0
  138. package/dist/sync/workspaceSyncModel.d.ts +4 -1
  139. package/dist/sync/workspaceSyncModel.js +9 -2
  140. package/dist/sync/workspaceSyncModel.test.js +29 -0
  141. package/dist/test/setup.js +1 -1
  142. package/dist/types.d.ts +1 -0
  143. package/dist/ui/assets/{AgentsModule-Bdq3T1Ts.js → AgentsModule-CpsTmrIW.js} +1 -1
  144. package/dist/ui/assets/{AnnotatedAttachmentWorkspace-jKUVo-PJ.js → AnnotatedAttachmentWorkspace-C_TmXZwA.js} +1 -1
  145. package/dist/ui/assets/{DocumentWorkspace-DuQa-uIC.js → DocumentWorkspace-C3GyzrWm.js} +2 -2
  146. package/dist/ui/assets/{InitiativesModule-C8Pi2VTk.js → InitiativesModule-4-Rh2K2n.js} +1 -1
  147. package/dist/ui/assets/PlansPage-BP7AYOqr.js +1 -0
  148. package/dist/ui/assets/TaskSettings-BOC5F6Ag.js +8 -0
  149. package/dist/ui/assets/{TaskSettings-BwB9NtmJ.css → TaskSettings-CnIBL_Eb.css} +1 -1
  150. package/dist/ui/assets/{WorkflowsModule-b6TLuHwJ.js → WorkflowsModule-CGk9s3NJ.js} +1 -1
  151. package/dist/ui/assets/index-CLIMgR6g.js +6 -0
  152. package/dist/ui/assets/index-DneETxcu.css +1 -0
  153. package/dist/ui/assets/{vendor-icons-QZyhEwgT.js → vendor-icons-pWocEipT.js} +1 -1
  154. package/dist/ui/index.html +3 -3
  155. package/dist/utils/accountProfileSummaryCache.d.ts +31 -0
  156. package/dist/utils/accountProfileSummaryCache.js +97 -0
  157. package/dist/utils/accountProfileSummaryCache.test.d.ts +1 -0
  158. package/dist/utils/accountProfileSummaryCache.test.js +63 -0
  159. package/dist/utils/taskActivity.js +17 -4
  160. package/dist/utils/taskActivity.test.js +27 -0
  161. package/dist/utils/taskNormalization.js +10 -2
  162. package/dist/utils/taskReferences.d.ts +11 -1
  163. package/dist/utils/taskReferences.js +33 -1
  164. package/package.json +4 -3
  165. package/scripts/qa-open-lib.mjs +175 -0
  166. package/scripts/qa-open-lib.mjs.d.ts +49 -0
  167. package/scripts/qa-open.mjs +352 -0
  168. package/dist/ui/assets/PlansPage-B6BDECSi.js +0 -1
  169. package/dist/ui/assets/TaskSettings-DUJgRTPU.js +0 -8
  170. package/dist/ui/assets/index-BiiF1cLQ.css +0 -1
  171. package/dist/ui/assets/index-xBWLPOEb.js +0 -6
@@ -173,6 +173,7 @@
173
173
  display: inline-flex;
174
174
  align-items: center;
175
175
  justify-content: center;
176
+ gap: 6px;
176
177
  background: var(--surface-page);
177
178
  color: var(--text-secondary);
178
179
  font-size: 12px;
@@ -185,6 +186,16 @@
185
186
  margin-left: 8px;
186
187
  }
187
188
 
189
+ .taskCountBadgeIcon {
190
+ flex: 0 0 auto;
191
+ }
192
+
193
+ .taskCountBadgeAlert {
194
+ color: #f87171;
195
+ border-color: color-mix(in srgb, #ef4444 45%, var(--border-default));
196
+ box-shadow: 0 0 10px rgba(239, 68, 68, 0.18);
197
+ }
198
+
188
199
  .headerActions {
189
200
  display: flex;
190
201
  align-items: center;
@@ -595,7 +606,7 @@
595
606
  }
596
607
 
597
608
  .taskIdBadge {
598
- display: flex;
609
+ display: inline-flex;
599
610
  align-items: center;
600
611
  gap: 5px;
601
612
  background: color-mix(in srgb, var(--color-info, #3b82f6) 12%, transparent);
@@ -611,9 +622,20 @@
611
622
  position: relative;
612
623
  z-index: 5;
613
624
  white-space: nowrap;
625
+ min-width: 0;
626
+ max-width: min(100%, 12rem);
614
627
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--color-info, #3b82f6) 8%, transparent);
615
628
  }
616
629
 
630
+ .taskIdBadgeLabel {
631
+ display: block;
632
+ min-width: 0;
633
+ max-width: 100%;
634
+ overflow: hidden;
635
+ text-overflow: ellipsis;
636
+ white-space: nowrap;
637
+ }
638
+
617
639
  .taskIdBadge:hover {
618
640
  background: color-mix(in srgb, var(--color-info, #3b82f6) 18%, transparent);
619
641
  color: var(--color-info, #3b82f6);
@@ -2861,6 +2883,19 @@
2861
2883
  line-height: 1;
2862
2884
  }
2863
2885
 
2886
+ .taskHierarchyMeta {
2887
+ display: inline-flex;
2888
+ align-items: center;
2889
+ padding: 2px 8px;
2890
+ border-radius: 999px;
2891
+ border: 1px solid rgba(245, 158, 11, 0.24);
2892
+ background: rgba(245, 158, 11, 0.14);
2893
+ color: var(--text-secondary);
2894
+ font-size: 11px;
2895
+ font-weight: 600;
2896
+ line-height: 1.2;
2897
+ }
2898
+
2864
2899
  .taskHierarchyAddBtn {
2865
2900
  justify-content: center;
2866
2901
  padding-inline: 6px;
@@ -3838,7 +3873,7 @@
3838
3873
  .codeBlock {
3839
3874
  margin: 0;
3840
3875
  background: #1e1e2e !important;
3841
- padding: 20px !important;
3876
+ padding: 14px 16px !important;
3842
3877
  border: none !important;
3843
3878
  overflow-x: auto;
3844
3879
  border-radius: 0 !important;
@@ -91,7 +91,7 @@ function installFetchStub(persistedState, options) {
91
91
  vi.stubGlobal('fetch', vi.fn((url, init) => {
92
92
  const method = String(init?.method || 'GET').toUpperCase();
93
93
  if (url.includes('/api/taskforce/auth/runtime-config')) {
94
- return Promise.resolve({ ok: true, json: () => Promise.resolve({ config: { runtimeMode: 'local', workspaceMode: 'single-local', authSource: 'local' } }) });
94
+ return Promise.resolve({ ok: true, json: () => Promise.resolve({ config: { runtimeMode: 'local', workspaceMode: 'single-local', authSource: 'cloud' } }) });
95
95
  }
96
96
  if (url.includes('/api/taskforce/auth/session')) {
97
97
  return Promise.resolve({ ok: true, json: () => Promise.resolve({ authenticated: false, authRequiredForApi: false, userId: 'anonymous', email: '', workspaceId: 'default' }) });
@@ -20,14 +20,6 @@ function isDebugModeEnabled() {
20
20
  return runtimeOverride;
21
21
  return typeof __DEBUG_MODE__ !== 'undefined' && __DEBUG_MODE__;
22
22
  }
23
- function buildDebugRegistrationCredentials() {
24
- const suffix = Date.now().toString(36);
25
- return {
26
- email: `debug+${suffix}@example.com`,
27
- password: 'DebugPass123!',
28
- displayName: `Debug ${suffix.slice(-4).toUpperCase()}`
29
- };
30
- }
31
23
  function defaultSetupTaxonomySections(pack) {
32
24
  return {
33
25
  categories: Boolean(pack?.categories?.length),
@@ -35,6 +27,16 @@ function defaultSetupTaxonomySections(pack) {
35
27
  priorities: Boolean(pack?.priorities?.length),
36
28
  };
37
29
  }
30
+ function buildPlansPath(params = {}) {
31
+ const query = new URLSearchParams();
32
+ query.set('screen', 'plans');
33
+ for (const [key, value] of Object.entries(params)) {
34
+ const normalized = String(value || '').trim();
35
+ if (normalized)
36
+ query.set(key, normalized);
37
+ }
38
+ return `/?${query.toString()}`;
39
+ }
38
40
  function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange, onHeaderMouseDown, isDragging, onClose, mode = 'standalone' }) {
39
41
  const location = useLocation();
40
42
  const routeQuery = useMemo(() => new URLSearchParams(location.search), [location.search]);
@@ -44,13 +46,15 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
44
46
  const isPricingRoute = location.pathname === '/pricing';
45
47
  const isPlansRoute = location.pathname === '/plans';
46
48
  const isPlansScreen = location.pathname === '/' && String(routeQuery.get('screen') || '').trim().toLowerCase() === 'plans';
47
- const planSelectionPath = '/?screen=plans&gate=plan_selection_required';
49
+ const planSelectionPath = buildPlansPath({ gate: 'plan_selection_required' });
48
50
  const navigate = useNavigate();
49
51
  const [loginEmail, setLoginEmail] = useState('');
50
52
  const [loginDisplayName, setLoginDisplayName] = useState('');
51
53
  const [loginPassword, setLoginPassword] = useState('');
52
54
  const [authMode, setAuthMode] = useState('login');
53
55
  const [verificationToken, setVerificationToken] = useState('');
56
+ const [pendingVerificationWorkspaceSetupRequired, setPendingVerificationWorkspaceSetupRequired] = useState(false);
57
+ const [pendingVerificationRedirectPath, setPendingVerificationRedirectPath] = useState(null);
54
58
  const [resetToken, setResetToken] = useState('');
55
59
  const [inviteToken, setInviteToken] = useState('');
56
60
  const [inviteEmail, setInviteEmail] = useState(null);
@@ -126,6 +130,7 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
126
130
  && !isPricingRoute
127
131
  && !isPlansRoute
128
132
  && !isPlansScreen;
133
+ const directRegistrationEnabled = cloudAuthConfigured;
129
134
  const setupStep = useMemo(() => String(new URLSearchParams(location.search).get('step') || '').trim().toLowerCase(), [location.search]);
130
135
  const setupIntent = useMemo(() => String(new URLSearchParams(location.search).get('intent') || '').trim().toLowerCase(), [location.search]);
131
136
  const isCreateWorkspaceIntent = isSetupRoute && setupStep === 'workspace' && setupIntent === 'create-workspace';
@@ -207,12 +212,12 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
207
212
  if (cancelled || !res.ok)
208
213
  return;
209
214
  const gate = String(payload?.gate || '').trim().toLowerCase();
210
- if (gate !== 'ok' && gate !== 'plan_selection_required' && gate !== 'misconfigured' && gate !== 'missing_entitlement') {
215
+ if (gate !== 'ok' && gate !== 'plan_selection_required' && gate !== 'checkout_pending' && gate !== 'misconfigured' && gate !== 'missing_entitlement') {
211
216
  setAccountAccessGate(null);
212
217
  return;
213
218
  }
214
219
  setAccountAccessGate({
215
- gate,
220
+ gate: gate,
216
221
  canAccessApp: payload?.canAccessApp === true,
217
222
  canAccessPlans: payload?.canAccessPlans !== false,
218
223
  message: typeof payload?.message === 'string' ? payload.message : null
@@ -358,25 +363,51 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
358
363
  return '/';
359
364
  return raw;
360
365
  }, [location.search]);
366
+ const resolvedSignupInterval = selectedSignupInterval === 'year' ? 'year' : 'month';
367
+ const resolvePostRegistrationPath = useCallback((input) => {
368
+ if (input?.planSelectionRequired || input?.commercialState === 'pending_plan_selection') {
369
+ return planSelectionPath;
370
+ }
371
+ if (input?.checkoutPending || input?.commercialState === 'checkout_pending') {
372
+ return buildPlansPath({
373
+ gate: 'checkout_pending',
374
+ checkout: selectedSignupPlanVersionId ? 'start' : null,
375
+ planId: selectedSignupPlanId || null,
376
+ planVersionId: selectedSignupPlanVersionId || null,
377
+ interval: selectedSignupPlanVersionId ? resolvedSignupInterval : null
378
+ });
379
+ }
380
+ return nextPath;
381
+ }, [
382
+ nextPath,
383
+ planSelectionPath,
384
+ resolvedSignupInterval,
385
+ selectedSignupPlanId,
386
+ selectedSignupPlanVersionId
387
+ ]);
361
388
  const loginRouteMode = useMemo(() => {
362
389
  const modeRaw = String(new URLSearchParams(location.search).get('mode') || '').trim().toLowerCase();
363
- if (modeRaw === 'register' || modeRaw === 'verify' || modeRaw === 'forgot' || modeRaw === 'reset' || modeRaw === 'invite') {
390
+ if (modeRaw === 'register') {
391
+ return directRegistrationEnabled ? 'register' : 'login';
392
+ }
393
+ if (modeRaw === 'verify' || modeRaw === 'forgot' || modeRaw === 'reset' || modeRaw === 'invite') {
364
394
  return modeRaw;
365
395
  }
366
396
  return 'login';
367
- }, [location.search]);
397
+ }, [directRegistrationEnabled, location.search]);
368
398
  const loginRouteToken = useMemo(() => String(new URLSearchParams(location.search).get('token') || '').trim(), [location.search]);
369
399
  const setLoginMode = useCallback((mode) => {
370
400
  const params = new URLSearchParams(location.search);
371
- if (mode === 'login') {
401
+ const nextMode = mode === 'register' && !directRegistrationEnabled ? 'login' : mode;
402
+ if (nextMode === 'login') {
372
403
  params.delete('mode');
373
404
  }
374
405
  else {
375
- params.set('mode', mode);
406
+ params.set('mode', nextMode);
376
407
  }
377
408
  const query = params.toString();
378
409
  navigate(`/login${query ? `?${query}` : ''}`, { replace: true });
379
- }, [location.search, navigate]);
410
+ }, [directRegistrationEnabled, location.search, navigate]);
380
411
  const routeToWorkspaceSetupIfNeeded = useCallback(async (hintWorkspaceSetupRequired) => {
381
412
  if (!workspaceSwitchingEnabled)
382
413
  return false;
@@ -408,6 +439,14 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
408
439
  if (loginRouteMode === 'invite' && loginRouteToken)
409
440
  setInviteToken(loginRouteToken);
410
441
  }, [isLoginRoute, loginRouteMode, loginRouteToken]);
442
+ useEffect(() => {
443
+ if (authMode === 'verify')
444
+ return;
445
+ if (isLoginRoute && loginRouteMode === 'verify')
446
+ return;
447
+ setPendingVerificationWorkspaceSetupRequired(false);
448
+ setPendingVerificationRedirectPath(null);
449
+ }, [authMode, isLoginRoute, loginRouteMode]);
411
450
  useEffect(() => {
412
451
  if (!isLoginRoute || authMode !== 'invite' || !inviteToken.trim())
413
452
  return;
@@ -625,15 +664,7 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
625
664
  ? 'New password'
626
665
  : (authMode === 'invite'
627
666
  ? (invitePasswordRequired ? 'Create password' : 'Password')
628
- : 'Password'), onChange: (event) => setLoginPassword(event.target.value) })), authMode === 'register' && isDebugModeEnabled() && (_jsx("button", { className: authStyles.authModeLink, type: "button", disabled: loginBusy, onClick: () => {
629
- const credentials = buildDebugRegistrationCredentials();
630
- setLoginEmail(credentials.email);
631
- setLoginDisplayName(credentials.displayName);
632
- setLoginPassword(credentials.password);
633
- setLoginNotice(null);
634
- setLoginError(null);
635
- setLoginErrorCode(null);
636
- }, children: "Fill Form" })), authMode === 'verify' && (_jsx("input", { className: `${styles.input} ${authStyles.loginInput}`, type: "text", value: verificationToken, placeholder: "Verification token", onChange: (event) => setVerificationToken(event.target.value) })), authMode === 'reset' && (_jsx("input", { className: `${styles.input} ${authStyles.loginInput}`, type: "text", value: resetToken, placeholder: "Reset token", onChange: (event) => setResetToken(event.target.value) })), authMode === 'invite' && (_jsxs(_Fragment, { children: [_jsx("input", { className: `${styles.input} ${authStyles.loginInput}`, type: "text", value: inviteToken, placeholder: "Invite token", onChange: (event) => setInviteToken(event.target.value) }), inviteEmail && (_jsxs("p", { className: authStyles.loginSubtitle, children: ["Invite for: ", _jsx("strong", { children: inviteEmail }), inviteWorkspaceId ? ` · Workspace: ${inviteWorkspaceId}` : ''] }))] })), loginNotice && _jsx("p", { className: authStyles.loginSubtitle, children: loginNotice }), loginError && _jsx("p", { className: authStyles.loginError, children: loginError }), authMode === 'login' && loginErrorCode === 'EMAIL_NOT_VERIFIED' && loginEmail.trim() && (_jsx("button", { className: authStyles.authModeLink, disabled: loginBusy || verifyCooldownActive, onClick: async () => {
667
+ : 'Password'), onChange: (event) => setLoginPassword(event.target.value) })), authMode === 'verify' && (_jsx("input", { className: `${styles.input} ${authStyles.loginInput}`, type: "text", value: verificationToken, placeholder: "Verification token", onChange: (event) => setVerificationToken(event.target.value) })), authMode === 'reset' && (_jsx("input", { className: `${styles.input} ${authStyles.loginInput}`, type: "text", value: resetToken, placeholder: "Reset token", onChange: (event) => setResetToken(event.target.value) })), authMode === 'invite' && (_jsxs(_Fragment, { children: [_jsx("input", { className: `${styles.input} ${authStyles.loginInput}`, type: "text", value: inviteToken, placeholder: "Invite token", onChange: (event) => setInviteToken(event.target.value) }), inviteEmail && (_jsxs("p", { className: authStyles.loginSubtitle, children: ["Invite for: ", _jsx("strong", { children: inviteEmail }), inviteWorkspaceId ? ` · Workspace: ${inviteWorkspaceId}` : ''] }))] })), loginNotice && _jsx("p", { className: authStyles.loginSubtitle, children: loginNotice }), loginError && _jsx("p", { className: authStyles.loginError, children: loginError }), authMode === 'login' && loginErrorCode === 'EMAIL_NOT_VERIFIED' && loginEmail.trim() && (_jsx("button", { className: authStyles.authModeLink, disabled: loginBusy || verifyCooldownActive, onClick: async () => {
637
668
  setLoginBusy(true);
638
669
  setLoginNotice(null);
639
670
  const sent = await requestEmailVerification(loginEmail);
@@ -721,11 +752,10 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
721
752
  }
722
753
  }
723
754
  else if (authMode === 'register') {
724
- if (await routeToWorkspaceSetupIfNeeded(result.workspaceSetupRequired)) {
725
- setLoginBusy(false);
726
- return;
727
- }
755
+ const postRegistrationPath = resolvePostRegistrationPath(result);
728
756
  if (result.verificationRequired) {
757
+ setPendingVerificationWorkspaceSetupRequired(Boolean(result.workspaceSetupRequired));
758
+ setPendingVerificationRedirectPath(postRegistrationPath);
729
759
  if (result.verificationToken)
730
760
  setVerificationToken(result.verificationToken);
731
761
  setVerifyCooldownUntil(Date.now() + 30_000);
@@ -735,17 +765,26 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
735
765
  : 'Account created. Check your email for verification instructions.');
736
766
  }
737
767
  else {
768
+ await retryBootstrapChecks();
769
+ if (await routeToWorkspaceSetupIfNeeded(result.workspaceSetupRequired)) {
770
+ setLoginBusy(false);
771
+ return;
772
+ }
738
773
  setLoginEmail('');
739
774
  setLoginDisplayName('');
740
775
  setLoginPassword('');
741
- navigate(nextPath, { replace: true });
776
+ navigate(postRegistrationPath, { replace: true });
742
777
  }
743
778
  }
744
779
  else if (authMode === 'verify') {
745
780
  await retryBootstrapChecks();
781
+ if (await routeToWorkspaceSetupIfNeeded(pendingVerificationWorkspaceSetupRequired)) {
782
+ setLoginBusy(false);
783
+ return;
784
+ }
746
785
  setLoginEmail('');
747
786
  setLoginPassword('');
748
- navigate(nextPath, { replace: true });
787
+ navigate(pendingVerificationRedirectPath || nextPath, { replace: true });
749
788
  }
750
789
  else if (authMode === 'forgot') {
751
790
  if (result.resetToken)
@@ -810,12 +849,12 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
810
849
  ? 'Reset Password'
811
850
  : (inviteResolution === 'existing_user_signed_out'
812
851
  ? 'Sign In to Continue'
813
- : (invitePasswordRequired ? 'Create Account and Join' : 'Join Workspace'))) }), authMode === 'register' && (_jsxs("p", { className: authStyles.registerConsent, children: ["By creating an account, you agree to the", ' ', _jsx("a", { className: authStyles.registerConsentLink, href: "https://taskforcehq.com/legal/terms", target: "_blank", rel: "noopener noreferrer", children: "Terms of Service" }), ' ', "and acknowledge the", ' ', _jsx("a", { className: authStyles.registerConsentLink, href: "https://taskforcehq.com/legal/privacy", target: "_blank", rel: "noopener noreferrer", children: "Privacy Policy" }), "."] })), (authMode === 'login' || authMode === 'register') && (_jsxs("p", { className: authStyles.authModeSwitch, children: [authMode === 'login' ? 'Need an account?' : 'Already have an account?', ' ', _jsx("button", { className: authStyles.authModeSwitchLink, disabled: loginBusy, onClick: () => {
852
+ : (invitePasswordRequired ? 'Create Account and Join' : 'Join Workspace'))) }), authMode === 'register' && (_jsxs("p", { className: authStyles.registerConsent, children: ["By creating an account, you agree to the", ' ', _jsx("a", { className: authStyles.registerConsentLink, href: "https://taskforcehq.com/legal/terms", target: "_blank", rel: "noopener noreferrer", children: "Terms of Service" }), ' ', "and acknowledge the", ' ', _jsx("a", { className: authStyles.registerConsentLink, href: "https://taskforcehq.com/legal/privacy", target: "_blank", rel: "noopener noreferrer", children: "Privacy Policy" }), "."] })), directRegistrationEnabled && (authMode === 'login' || authMode === 'register') && (_jsxs("p", { className: authStyles.authModeSwitch, children: [authMode === 'login' ? 'Need an account?' : 'Already have an account?', ' ', _jsx("button", { className: authStyles.authModeSwitchLink, disabled: loginBusy, onClick: () => {
814
853
  setLoginError(null);
815
854
  setLoginErrorCode(null);
816
855
  setLoginNotice(null);
817
856
  setLoginMode(authMode === 'login' ? 'register' : 'login');
818
- }, children: authMode === 'login' ? 'Register' : 'Sign In' })] })), _jsxs("div", { className: authStyles.authModeLinks, children: [(authMode === 'login' || authMode === 'register') && (_jsxs(_Fragment, { children: [_jsx("button", { className: authStyles.authModeLink, disabled: loginBusy || resetCooldownActive, onClick: () => { setLoginError(null); setLoginErrorCode(null); setLoginNotice(null); setLoginMode('forgot'); }, children: resetCooldownActive ? `Forgot Password (${resetCooldownSeconds}s)` : 'Forgot Password' }), _jsx("button", { className: authStyles.authModeLink, disabled: loginBusy || verifyCooldownActive, onClick: () => { setLoginError(null); setLoginErrorCode(null); setLoginNotice(null); setLoginMode('verify'); }, children: verifyCooldownActive ? `Resend Verification (${verifyCooldownSeconds}s)` : 'Resend Verification' }), _jsx("button", { className: authStyles.authModeLink, disabled: loginBusy, onClick: () => { setLoginError(null); setLoginErrorCode(null); setLoginNotice(null); setLoginMode('invite'); }, children: "Accept Invite" })] })), (authMode !== 'login' && authMode !== 'register') && (_jsxs(_Fragment, { children: [_jsx("button", { className: authStyles.authModeLink, disabled: loginBusy, onClick: () => { setLoginError(null); setLoginErrorCode(null); setLoginNotice(null); setLoginMode('login'); }, children: "Sign In" }), _jsx("button", { className: authStyles.authModeLink, disabled: loginBusy, onClick: () => { setLoginError(null); setLoginErrorCode(null); setLoginNotice(null); setLoginMode('register'); }, children: "Register" }), authMode === 'reset' && (_jsx("button", { className: authStyles.authModeLink, disabled: loginBusy || resetCooldownActive, onClick: () => { setLoginError(null); setLoginErrorCode(null); setLoginNotice(null); setLoginMode('forgot'); }, children: resetCooldownActive ? `Forgot Password (${resetCooldownSeconds}s)` : 'Forgot Password' }))] }))] })] }) }) }));
857
+ }, children: authMode === 'login' ? 'Register' : 'Sign In' })] })), _jsxs("div", { className: authStyles.authModeLinks, children: [(authMode === 'login' || authMode === 'register') && (_jsxs(_Fragment, { children: [_jsx("button", { className: authStyles.authModeLink, disabled: loginBusy || resetCooldownActive, onClick: () => { setLoginError(null); setLoginErrorCode(null); setLoginNotice(null); setLoginMode('forgot'); }, children: resetCooldownActive ? `Forgot Password (${resetCooldownSeconds}s)` : 'Forgot Password' }), _jsx("button", { className: authStyles.authModeLink, disabled: loginBusy || verifyCooldownActive, onClick: () => { setLoginError(null); setLoginErrorCode(null); setLoginNotice(null); setLoginMode('verify'); }, children: verifyCooldownActive ? `Resend Verification (${verifyCooldownSeconds}s)` : 'Resend Verification' }), _jsx("button", { className: authStyles.authModeLink, disabled: loginBusy, onClick: () => { setLoginError(null); setLoginErrorCode(null); setLoginNotice(null); setLoginMode('invite'); }, children: "Accept Invite" })] })), (authMode !== 'login' && authMode !== 'register') && (_jsxs(_Fragment, { children: [_jsx("button", { className: authStyles.authModeLink, disabled: loginBusy, onClick: () => { setLoginError(null); setLoginErrorCode(null); setLoginNotice(null); setLoginMode('login'); }, children: "Sign In" }), directRegistrationEnabled && (_jsx("button", { className: authStyles.authModeLink, disabled: loginBusy, onClick: () => { setLoginError(null); setLoginErrorCode(null); setLoginNotice(null); setLoginMode('register'); }, children: "Register" })), authMode === 'reset' && (_jsx("button", { className: authStyles.authModeLink, disabled: loginBusy || resetCooldownActive, onClick: () => { setLoginError(null); setLoginErrorCode(null); setLoginNotice(null); setLoginMode('forgot'); }, children: resetCooldownActive ? `Forgot Password (${resetCooldownSeconds}s)` : 'Forgot Password' }))] }))] })] }) }) }));
819
858
  }
820
859
  if (shouldForceLogin && !isLoginRoute) {
821
860
  return (_jsx("div", { className: shellStyles.standaloneWrapper, "data-theme": currentTheme, children: _jsx("div", { className: authStyles.loginView, children: _jsxs("div", { className: authStyles.loginCard, children: [_jsxs("div", { className: authStyles.authBrandRow, children: [_jsx("img", { src: authBrandLogo, alt: "Taskforce", className: authStyles.loginLogo }), _jsx("h1", { className: authStyles.loginTitle, children: "Loading Taskforce" })] }), _jsx("p", { className: authStyles.loginSubtitle, children: "Redirecting to sign in..." })] }) }) }));