@taskforcehq/taskforce 0.3.300 → 0.3.302

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (153) hide show
  1. package/README.md +19 -0
  2. package/dist/Taskforce.module.css +26 -2
  3. package/dist/TaskforceCore.filter-persistence.test.js +1 -1
  4. package/dist/TaskforceCore.js +13 -24
  5. package/dist/TaskforceCore.test.js +167 -152
  6. package/dist/components/features/TaskSettings.js +14 -82
  7. package/dist/components/features/TaskSettings.test.js +33 -70
  8. package/dist/components/features/settings/TaxonomyLibraryManager.js +8 -2
  9. package/dist/components/features/settings/TaxonomyLibraryManager.test.js +1 -1
  10. package/dist/components/task/TaskActionHeader.js +6 -3
  11. package/dist/components/task/TaskCard.js +6 -3
  12. package/dist/components/task/TaskForm.js +2 -2
  13. package/dist/components/task/TaskForm.test.js +41 -2
  14. package/dist/components/ui/ReferenceBadgeButton.js +1 -1
  15. package/dist/components/views/PlansPage.js +9 -8
  16. package/dist/components/views/StandaloneLayout.js +490 -207
  17. package/dist/components/views/panels/FilterToolbar.d.ts +2 -0
  18. package/dist/components/views/panels/FilterToolbarControls.d.ts +1 -0
  19. package/dist/components/views/panels/FilterToolbarControls.js +1 -1
  20. package/dist/components/views/panels/PlanningDrawer.js +19 -2
  21. package/dist/components/views/standalone/modals/AccountHubModal.d.ts +43 -0
  22. package/dist/components/views/standalone/modals/AccountHubModal.js +22 -0
  23. package/dist/components/views/standalone/modals/CreateWorkspaceConfirmModal.d.ts +9 -0
  24. package/dist/components/views/standalone/modals/CreateWorkspaceConfirmModal.js +8 -0
  25. package/dist/components/views/standalone/modals/EditProfileModal.d.ts +26 -0
  26. package/dist/components/views/standalone/modals/EditProfileModal.js +10 -0
  27. package/dist/components/views/standalone/modals/HelpModal.d.ts +9 -0
  28. package/dist/components/views/standalone/modals/HelpModal.js +8 -0
  29. package/dist/components/views/standalone/modals/ScheduleWarningModal.d.ts +13 -0
  30. package/dist/components/views/standalone/modals/ScheduleWarningModal.js +6 -0
  31. package/dist/components/views/standalone/modals/SyncStatusModal.d.ts +61 -0
  32. package/dist/components/views/standalone/modals/SyncStatusModal.js +38 -0
  33. package/dist/components/views/standalone/modals/TeamManagementModal.d.ts +64 -0
  34. package/dist/components/views/standalone/modals/TeamManagementModal.js +10 -0
  35. package/dist/components/views/standalone/modals/types.d.ts +2 -0
  36. package/dist/components/views/standalone/modals/types.js +1 -0
  37. package/dist/components/views/teamManagementAccess.d.ts +1 -1
  38. package/dist/components/views/teamManagementAccess.js +2 -2
  39. package/dist/components/views/teamManagementAccess.test.js +3 -3
  40. package/dist/config/deployment.js +2 -2
  41. package/dist/config/deployment.test.js +5 -3
  42. package/dist/core/AiProfileService.js +7 -2
  43. package/dist/core/AiProfiles.test.js +19 -0
  44. package/dist/core/SyncReconciliationService.d.ts +2 -0
  45. package/dist/core/SyncReconciliationService.js +3 -0
  46. package/dist/core/TaskAuditTimeline.test.js +40 -0
  47. package/dist/core/Taskforce.d.ts +49 -0
  48. package/dist/core/Taskforce.ids.test.js +133 -0
  49. package/dist/core/Taskforce.js +241 -78
  50. package/dist/core/UserProfileAvatarDrafts.test.d.ts +1 -0
  51. package/dist/core/UserProfileAvatarDrafts.test.js +102 -0
  52. package/dist/core/taskReferencePolicy.d.ts +28 -0
  53. package/dist/core/taskReferencePolicy.js +189 -0
  54. package/dist/core/types.d.ts +2 -0
  55. package/dist/hooks/tasks/useTaskFormActions.js +3 -0
  56. package/dist/hooks/useTaskData.js +16 -11
  57. package/dist/hooks/useTaskData.test.js +127 -0
  58. package/dist/hooks/useTaskforce.d.ts +5 -0
  59. package/dist/hooks/useTaskforce.js +63 -15
  60. package/dist/hooks/useTaskforce.sync-behavior.test.js +185 -6
  61. package/dist/mcp/aiProfileBootstrap.d.ts +29 -0
  62. package/dist/mcp/aiProfileBootstrap.js +80 -0
  63. package/dist/mcp/aiProfileBootstrap.test.d.ts +1 -0
  64. package/dist/mcp/aiProfileBootstrap.test.js +68 -0
  65. package/dist/mcp/clientIntegrations.d.ts +2 -8
  66. package/dist/mcp/clientIntegrations.js +21 -40
  67. package/dist/mcp/clientIntegrations.test.js +28 -2
  68. package/dist/mcp/clientProfileDefaults.d.ts +12 -0
  69. package/dist/mcp/clientProfileDefaults.js +37 -0
  70. package/dist/mcp/runtime.d.ts +10 -7
  71. package/dist/mcp/runtime.js +45 -23
  72. package/dist/mcp/runtime.test.js +56 -3
  73. package/dist/migrations/taskSchemaMigrations.js +53 -1
  74. package/dist/qaOpen.test.d.ts +1 -0
  75. package/dist/qaOpen.test.js +56 -0
  76. package/dist/server/index.cookieProxy.test.js +1 -0
  77. package/dist/server/index.d.ts +1 -0
  78. package/dist/server/index.js +15 -1
  79. package/dist/server/index.test.js +14 -1
  80. package/dist/server/routes/admin.js +25 -22
  81. package/dist/server/routes/auth.d.ts +5 -0
  82. package/dist/server/routes/auth.js +350 -16
  83. package/dist/server/routes/billing.js +11 -1
  84. package/dist/server/routes/billing.test.js +32 -2
  85. package/dist/server/routes/documents.js +6 -1
  86. package/dist/server/routes/shared.d.ts +1 -1
  87. package/dist/server/routes/shared.js +11 -3
  88. package/dist/server/routes/sync.integration.test.js +130 -0
  89. package/dist/server/routes/sync.js +226 -1924
  90. package/dist/server/routes/tasks.js +18 -4
  91. package/dist/server/routes.js +26 -19
  92. package/dist/server/routes.test.js +352 -6
  93. package/dist/shared/runtimeContract.d.ts +1 -1
  94. package/dist/sync/collaborationSyncPayload.d.ts +17 -0
  95. package/dist/sync/collaborationSyncPayload.js +131 -0
  96. package/dist/sync/collaborationSyncPayload.test.d.ts +1 -0
  97. package/dist/sync/collaborationSyncPayload.test.js +104 -0
  98. package/dist/sync/collaborationSyncState.d.ts +15 -0
  99. package/dist/sync/collaborationSyncState.js +60 -0
  100. package/dist/sync/contentSyncPayload.d.ts +45 -0
  101. package/dist/sync/contentSyncPayload.js +104 -0
  102. package/dist/sync/contentSyncPayload.test.d.ts +1 -0
  103. package/dist/sync/contentSyncPayload.test.js +104 -0
  104. package/dist/sync/contentSyncState.d.ts +88 -0
  105. package/dist/sync/contentSyncState.js +743 -0
  106. package/dist/sync/planningSyncPayload.d.ts +9 -0
  107. package/dist/sync/planningSyncPayload.js +82 -0
  108. package/dist/sync/planningSyncPayload.test.d.ts +1 -0
  109. package/dist/sync/planningSyncPayload.test.js +68 -0
  110. package/dist/sync/syncApplyHandlers.d.ts +83 -0
  111. package/dist/sync/syncApplyHandlers.js +445 -0
  112. package/dist/sync/taskSyncPayload.d.ts +33 -0
  113. package/dist/sync/taskSyncPayload.js +126 -0
  114. package/dist/sync/taskSyncPayload.test.d.ts +1 -0
  115. package/dist/sync/taskSyncPayload.test.js +54 -0
  116. package/dist/sync/workspacePullFeed.d.ts +28 -0
  117. package/dist/sync/workspacePullFeed.js +301 -0
  118. package/dist/sync/workspacePullFeed.test.d.ts +1 -0
  119. package/dist/sync/workspacePullFeed.test.js +166 -0
  120. package/dist/sync/workspaceSyncModel.d.ts +4 -1
  121. package/dist/sync/workspaceSyncModel.js +9 -2
  122. package/dist/sync/workspaceSyncModel.test.js +29 -0
  123. package/dist/test/setup.js +1 -1
  124. package/dist/types.d.ts +1 -0
  125. package/dist/ui/assets/{AgentsModule-TVmCgizS.js → AgentsModule-JG5jc3he.js} +1 -1
  126. package/dist/ui/assets/{AnnotatedAttachmentWorkspace-CAyhnPeR.js → AnnotatedAttachmentWorkspace-Dvi02Y7q.js} +1 -1
  127. package/dist/ui/assets/{DocumentWorkspace-BOCw5e4D.js → DocumentWorkspace-iys3zfca.js} +2 -2
  128. package/dist/ui/assets/{InitiativesModule-CN0r02Oi.js → InitiativesModule-DJvLJI3A.js} +1 -1
  129. package/dist/ui/assets/PlansPage-BMPlpFpw.js +1 -0
  130. package/dist/ui/assets/TaskSettings-Bqd4cCzW.js +8 -0
  131. package/dist/ui/assets/{TaskSettings-BwB9NtmJ.css → TaskSettings-CnIBL_Eb.css} +1 -1
  132. package/dist/ui/assets/{WorkflowsModule-CY9R2zPB.js → WorkflowsModule-BguHeQaV.js} +1 -1
  133. package/dist/ui/assets/index-MXeWoebC.css +1 -0
  134. package/dist/ui/assets/index-YJ4k5QNa.js +6 -0
  135. package/dist/ui/assets/{vendor-icons-QZyhEwgT.js → vendor-icons-C8EED3Oh.js} +1 -1
  136. package/dist/ui/index.html +3 -3
  137. package/dist/utils/billingStatusCache.d.ts +21 -0
  138. package/dist/utils/billingStatusCache.js +70 -0
  139. package/dist/utils/taskActivity.js +17 -4
  140. package/dist/utils/taskActivity.test.js +27 -0
  141. package/dist/utils/taskNormalization.js +10 -2
  142. package/dist/utils/taskReferences.d.ts +11 -1
  143. package/dist/utils/taskReferences.js +33 -1
  144. package/package.json +4 -1
  145. package/scripts/deploy-prod.sh +41 -0
  146. package/scripts/deploy-staging.sh +23 -0
  147. package/scripts/qa-open-lib.mjs +175 -0
  148. package/scripts/qa-open-lib.mjs.d.ts +49 -0
  149. package/scripts/qa-open.mjs +352 -0
  150. package/dist/ui/assets/PlansPage-FcQSNLZd.js +0 -1
  151. package/dist/ui/assets/TaskSettings-Cw5zYD1L.js +0 -8
  152. package/dist/ui/assets/index-Bzqy5QGm.js +0 -6
  153. package/dist/ui/assets/index-DxRiI3tY.css +0 -1
package/README.md CHANGED
@@ -177,6 +177,25 @@ npx @taskforcehq/taskforce open [port]
177
177
  - Before changing sync logic, confirm the watched build actually rebuilt, restart the local CLI/runtime if behavior still looks stale, and then retest sync.
178
178
  - If a sync bug seems "fixed in code but still reproducible," treat stale build output or a long-lived local runtime process as the first thing to rule out.
179
179
 
180
+ ### Local Validation Runbooks
181
+
182
+ These checks used to exist as manual-only GitHub Actions workflows, but they now live as local runbooks to avoid burning Actions credits.
183
+
184
+ - Full surface validation:
185
+ - `npm ci`
186
+ - `npm run check:deploy-preflight`
187
+ - `npm run build:app`
188
+ - `npm run build:site`
189
+ - `npm run build:admin`
190
+ - `npm run test -- src/server/auth.test.ts src/server/routes.test.ts src/server/cors.test.ts`
191
+ - Auth and runtime guard:
192
+ - `npm ci`
193
+ - `npm run test:auth-runtime-guard`
194
+ - Sync regression guard:
195
+ - `npm ci`
196
+ - `npx vitest src/sync/workspaceSyncModel.test.ts src/sync/syncService.test.ts src/sync/cloudSyncApi.test.ts src/hooks/useWorkspaceSyncController.test.ts src/hooks/useSyncOrchestrator.retry-closure.test.tsx src/server/routes.sync.integration.test.ts src/server/routes.test.ts`
197
+ - optional realtime attachment check: `npm run test:e2e:realtime:attachments`
198
+
180
199
  ### 🧭 Initiatives
181
200
  Initiatives provide template-based planning for complex work (for example `launch`, `migration`, `incident`) with reusable Definition of Done and Rollback checklist blocks.
182
201
 
@@ -595,7 +595,7 @@
595
595
  }
596
596
 
597
597
  .taskIdBadge {
598
- display: flex;
598
+ display: inline-flex;
599
599
  align-items: center;
600
600
  gap: 5px;
601
601
  background: color-mix(in srgb, var(--color-info, #3b82f6) 12%, transparent);
@@ -611,9 +611,20 @@
611
611
  position: relative;
612
612
  z-index: 5;
613
613
  white-space: nowrap;
614
+ min-width: 0;
615
+ max-width: min(100%, 12rem);
614
616
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--color-info, #3b82f6) 8%, transparent);
615
617
  }
616
618
 
619
+ .taskIdBadgeLabel {
620
+ display: block;
621
+ min-width: 0;
622
+ max-width: 100%;
623
+ overflow: hidden;
624
+ text-overflow: ellipsis;
625
+ white-space: nowrap;
626
+ }
627
+
617
628
  .taskIdBadge:hover {
618
629
  background: color-mix(in srgb, var(--color-info, #3b82f6) 18%, transparent);
619
630
  color: var(--color-info, #3b82f6);
@@ -2861,6 +2872,19 @@
2861
2872
  line-height: 1;
2862
2873
  }
2863
2874
 
2875
+ .taskHierarchyMeta {
2876
+ display: inline-flex;
2877
+ align-items: center;
2878
+ padding: 2px 8px;
2879
+ border-radius: 999px;
2880
+ border: 1px solid rgba(245, 158, 11, 0.24);
2881
+ background: rgba(245, 158, 11, 0.14);
2882
+ color: var(--text-secondary);
2883
+ font-size: 11px;
2884
+ font-weight: 600;
2885
+ line-height: 1.2;
2886
+ }
2887
+
2864
2888
  .taskHierarchyAddBtn {
2865
2889
  justify-content: center;
2866
2890
  padding-inline: 6px;
@@ -3838,7 +3862,7 @@
3838
3862
  .codeBlock {
3839
3863
  margin: 0;
3840
3864
  background: #1e1e2e !important;
3841
- padding: 20px !important;
3865
+ padding: 14px 16px !important;
3842
3866
  border: none !important;
3843
3867
  overflow-x: auto;
3844
3868
  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),
@@ -126,6 +118,7 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
126
118
  && !isPricingRoute
127
119
  && !isPlansRoute
128
120
  && !isPlansScreen;
121
+ const directRegistrationEnabled = cloudAuthConfigured;
129
122
  const setupStep = useMemo(() => String(new URLSearchParams(location.search).get('step') || '').trim().toLowerCase(), [location.search]);
130
123
  const setupIntent = useMemo(() => String(new URLSearchParams(location.search).get('intent') || '').trim().toLowerCase(), [location.search]);
131
124
  const isCreateWorkspaceIntent = isSetupRoute && setupStep === 'workspace' && setupIntent === 'create-workspace';
@@ -360,23 +353,27 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
360
353
  }, [location.search]);
361
354
  const loginRouteMode = useMemo(() => {
362
355
  const modeRaw = String(new URLSearchParams(location.search).get('mode') || '').trim().toLowerCase();
363
- if (modeRaw === 'register' || modeRaw === 'verify' || modeRaw === 'forgot' || modeRaw === 'reset' || modeRaw === 'invite') {
356
+ if (modeRaw === 'register') {
357
+ return directRegistrationEnabled ? 'register' : 'login';
358
+ }
359
+ if (modeRaw === 'verify' || modeRaw === 'forgot' || modeRaw === 'reset' || modeRaw === 'invite') {
364
360
  return modeRaw;
365
361
  }
366
362
  return 'login';
367
- }, [location.search]);
363
+ }, [directRegistrationEnabled, location.search]);
368
364
  const loginRouteToken = useMemo(() => String(new URLSearchParams(location.search).get('token') || '').trim(), [location.search]);
369
365
  const setLoginMode = useCallback((mode) => {
370
366
  const params = new URLSearchParams(location.search);
371
- if (mode === 'login') {
367
+ const nextMode = mode === 'register' && !directRegistrationEnabled ? 'login' : mode;
368
+ if (nextMode === 'login') {
372
369
  params.delete('mode');
373
370
  }
374
371
  else {
375
- params.set('mode', mode);
372
+ params.set('mode', nextMode);
376
373
  }
377
374
  const query = params.toString();
378
375
  navigate(`/login${query ? `?${query}` : ''}`, { replace: true });
379
- }, [location.search, navigate]);
376
+ }, [directRegistrationEnabled, location.search, navigate]);
380
377
  const routeToWorkspaceSetupIfNeeded = useCallback(async (hintWorkspaceSetupRequired) => {
381
378
  if (!workspaceSwitchingEnabled)
382
379
  return false;
@@ -625,15 +622,7 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
625
622
  ? 'New password'
626
623
  : (authMode === 'invite'
627
624
  ? (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 () => {
625
+ : '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
626
  setLoginBusy(true);
638
627
  setLoginNotice(null);
639
628
  const sent = await requestEmailVerification(loginEmail);
@@ -810,12 +799,12 @@ function TaskforceCoreWithRouter({ config = {}, initialTaskId, onTaskCountChange
810
799
  ? 'Reset Password'
811
800
  : (inviteResolution === 'existing_user_signed_out'
812
801
  ? '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: () => {
802
+ : (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
803
  setLoginError(null);
815
804
  setLoginErrorCode(null);
816
805
  setLoginNotice(null);
817
806
  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' }))] }))] })] }) }) }));
807
+ }, 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
808
  }
820
809
  if (shouldForceLogin && !isLoginRoute) {
821
810
  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..." })] }) }) }));