@stackframe/react 2.8.58 → 2.8.60

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 (235) hide show
  1. package/dist/components/api-key-dialogs.js.map +1 -1
  2. package/dist/components/api-key-table.js.map +1 -1
  3. package/dist/components/credential-sign-in.js.map +1 -1
  4. package/dist/components/credential-sign-up.js.map +1 -1
  5. package/dist/components/elements/form-warning.js.map +1 -1
  6. package/dist/components/elements/maybe-full-page.js.map +1 -1
  7. package/dist/components/elements/separator-with-text.js.map +1 -1
  8. package/dist/components/elements/sidebar-layout.js.map +1 -1
  9. package/dist/components/elements/ssr-layout-effect.js.map +1 -1
  10. package/dist/components/elements/user-avatar.js.map +1 -1
  11. package/dist/components/link.js.map +1 -1
  12. package/dist/components/magic-link-sign-in.js.map +1 -1
  13. package/dist/components/message-cards/known-error-message-card.js.map +1 -1
  14. package/dist/components/message-cards/message-card.js.map +1 -1
  15. package/dist/components/message-cards/predefined-message-card.js.map +1 -1
  16. package/dist/components/oauth-button-group.js.map +1 -1
  17. package/dist/components/oauth-button.js.map +1 -1
  18. package/dist/components/passkey-button.js.map +1 -1
  19. package/dist/components/profile-image-editor.js.map +1 -1
  20. package/dist/components/selected-team-switcher.js.map +1 -1
  21. package/dist/components/team-icon.js.map +1 -1
  22. package/dist/components/team-switcher.js.map +1 -1
  23. package/dist/components/use-in-iframe.js.map +1 -1
  24. package/dist/components/user-button.js.map +1 -1
  25. package/dist/components-page/account-settings/active-sessions/active-sessions-page.js.map +1 -1
  26. package/dist/components-page/account-settings/api-keys/api-keys-page.js.map +1 -1
  27. package/dist/components-page/account-settings/editable-text.js.map +1 -1
  28. package/dist/components-page/account-settings/email-and-auth/email-and-auth-page.js.map +1 -1
  29. package/dist/components-page/account-settings/email-and-auth/emails-section.js.map +1 -1
  30. package/dist/components-page/account-settings/email-and-auth/mfa-section.js.map +1 -1
  31. package/dist/components-page/account-settings/email-and-auth/otp-section.js.map +1 -1
  32. package/dist/components-page/account-settings/email-and-auth/passkey-section.js.map +1 -1
  33. package/dist/components-page/account-settings/email-and-auth/password-section.js.map +1 -1
  34. package/dist/components-page/account-settings/notifications/notifications-page.js.map +1 -1
  35. package/dist/components-page/account-settings/page-layout.js.map +1 -1
  36. package/dist/components-page/account-settings/payments/payments-page.js +79 -0
  37. package/dist/components-page/account-settings/payments/payments-page.js.map +1 -0
  38. package/dist/components-page/account-settings/payments/payments-panel.js +374 -0
  39. package/dist/components-page/account-settings/payments/payments-panel.js.map +1 -0
  40. package/dist/components-page/account-settings/profile-page/profile-page.js.map +1 -1
  41. package/dist/components-page/account-settings/section.js.map +1 -1
  42. package/dist/components-page/account-settings/settings/delete-account-section.js.map +1 -1
  43. package/dist/components-page/account-settings/settings/settings-page.js.map +1 -1
  44. package/dist/components-page/account-settings/settings/sign-out-section.js.map +1 -1
  45. package/dist/components-page/account-settings/teams/leave-team-section.js.map +1 -1
  46. package/dist/components-page/account-settings/teams/team-api-keys-section.js.map +1 -1
  47. package/dist/components-page/account-settings/teams/team-creation-page.js.map +1 -1
  48. package/dist/components-page/account-settings/teams/team-display-name-section.js.map +1 -1
  49. package/dist/components-page/account-settings/teams/team-member-invitation-section.js.map +1 -1
  50. package/dist/components-page/account-settings/teams/team-member-list-section.js.map +1 -1
  51. package/dist/components-page/account-settings/teams/team-page.js.map +1 -1
  52. package/dist/components-page/account-settings/teams/team-profile-image-section.js.map +1 -1
  53. package/dist/components-page/account-settings/teams/team-profile-user-section.js.map +1 -1
  54. package/dist/components-page/account-settings.js +22 -2
  55. package/dist/components-page/account-settings.js.map +1 -1
  56. package/dist/components-page/auth-page.js.map +1 -1
  57. package/dist/components-page/cli-auth-confirm.js.map +1 -1
  58. package/dist/components-page/email-verification.js.map +1 -1
  59. package/dist/components-page/error-page.js.map +1 -1
  60. package/dist/components-page/forgot-password.js.map +1 -1
  61. package/dist/components-page/magic-link-callback.js.map +1 -1
  62. package/dist/components-page/mfa.js.map +1 -1
  63. package/dist/components-page/oauth-callback.js.map +1 -1
  64. package/dist/components-page/onboarding.js.map +1 -1
  65. package/dist/components-page/password-reset.js.map +1 -1
  66. package/dist/components-page/section.js.map +1 -1
  67. package/dist/components-page/sign-in.js.map +1 -1
  68. package/dist/components-page/sign-out.js.map +1 -1
  69. package/dist/components-page/sign-up.js.map +1 -1
  70. package/dist/components-page/stack-handler-client.js.map +1 -1
  71. package/dist/components-page/stack-handler.js.map +1 -1
  72. package/dist/components-page/team-creation.js.map +1 -1
  73. package/dist/components-page/team-invitation.js.map +1 -1
  74. package/dist/esm/components/api-key-dialogs.js.map +1 -1
  75. package/dist/esm/components/api-key-table.js.map +1 -1
  76. package/dist/esm/components/credential-sign-in.js.map +1 -1
  77. package/dist/esm/components/credential-sign-up.js.map +1 -1
  78. package/dist/esm/components/elements/form-warning.js.map +1 -1
  79. package/dist/esm/components/elements/maybe-full-page.js.map +1 -1
  80. package/dist/esm/components/elements/separator-with-text.js.map +1 -1
  81. package/dist/esm/components/elements/sidebar-layout.js.map +1 -1
  82. package/dist/esm/components/elements/ssr-layout-effect.js.map +1 -1
  83. package/dist/esm/components/elements/user-avatar.js.map +1 -1
  84. package/dist/esm/components/link.js.map +1 -1
  85. package/dist/esm/components/magic-link-sign-in.js.map +1 -1
  86. package/dist/esm/components/message-cards/known-error-message-card.js.map +1 -1
  87. package/dist/esm/components/message-cards/message-card.js.map +1 -1
  88. package/dist/esm/components/message-cards/predefined-message-card.js.map +1 -1
  89. package/dist/esm/components/oauth-button-group.js.map +1 -1
  90. package/dist/esm/components/oauth-button.js.map +1 -1
  91. package/dist/esm/components/passkey-button.js.map +1 -1
  92. package/dist/esm/components/profile-image-editor.js.map +1 -1
  93. package/dist/esm/components/selected-team-switcher.js.map +1 -1
  94. package/dist/esm/components/team-icon.js.map +1 -1
  95. package/dist/esm/components/team-switcher.js.map +1 -1
  96. package/dist/esm/components/use-in-iframe.js.map +1 -1
  97. package/dist/esm/components/user-button.js.map +1 -1
  98. package/dist/esm/components-page/account-settings/active-sessions/active-sessions-page.js.map +1 -1
  99. package/dist/esm/components-page/account-settings/api-keys/api-keys-page.js.map +1 -1
  100. package/dist/esm/components-page/account-settings/editable-text.js.map +1 -1
  101. package/dist/esm/components-page/account-settings/email-and-auth/email-and-auth-page.js.map +1 -1
  102. package/dist/esm/components-page/account-settings/email-and-auth/emails-section.js.map +1 -1
  103. package/dist/esm/components-page/account-settings/email-and-auth/mfa-section.js.map +1 -1
  104. package/dist/esm/components-page/account-settings/email-and-auth/otp-section.js.map +1 -1
  105. package/dist/esm/components-page/account-settings/email-and-auth/passkey-section.js.map +1 -1
  106. package/dist/esm/components-page/account-settings/email-and-auth/password-section.js.map +1 -1
  107. package/dist/esm/components-page/account-settings/notifications/notifications-page.js.map +1 -1
  108. package/dist/esm/components-page/account-settings/page-layout.js.map +1 -1
  109. package/dist/esm/components-page/account-settings/payments/payments-page.js +55 -0
  110. package/dist/esm/components-page/account-settings/payments/payments-page.js.map +1 -0
  111. package/dist/esm/components-page/account-settings/payments/payments-panel.js +350 -0
  112. package/dist/esm/components-page/account-settings/payments/payments-panel.js.map +1 -0
  113. package/dist/esm/components-page/account-settings/profile-page/profile-page.js.map +1 -1
  114. package/dist/esm/components-page/account-settings/section.js.map +1 -1
  115. package/dist/esm/components-page/account-settings/settings/delete-account-section.js.map +1 -1
  116. package/dist/esm/components-page/account-settings/settings/settings-page.js.map +1 -1
  117. package/dist/esm/components-page/account-settings/settings/sign-out-section.js.map +1 -1
  118. package/dist/esm/components-page/account-settings/teams/leave-team-section.js.map +1 -1
  119. package/dist/esm/components-page/account-settings/teams/team-api-keys-section.js.map +1 -1
  120. package/dist/esm/components-page/account-settings/teams/team-creation-page.js.map +1 -1
  121. package/dist/esm/components-page/account-settings/teams/team-display-name-section.js.map +1 -1
  122. package/dist/esm/components-page/account-settings/teams/team-member-invitation-section.js.map +1 -1
  123. package/dist/esm/components-page/account-settings/teams/team-member-list-section.js.map +1 -1
  124. package/dist/esm/components-page/account-settings/teams/team-page.js.map +1 -1
  125. package/dist/esm/components-page/account-settings/teams/team-profile-image-section.js.map +1 -1
  126. package/dist/esm/components-page/account-settings/teams/team-profile-user-section.js.map +1 -1
  127. package/dist/esm/components-page/account-settings.js +23 -3
  128. package/dist/esm/components-page/account-settings.js.map +1 -1
  129. package/dist/esm/components-page/auth-page.js.map +1 -1
  130. package/dist/esm/components-page/cli-auth-confirm.js.map +1 -1
  131. package/dist/esm/components-page/email-verification.js.map +1 -1
  132. package/dist/esm/components-page/error-page.js.map +1 -1
  133. package/dist/esm/components-page/forgot-password.js.map +1 -1
  134. package/dist/esm/components-page/magic-link-callback.js.map +1 -1
  135. package/dist/esm/components-page/mfa.js.map +1 -1
  136. package/dist/esm/components-page/oauth-callback.js.map +1 -1
  137. package/dist/esm/components-page/onboarding.js.map +1 -1
  138. package/dist/esm/components-page/password-reset.js.map +1 -1
  139. package/dist/esm/components-page/section.js.map +1 -1
  140. package/dist/esm/components-page/sign-in.js.map +1 -1
  141. package/dist/esm/components-page/sign-out.js.map +1 -1
  142. package/dist/esm/components-page/sign-up.js.map +1 -1
  143. package/dist/esm/components-page/stack-handler-client.js.map +1 -1
  144. package/dist/esm/components-page/stack-handler.js.map +1 -1
  145. package/dist/esm/components-page/team-creation.js.map +1 -1
  146. package/dist/esm/components-page/team-invitation.js.map +1 -1
  147. package/dist/esm/generated/global-css.js +1 -1
  148. package/dist/esm/generated/global-css.js.map +1 -1
  149. package/dist/esm/generated/quetzal-translations.js +4032 -3556
  150. package/dist/esm/generated/quetzal-translations.js.map +1 -1
  151. package/dist/esm/index.js.map +1 -1
  152. package/dist/esm/integrations/convex/component/convex.config.js.map +1 -1
  153. package/dist/esm/integrations/convex.js.map +1 -1
  154. package/dist/esm/lib/auth.js.map +1 -1
  155. package/dist/esm/lib/cookie.js.map +1 -1
  156. package/dist/esm/lib/hooks.js.map +1 -1
  157. package/dist/esm/lib/stack-app/api-keys/index.js.map +1 -1
  158. package/dist/esm/lib/stack-app/apps/implementations/admin-app-impl.js.map +1 -1
  159. package/dist/esm/lib/stack-app/apps/implementations/client-app-impl.js +63 -4
  160. package/dist/esm/lib/stack-app/apps/implementations/client-app-impl.js.map +1 -1
  161. package/dist/esm/lib/stack-app/apps/implementations/common.js +1 -1
  162. package/dist/esm/lib/stack-app/apps/implementations/common.js.map +1 -1
  163. package/dist/esm/lib/stack-app/apps/implementations/index.js.map +1 -1
  164. package/dist/esm/lib/stack-app/apps/implementations/server-app-impl.js.map +1 -1
  165. package/dist/esm/lib/stack-app/apps/index.js.map +1 -1
  166. package/dist/esm/lib/stack-app/apps/interfaces/admin-app.js.map +1 -1
  167. package/dist/esm/lib/stack-app/apps/interfaces/client-app.js.map +1 -1
  168. package/dist/esm/lib/stack-app/apps/interfaces/server-app.js.map +1 -1
  169. package/dist/esm/lib/stack-app/common.js.map +1 -1
  170. package/dist/esm/lib/stack-app/contact-channels/index.js.map +1 -1
  171. package/dist/esm/lib/stack-app/email-templates/index.js.map +1 -1
  172. package/dist/esm/lib/stack-app/index.js.map +1 -1
  173. package/dist/esm/lib/stack-app/internal-api-keys/index.js.map +1 -1
  174. package/dist/esm/lib/stack-app/permissions/index.js.map +1 -1
  175. package/dist/esm/lib/stack-app/projects/index.js.map +1 -1
  176. package/dist/esm/lib/stack-app/teams/index.js.map +1 -1
  177. package/dist/esm/lib/stack-app/users/index.js.map +1 -1
  178. package/dist/esm/lib/translations.js.map +1 -1
  179. package/dist/esm/providers/stack-provider-client.js.map +1 -1
  180. package/dist/esm/providers/stack-provider.js.map +1 -1
  181. package/dist/esm/providers/theme-provider.js.map +1 -1
  182. package/dist/esm/providers/translation-provider-client.js.map +1 -1
  183. package/dist/esm/providers/translation-provider.js.map +1 -1
  184. package/dist/esm/utils/browser-script.js.map +1 -1
  185. package/dist/esm/utils/constants.js.map +1 -1
  186. package/dist/esm/utils/url.js.map +1 -1
  187. package/dist/generated/global-css.js +1 -1
  188. package/dist/generated/global-css.js.map +1 -1
  189. package/dist/generated/quetzal-translations.js +4032 -3556
  190. package/dist/generated/quetzal-translations.js.map +1 -1
  191. package/dist/index.d.mts +38 -2
  192. package/dist/index.d.ts +38 -2
  193. package/dist/index.js.map +1 -1
  194. package/dist/integrations/convex/component/convex.config.js.map +1 -1
  195. package/dist/integrations/convex.js.map +1 -1
  196. package/dist/lib/auth.js.map +1 -1
  197. package/dist/lib/cookie.js.map +1 -1
  198. package/dist/lib/hooks.js.map +1 -1
  199. package/dist/lib/stack-app/api-keys/index.js.map +1 -1
  200. package/dist/lib/stack-app/apps/implementations/admin-app-impl.js.map +1 -1
  201. package/dist/lib/stack-app/apps/implementations/client-app-impl.js +63 -4
  202. package/dist/lib/stack-app/apps/implementations/client-app-impl.js.map +1 -1
  203. package/dist/lib/stack-app/apps/implementations/common.js +1 -1
  204. package/dist/lib/stack-app/apps/implementations/common.js.map +1 -1
  205. package/dist/lib/stack-app/apps/implementations/index.js.map +1 -1
  206. package/dist/lib/stack-app/apps/implementations/server-app-impl.js.map +1 -1
  207. package/dist/lib/stack-app/apps/index.js.map +1 -1
  208. package/dist/lib/stack-app/apps/interfaces/admin-app.js.map +1 -1
  209. package/dist/lib/stack-app/apps/interfaces/client-app.js.map +1 -1
  210. package/dist/lib/stack-app/apps/interfaces/server-app.js.map +1 -1
  211. package/dist/lib/stack-app/common.js.map +1 -1
  212. package/dist/lib/stack-app/connected-accounts/index.js.map +1 -1
  213. package/dist/lib/stack-app/contact-channels/index.js.map +1 -1
  214. package/dist/lib/stack-app/customers/index.js.map +1 -1
  215. package/dist/lib/stack-app/data-vault/index.js.map +1 -1
  216. package/dist/lib/stack-app/email/index.js.map +1 -1
  217. package/dist/lib/stack-app/email-templates/index.js.map +1 -1
  218. package/dist/lib/stack-app/index.js.map +1 -1
  219. package/dist/lib/stack-app/internal-api-keys/index.js.map +1 -1
  220. package/dist/lib/stack-app/notification-categories/index.js.map +1 -1
  221. package/dist/lib/stack-app/permissions/index.js.map +1 -1
  222. package/dist/lib/stack-app/project-configs/index.js.map +1 -1
  223. package/dist/lib/stack-app/projects/index.js.map +1 -1
  224. package/dist/lib/stack-app/teams/index.js.map +1 -1
  225. package/dist/lib/stack-app/users/index.js.map +1 -1
  226. package/dist/lib/translations.js.map +1 -1
  227. package/dist/providers/stack-provider-client.js.map +1 -1
  228. package/dist/providers/stack-provider.js.map +1 -1
  229. package/dist/providers/theme-provider.js.map +1 -1
  230. package/dist/providers/translation-provider-client.js.map +1 -1
  231. package/dist/providers/translation-provider.js.map +1 -1
  232. package/dist/utils/browser-script.js.map +1 -1
  233. package/dist/utils/constants.js.map +1 -1
  234. package/dist/utils/url.js.map +1 -1
  235. package/package.json +18 -16
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/team-switcher.tsx"],"sourcesContent":["'use client';\n\n\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { StackAssertionError } from \"@stackframe/stack-shared/dist/utils/errors\";\nimport { runAsynchronouslyWithAlert } from \"@stackframe/stack-shared/dist/utils/promises\";\nimport {\n Button,\n Select,\n SelectContent,\n SelectGroup,\n SelectItem,\n SelectLabel,\n SelectSeparator,\n SelectTrigger,\n SelectValue,\n Skeleton,\n Typography,\n cn,\n} from \"@stackframe/stack-ui\";\nimport { PlusCircle, Settings } from \"lucide-react\";\nimport { Suspense, useMemo } from \"react\";\nimport { Team, useStackApp, useUser } from \"..\";\nimport { useTranslation } from \"../lib/translations\";\nimport { TeamIcon } from \"./team-icon\";\n\ntype MockTeam = {\n id: string,\n displayName: string,\n profileImageUrl?: string | null,\n};\n\ntype TeamSwitcherProps<AllowNull extends boolean = false> = {\n team?: Team,\n teamId?: string,\n allowNull?: AllowNull,\n nullLabel?: string,\n triggerClassName?: string,\n onChange?: (team: AllowNull extends true ? Team | null : Team) => Promise<void>,\n // Mock data props\n mockUser?: {\n team?: MockTeam,\n },\n mockTeams?: MockTeam[],\n mockProject?: {\n config: {\n clientTeamCreationEnabled: boolean,\n },\n },\n};\n\nexport function TeamSwitcher<AllowNull extends boolean = false>(props: TeamSwitcherProps<AllowNull>) {\n return <Suspense fallback={<Fallback />}>\n <Inner {...props} />\n </Suspense>;\n}\n\nfunction Fallback() {\n return <Skeleton className=\"h-9 w-full max-w-64 stack-scope\" />;\n}\n\nfunction Inner<AllowNull extends boolean>(props: TeamSwitcherProps<AllowNull>) {\n const { t } = useTranslation();\n const appFromHook = useStackApp();\n const userFromHook = useUser();\n\n // Use mock data if provided, otherwise use real data\n const app = props.mockUser ? {\n useProject: () => props.mockProject || { config: { clientTeamCreationEnabled: false } },\n useNavigate: () => () => {}, // Mock navigate function\n urls: { accountSettings: '/account-settings' },\n } : appFromHook;\n\n const user = props.mockUser ? {\n selectedTeam: props.mockUser.team,\n useTeams: () => props.mockTeams || [],\n setSelectedTeam: async () => {}, // Mock function\n } : userFromHook;\n\n const navigate = app.useNavigate();\n const project = app.useProject();\n const rawTeams = user?.useTeams();\n const selectedTeam = props.team || rawTeams?.find(team => team.id === props.teamId);\n const teams = useMemo(() => rawTeams?.sort((a, b) => b.id === selectedTeam?.id ? 1 : -1), [rawTeams, selectedTeam]);\n\n\n return (\n <Select\n value={selectedTeam?.id || (props.allowNull ? 'null-sentinel' : undefined)}\n onValueChange={(value) => {\n runAsynchronouslyWithAlert(async () => {\n let team: MockTeam | null = null;\n if (value !== 'null-sentinel') {\n team = teams?.find(team => team.id === value) || null;\n if (!team) {\n throw new StackAssertionError('Team not found, this should not happen');\n }\n } else {\n team = null;\n }\n\n // Call onChange callback if provided\n if (props.onChange) {\n await props.onChange(team as Team);\n }\n });\n }}\n >\n <SelectTrigger className={cn(\"stack-scope max-w-64\", props.triggerClassName)}>\n <SelectValue placeholder=\"Select team\"/>\n </SelectTrigger>\n <SelectContent className=\"stack-scope\">\n {selectedTeam ? <SelectGroup>\n <SelectLabel>\n <div className=\"flex items-center justify-between\">\n <span>\n {t('Current team')}\n </span>\n <Button\n variant='ghost'\n size='icon'\n className=\"h-6 w-6\"\n onClick={() => {\n if (!props.mockUser) {\n navigate(`${app.urls.accountSettings}#team-${selectedTeam.id}`);\n }\n }}\n >\n <Settings className=\"h-4 w-4\"/>\n </Button>\n </div>\n </SelectLabel>\n <SelectItem value={selectedTeam.id}>\n <div className=\"flex items-center gap-2\">\n <TeamIcon team={selectedTeam as Team} />\n <Typography className=\"max-w-40 truncate\">{selectedTeam.displayName}</Typography>\n </div>\n </SelectItem>\n </SelectGroup> : undefined}\n\n {props.allowNull && <SelectGroup>\n <SelectItem value=\"null-sentinel\">\n <div className=\"flex items-center gap-2\">\n <TeamIcon team='personal' />\n <Typography className=\"max-w-40 truncate\">{props.nullLabel || t('No team')}</Typography>\n </div>\n </SelectItem>\n </SelectGroup>}\n\n {teams?.length ?\n <SelectGroup>\n <SelectLabel>{t('Other teams')}</SelectLabel>\n {teams.filter(team => team.id !== selectedTeam?.id)\n .map(team => (\n <SelectItem value={team.id} key={team.id}>\n <div className=\"flex items-center gap-2\">\n <TeamIcon team={team as Team} />\n <Typography className=\"max-w-64 truncate\">{team.displayName}</Typography>\n </div>\n </SelectItem>\n ))}\n </SelectGroup> : null}\n\n {!teams?.length && !props.allowNull ?\n <SelectGroup>\n <SelectLabel>{t('No teams yet')}</SelectLabel>\n </SelectGroup> : null}\n\n {project.config.clientTeamCreationEnabled && <>\n <SelectSeparator/>\n <div>\n <Button\n onClick={() => {\n if (!props.mockUser) {\n navigate(`${app.urls.accountSettings}#team-creation`);\n }\n }}\n className=\"w-full\"\n variant='ghost'\n >\n <PlusCircle className=\"mr-2 h-4 w-4\"/> {t('Create a team')}\n </Button>\n </div>\n </>}\n </SelectContent>\n </Select>\n );\n}\n"],"mappings":"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","names":["team"]}
1
+ {"version":3,"sources":["../../src/components/team-switcher.tsx"],"sourcesContent":["'use client';\n\n\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY, INSTEAD EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\nimport { StackAssertionError } from \"@stackframe/stack-shared/dist/utils/errors\";\nimport { runAsynchronouslyWithAlert } from \"@stackframe/stack-shared/dist/utils/promises\";\nimport {\n Button,\n Select,\n SelectContent,\n SelectGroup,\n SelectItem,\n SelectLabel,\n SelectSeparator,\n SelectTrigger,\n SelectValue,\n Skeleton,\n Typography,\n cn,\n} from \"@stackframe/stack-ui\";\nimport { PlusCircle, Settings } from \"lucide-react\";\nimport { Suspense, useMemo } from \"react\";\nimport { Team, useStackApp, useUser } from \"..\";\nimport { useTranslation } from \"../lib/translations\";\nimport { TeamIcon } from \"./team-icon\";\n\ntype MockTeam = {\n id: string,\n displayName: string,\n profileImageUrl?: string | null,\n};\n\ntype TeamSwitcherProps<AllowNull extends boolean = false> = {\n team?: Team,\n teamId?: string,\n allowNull?: AllowNull,\n nullLabel?: string,\n triggerClassName?: string,\n onChange?: (team: AllowNull extends true ? Team | null : Team) => Promise<void>,\n // Mock data props\n mockUser?: {\n team?: MockTeam,\n },\n mockTeams?: MockTeam[],\n mockProject?: {\n config: {\n clientTeamCreationEnabled: boolean,\n },\n },\n};\n\nexport function TeamSwitcher<AllowNull extends boolean = false>(props: TeamSwitcherProps<AllowNull>) {\n return <Suspense fallback={<Fallback />}>\n <Inner {...props} />\n </Suspense>;\n}\n\nfunction Fallback() {\n return <Skeleton className=\"h-9 w-full max-w-64 stack-scope\" />;\n}\n\nfunction Inner<AllowNull extends boolean>(props: TeamSwitcherProps<AllowNull>) {\n const { t } = useTranslation();\n const appFromHook = useStackApp();\n const userFromHook = useUser();\n\n // Use mock data if provided, otherwise use real data\n const app = props.mockUser ? {\n useProject: () => props.mockProject || { config: { clientTeamCreationEnabled: false } },\n useNavigate: () => () => {}, // Mock navigate function\n urls: { accountSettings: '/account-settings' },\n } : appFromHook;\n\n const user = props.mockUser ? {\n selectedTeam: props.mockUser.team,\n useTeams: () => props.mockTeams || [],\n setSelectedTeam: async () => {}, // Mock function\n } : userFromHook;\n\n const navigate = app.useNavigate();\n const project = app.useProject();\n const rawTeams = user?.useTeams();\n const selectedTeam = props.team || rawTeams?.find(team => team.id === props.teamId);\n const teams = useMemo(() => rawTeams?.sort((a, b) => b.id === selectedTeam?.id ? 1 : -1), [rawTeams, selectedTeam]);\n\n\n return (\n <Select\n value={selectedTeam?.id || (props.allowNull ? 'null-sentinel' : undefined)}\n onValueChange={(value) => {\n runAsynchronouslyWithAlert(async () => {\n let team: MockTeam | null = null;\n if (value !== 'null-sentinel') {\n team = teams?.find(team => team.id === value) || null;\n if (!team) {\n throw new StackAssertionError('Team not found, this should not happen');\n }\n } else {\n team = null;\n }\n\n // Call onChange callback if provided\n if (props.onChange) {\n await props.onChange(team as Team);\n }\n });\n }}\n >\n <SelectTrigger className={cn(\"stack-scope max-w-64\", props.triggerClassName)}>\n <SelectValue placeholder=\"Select team\"/>\n </SelectTrigger>\n <SelectContent className=\"stack-scope\">\n {selectedTeam ? <SelectGroup>\n <SelectLabel>\n <div className=\"flex items-center justify-between\">\n <span>\n {t('Current team')}\n </span>\n <Button\n variant='ghost'\n size='icon'\n className=\"h-6 w-6\"\n onClick={() => {\n if (!props.mockUser) {\n navigate(`${app.urls.accountSettings}#team-${selectedTeam.id}`);\n }\n }}\n >\n <Settings className=\"h-4 w-4\"/>\n </Button>\n </div>\n </SelectLabel>\n <SelectItem value={selectedTeam.id}>\n <div className=\"flex items-center gap-2\">\n <TeamIcon team={selectedTeam as Team} />\n <Typography className=\"max-w-40 truncate\">{selectedTeam.displayName}</Typography>\n </div>\n </SelectItem>\n </SelectGroup> : undefined}\n\n {props.allowNull && <SelectGroup>\n <SelectItem value=\"null-sentinel\">\n <div className=\"flex items-center gap-2\">\n <TeamIcon team='personal' />\n <Typography className=\"max-w-40 truncate\">{props.nullLabel || t('No team')}</Typography>\n </div>\n </SelectItem>\n </SelectGroup>}\n\n {teams?.length ?\n <SelectGroup>\n <SelectLabel>{t('Other teams')}</SelectLabel>\n {teams.filter(team => team.id !== selectedTeam?.id)\n .map(team => (\n <SelectItem value={team.id} key={team.id}>\n <div className=\"flex items-center gap-2\">\n <TeamIcon team={team as Team} />\n <Typography className=\"max-w-64 truncate\">{team.displayName}</Typography>\n </div>\n </SelectItem>\n ))}\n </SelectGroup> : null}\n\n {!teams?.length && !props.allowNull ?\n <SelectGroup>\n <SelectLabel>{t('No teams yet')}</SelectLabel>\n </SelectGroup> : null}\n\n {project.config.clientTeamCreationEnabled && <>\n <SelectSeparator/>\n <div>\n <Button\n onClick={() => {\n if (!props.mockUser) {\n navigate(`${app.urls.accountSettings}#team-creation`);\n }\n }}\n className=\"w-full\"\n variant='ghost'\n >\n <PlusCircle className=\"mr-2 h-4 w-4\"/> {t('Create a team')}\n </Button>\n </div>\n </>}\n </SelectContent>\n </Select>\n );\n}\n"],"mappings":"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","names":["team"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/use-in-iframe.tsx"],"sourcesContent":["'use client';\n\n\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { useEffect, useState } from \"react\";\n\nexport function useInIframe() {\n const [isIframe, setIsIframe] = useState(false);\n useEffect(() => {\n if (window.self !== window.top) {\n setIsIframe(true);\n }\n }, []);\n\n return isIframe;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAMA,mBAAoC;AAE7B,SAAS,cAAc;AAC5B,QAAM,CAAC,UAAU,WAAW,QAAI,uBAAS,KAAK;AAC9C,8BAAU,MAAM;AACd,QAAI,OAAO,SAAS,OAAO,KAAK;AAC9B,kBAAY,IAAI;AAAA,IAClB;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,SAAO;AACT;","names":[]}
1
+ {"version":3,"sources":["../../src/components/use-in-iframe.tsx"],"sourcesContent":["'use client';\n\n\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY, INSTEAD EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\nimport { useEffect, useState } from \"react\";\n\nexport function useInIframe() {\n const [isIframe, setIsIframe] = useState(false);\n useEffect(() => {\n if (window.self !== window.top) {\n setIsIframe(true);\n }\n }, []);\n\n return isIframe;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAMA,mBAAoC;AAE7B,SAAS,cAAc;AAC5B,QAAM,CAAC,UAAU,WAAW,QAAI,uBAAS,KAAK;AAC9C,8BAAU,MAAM;AACd,QAAI,OAAO,SAAS,OAAO,KAAK;AAC9B,kBAAY,IAAI;AAAA,IAClB;AAAA,EACF,GAAG,CAAC,CAAC;AAEL,SAAO;AACT;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/user-button.tsx"],"sourcesContent":["'use client';\n\n\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\n\nimport { runAsynchronouslyWithAlert } from \"@stackframe/stack-shared/dist/utils/promises\";\nimport { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, Skeleton, Typography } from \"@stackframe/stack-ui\";\nimport { CircleUser, LogIn, LogOut, SunMoon, UserPlus } from \"lucide-react\";\nimport React, { Suspense } from \"react\";\nimport { CurrentUser, useStackApp, useUser } from \"..\";\nimport { useTranslation } from \"../lib/translations\";\nimport { UserAvatar } from \"./elements/user-avatar\";\n\nfunction Item(props: { text: string, icon: React.ReactNode, onClick: () => void | Promise<void> }) {\n return (\n <DropdownMenuItem onClick={() => runAsynchronouslyWithAlert(props.onClick)}>\n <div className=\"flex gap-2 items-center\">\n {props.icon}\n <Typography>{props.text}</Typography>\n </div>\n </DropdownMenuItem>\n );\n}\n\ntype UserButtonProps = {\n showUserInfo?: boolean,\n colorModeToggle?: () => void | Promise<void>,\n extraItems?: {\n text: string,\n icon: React.ReactNode,\n onClick: () => void | Promise<void>,\n }[],\n mockUser?: {\n displayName?: string,\n primaryEmail?: string,\n profileImageUrl?: string,\n },\n};\n\nexport function UserButton(props: UserButtonProps) {\n return (\n <Suspense fallback={<Skeleton className=\"h-[34px] w-[34px] rounded-full stack-scope\" />}>\n <UserButtonInner {...props} />\n </Suspense>\n );\n}\n\nfunction UserButtonInner(props: UserButtonProps) {\n const userFromHook = useUser();\n\n // Use mock user if provided, otherwise use real user\n const user = props.mockUser ? {\n displayName: props.mockUser.displayName || 'Mock User',\n primaryEmail: props.mockUser.primaryEmail || 'mock@example.com',\n profileImageUrl: props.mockUser.profileImageUrl,\n signOut: () => {\n console.log('Mock sign out - no action taken in demo mode');\n return Promise.resolve();\n }\n } as CurrentUser : userFromHook;\n\n return <UserButtonInnerInner {...props} user={user} />;\n}\n\n\nfunction UserButtonInnerInner(props: UserButtonProps & { user: CurrentUser | null }) {\n const { t } = useTranslation();\n const user = props.user;\n const app = useStackApp();\n\n const iconProps = { size: 20, className: 'h-4 w-4' };\n\n return (\n <DropdownMenu>\n <DropdownMenuTrigger className=\"outline-none stack-scope rounded-lg hover:bg-muted/50 transition-colors hover:transition-none p-1.5\">\n <div className=\"flex gap-2 items-center\">\n <UserAvatar user={user} />\n {user && props.showUserInfo &&\n <div className=\"flex flex-col justify-center text-left min-w-0\">\n <div className=\"max-w-40 truncate text-sm font-medium\">{user.displayName}</div>\n <div className=\"max-w-40 truncate text-xs text-muted-foreground\">{user.primaryEmail}</div>\n </div>\n }\n </div>\n </DropdownMenuTrigger>\n <DropdownMenuContent className=\"stack-scope\">\n <DropdownMenuLabel>\n <div className=\"flex gap-2 items-center\">\n <UserAvatar user={user} />\n <div>\n {user && <Typography className=\"max-w-40 truncate\">{user.displayName}</Typography>}\n {user && <Typography className=\"max-w-40 truncate\" variant=\"secondary\" type='label'>{user.primaryEmail}</Typography>}\n {!user && <Typography>{t('Not signed in')}</Typography>}\n </div>\n </div>\n </DropdownMenuLabel>\n <DropdownMenuSeparator />\n {user && <Item\n text={t('Account settings')}\n onClick={async () => {\n if (props.mockUser) {\n console.log('Mock account settings - no navigation in demo mode');\n } else {\n await app.redirectToAccountSettings();\n }\n }}\n icon={<CircleUser {...iconProps} />}\n />}\n {!user && <Item\n text={t('Sign in')}\n onClick={async () => {\n if (props.mockUser) {\n console.log('Mock sign in - no navigation in demo mode');\n } else {\n await app.redirectToSignIn();\n }\n }}\n icon={<LogIn {...iconProps} />}\n />}\n {!user && <Item\n text={t('Sign up')}\n onClick={async () => {\n if (props.mockUser) {\n console.log('Mock sign up - no navigation in demo mode');\n } else {\n await app.redirectToSignUp();\n }\n }}\n icon={<UserPlus {...iconProps}/> }\n />}\n {user && props.extraItems && props.extraItems.map((item, index) => (\n <Item key={index} {...item} />\n ))}\n {props.colorModeToggle && (\n <Item\n text={t('Toggle theme')}\n onClick={props.colorModeToggle}\n icon={<SunMoon {...iconProps} />}\n />\n )}\n {user && <Item\n text={t('Sign out')}\n onClick={async () => {\n if (props.mockUser) {\n console.log('Mock sign out - no action taken in demo mode');\n } else {\n await user.signOut();\n }\n }}\n icon={<LogOut {...iconProps} />}\n />}\n </DropdownMenuContent>\n </DropdownMenu>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAOA,sBAA2C;AAC3C,sBAAyJ;AACzJ,0BAA6D;AAC7D,mBAAgC;AAChC,eAAkD;AAClD,0BAA+B;AAC/B,yBAA2B;AAKrB;AAHN,SAAS,KAAK,OAAqF;AACjG,SACE,4CAAC,oCAAiB,SAAS,UAAM,4CAA2B,MAAM,OAAO,GACvE,uDAAC,SAAI,WAAU,2BACZ;AAAA,UAAM;AAAA,IACP,4CAAC,8BAAY,gBAAM,MAAK;AAAA,KAC1B,GACF;AAEJ;AAiBO,SAAS,WAAW,OAAwB;AACjD,SACE,4CAAC,yBAAS,UAAU,4CAAC,4BAAS,WAAU,8CAA6C,GACnF,sDAAC,mBAAiB,GAAG,OAAO,GAC9B;AAEJ;AAEA,SAAS,gBAAgB,OAAwB;AAC/C,QAAM,mBAAe,kBAAQ;AAG7B,QAAM,OAAO,MAAM,WAAW;AAAA,IAC5B,aAAa,MAAM,SAAS,eAAe;AAAA,IAC3C,cAAc,MAAM,SAAS,gBAAgB;AAAA,IAC7C,iBAAiB,MAAM,SAAS;AAAA,IAChC,SAAS,MAAM;AACb,cAAQ,IAAI,8CAA8C;AAC1D,aAAO,QAAQ,QAAQ;AAAA,IACzB;AAAA,EACF,IAAmB;AAEnB,SAAO,4CAAC,wBAAsB,GAAG,OAAO,MAAY;AACtD;AAGA,SAAS,qBAAqB,OAAuD;AACnF,QAAM,EAAE,EAAE,QAAI,oCAAe;AAC7B,QAAM,OAAO,MAAM;AACnB,QAAM,UAAM,sBAAY;AAExB,QAAM,YAAY,EAAE,MAAM,IAAI,WAAW,UAAU;AAEnD,SACE,6CAAC,gCACC;AAAA,gDAAC,uCAAoB,WAAU,uGAC7B,uDAAC,SAAI,WAAU,2BACb;AAAA,kDAAC,iCAAW,MAAY;AAAA,MACvB,QAAQ,MAAM,gBACb,6CAAC,SAAI,WAAU,kDACb;AAAA,oDAAC,SAAI,WAAU,yCAAyC,eAAK,aAAY;AAAA,QACzE,4CAAC,SAAI,WAAU,mDAAmD,eAAK,cAAa;AAAA,SACtF;AAAA,OAEJ,GACF;AAAA,IACA,6CAAC,uCAAoB,WAAU,eAC7B;AAAA,kDAAC,qCACC,uDAAC,SAAI,WAAU,2BACb;AAAA,oDAAC,iCAAW,MAAY;AAAA,QACxB,6CAAC,SACE;AAAA,kBAAQ,4CAAC,8BAAW,WAAU,qBAAqB,eAAK,aAAY;AAAA,UACpE,QAAQ,4CAAC,8BAAW,WAAU,qBAAoB,SAAQ,aAAY,MAAK,SAAS,eAAK,cAAa;AAAA,UACtG,CAAC,QAAQ,4CAAC,8BAAY,YAAE,eAAe,GAAE;AAAA,WAC5C;AAAA,SACF,GACF;AAAA,MACA,4CAAC,yCAAsB;AAAA,MACtB,QAAQ;AAAA,QAAC;AAAA;AAAA,UACR,MAAM,EAAE,kBAAkB;AAAA,UAC1B,SAAS,YAAY;AACnB,gBAAI,MAAM,UAAU;AAClB,sBAAQ,IAAI,oDAAoD;AAAA,YAClE,OAAO;AACL,oBAAM,IAAI,0BAA0B;AAAA,YACtC;AAAA,UACF;AAAA,UACA,MAAM,4CAAC,kCAAY,GAAG,WAAW;AAAA;AAAA,MACnC;AAAA,MACC,CAAC,QAAQ;AAAA,QAAC;AAAA;AAAA,UACT,MAAM,EAAE,SAAS;AAAA,UACjB,SAAS,YAAY;AACnB,gBAAI,MAAM,UAAU;AAClB,sBAAQ,IAAI,2CAA2C;AAAA,YACzD,OAAO;AACL,oBAAM,IAAI,iBAAiB;AAAA,YAC7B;AAAA,UACF;AAAA,UACA,MAAM,4CAAC,6BAAO,GAAG,WAAW;AAAA;AAAA,MAC9B;AAAA,MACC,CAAC,QAAQ;AAAA,QAAC;AAAA;AAAA,UACT,MAAM,EAAE,SAAS;AAAA,UACjB,SAAS,YAAY;AACnB,gBAAI,MAAM,UAAU;AAClB,sBAAQ,IAAI,2CAA2C;AAAA,YACzD,OAAO;AACL,oBAAM,IAAI,iBAAiB;AAAA,YAC7B;AAAA,UACF;AAAA,UACA,MAAM,4CAAC,gCAAU,GAAG,WAAU;AAAA;AAAA,MAChC;AAAA,MACC,QAAQ,MAAM,cAAc,MAAM,WAAW,IAAI,CAAC,MAAM,UACvD,4CAAC,QAAkB,GAAG,QAAX,KAAiB,CAC7B;AAAA,MACA,MAAM,mBACL;AAAA,QAAC;AAAA;AAAA,UACC,MAAM,EAAE,cAAc;AAAA,UACtB,SAAS,MAAM;AAAA,UACf,MAAM,4CAAC,+BAAS,GAAG,WAAW;AAAA;AAAA,MAChC;AAAA,MAED,QAAQ;AAAA,QAAC;AAAA;AAAA,UACR,MAAM,EAAE,UAAU;AAAA,UAClB,SAAS,YAAY;AACnB,gBAAI,MAAM,UAAU;AAClB,sBAAQ,IAAI,8CAA8C;AAAA,YAC5D,OAAO;AACL,oBAAM,KAAK,QAAQ;AAAA,YACrB;AAAA,UACF;AAAA,UACA,MAAM,4CAAC,8BAAQ,GAAG,WAAW;AAAA;AAAA,MAC/B;AAAA,OACF;AAAA,KACF;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../src/components/user-button.tsx"],"sourcesContent":["'use client';\n\n\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY, INSTEAD EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\n\nimport { runAsynchronouslyWithAlert } from \"@stackframe/stack-shared/dist/utils/promises\";\nimport { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, Skeleton, Typography } from \"@stackframe/stack-ui\";\nimport { CircleUser, LogIn, LogOut, SunMoon, UserPlus } from \"lucide-react\";\nimport React, { Suspense } from \"react\";\nimport { CurrentUser, useStackApp, useUser } from \"..\";\nimport { useTranslation } from \"../lib/translations\";\nimport { UserAvatar } from \"./elements/user-avatar\";\n\nfunction Item(props: { text: string, icon: React.ReactNode, onClick: () => void | Promise<void> }) {\n return (\n <DropdownMenuItem onClick={() => runAsynchronouslyWithAlert(props.onClick)}>\n <div className=\"flex gap-2 items-center\">\n {props.icon}\n <Typography>{props.text}</Typography>\n </div>\n </DropdownMenuItem>\n );\n}\n\ntype UserButtonProps = {\n showUserInfo?: boolean,\n colorModeToggle?: () => void | Promise<void>,\n extraItems?: {\n text: string,\n icon: React.ReactNode,\n onClick: () => void | Promise<void>,\n }[],\n mockUser?: {\n displayName?: string,\n primaryEmail?: string,\n profileImageUrl?: string,\n },\n};\n\nexport function UserButton(props: UserButtonProps) {\n return (\n <Suspense fallback={<Skeleton className=\"h-[34px] w-[34px] rounded-full stack-scope\" />}>\n <UserButtonInner {...props} />\n </Suspense>\n );\n}\n\nfunction UserButtonInner(props: UserButtonProps) {\n const userFromHook = useUser();\n\n // Use mock user if provided, otherwise use real user\n const user = props.mockUser ? {\n displayName: props.mockUser.displayName || 'Mock User',\n primaryEmail: props.mockUser.primaryEmail || 'mock@example.com',\n profileImageUrl: props.mockUser.profileImageUrl,\n signOut: () => {\n console.log('Mock sign out - no action taken in demo mode');\n return Promise.resolve();\n }\n } as CurrentUser : userFromHook;\n\n return <UserButtonInnerInner {...props} user={user} />;\n}\n\n\nfunction UserButtonInnerInner(props: UserButtonProps & { user: CurrentUser | null }) {\n const { t } = useTranslation();\n const user = props.user;\n const app = useStackApp();\n\n const iconProps = { size: 20, className: 'h-4 w-4' };\n\n return (\n <DropdownMenu>\n <DropdownMenuTrigger className=\"outline-none stack-scope rounded-lg hover:bg-muted/50 transition-colors hover:transition-none p-1.5\">\n <div className=\"flex gap-2 items-center\">\n <UserAvatar user={user} />\n {user && props.showUserInfo &&\n <div className=\"flex flex-col justify-center text-left min-w-0\">\n <div className=\"max-w-40 truncate text-sm font-medium\">{user.displayName}</div>\n <div className=\"max-w-40 truncate text-xs text-muted-foreground\">{user.primaryEmail}</div>\n </div>\n }\n </div>\n </DropdownMenuTrigger>\n <DropdownMenuContent className=\"stack-scope\">\n <DropdownMenuLabel>\n <div className=\"flex gap-2 items-center\">\n <UserAvatar user={user} />\n <div>\n {user && <Typography className=\"max-w-40 truncate\">{user.displayName}</Typography>}\n {user && <Typography className=\"max-w-40 truncate\" variant=\"secondary\" type='label'>{user.primaryEmail}</Typography>}\n {!user && <Typography>{t('Not signed in')}</Typography>}\n </div>\n </div>\n </DropdownMenuLabel>\n <DropdownMenuSeparator />\n {user && <Item\n text={t('Account settings')}\n onClick={async () => {\n if (props.mockUser) {\n console.log('Mock account settings - no navigation in demo mode');\n } else {\n await app.redirectToAccountSettings();\n }\n }}\n icon={<CircleUser {...iconProps} />}\n />}\n {!user && <Item\n text={t('Sign in')}\n onClick={async () => {\n if (props.mockUser) {\n console.log('Mock sign in - no navigation in demo mode');\n } else {\n await app.redirectToSignIn();\n }\n }}\n icon={<LogIn {...iconProps} />}\n />}\n {!user && <Item\n text={t('Sign up')}\n onClick={async () => {\n if (props.mockUser) {\n console.log('Mock sign up - no navigation in demo mode');\n } else {\n await app.redirectToSignUp();\n }\n }}\n icon={<UserPlus {...iconProps}/> }\n />}\n {user && props.extraItems && props.extraItems.map((item, index) => (\n <Item key={index} {...item} />\n ))}\n {props.colorModeToggle && (\n <Item\n text={t('Toggle theme')}\n onClick={props.colorModeToggle}\n icon={<SunMoon {...iconProps} />}\n />\n )}\n {user && <Item\n text={t('Sign out')}\n onClick={async () => {\n if (props.mockUser) {\n console.log('Mock sign out - no action taken in demo mode');\n } else {\n await user.signOut();\n }\n }}\n icon={<LogOut {...iconProps} />}\n />}\n </DropdownMenuContent>\n </DropdownMenu>\n );\n}\n"],"mappings":"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","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/components-page/account-settings/active-sessions/active-sessions-page.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { fromNow } from \"@stackframe/stack-shared/dist/utils/dates\";\nimport { captureError } from \"@stackframe/stack-shared/dist/utils/errors\";\nimport { runAsynchronously } from \"@stackframe/stack-shared/dist/utils/promises\";\nimport { ActionCell, Badge, Button, Skeleton, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Typography } from \"@stackframe/stack-ui\";\nimport { useEffect, useState } from \"react\";\nimport { useUser } from \"../../../lib/hooks\";\nimport { ActiveSession } from \"../../../lib/stack-app/users\";\nimport { useTranslation } from \"../../../lib/translations\";\nimport { PageLayout } from \"../page-layout\";\n\nexport function ActiveSessionsPage(props?: {\n mockSessions?: Array<{\n id: string,\n isCurrentSession: boolean,\n isImpersonation?: boolean,\n createdAt: string,\n lastUsedAt?: string,\n geoInfo?: {\n ip?: string,\n cityName?: string,\n },\n }>,\n mockMode?: boolean,\n}) {\n const { t } = useTranslation();\n const userFromHook = useUser({ or: (props?.mockSessions || props?.mockMode) ? 'return-null' : 'throw' });\n const [isLoading, setIsLoading] = useState(!props?.mockSessions);\n const [isRevokingAll, setIsRevokingAll] = useState(false);\n const [sessions, setSessions] = useState<ActiveSession[]>([]);\n const [showConfirmRevokeAll, setShowConfirmRevokeAll] = useState(false);\n\n // Use mock data if provided\n const mockSessionsData = props?.mockSessions ? props.mockSessions.map(session => ({\n id: session.id,\n isCurrentSession: session.isCurrentSession,\n isImpersonation: session.isImpersonation || false,\n createdAt: session.createdAt,\n lastUsedAt: session.lastUsedAt,\n geoInfo: session.geoInfo,\n })) : [\n {\n id: 'current-session',\n isCurrentSession: true,\n createdAt: new Date().toISOString(),\n lastUsedAt: new Date().toISOString(),\n geoInfo: { ip: '192.168.1.1', cityName: 'San Francisco' }\n },\n {\n id: 'mobile-session',\n isCurrentSession: false,\n createdAt: new Date(Date.now() - 86400000).toISOString(), // 1 day ago\n lastUsedAt: new Date(Date.now() - 7200000).toISOString(), // 2 hours ago\n geoInfo: { ip: '10.0.0.1', cityName: 'New York' }\n }\n ];\n\n // Fetch sessions when component mounts (only if not using mock data)\n useEffect(() => {\n if (props?.mockSessions) {\n setSessions(mockSessionsData as any);\n setIsLoading(false);\n return;\n }\n\n // If in mock mode but no mock sessions provided, use default mock data\n if (props?.mockMode && !userFromHook) {\n setSessions(mockSessionsData as any);\n setIsLoading(false);\n return;\n }\n\n if (!userFromHook) return;\n\n runAsynchronously(async () => {\n setIsLoading(true);\n const sessionsData = await userFromHook.getActiveSessions();\n const enhancedSessions = sessionsData;\n setSessions(enhancedSessions);\n setIsLoading(false);\n });\n }, [userFromHook, props?.mockSessions]);\n\n const handleRevokeSession = async (sessionId: string) => {\n if (props?.mockSessions) {\n // Mock revoke - just remove from list\n setSessions(prev => prev.filter(session => session.id !== sessionId));\n return;\n }\n\n if (!userFromHook) return;\n\n try {\n await userFromHook.revokeSession(sessionId);\n setSessions(prev => prev.filter(session => session.id !== sessionId));\n } catch (error) {\n captureError(\"session-revoke\", { sessionId ,error });\n throw error;\n }\n };\n\n const handleRevokeAllSessions = async () => {\n setIsRevokingAll(true);\n try {\n if (props?.mockSessions) {\n // Mock revoke all - just keep current session\n setSessions(prevSessions => prevSessions.filter(session => session.isCurrentSession));\n } else if (userFromHook) {\n const deletionPromises = sessions\n .filter(session => !session.isCurrentSession)\n .map(session => userFromHook.revokeSession(session.id));\n await Promise.all(deletionPromises);\n setSessions(prevSessions => prevSessions.filter(session => session.isCurrentSession));\n }\n } catch (error) {\n captureError(\"all-sessions-revoke\", { error, sessionIds: sessions.map(session => session.id) });\n throw error;\n } finally {\n setIsRevokingAll(false);\n setShowConfirmRevokeAll(false);\n }\n };\n\n return (\n <PageLayout>\n <div>\n <div className=\"flex justify-between items-center mb-2\">\n <Typography className='font-medium'>{t(\"Active Sessions\")}</Typography>\n {sessions.filter(s => !s.isCurrentSession).length > 0 && !isLoading && (\n showConfirmRevokeAll ? (\n <div className=\"flex gap-2\">\n <Button\n variant=\"destructive\"\n size=\"sm\"\n loading={isRevokingAll}\n onClick={handleRevokeAllSessions}\n >\n {t(\"Confirm\")}\n </Button>\n <Button\n variant=\"secondary\"\n size=\"sm\"\n disabled={isRevokingAll}\n onClick={() => setShowConfirmRevokeAll(false)}\n >\n {t(\"Cancel\")}\n </Button>\n </div>\n ) : (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setShowConfirmRevokeAll(true)}\n >\n {t(\"Revoke All Other Sessions\")}\n </Button>\n )\n )}\n </div>\n <Typography variant='secondary' type='footnote' className=\"mb-4\">\n {t(\"These are devices where you're currently logged in. You can revoke access to end a session.\")}\n </Typography>\n\n {isLoading ? (\n <Skeleton className=\"h-[300px] w-full rounded-md\" />\n ) : (\n <div className='border rounded-md'>\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead className=\"w-[200px]\">{t(\"Session\")}</TableHead>\n <TableHead className=\"w-[150px]\">{t(\"IP Address\")}</TableHead>\n <TableHead className=\"w-[150px]\">{t(\"Location\")}</TableHead>\n <TableHead className=\"w-[150px]\">{t(\"Last used\")}</TableHead>\n <TableHead className=\"w-[80px]\"></TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {sessions.length === 0 ? (\n <TableRow>\n <TableCell colSpan={5} className=\"text-center py-6\">\n <Typography variant=\"secondary\">{t(\"No active sessions found\")}</Typography>\n </TableCell>\n </TableRow>\n ) : (\n sessions.map((session) => (\n <TableRow key={session.id}>\n <TableCell>\n <div className=\"flex flex-col\">\n {/* We currently do not save any usefull information about the user, in the future, the name should probably say what kind of session it is (e.g. cli, browser, maybe what auth method was used) */}\n <Typography>{session.isCurrentSession ? t(\"Current Session\") : t(\"Other Session\")}</Typography>\n {session.isImpersonation && <Badge variant=\"secondary\" className=\"w-fit mt-1\">{t(\"Impersonation\")}</Badge>}\n <Typography variant='secondary' type='footnote'>\n {t(\"Signed in {time}\", { time: new Date(session.createdAt).toLocaleDateString() })}\n </Typography>\n </div>\n </TableCell>\n <TableCell>\n <Typography>{session.geoInfo?.ip || t('-')}</Typography>\n </TableCell>\n <TableCell>\n <Typography>{session.geoInfo?.cityName || t('Unknown')}</Typography>\n </TableCell>\n <TableCell>\n <div className=\"flex flex-col\">\n <Typography>{session.lastUsedAt ? fromNow(new Date(session.lastUsedAt)) : t(\"Never\")}</Typography>\n <Typography variant='secondary' type='footnote' title={session.lastUsedAt ? new Date(session.lastUsedAt).toLocaleString() : \"\"}>\n {session.lastUsedAt ? new Date(session.lastUsedAt).toLocaleDateString() : \"\"}\n </Typography>\n </div>\n </TableCell>\n <TableCell align=\"right\">\n <ActionCell\n items={[\n {\n item: t(\"Revoke\"),\n onClick: () => handleRevokeSession(session.id),\n danger: true,\n disabled: session.isCurrentSession,\n disabledTooltip: session.isCurrentSession ? t(\"You cannot revoke your current session\") : undefined,\n },\n ]}\n />\n </TableCell>\n </TableRow>\n ))\n )}\n </TableBody>\n </Table>\n </div>\n )}\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../../src/components-page/account-settings/active-sessions/active-sessions-page.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY, INSTEAD EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\nimport { fromNow } from \"@stackframe/stack-shared/dist/utils/dates\";\nimport { captureError } from \"@stackframe/stack-shared/dist/utils/errors\";\nimport { runAsynchronously } from \"@stackframe/stack-shared/dist/utils/promises\";\nimport { ActionCell, Badge, Button, Skeleton, Table, TableBody, TableCell, TableHead, TableHeader, TableRow, Typography } from \"@stackframe/stack-ui\";\nimport { useEffect, useState } from \"react\";\nimport { useUser } from \"../../../lib/hooks\";\nimport { ActiveSession } from \"../../../lib/stack-app/users\";\nimport { useTranslation } from \"../../../lib/translations\";\nimport { PageLayout } from \"../page-layout\";\n\nexport function ActiveSessionsPage(props?: {\n mockSessions?: Array<{\n id: string,\n isCurrentSession: boolean,\n isImpersonation?: boolean,\n createdAt: string,\n lastUsedAt?: string,\n geoInfo?: {\n ip?: string,\n cityName?: string,\n },\n }>,\n mockMode?: boolean,\n}) {\n const { t } = useTranslation();\n const userFromHook = useUser({ or: (props?.mockSessions || props?.mockMode) ? 'return-null' : 'throw' });\n const [isLoading, setIsLoading] = useState(!props?.mockSessions);\n const [isRevokingAll, setIsRevokingAll] = useState(false);\n const [sessions, setSessions] = useState<ActiveSession[]>([]);\n const [showConfirmRevokeAll, setShowConfirmRevokeAll] = useState(false);\n\n // Use mock data if provided\n const mockSessionsData = props?.mockSessions ? props.mockSessions.map(session => ({\n id: session.id,\n isCurrentSession: session.isCurrentSession,\n isImpersonation: session.isImpersonation || false,\n createdAt: session.createdAt,\n lastUsedAt: session.lastUsedAt,\n geoInfo: session.geoInfo,\n })) : [\n {\n id: 'current-session',\n isCurrentSession: true,\n createdAt: new Date().toISOString(),\n lastUsedAt: new Date().toISOString(),\n geoInfo: { ip: '192.168.1.1', cityName: 'San Francisco' }\n },\n {\n id: 'mobile-session',\n isCurrentSession: false,\n createdAt: new Date(Date.now() - 86400000).toISOString(), // 1 day ago\n lastUsedAt: new Date(Date.now() - 7200000).toISOString(), // 2 hours ago\n geoInfo: { ip: '10.0.0.1', cityName: 'New York' }\n }\n ];\n\n // Fetch sessions when component mounts (only if not using mock data)\n useEffect(() => {\n if (props?.mockSessions) {\n setSessions(mockSessionsData as any);\n setIsLoading(false);\n return;\n }\n\n // If in mock mode but no mock sessions provided, use default mock data\n if (props?.mockMode && !userFromHook) {\n setSessions(mockSessionsData as any);\n setIsLoading(false);\n return;\n }\n\n if (!userFromHook) return;\n\n runAsynchronously(async () => {\n setIsLoading(true);\n const sessionsData = await userFromHook.getActiveSessions();\n const enhancedSessions = sessionsData;\n setSessions(enhancedSessions);\n setIsLoading(false);\n });\n }, [userFromHook, props?.mockSessions]);\n\n const handleRevokeSession = async (sessionId: string) => {\n if (props?.mockSessions) {\n // Mock revoke - just remove from list\n setSessions(prev => prev.filter(session => session.id !== sessionId));\n return;\n }\n\n if (!userFromHook) return;\n\n try {\n await userFromHook.revokeSession(sessionId);\n setSessions(prev => prev.filter(session => session.id !== sessionId));\n } catch (error) {\n captureError(\"session-revoke\", { sessionId ,error });\n throw error;\n }\n };\n\n const handleRevokeAllSessions = async () => {\n setIsRevokingAll(true);\n try {\n if (props?.mockSessions) {\n // Mock revoke all - just keep current session\n setSessions(prevSessions => prevSessions.filter(session => session.isCurrentSession));\n } else if (userFromHook) {\n const deletionPromises = sessions\n .filter(session => !session.isCurrentSession)\n .map(session => userFromHook.revokeSession(session.id));\n await Promise.all(deletionPromises);\n setSessions(prevSessions => prevSessions.filter(session => session.isCurrentSession));\n }\n } catch (error) {\n captureError(\"all-sessions-revoke\", { error, sessionIds: sessions.map(session => session.id) });\n throw error;\n } finally {\n setIsRevokingAll(false);\n setShowConfirmRevokeAll(false);\n }\n };\n\n return (\n <PageLayout>\n <div>\n <div className=\"flex justify-between items-center mb-2\">\n <Typography className='font-medium'>{t(\"Active Sessions\")}</Typography>\n {sessions.filter(s => !s.isCurrentSession).length > 0 && !isLoading && (\n showConfirmRevokeAll ? (\n <div className=\"flex gap-2\">\n <Button\n variant=\"destructive\"\n size=\"sm\"\n loading={isRevokingAll}\n onClick={handleRevokeAllSessions}\n >\n {t(\"Confirm\")}\n </Button>\n <Button\n variant=\"secondary\"\n size=\"sm\"\n disabled={isRevokingAll}\n onClick={() => setShowConfirmRevokeAll(false)}\n >\n {t(\"Cancel\")}\n </Button>\n </div>\n ) : (\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setShowConfirmRevokeAll(true)}\n >\n {t(\"Revoke All Other Sessions\")}\n </Button>\n )\n )}\n </div>\n <Typography variant='secondary' type='footnote' className=\"mb-4\">\n {t(\"These are devices where you're currently logged in. You can revoke access to end a session.\")}\n </Typography>\n\n {isLoading ? (\n <Skeleton className=\"h-[300px] w-full rounded-md\" />\n ) : (\n <div className='border rounded-md'>\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead className=\"w-[200px]\">{t(\"Session\")}</TableHead>\n <TableHead className=\"w-[150px]\">{t(\"IP Address\")}</TableHead>\n <TableHead className=\"w-[150px]\">{t(\"Location\")}</TableHead>\n <TableHead className=\"w-[150px]\">{t(\"Last used\")}</TableHead>\n <TableHead className=\"w-[80px]\"></TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {sessions.length === 0 ? (\n <TableRow>\n <TableCell colSpan={5} className=\"text-center py-6\">\n <Typography variant=\"secondary\">{t(\"No active sessions found\")}</Typography>\n </TableCell>\n </TableRow>\n ) : (\n sessions.map((session) => (\n <TableRow key={session.id}>\n <TableCell>\n <div className=\"flex flex-col\">\n {/* We currently do not save any usefull information about the user, in the future, the name should probably say what kind of session it is (e.g. cli, browser, maybe what auth method was used) */}\n <Typography>{session.isCurrentSession ? t(\"Current Session\") : t(\"Other Session\")}</Typography>\n {session.isImpersonation && <Badge variant=\"secondary\" className=\"w-fit mt-1\">{t(\"Impersonation\")}</Badge>}\n <Typography variant='secondary' type='footnote'>\n {t(\"Signed in {time}\", { time: new Date(session.createdAt).toLocaleDateString() })}\n </Typography>\n </div>\n </TableCell>\n <TableCell>\n <Typography>{session.geoInfo?.ip || t('-')}</Typography>\n </TableCell>\n <TableCell>\n <Typography>{session.geoInfo?.cityName || t('Unknown')}</Typography>\n </TableCell>\n <TableCell>\n <div className=\"flex flex-col\">\n <Typography>{session.lastUsedAt ? fromNow(new Date(session.lastUsedAt)) : t(\"Never\")}</Typography>\n <Typography variant='secondary' type='footnote' title={session.lastUsedAt ? new Date(session.lastUsedAt).toLocaleString() : \"\"}>\n {session.lastUsedAt ? new Date(session.lastUsedAt).toLocaleDateString() : \"\"}\n </Typography>\n </div>\n </TableCell>\n <TableCell align=\"right\">\n <ActionCell\n items={[\n {\n item: t(\"Revoke\"),\n onClick: () => handleRevokeSession(session.id),\n danger: true,\n disabled: session.isCurrentSession,\n disabledTooltip: session.isCurrentSession ? t(\"You cannot revoke your current session\") : undefined,\n },\n ]}\n />\n </TableCell>\n </TableRow>\n ))\n )}\n </TableBody>\n </Table>\n </div>\n )}\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/components-page/account-settings/api-keys/api-keys-page.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { Button } from \"@stackframe/stack-ui\";\nimport { useState } from \"react\";\nimport { CreateApiKeyDialog, ShowApiKeyDialog } from \"../../../components/api-key-dialogs\";\nimport { ApiKeyTable } from \"../../../components/api-key-table\";\nimport { useUser } from \"../../../lib/hooks\";\nimport { ApiKey, ApiKeyCreationOptions } from \"../../../lib/stack-app/api-keys\";\nimport { useTranslation } from \"../../../lib/translations\";\nimport { PageLayout } from \"../page-layout\";\n\n\nexport function ApiKeysPage(props?: {\n mockApiKeys?: Array<{\n id: string,\n description: string,\n createdAt: string,\n expiresAt?: string,\n manuallyRevokedAt?: string,\n }>,\n mockMode?: boolean,\n}) {\n const { t } = useTranslation();\n\n // Check if we're in any kind of mock mode first\n const isInMockMode = !!(props?.mockApiKeys || props?.mockMode);\n\n const userFromHook = useUser({ or: isInMockMode ? 'return-null' : 'redirect' });\n\n // In mock mode, we don't need a user - just show mock data\n if (isInMockMode && !userFromHook) {\n // This is expected in mock mode, continue with mock data\n }\n\n // Only return null if we're not in mock mode and don't have a user\n if (!isInMockMode && !userFromHook) {\n return null; // This shouldn't happen due to redirect, but just in case\n }\n\n // Use mock data if provided\n const mockApiKeysData = props?.mockApiKeys ? props.mockApiKeys.map(mockKey => ({\n id: mockKey.id,\n description: mockKey.description,\n createdAt: new Date(mockKey.createdAt),\n expiresAt: mockKey.expiresAt ? new Date(mockKey.expiresAt) : undefined,\n manuallyRevokedAt: mockKey.manuallyRevokedAt ? new Date(mockKey.manuallyRevokedAt) : null,\n value: {\n lastFour: mockKey.id.slice(-4).padStart(4, '0'), // Use last 4 chars of ID or pad with zeros\n },\n type: 'user' as const,\n userId: 'mock-user-id',\n update: async () => {\n console.log('Mock API key update called');\n },\n revoke: async () => {\n console.log('Mock API key revoke called');\n },\n isValid: () => {\n const now = new Date();\n const isExpired = mockKey.expiresAt ? new Date(mockKey.expiresAt) < now : false;\n const isRevoked = !!mockKey.manuallyRevokedAt;\n return !isExpired && !isRevoked;\n },\n whyInvalid: () => {\n const now = new Date();\n if (mockKey.manuallyRevokedAt) return 'manually-revoked';\n if (mockKey.expiresAt && new Date(mockKey.expiresAt) < now) return 'expired';\n return null;\n },\n })) : [\n {\n id: 'key-1',\n description: 'Development Key',\n createdAt: new Date(Date.now() - 172800000), // 2 days ago\n expiresAt: undefined,\n manuallyRevokedAt: null,\n value: {\n lastFour: 'ey-1'.slice(-4).padStart(4, '0'),\n },\n type: 'user' as const,\n userId: 'mock-user-id',\n update: async () => {\n console.log('Mock API key update called');\n },\n revoke: async () => {\n console.log('Mock API key revoke called');\n },\n isValid: () => true,\n whyInvalid: () => null,\n }\n ];\n\n // Determine which API keys to use\n let apiKeys: any[];\n if (isInMockMode) {\n apiKeys = mockApiKeysData;\n } else if (userFromHook) {\n apiKeys = userFromHook.useApiKeys();\n } else {\n apiKeys = [];\n }\n\n const [isNewApiKeyDialogOpen, setIsNewApiKeyDialogOpen] = useState(false);\n const [returnedApiKey, setReturnedApiKey] = useState<ApiKey<\"user\", true> | null>(null);\n\n const CreateDialog = CreateApiKeyDialog<\"user\">;\n const ShowDialog = ShowApiKeyDialog<\"user\">;\n\n const handleCreateApiKey = async (data: ApiKeyCreationOptions<\"user\">) => {\n if (isInMockMode) {\n // Mock API key creation\n const mockApiKey = {\n id: `key-${Date.now()}`,\n description: data.description,\n createdAt: new Date().toISOString(),\n expiresAt: data.expiresAt?.toISOString(),\n value: 'sk_dev_mock_key_' + Math.random().toString(36).substring(2),\n update: async () => {\n console.log('Mock API key update called');\n },\n revoke: async () => {\n console.log('Mock API key revoke called');\n },\n isValid: () => true,\n whyInvalid: () => null,\n type: 'user' as const,\n userId: 'mock-user-id',\n };\n return mockApiKey as any;\n }\n\n if (!userFromHook) throw new Error('User not available');\n return await userFromHook.createApiKey(data);\n };\n\n return (\n <PageLayout>\n <Button onClick={() => setIsNewApiKeyDialogOpen(true)}>\n {t(\"Create API Key\")}\n </Button>\n <ApiKeyTable apiKeys={apiKeys} />\n <CreateDialog\n open={isNewApiKeyDialogOpen}\n onOpenChange={setIsNewApiKeyDialogOpen}\n onKeyCreated={setReturnedApiKey}\n createApiKey={handleCreateApiKey}\n mockMode={isInMockMode}\n />\n <ShowDialog\n apiKey={returnedApiKey}\n onClose={() => setReturnedApiKey(null)}\n />\n </PageLayout>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../../src/components-page/account-settings/api-keys/api-keys-page.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY, INSTEAD EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\nimport { Button } from \"@stackframe/stack-ui\";\nimport { useState } from \"react\";\nimport { CreateApiKeyDialog, ShowApiKeyDialog } from \"../../../components/api-key-dialogs\";\nimport { ApiKeyTable } from \"../../../components/api-key-table\";\nimport { useUser } from \"../../../lib/hooks\";\nimport { ApiKey, ApiKeyCreationOptions } from \"../../../lib/stack-app/api-keys\";\nimport { useTranslation } from \"../../../lib/translations\";\nimport { PageLayout } from \"../page-layout\";\n\n\nexport function ApiKeysPage(props?: {\n mockApiKeys?: Array<{\n id: string,\n description: string,\n createdAt: string,\n expiresAt?: string,\n manuallyRevokedAt?: string,\n }>,\n mockMode?: boolean,\n}) {\n const { t } = useTranslation();\n\n // Check if we're in any kind of mock mode first\n const isInMockMode = !!(props?.mockApiKeys || props?.mockMode);\n\n const userFromHook = useUser({ or: isInMockMode ? 'return-null' : 'redirect' });\n\n // In mock mode, we don't need a user - just show mock data\n if (isInMockMode && !userFromHook) {\n // This is expected in mock mode, continue with mock data\n }\n\n // Only return null if we're not in mock mode and don't have a user\n if (!isInMockMode && !userFromHook) {\n return null; // This shouldn't happen due to redirect, but just in case\n }\n\n // Use mock data if provided\n const mockApiKeysData = props?.mockApiKeys ? props.mockApiKeys.map(mockKey => ({\n id: mockKey.id,\n description: mockKey.description,\n createdAt: new Date(mockKey.createdAt),\n expiresAt: mockKey.expiresAt ? new Date(mockKey.expiresAt) : undefined,\n manuallyRevokedAt: mockKey.manuallyRevokedAt ? new Date(mockKey.manuallyRevokedAt) : null,\n value: {\n lastFour: mockKey.id.slice(-4).padStart(4, '0'), // Use last 4 chars of ID or pad with zeros\n },\n type: 'user' as const,\n userId: 'mock-user-id',\n update: async () => {\n console.log('Mock API key update called');\n },\n revoke: async () => {\n console.log('Mock API key revoke called');\n },\n isValid: () => {\n const now = new Date();\n const isExpired = mockKey.expiresAt ? new Date(mockKey.expiresAt) < now : false;\n const isRevoked = !!mockKey.manuallyRevokedAt;\n return !isExpired && !isRevoked;\n },\n whyInvalid: () => {\n const now = new Date();\n if (mockKey.manuallyRevokedAt) return 'manually-revoked';\n if (mockKey.expiresAt && new Date(mockKey.expiresAt) < now) return 'expired';\n return null;\n },\n })) : [\n {\n id: 'key-1',\n description: 'Development Key',\n createdAt: new Date(Date.now() - 172800000), // 2 days ago\n expiresAt: undefined,\n manuallyRevokedAt: null,\n value: {\n lastFour: 'ey-1'.slice(-4).padStart(4, '0'),\n },\n type: 'user' as const,\n userId: 'mock-user-id',\n update: async () => {\n console.log('Mock API key update called');\n },\n revoke: async () => {\n console.log('Mock API key revoke called');\n },\n isValid: () => true,\n whyInvalid: () => null,\n }\n ];\n\n // Determine which API keys to use\n let apiKeys: any[];\n if (isInMockMode) {\n apiKeys = mockApiKeysData;\n } else if (userFromHook) {\n apiKeys = userFromHook.useApiKeys();\n } else {\n apiKeys = [];\n }\n\n const [isNewApiKeyDialogOpen, setIsNewApiKeyDialogOpen] = useState(false);\n const [returnedApiKey, setReturnedApiKey] = useState<ApiKey<\"user\", true> | null>(null);\n\n const CreateDialog = CreateApiKeyDialog<\"user\">;\n const ShowDialog = ShowApiKeyDialog<\"user\">;\n\n const handleCreateApiKey = async (data: ApiKeyCreationOptions<\"user\">) => {\n if (isInMockMode) {\n // Mock API key creation\n const mockApiKey = {\n id: `key-${Date.now()}`,\n description: data.description,\n createdAt: new Date().toISOString(),\n expiresAt: data.expiresAt?.toISOString(),\n value: 'sk_dev_mock_key_' + Math.random().toString(36).substring(2),\n update: async () => {\n console.log('Mock API key update called');\n },\n revoke: async () => {\n console.log('Mock API key revoke called');\n },\n isValid: () => true,\n whyInvalid: () => null,\n type: 'user' as const,\n userId: 'mock-user-id',\n };\n return mockApiKey as any;\n }\n\n if (!userFromHook) throw new Error('User not available');\n return await userFromHook.createApiKey(data);\n };\n\n return (\n <PageLayout>\n <Button onClick={() => setIsNewApiKeyDialogOpen(true)}>\n {t(\"Create API Key\")}\n </Button>\n <ApiKeyTable apiKeys={apiKeys} />\n <CreateDialog\n open={isNewApiKeyDialogOpen}\n onOpenChange={setIsNewApiKeyDialogOpen}\n onKeyCreated={setReturnedApiKey}\n createApiKey={handleCreateApiKey}\n mockMode={isInMockMode}\n />\n <ShowDialog\n apiKey={returnedApiKey}\n onClose={() => setReturnedApiKey(null)}\n />\n </PageLayout>\n );\n}\n"],"mappings":"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","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components-page/account-settings/editable-text.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { Button, Input, Typography } from \"@stackframe/stack-ui\";\nimport { Edit } from \"lucide-react\";\nimport { useState } from \"react\";\nimport { useTranslation } from \"../../lib/translations\";\n\n\nexport function EditableText(props: { value: string, onSave?: (value: string) => void | Promise<void> }) {\n const [editing, setEditing] = useState(false);\n const [editingValue, setEditingValue] = useState(props.value);\n const { t } = useTranslation();\n\n return (\n <div className='flex items-center gap-2'>\n {editing ? (\n <>\n <Input\n value={editingValue}\n onChange={(e) => setEditingValue(e.target.value)}\n />\n <Button\n size='sm'\n onClick={async () => {\n await props.onSave?.(editingValue);\n setEditing(false);\n }}\n >\n {t(\"Save\")}\n </Button>\n <Button\n size='sm'\n variant='secondary'\n onClick={() => {\n setEditingValue(props.value);\n setEditing(false);\n }}>\n {t(\"Cancel\")}\n </Button>\n </>\n ) : (\n <>\n <Typography>{props.value}</Typography>\n <Button onClick={() => setEditing(true)} size='icon' variant='ghost'>\n <Edit className=\"w-4 h-4\" />\n </Button>\n </>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAIA,sBAA0C;AAC1C,0BAAqB;AACrB,mBAAyB;AACzB,0BAA+B;AAWvB;AARD,SAAS,aAAa,OAA4E;AACvG,QAAM,CAAC,SAAS,UAAU,QAAI,uBAAS,KAAK;AAC5C,QAAM,CAAC,cAAc,eAAe,QAAI,uBAAS,MAAM,KAAK;AAC5D,QAAM,EAAE,EAAE,QAAI,oCAAe;AAE7B,SACE,4CAAC,SAAI,WAAU,2BACZ,oBACC,4EACE;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,OAAO;AAAA,QACP,UAAU,CAAC,MAAM,gBAAgB,EAAE,OAAO,KAAK;AAAA;AAAA,IACjD;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAS,YAAY;AACnB,gBAAM,MAAM,SAAS,YAAY;AACjC,qBAAW,KAAK;AAAA,QAClB;AAAA,QAEC,YAAE,MAAM;AAAA;AAAA,IACX;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAQ;AAAA,QACR,SAAS,MAAM;AACb,0BAAgB,MAAM,KAAK;AAC3B,qBAAW,KAAK;AAAA,QAClB;AAAA,QACC,YAAE,QAAQ;AAAA;AAAA,IACb;AAAA,KACF,IAEA,4EACE;AAAA,gDAAC,8BAAY,gBAAM,OAAM;AAAA,IACzB,4CAAC,0BAAO,SAAS,MAAM,WAAW,IAAI,GAAG,MAAK,QAAO,SAAQ,SAC3D,sDAAC,4BAAK,WAAU,WAAU,GAC5B;AAAA,KACF,GAEJ;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../../src/components-page/account-settings/editable-text.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY, INSTEAD EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\nimport { Button, Input, Typography } from \"@stackframe/stack-ui\";\nimport { Edit } from \"lucide-react\";\nimport { useState } from \"react\";\nimport { useTranslation } from \"../../lib/translations\";\n\n\nexport function EditableText(props: { value: string, onSave?: (value: string) => void | Promise<void> }) {\n const [editing, setEditing] = useState(false);\n const [editingValue, setEditingValue] = useState(props.value);\n const { t } = useTranslation();\n\n return (\n <div className='flex items-center gap-2'>\n {editing ? (\n <>\n <Input\n value={editingValue}\n onChange={(e) => setEditingValue(e.target.value)}\n />\n <Button\n size='sm'\n onClick={async () => {\n await props.onSave?.(editingValue);\n setEditing(false);\n }}\n >\n {t(\"Save\")}\n </Button>\n <Button\n size='sm'\n variant='secondary'\n onClick={() => {\n setEditingValue(props.value);\n setEditing(false);\n }}>\n {t(\"Cancel\")}\n </Button>\n </>\n ) : (\n <>\n <Typography>{props.value}</Typography>\n <Button onClick={() => setEditing(true)} size='icon' variant='ghost'>\n <Edit className=\"w-4 h-4\" />\n </Button>\n </>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAIA,sBAA0C;AAC1C,0BAAqB;AACrB,mBAAyB;AACzB,0BAA+B;AAWvB;AARD,SAAS,aAAa,OAA4E;AACvG,QAAM,CAAC,SAAS,UAAU,QAAI,uBAAS,KAAK;AAC5C,QAAM,CAAC,cAAc,eAAe,QAAI,uBAAS,MAAM,KAAK;AAC5D,QAAM,EAAE,EAAE,QAAI,oCAAe;AAE7B,SACE,4CAAC,SAAI,WAAU,2BACZ,oBACC,4EACE;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,OAAO;AAAA,QACP,UAAU,CAAC,MAAM,gBAAgB,EAAE,OAAO,KAAK;AAAA;AAAA,IACjD;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAS,YAAY;AACnB,gBAAM,MAAM,SAAS,YAAY;AACjC,qBAAW,KAAK;AAAA,QAClB;AAAA,QAEC,YAAE,MAAM;AAAA;AAAA,IACX;AAAA,IACA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAQ;AAAA,QACR,SAAS,MAAM;AACb,0BAAgB,MAAM,KAAK;AAC3B,qBAAW,KAAK;AAAA,QAClB;AAAA,QACC,YAAE,QAAQ;AAAA;AAAA,IACb;AAAA,KACF,IAEA,4EACE;AAAA,gDAAC,8BAAY,gBAAM,OAAM;AAAA,IACzB,4CAAC,0BAAO,SAAS,MAAM,WAAW,IAAI,GAAG,MAAK,QAAO,SAAQ,SAC3D,sDAAC,4BAAK,WAAU,WAAU,GAC5B;AAAA,KACF,GAEJ;AAEJ;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/components-page/account-settings/email-and-auth/email-and-auth-page.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { PageLayout } from \"../page-layout\";\nimport { EmailsSection } from \"./emails-section\";\nimport { MfaSection } from \"./mfa-section\";\nimport { OtpSection } from \"./otp-section\";\nimport { PasskeySection } from \"./passkey-section\";\nimport { PasswordSection } from \"./password-section\";\n\nexport function EmailsAndAuthPage(props?: {\n mockMode?: boolean,\n}) {\n return (\n <PageLayout>\n <EmailsSection mockMode={props?.mockMode}/>\n <PasswordSection mockMode={props?.mockMode} />\n <PasskeySection mockMode={props?.mockMode} />\n <OtpSection mockMode={props?.mockMode} />\n <MfaSection mockMode={props?.mockMode} />\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAIA,yBAA2B;AAC3B,4BAA8B;AAC9B,yBAA2B;AAC3B,yBAA2B;AAC3B,6BAA+B;AAC/B,8BAAgC;AAM5B;AAJG,SAAS,kBAAkB,OAE/B;AACD,SACE,6CAAC,iCACC;AAAA,gDAAC,uCAAc,UAAU,OAAO,UAAS;AAAA,IACzC,4CAAC,2CAAgB,UAAU,OAAO,UAAU;AAAA,IAC5C,4CAAC,yCAAe,UAAU,OAAO,UAAU;AAAA,IAC3C,4CAAC,iCAAW,UAAU,OAAO,UAAU;AAAA,IACvC,4CAAC,iCAAW,UAAU,OAAO,UAAU;AAAA,KACzC;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../../../src/components-page/account-settings/email-and-auth/email-and-auth-page.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY, INSTEAD EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\nimport { PageLayout } from \"../page-layout\";\nimport { EmailsSection } from \"./emails-section\";\nimport { MfaSection } from \"./mfa-section\";\nimport { OtpSection } from \"./otp-section\";\nimport { PasskeySection } from \"./passkey-section\";\nimport { PasswordSection } from \"./password-section\";\n\nexport function EmailsAndAuthPage(props?: {\n mockMode?: boolean,\n}) {\n return (\n <PageLayout>\n <EmailsSection mockMode={props?.mockMode}/>\n <PasswordSection mockMode={props?.mockMode} />\n <PasskeySection mockMode={props?.mockMode} />\n <OtpSection mockMode={props?.mockMode} />\n <MfaSection mockMode={props?.mockMode} />\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAIA,yBAA2B;AAC3B,4BAA8B;AAC9B,yBAA2B;AAC3B,yBAA2B;AAC3B,6BAA+B;AAC/B,8BAAgC;AAM5B;AAJG,SAAS,kBAAkB,OAE/B;AACD,SACE,6CAAC,iCACC;AAAA,gDAAC,uCAAc,UAAU,OAAO,UAAS;AAAA,IACzC,4CAAC,2CAAgB,UAAU,OAAO,UAAU;AAAA,IAC5C,4CAAC,yCAAe,UAAU,OAAO,UAAU;AAAA,IAC3C,4CAAC,iCAAW,UAAU,OAAO,UAAU;AAAA,IACvC,4CAAC,iCAAW,UAAU,OAAO,UAAU;AAAA,KACzC;AAEJ;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/components-page/account-settings/email-and-auth/emails-section.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { yupResolver } from \"@hookform/resolvers/yup\";\nimport { KnownErrors } from \"@stackframe/stack-shared/dist/known-errors\";\nimport { strictEmailSchema, yupObject } from \"@stackframe/stack-shared/dist/schema-fields\";\nimport { runAsynchronously } from \"@stackframe/stack-shared/dist/utils/promises\";\nimport { ActionCell, Badge, Button, Input, Table, TableBody, TableCell, TableRow, Typography } from \"@stackframe/stack-ui\";\nimport { useEffect, useState } from \"react\";\nimport { useForm } from \"react-hook-form\";\nimport * as yup from \"yup\";\nimport { FormWarningText } from \"../../../components/elements/form-warning\";\nimport { useUser } from \"../../../lib/hooks\";\nimport { useTranslation } from \"../../../lib/translations\";\n\nexport function EmailsSection(props?: {\n mockMode?: boolean,\n}) {\n const { t } = useTranslation();\n const user = useUser({ or: props?.mockMode ? 'return-null' : 'redirect' });\n\n // In mock mode, show a placeholder message\n if (props?.mockMode && !user) {\n return (\n <div>\n <div className='flex flex-col md:flex-row justify-between mb-4 gap-4'>\n <Typography className='font-medium'>{t(\"Emails\")}</Typography>\n </div>\n <Typography variant='secondary'>{t(\"Email management is not available in demo mode.\")}</Typography>\n </div>\n );\n }\n\n if (!user) {\n return null; // This shouldn't happen in non-mock mode due to redirect\n }\n\n const contactChannels = user.useContactChannels();\n const [addingEmail, setAddingEmail] = useState(contactChannels.length === 0);\n const [addingEmailLoading, setAddingEmailLoading] = useState(false);\n const [addedEmail, setAddedEmail] = useState<string | null>(null);\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n const isLastEmailUsedForAuth = contactChannels.filter(x => x.usedForAuth && x.type === 'email').length === 1;\n\n useEffect(() => {\n if (addedEmail) {\n runAsynchronously(async () => {\n const cc = contactChannels.find(x => x.value === addedEmail);\n if (cc && !cc.isVerified) {\n await cc.sendVerificationEmail();\n }\n setAddedEmail(null);\n });\n }\n }, [contactChannels, addedEmail]);\n\n const emailSchema = yupObject({\n email: strictEmailSchema(t('Please enter a valid email address'))\n .notOneOf(contactChannels.map(x => x.value), t('Email already exists'))\n .defined()\n .nonEmpty(t('Email is required')),\n });\n\n const { register, handleSubmit, formState: { errors }, reset } = useForm({\n resolver: yupResolver(emailSchema)\n });\n\n const onSubmit = async (data: yup.InferType<typeof emailSchema>) => {\n setAddingEmailLoading(true);\n try {\n await user.createContactChannel({ type: 'email', value: data.email, usedForAuth: false });\n setAddedEmail(data.email);\n } finally {\n setAddingEmailLoading(false);\n }\n setAddingEmail(false);\n reset();\n };\n\n return (\n <div>\n <div className='flex flex-col md:flex-row justify-between mb-4 gap-4'>\n <Typography className='font-medium'>{t(\"Emails\")}</Typography>\n {addingEmail ? (\n <form\n onSubmit={(e) => {\n e.preventDefault();\n runAsynchronously(handleSubmit(onSubmit));\n }}\n className='flex flex-col'\n >\n <div className='flex gap-2'>\n <Input\n {...register(\"email\")}\n placeholder={t(\"Enter email\")}\n />\n <Button type=\"submit\" loading={addingEmailLoading}>\n {t(\"Add\")}\n </Button>\n <Button\n variant='secondary'\n onClick={() => {\n setAddingEmail(false);\n reset();\n }}\n >\n {t(\"Cancel\")}\n </Button>\n </div>\n {errors.email && <FormWarningText text={errors.email.message} />}\n </form>\n ) : (\n <div className='flex md:justify-end'>\n <Button variant='secondary' onClick={() => setAddingEmail(true)}>{t(\"Add an email\")}</Button>\n </div>\n )}\n </div>\n\n {contactChannels.length > 0 ? (\n <div className='border rounded-md'>\n <Table>\n <TableBody>\n {/*eslint-disable-next-line @typescript-eslint/no-unnecessary-condition*/}\n {contactChannels.filter(x => x.type === 'email')\n .sort((a, b) => {\n if (a.isPrimary !== b.isPrimary) return a.isPrimary ? -1 : 1;\n if (a.isVerified !== b.isVerified) return a.isVerified ? -1 : 1;\n return 0;\n })\n .map(x => (\n <TableRow key={x.id}>\n <TableCell>\n <div className='flex flex-col md:flex-row gap-2 md:gap-4'>\n {x.value}\n <div className='flex gap-2'>\n {x.isPrimary ? <Badge>{t(\"Primary\")}</Badge> : null}\n {!x.isVerified ? <Badge variant='destructive'>{t(\"Unverified\")}</Badge> : null}\n {x.usedForAuth ? <Badge variant='outline'>{t(\"Used for sign-in\")}</Badge> : null}\n </div>\n </div>\n </TableCell>\n <TableCell className=\"flex justify-end\">\n <ActionCell items={[\n ...(!x.isVerified ? [{\n item: t(\"Send verification email\"),\n onClick: async () => { await x.sendVerificationEmail(); },\n }] : []),\n ...(!x.isPrimary && x.isVerified ? [{\n item: t(\"Set as primary\"),\n onClick: async () => { await x.update({ isPrimary: true }); },\n }] :\n !x.isPrimary ? [{\n item: t(\"Set as primary\"),\n onClick: async () => {},\n disabled: true,\n disabledTooltip: t(\"Please verify your email first\"),\n }] : []),\n ...(!x.usedForAuth && x.isVerified ? [{\n item: t(\"Use for sign-in\"),\n onClick: async () => {\n try {\n await x.update({ usedForAuth: true });\n } catch (e) {\n if (KnownErrors.ContactChannelAlreadyUsedForAuthBySomeoneElse.isInstance(e)) {\n alert(t(\"This email is already used for sign-in by another user.\"));\n }\n }\n }\n }] : []),\n ...(x.usedForAuth && !isLastEmailUsedForAuth ? [{\n item: t(\"Stop using for sign-in\"),\n onClick: async () => { await x.update({ usedForAuth: false }); },\n }] : x.usedForAuth ? [{\n item: t(\"Stop using for sign-in\"),\n onClick: async () => {},\n disabled: true,\n disabledTooltip: t(\"You can not remove your last sign-in email\"),\n }] : []),\n // Determine if this email can be removed\n ...(!isLastEmailUsedForAuth || !x.usedForAuth ? [{\n item: t(\"Remove\"),\n onClick: async () => { await x.delete(); },\n danger: true,\n }] : [{\n item: t(\"Remove\"),\n onClick: async () => {},\n disabled: true,\n disabledTooltip: t(\"You can not remove your last sign-in email\"),\n }]),\n ]}/>\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </div>\n ) : null}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../../src/components-page/account-settings/email-and-auth/emails-section.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY, INSTEAD EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\nimport { yupResolver } from \"@hookform/resolvers/yup\";\nimport { KnownErrors } from \"@stackframe/stack-shared/dist/known-errors\";\nimport { strictEmailSchema, yupObject } from \"@stackframe/stack-shared/dist/schema-fields\";\nimport { runAsynchronously } from \"@stackframe/stack-shared/dist/utils/promises\";\nimport { ActionCell, Badge, Button, Input, Table, TableBody, TableCell, TableRow, Typography } from \"@stackframe/stack-ui\";\nimport { useEffect, useState } from \"react\";\nimport { useForm } from \"react-hook-form\";\nimport * as yup from \"yup\";\nimport { FormWarningText } from \"../../../components/elements/form-warning\";\nimport { useUser } from \"../../../lib/hooks\";\nimport { useTranslation } from \"../../../lib/translations\";\n\nexport function EmailsSection(props?: {\n mockMode?: boolean,\n}) {\n const { t } = useTranslation();\n const user = useUser({ or: props?.mockMode ? 'return-null' : 'redirect' });\n\n // In mock mode, show a placeholder message\n if (props?.mockMode && !user) {\n return (\n <div>\n <div className='flex flex-col md:flex-row justify-between mb-4 gap-4'>\n <Typography className='font-medium'>{t(\"Emails\")}</Typography>\n </div>\n <Typography variant='secondary'>{t(\"Email management is not available in demo mode.\")}</Typography>\n </div>\n );\n }\n\n if (!user) {\n return null; // This shouldn't happen in non-mock mode due to redirect\n }\n\n const contactChannels = user.useContactChannels();\n const [addingEmail, setAddingEmail] = useState(contactChannels.length === 0);\n const [addingEmailLoading, setAddingEmailLoading] = useState(false);\n const [addedEmail, setAddedEmail] = useState<string | null>(null);\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n const isLastEmailUsedForAuth = contactChannels.filter(x => x.usedForAuth && x.type === 'email').length === 1;\n\n useEffect(() => {\n if (addedEmail) {\n runAsynchronously(async () => {\n const cc = contactChannels.find(x => x.value === addedEmail);\n if (cc && !cc.isVerified) {\n await cc.sendVerificationEmail();\n }\n setAddedEmail(null);\n });\n }\n }, [contactChannels, addedEmail]);\n\n const emailSchema = yupObject({\n email: strictEmailSchema(t('Please enter a valid email address'))\n .notOneOf(contactChannels.map(x => x.value), t('Email already exists'))\n .defined()\n .nonEmpty(t('Email is required')),\n });\n\n const { register, handleSubmit, formState: { errors }, reset } = useForm({\n resolver: yupResolver(emailSchema)\n });\n\n const onSubmit = async (data: yup.InferType<typeof emailSchema>) => {\n setAddingEmailLoading(true);\n try {\n await user.createContactChannel({ type: 'email', value: data.email, usedForAuth: false });\n setAddedEmail(data.email);\n } finally {\n setAddingEmailLoading(false);\n }\n setAddingEmail(false);\n reset();\n };\n\n return (\n <div>\n <div className='flex flex-col md:flex-row justify-between mb-4 gap-4'>\n <Typography className='font-medium'>{t(\"Emails\")}</Typography>\n {addingEmail ? (\n <form\n onSubmit={(e) => {\n e.preventDefault();\n runAsynchronously(handleSubmit(onSubmit));\n }}\n className='flex flex-col'\n >\n <div className='flex gap-2'>\n <Input\n {...register(\"email\")}\n placeholder={t(\"Enter email\")}\n />\n <Button type=\"submit\" loading={addingEmailLoading}>\n {t(\"Add\")}\n </Button>\n <Button\n variant='secondary'\n onClick={() => {\n setAddingEmail(false);\n reset();\n }}\n >\n {t(\"Cancel\")}\n </Button>\n </div>\n {errors.email && <FormWarningText text={errors.email.message} />}\n </form>\n ) : (\n <div className='flex md:justify-end'>\n <Button variant='secondary' onClick={() => setAddingEmail(true)}>{t(\"Add an email\")}</Button>\n </div>\n )}\n </div>\n\n {contactChannels.length > 0 ? (\n <div className='border rounded-md'>\n <Table>\n <TableBody>\n {/*eslint-disable-next-line @typescript-eslint/no-unnecessary-condition*/}\n {contactChannels.filter(x => x.type === 'email')\n .sort((a, b) => {\n if (a.isPrimary !== b.isPrimary) return a.isPrimary ? -1 : 1;\n if (a.isVerified !== b.isVerified) return a.isVerified ? -1 : 1;\n return 0;\n })\n .map(x => (\n <TableRow key={x.id}>\n <TableCell>\n <div className='flex flex-col md:flex-row gap-2 md:gap-4'>\n {x.value}\n <div className='flex gap-2'>\n {x.isPrimary ? <Badge>{t(\"Primary\")}</Badge> : null}\n {!x.isVerified ? <Badge variant='destructive'>{t(\"Unverified\")}</Badge> : null}\n {x.usedForAuth ? <Badge variant='outline'>{t(\"Used for sign-in\")}</Badge> : null}\n </div>\n </div>\n </TableCell>\n <TableCell className=\"flex justify-end\">\n <ActionCell items={[\n ...(!x.isVerified ? [{\n item: t(\"Send verification email\"),\n onClick: async () => { await x.sendVerificationEmail(); },\n }] : []),\n ...(!x.isPrimary && x.isVerified ? [{\n item: t(\"Set as primary\"),\n onClick: async () => { await x.update({ isPrimary: true }); },\n }] :\n !x.isPrimary ? [{\n item: t(\"Set as primary\"),\n onClick: async () => {},\n disabled: true,\n disabledTooltip: t(\"Please verify your email first\"),\n }] : []),\n ...(!x.usedForAuth && x.isVerified ? [{\n item: t(\"Use for sign-in\"),\n onClick: async () => {\n try {\n await x.update({ usedForAuth: true });\n } catch (e) {\n if (KnownErrors.ContactChannelAlreadyUsedForAuthBySomeoneElse.isInstance(e)) {\n alert(t(\"This email is already used for sign-in by another user.\"));\n }\n }\n }\n }] : []),\n ...(x.usedForAuth && !isLastEmailUsedForAuth ? [{\n item: t(\"Stop using for sign-in\"),\n onClick: async () => { await x.update({ usedForAuth: false }); },\n }] : x.usedForAuth ? [{\n item: t(\"Stop using for sign-in\"),\n onClick: async () => {},\n disabled: true,\n disabledTooltip: t(\"You can not remove your last sign-in email\"),\n }] : []),\n // Determine if this email can be removed\n ...(!isLastEmailUsedForAuth || !x.usedForAuth ? [{\n item: t(\"Remove\"),\n onClick: async () => { await x.delete(); },\n danger: true,\n }] : [{\n item: t(\"Remove\"),\n onClick: async () => {},\n disabled: true,\n disabledTooltip: t(\"You can not remove your last sign-in email\"),\n }]),\n ]}/>\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </div>\n ) : null}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/components-page/account-settings/email-and-auth/mfa-section.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { createTOTPKeyURI, verifyTOTP } from \"@oslojs/otp\";\nimport { useAsyncCallback } from '@stackframe/stack-shared/dist/hooks/use-async-callback';\nimport { generateRandomValues } from '@stackframe/stack-shared/dist/utils/crypto';\nimport { throwErr } from \"@stackframe/stack-shared/dist/utils/errors\";\nimport { runAsynchronouslyWithAlert } from \"@stackframe/stack-shared/dist/utils/promises\";\nimport { Button, Input, Typography } from \"@stackframe/stack-ui\";\nimport * as QRCode from 'qrcode';\nimport { useEffect, useState } from \"react\";\nimport { CurrentUser, Project } from '../../..';\nimport { useStackApp, useUser } from \"../../../lib/hooks\";\nimport { useTranslation } from \"../../../lib/translations\";\nimport { Section } from \"../section\";\n\nexport function MfaSection(props?: {\n mockMode?: boolean,\n}) {\n const { t } = useTranslation();\n const project = useStackApp().useProject();\n const user = useUser({ or: props?.mockMode ? 'return-null' : \"throw\" });\n\n // In mock mode, show a placeholder message\n if (props?.mockMode && !user) {\n return (\n <Section\n title={t(\"Multi-factor authentication\")}\n description={t(\"MFA management is not available in demo mode.\")}\n >\n <Typography variant='secondary'>{t(\"MFA management is not available in demo mode.\")}</Typography>\n </Section>\n );\n }\n\n if (!user) {\n return null; // This shouldn't happen in non-mock mode due to throw\n }\n const [generatedSecret, setGeneratedSecret] = useState<Uint8Array | null>(null);\n const [qrCodeUrl, setQrCodeUrl] = useState<string | null>(null);\n const [mfaCode, setMfaCode] = useState<string>(\"\");\n const [isMaybeWrong, setIsMaybeWrong] = useState(false);\n const isEnabled = user.isMultiFactorRequired;\n\n const [handleSubmit, isLoading] = useAsyncCallback(async () => {\n await user.update({\n totpMultiFactorSecret: generatedSecret,\n });\n setGeneratedSecret(null);\n setQrCodeUrl(null);\n setMfaCode(\"\");\n }, [generatedSecret, user]);\n\n useEffect(() => {\n setIsMaybeWrong(false);\n runAsynchronouslyWithAlert(async () => {\n if (generatedSecret && verifyTOTP(generatedSecret, 30, 6, mfaCode)) {\n await handleSubmit();\n }\n setIsMaybeWrong(true);\n });\n }, [mfaCode, generatedSecret, handleSubmit]);\n\n return (\n <Section\n title={t(\"Multi-factor authentication\")}\n description={isEnabled\n ? t(\"Multi-factor authentication is currently enabled.\")\n : t(\"Multi-factor authentication is currently disabled.\")}\n >\n <div className='flex flex-col gap-4'>\n {!isEnabled && generatedSecret && (\n <>\n <Typography>{t(\"Scan this QR code with your authenticator app:\")}</Typography>\n <img width={200} height={200} src={qrCodeUrl ?? throwErr(\"TOTP QR code failed to generate\")} alt={t(\"TOTP multi-factor authentication QR code\")} />\n <Typography>{t(\"Then, enter your six-digit MFA code:\")}</Typography>\n <Input\n value={mfaCode}\n onChange={(e) => {\n setIsMaybeWrong(false);\n setMfaCode(e.target.value);\n }}\n placeholder=\"123456\"\n maxLength={6}\n disabled={isLoading}\n />\n {isMaybeWrong && mfaCode.length === 6 && (\n <Typography variant=\"destructive\">{t(\"Incorrect code. Please try again.\")}</Typography>\n )}\n <div className='flex'>\n <Button\n variant='secondary'\n onClick={() => {\n setGeneratedSecret(null);\n setQrCodeUrl(null);\n setMfaCode(\"\");\n }}\n >\n {t(\"Cancel\")}\n </Button>\n </div>\n </>\n )}\n <div className='flex gap-2'>\n {isEnabled ? (\n <Button\n variant='secondary'\n onClick={async () => {\n await user.update({\n totpMultiFactorSecret: null,\n });\n }}\n >\n {t(\"Disable MFA\")}\n </Button>\n ) : !generatedSecret && (\n <Button\n variant='secondary'\n onClick={async () => {\n const secret = generateRandomValues(new Uint8Array(20));\n setQrCodeUrl(await generateTotpQrCode(project, user, secret));\n setGeneratedSecret(secret);\n }}\n >\n {t(\"Enable MFA\")}\n </Button>\n )}\n </div>\n </div>\n </Section>\n );\n}\n\n\nasync function generateTotpQrCode(project: Project, user: CurrentUser, secret: Uint8Array) {\n const uri = createTOTPKeyURI(project.displayName, user.primaryEmail ?? user.id, secret, 30, 6);\n return await QRCode.toDataURL(uri) as any;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../../src/components-page/account-settings/email-and-auth/mfa-section.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY, INSTEAD EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\nimport { createTOTPKeyURI, verifyTOTP } from \"@oslojs/otp\";\nimport { useAsyncCallback } from '@stackframe/stack-shared/dist/hooks/use-async-callback';\nimport { generateRandomValues } from '@stackframe/stack-shared/dist/utils/crypto';\nimport { throwErr } from \"@stackframe/stack-shared/dist/utils/errors\";\nimport { runAsynchronouslyWithAlert } from \"@stackframe/stack-shared/dist/utils/promises\";\nimport { Button, Input, Typography } from \"@stackframe/stack-ui\";\nimport * as QRCode from 'qrcode';\nimport { useEffect, useState } from \"react\";\nimport { CurrentUser, Project } from '../../..';\nimport { useStackApp, useUser } from \"../../../lib/hooks\";\nimport { useTranslation } from \"../../../lib/translations\";\nimport { Section } from \"../section\";\n\nexport function MfaSection(props?: {\n mockMode?: boolean,\n}) {\n const { t } = useTranslation();\n const project = useStackApp().useProject();\n const user = useUser({ or: props?.mockMode ? 'return-null' : \"throw\" });\n\n // In mock mode, show a placeholder message\n if (props?.mockMode && !user) {\n return (\n <Section\n title={t(\"Multi-factor authentication\")}\n description={t(\"MFA management is not available in demo mode.\")}\n >\n <Typography variant='secondary'>{t(\"MFA management is not available in demo mode.\")}</Typography>\n </Section>\n );\n }\n\n if (!user) {\n return null; // This shouldn't happen in non-mock mode due to throw\n }\n const [generatedSecret, setGeneratedSecret] = useState<Uint8Array | null>(null);\n const [qrCodeUrl, setQrCodeUrl] = useState<string | null>(null);\n const [mfaCode, setMfaCode] = useState<string>(\"\");\n const [isMaybeWrong, setIsMaybeWrong] = useState(false);\n const isEnabled = user.isMultiFactorRequired;\n\n const [handleSubmit, isLoading] = useAsyncCallback(async () => {\n await user.update({\n totpMultiFactorSecret: generatedSecret,\n });\n setGeneratedSecret(null);\n setQrCodeUrl(null);\n setMfaCode(\"\");\n }, [generatedSecret, user]);\n\n useEffect(() => {\n setIsMaybeWrong(false);\n runAsynchronouslyWithAlert(async () => {\n if (generatedSecret && verifyTOTP(generatedSecret, 30, 6, mfaCode)) {\n await handleSubmit();\n }\n setIsMaybeWrong(true);\n });\n }, [mfaCode, generatedSecret, handleSubmit]);\n\n return (\n <Section\n title={t(\"Multi-factor authentication\")}\n description={isEnabled\n ? t(\"Multi-factor authentication is currently enabled.\")\n : t(\"Multi-factor authentication is currently disabled.\")}\n >\n <div className='flex flex-col gap-4'>\n {!isEnabled && generatedSecret && (\n <>\n <Typography>{t(\"Scan this QR code with your authenticator app:\")}</Typography>\n <img width={200} height={200} src={qrCodeUrl ?? throwErr(\"TOTP QR code failed to generate\")} alt={t(\"TOTP multi-factor authentication QR code\")} />\n <Typography>{t(\"Then, enter your six-digit MFA code:\")}</Typography>\n <Input\n value={mfaCode}\n onChange={(e) => {\n setIsMaybeWrong(false);\n setMfaCode(e.target.value);\n }}\n placeholder=\"123456\"\n maxLength={6}\n disabled={isLoading}\n />\n {isMaybeWrong && mfaCode.length === 6 && (\n <Typography variant=\"destructive\">{t(\"Incorrect code. Please try again.\")}</Typography>\n )}\n <div className='flex'>\n <Button\n variant='secondary'\n onClick={() => {\n setGeneratedSecret(null);\n setQrCodeUrl(null);\n setMfaCode(\"\");\n }}\n >\n {t(\"Cancel\")}\n </Button>\n </div>\n </>\n )}\n <div className='flex gap-2'>\n {isEnabled ? (\n <Button\n variant='secondary'\n onClick={async () => {\n await user.update({\n totpMultiFactorSecret: null,\n });\n }}\n >\n {t(\"Disable MFA\")}\n </Button>\n ) : !generatedSecret && (\n <Button\n variant='secondary'\n onClick={async () => {\n const secret = generateRandomValues(new Uint8Array(20));\n setQrCodeUrl(await generateTotpQrCode(project, user, secret));\n setGeneratedSecret(secret);\n }}\n >\n {t(\"Enable MFA\")}\n </Button>\n )}\n </div>\n </div>\n </Section>\n );\n}\n\n\nasync function generateTotpQrCode(project: Project, user: CurrentUser, secret: Uint8Array) {\n const uri = createTOTPKeyURI(project.displayName, user.primaryEmail ?? user.id, secret, 30, 6);\n return await QRCode.toDataURL(uri) as any;\n}\n"],"mappings":"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","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/components-page/account-settings/email-and-auth/otp-section.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { Button, Typography } from \"@stackframe/stack-ui\";\nimport { useState } from \"react\";\nimport { useStackApp, useUser } from \"../../../lib/hooks\";\nimport { useTranslation } from \"../../../lib/translations\";\nimport { Section } from \"../section\";\n\nexport function OtpSection(props?: {\n mockMode?: boolean,\n}) {\n const { t } = useTranslation();\n const user = useUser({ or: props?.mockMode ? 'return-null' : \"throw\" });\n\n // In mock mode, show a placeholder message\n if (props?.mockMode && !user) {\n return (\n <Section\n title={t(\"One-Time Password\")}\n description={t(\"OTP management is not available in demo mode.\")}\n >\n <Typography variant='secondary'>{t(\"OTP management is not available in demo mode.\")}</Typography>\n </Section>\n );\n }\n\n if (!user) {\n return null; // This shouldn't happen in non-mock mode due to throw\n }\n const project = useStackApp().useProject();\n const contactChannels = user.useContactChannels();\n const isLastAuth = user.otpAuthEnabled && !user.hasPassword && user.oauthProviders.length === 0 && !user.passkeyAuthEnabled;\n const [disabling, setDisabling] = useState(false);\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n const hasValidEmail = contactChannels.filter(x => x.type === 'email' && x.isVerified && x.usedForAuth).length > 0;\n\n if (!project.config.magicLinkEnabled) {\n return null;\n }\n\n const handleDisableOTP = async () => {\n await user.update({ otpAuthEnabled: false });\n setDisabling(false);\n };\n\n return (\n <Section title={t(\"OTP sign-in\")} description={user.otpAuthEnabled ? t(\"OTP/magic link sign-in is currently enabled.\") : t(\"Enable sign-in via magic link or OTP sent to your sign-in emails.\")}>\n <div className='flex md:justify-end'>\n {hasValidEmail ? (\n user.otpAuthEnabled ? (\n !isLastAuth ? (\n !disabling ? (\n <Button\n variant='secondary'\n onClick={() => setDisabling(true)}\n >\n {t(\"Disable OTP\")}\n </Button>\n ) : (\n <div className='flex flex-col gap-2'>\n <Typography variant='destructive'>\n {t(\"Are you sure you want to disable OTP sign-in? You will not be able to sign in with only emails anymore.\")}\n </Typography>\n <div className='flex gap-2'>\n <Button\n variant='destructive'\n onClick={handleDisableOTP}\n >\n {t(\"Disable\")}\n </Button>\n <Button\n variant='secondary'\n onClick={() => setDisabling(false)}\n >\n {t(\"Cancel\")}\n </Button>\n </div>\n </div>\n )\n ) : (\n <Typography variant='secondary' type='label'>{t(\"OTP sign-in is enabled and cannot be disabled as it is currently the only sign-in method\")}</Typography>\n )\n ) : (\n <Button\n variant='secondary'\n onClick={async () => {\n await user.update({ otpAuthEnabled: true });\n }}\n >\n {t(\"Enable OTP\")}\n </Button>\n )\n ) : (\n <Typography variant='secondary' type='label'>{t(\"To enable OTP sign-in, please add a verified sign-in email.\")}</Typography>\n )}\n </div>\n </Section>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAIA,sBAAmC;AACnC,mBAAyB;AACzB,mBAAqC;AACrC,0BAA+B;AAC/B,qBAAwB;AAehB;AAbD,SAAS,WAAW,OAExB;AACD,QAAM,EAAE,EAAE,QAAI,oCAAe;AAC7B,QAAM,WAAO,sBAAQ,EAAE,IAAI,OAAO,WAAW,gBAAgB,QAAQ,CAAC;AAGtE,MAAI,OAAO,YAAY,CAAC,MAAM;AAC5B,WACE;AAAA,MAAC;AAAA;AAAA,QACC,OAAO,EAAE,mBAAmB;AAAA,QAC5B,aAAa,EAAE,+CAA+C;AAAA,QAE9D,sDAAC,8BAAW,SAAQ,aAAa,YAAE,+CAA+C,GAAE;AAAA;AAAA,IACtF;AAAA,EAEJ;AAEA,MAAI,CAAC,MAAM;AACT,WAAO;AAAA,EACT;AACA,QAAM,cAAU,0BAAY,EAAE,WAAW;AACzC,QAAM,kBAAkB,KAAK,mBAAmB;AAChD,QAAM,aAAa,KAAK,kBAAkB,CAAC,KAAK,eAAe,KAAK,eAAe,WAAW,KAAK,CAAC,KAAK;AACzG,QAAM,CAAC,WAAW,YAAY,QAAI,uBAAS,KAAK;AAGhD,QAAM,gBAAgB,gBAAgB,OAAO,OAAK,EAAE,SAAS,WAAW,EAAE,cAAc,EAAE,WAAW,EAAE,SAAS;AAEhH,MAAI,CAAC,QAAQ,OAAO,kBAAkB;AACpC,WAAO;AAAA,EACT;AAEA,QAAM,mBAAmB,YAAY;AACnC,UAAM,KAAK,OAAO,EAAE,gBAAgB,MAAM,CAAC;AAC3C,iBAAa,KAAK;AAAA,EACpB;AAEA,SACE,4CAAC,0BAAQ,OAAO,EAAE,aAAa,GAAG,aAAa,KAAK,iBAAiB,EAAE,8CAA8C,IAAI,EAAE,mEAAmE,GAC5L,sDAAC,SAAI,WAAU,uBACZ,0BACC,KAAK,iBACH,CAAC,aACC,CAAC,YACC;AAAA,IAAC;AAAA;AAAA,MACC,SAAQ;AAAA,MACR,SAAS,MAAM,aAAa,IAAI;AAAA,MAE/B,YAAE,aAAa;AAAA;AAAA,EAClB,IAEA,6CAAC,SAAI,WAAU,uBACb;AAAA,gDAAC,8BAAW,SAAQ,eACjB,YAAE,yGAAyG,GAC9G;AAAA,IACA,6CAAC,SAAI,WAAU,cACb;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,SAAQ;AAAA,UACR,SAAS;AAAA,UAER,YAAE,SAAS;AAAA;AAAA,MACd;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC,SAAQ;AAAA,UACR,SAAS,MAAM,aAAa,KAAK;AAAA,UAEhC,YAAE,QAAQ;AAAA;AAAA,MACb;AAAA,OACF;AAAA,KACF,IAGF,4CAAC,8BAAW,SAAQ,aAAY,MAAK,SAAS,YAAE,0FAA0F,GAAE,IAG9I;AAAA,IAAC;AAAA;AAAA,MACC,SAAQ;AAAA,MACR,SAAS,YAAY;AACnB,cAAM,KAAK,OAAO,EAAE,gBAAgB,KAAK,CAAC;AAAA,MAC5C;AAAA,MAEC,YAAE,YAAY;AAAA;AAAA,EACjB,IAGF,4CAAC,8BAAW,SAAQ,aAAY,MAAK,SAAS,YAAE,6DAA6D,GAAE,GAEnH,GACF;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../../../src/components-page/account-settings/email-and-auth/otp-section.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY, INSTEAD EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\nimport { Button, Typography } from \"@stackframe/stack-ui\";\nimport { useState } from \"react\";\nimport { useStackApp, useUser } from \"../../../lib/hooks\";\nimport { useTranslation } from \"../../../lib/translations\";\nimport { Section } from \"../section\";\n\nexport function OtpSection(props?: {\n mockMode?: boolean,\n}) {\n const { t } = useTranslation();\n const user = useUser({ or: props?.mockMode ? 'return-null' : \"throw\" });\n\n // In mock mode, show a placeholder message\n if (props?.mockMode && !user) {\n return (\n <Section\n title={t(\"One-Time Password\")}\n description={t(\"OTP management is not available in demo mode.\")}\n >\n <Typography variant='secondary'>{t(\"OTP management is not available in demo mode.\")}</Typography>\n </Section>\n );\n }\n\n if (!user) {\n return null; // This shouldn't happen in non-mock mode due to throw\n }\n const project = useStackApp().useProject();\n const contactChannels = user.useContactChannels();\n const isLastAuth = user.otpAuthEnabled && !user.hasPassword && user.oauthProviders.length === 0 && !user.passkeyAuthEnabled;\n const [disabling, setDisabling] = useState(false);\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n const hasValidEmail = contactChannels.filter(x => x.type === 'email' && x.isVerified && x.usedForAuth).length > 0;\n\n if (!project.config.magicLinkEnabled) {\n return null;\n }\n\n const handleDisableOTP = async () => {\n await user.update({ otpAuthEnabled: false });\n setDisabling(false);\n };\n\n return (\n <Section title={t(\"OTP sign-in\")} description={user.otpAuthEnabled ? t(\"OTP/magic link sign-in is currently enabled.\") : t(\"Enable sign-in via magic link or OTP sent to your sign-in emails.\")}>\n <div className='flex md:justify-end'>\n {hasValidEmail ? (\n user.otpAuthEnabled ? (\n !isLastAuth ? (\n !disabling ? (\n <Button\n variant='secondary'\n onClick={() => setDisabling(true)}\n >\n {t(\"Disable OTP\")}\n </Button>\n ) : (\n <div className='flex flex-col gap-2'>\n <Typography variant='destructive'>\n {t(\"Are you sure you want to disable OTP sign-in? You will not be able to sign in with only emails anymore.\")}\n </Typography>\n <div className='flex gap-2'>\n <Button\n variant='destructive'\n onClick={handleDisableOTP}\n >\n {t(\"Disable\")}\n </Button>\n <Button\n variant='secondary'\n onClick={() => setDisabling(false)}\n >\n {t(\"Cancel\")}\n </Button>\n </div>\n </div>\n )\n ) : (\n <Typography variant='secondary' type='label'>{t(\"OTP sign-in is enabled and cannot be disabled as it is currently the only sign-in method\")}</Typography>\n )\n ) : (\n <Button\n variant='secondary'\n onClick={async () => {\n await user.update({ otpAuthEnabled: true });\n }}\n >\n {t(\"Enable OTP\")}\n </Button>\n )\n ) : (\n <Typography variant='secondary' type='label'>{t(\"To enable OTP sign-in, please add a verified sign-in email.\")}</Typography>\n )}\n </div>\n </Section>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAIA,sBAAmC;AACnC,mBAAyB;AACzB,mBAAqC;AACrC,0BAA+B;AAC/B,qBAAwB;AAehB;AAbD,SAAS,WAAW,OAExB;AACD,QAAM,EAAE,EAAE,QAAI,oCAAe;AAC7B,QAAM,WAAO,sBAAQ,EAAE,IAAI,OAAO,WAAW,gBAAgB,QAAQ,CAAC;AAGtE,MAAI,OAAO,YAAY,CAAC,MAAM;AAC5B,WACE;AAAA,MAAC;AAAA;AAAA,QACC,OAAO,EAAE,mBAAmB;AAAA,QAC5B,aAAa,EAAE,+CAA+C;AAAA,QAE9D,sDAAC,8BAAW,SAAQ,aAAa,YAAE,+CAA+C,GAAE;AAAA;AAAA,IACtF;AAAA,EAEJ;AAEA,MAAI,CAAC,MAAM;AACT,WAAO;AAAA,EACT;AACA,QAAM,cAAU,0BAAY,EAAE,WAAW;AACzC,QAAM,kBAAkB,KAAK,mBAAmB;AAChD,QAAM,aAAa,KAAK,kBAAkB,CAAC,KAAK,eAAe,KAAK,eAAe,WAAW,KAAK,CAAC,KAAK;AACzG,QAAM,CAAC,WAAW,YAAY,QAAI,uBAAS,KAAK;AAGhD,QAAM,gBAAgB,gBAAgB,OAAO,OAAK,EAAE,SAAS,WAAW,EAAE,cAAc,EAAE,WAAW,EAAE,SAAS;AAEhH,MAAI,CAAC,QAAQ,OAAO,kBAAkB;AACpC,WAAO;AAAA,EACT;AAEA,QAAM,mBAAmB,YAAY;AACnC,UAAM,KAAK,OAAO,EAAE,gBAAgB,MAAM,CAAC;AAC3C,iBAAa,KAAK;AAAA,EACpB;AAEA,SACE,4CAAC,0BAAQ,OAAO,EAAE,aAAa,GAAG,aAAa,KAAK,iBAAiB,EAAE,8CAA8C,IAAI,EAAE,mEAAmE,GAC5L,sDAAC,SAAI,WAAU,uBACZ,0BACC,KAAK,iBACH,CAAC,aACC,CAAC,YACC;AAAA,IAAC;AAAA;AAAA,MACC,SAAQ;AAAA,MACR,SAAS,MAAM,aAAa,IAAI;AAAA,MAE/B,YAAE,aAAa;AAAA;AAAA,EAClB,IAEA,6CAAC,SAAI,WAAU,uBACb;AAAA,gDAAC,8BAAW,SAAQ,eACjB,YAAE,yGAAyG,GAC9G;AAAA,IACA,6CAAC,SAAI,WAAU,cACb;AAAA;AAAA,QAAC;AAAA;AAAA,UACC,SAAQ;AAAA,UACR,SAAS;AAAA,UAER,YAAE,SAAS;AAAA;AAAA,MACd;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC,SAAQ;AAAA,UACR,SAAS,MAAM,aAAa,KAAK;AAAA,UAEhC,YAAE,QAAQ;AAAA;AAAA,MACb;AAAA,OACF;AAAA,KACF,IAGF,4CAAC,8BAAW,SAAQ,aAAY,MAAK,SAAS,YAAE,0FAA0F,GAAE,IAG9I;AAAA,IAAC;AAAA;AAAA,MACC,SAAQ;AAAA,MACR,SAAS,YAAY;AACnB,cAAM,KAAK,OAAO,EAAE,gBAAgB,KAAK,CAAC;AAAA,MAC5C;AAAA,MAEC,YAAE,YAAY;AAAA;AAAA,EACjB,IAGF,4CAAC,8BAAW,SAAQ,aAAY,MAAK,SAAS,YAAE,6DAA6D,GAAE,GAEnH,GACF;AAEJ;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/components-page/account-settings/email-and-auth/passkey-section.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { Button, Typography } from \"@stackframe/stack-ui\";\nimport { useState } from \"react\";\nimport { useStackApp } from \"../../..\";\nimport { useUser } from \"../../../lib/hooks\";\nimport { useTranslation } from \"../../../lib/translations\";\nimport { Section } from \"../section\";\n\nexport function PasskeySection(props?: {\n mockMode?: boolean,\n}) {\n const { t } = useTranslation();\n const user = useUser({ or: props?.mockMode ? 'return-null' : \"throw\" });\n\n // In mock mode, show a placeholder message\n if (props?.mockMode && !user) {\n return (\n <Section\n title={t(\"Passkey\")}\n description={t(\"Passkey management is not available in demo mode.\")}\n >\n <Typography variant='secondary'>{t(\"Passkey management is not available in demo mode.\")}</Typography>\n </Section>\n );\n }\n\n if (!user) {\n return null; // This shouldn't happen in non-mock mode due to throw\n }\n const stackApp = useStackApp();\n const project = stackApp.useProject();\n const contactChannels = user.useContactChannels();\n\n\n // passkey is enabled if there is a passkey\n const hasPasskey = user.passkeyAuthEnabled;\n\n const isLastAuth = user.passkeyAuthEnabled && !user.hasPassword && user.oauthProviders.length === 0 && !user.otpAuthEnabled;\n const [showConfirmationModal, setShowConfirmationModal] = useState(false);\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n const hasValidEmail = contactChannels.filter(x => x.type === 'email' && x.isVerified && x.usedForAuth).length > 0;\n\n if (!project.config.passkeyEnabled) {\n return null;\n }\n\n const handleDeletePasskey = async () => {\n await user.update({ passkeyAuthEnabled: false });\n setShowConfirmationModal(false);\n };\n\n\n const handleAddNewPasskey = async () => {\n await user.registerPasskey();\n };\n\n return (\n <>\n <Section title={t(\"Passkey\")} description={hasPasskey ? t(\"Passkey registered\") : t(\"Register a passkey\")}>\n <div className='flex md:justify-end gap-2'>\n {!hasValidEmail && (\n <Typography variant='secondary' type='label'>{t(\"To enable Passkey sign-in, please add a verified sign-in email.\")}</Typography>\n )}\n {hasValidEmail && hasPasskey && isLastAuth && (\n <Typography variant='secondary' type='label'>{t(\"Passkey sign-in is enabled and cannot be disabled as it is currently the only sign-in method\")}</Typography>\n )}\n {!hasPasskey && hasValidEmail && (\n <div>\n <Button onClick={handleAddNewPasskey} variant='secondary'>{t(\"Add new passkey\")}</Button>\n </div>\n )}\n {hasValidEmail && hasPasskey && !isLastAuth && !showConfirmationModal && (\n <Button\n variant='secondary'\n onClick={() => setShowConfirmationModal(true)}\n >\n {t(\"Delete Passkey\")}\n </Button>\n )}\n {hasValidEmail && hasPasskey && !isLastAuth && showConfirmationModal && (\n <div className='flex flex-col gap-2'>\n <Typography variant='destructive'>\n {t(\"Are you sure you want to disable Passkey sign-in? You will not be able to sign in with your passkey anymore.\")}\n </Typography>\n <div className='flex gap-2'>\n <Button\n variant='destructive'\n onClick={handleDeletePasskey}\n >\n {t(\"Disable\")}\n </Button>\n <Button\n variant='secondary'\n onClick={() => setShowConfirmationModal(false)}\n >\n {t(\"Cancel\")}\n </Button>\n </div>\n </div>\n )}\n </div>\n </Section>\n\n\n </>\n\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAIA,sBAAmC;AACnC,mBAAyB;AACzB,eAA4B;AAC5B,mBAAwB;AACxB,0BAA+B;AAC/B,qBAAwB;AAehB;AAbD,SAAS,eAAe,OAE5B;AACD,QAAM,EAAE,EAAE,QAAI,oCAAe;AAC7B,QAAM,WAAO,sBAAQ,EAAE,IAAI,OAAO,WAAW,gBAAgB,QAAQ,CAAC;AAGtE,MAAI,OAAO,YAAY,CAAC,MAAM;AAC5B,WACE;AAAA,MAAC;AAAA;AAAA,QACC,OAAO,EAAE,SAAS;AAAA,QAClB,aAAa,EAAE,mDAAmD;AAAA,QAElE,sDAAC,8BAAW,SAAQ,aAAa,YAAE,mDAAmD,GAAE;AAAA;AAAA,IAC1F;AAAA,EAEJ;AAEA,MAAI,CAAC,MAAM;AACT,WAAO;AAAA,EACT;AACA,QAAM,eAAW,sBAAY;AAC7B,QAAM,UAAU,SAAS,WAAW;AACpC,QAAM,kBAAkB,KAAK,mBAAmB;AAIhD,QAAM,aAAa,KAAK;AAExB,QAAM,aAAa,KAAK,sBAAsB,CAAC,KAAK,eAAe,KAAK,eAAe,WAAW,KAAK,CAAC,KAAK;AAC7G,QAAM,CAAC,uBAAuB,wBAAwB,QAAI,uBAAS,KAAK;AAGxE,QAAM,gBAAgB,gBAAgB,OAAO,OAAK,EAAE,SAAS,WAAW,EAAE,cAAc,EAAE,WAAW,EAAE,SAAS;AAEhH,MAAI,CAAC,QAAQ,OAAO,gBAAgB;AAClC,WAAO;AAAA,EACT;AAEA,QAAM,sBAAsB,YAAY;AACtC,UAAM,KAAK,OAAO,EAAE,oBAAoB,MAAM,CAAC;AAC/C,6BAAyB,KAAK;AAAA,EAChC;AAGA,QAAM,sBAAsB,YAAY;AACtC,UAAM,KAAK,gBAAgB;AAAA,EAC7B;AAEA,SACE,2EACE,sDAAC,0BAAQ,OAAO,EAAE,SAAS,GAAG,aAAa,aAAa,EAAE,oBAAoB,IAAI,EAAE,oBAAoB,GACtG,uDAAC,SAAI,WAAU,6BACZ;AAAA,KAAC,iBACA,4CAAC,8BAAW,SAAQ,aAAY,MAAK,SAAS,YAAE,iEAAiE,GAAE;AAAA,IAEpH,iBAAiB,cAAc,cAC9B,4CAAC,8BAAW,SAAQ,aAAY,MAAK,SAAS,YAAE,8FAA8F,GAAE;AAAA,IAEjJ,CAAC,cAAc,iBACd,4CAAC,SACC,sDAAC,0BAAO,SAAS,qBAAqB,SAAQ,aAAa,YAAE,iBAAiB,GAAE,GAClF;AAAA,IAED,iBAAiB,cAAc,CAAC,cAAc,CAAC,yBAC9C;AAAA,MAAC;AAAA;AAAA,QACC,SAAQ;AAAA,QACR,SAAS,MAAM,yBAAyB,IAAI;AAAA,QAE3C,YAAE,gBAAgB;AAAA;AAAA,IACrB;AAAA,IAED,iBAAiB,cAAc,CAAC,cAAc,yBAC7C,6CAAC,SAAI,WAAU,uBACb;AAAA,kDAAC,8BAAW,SAAQ,eACjB,YAAE,8GAA8G,GACnH;AAAA,MACA,6CAAC,SAAI,WAAU,cACb;AAAA;AAAA,UAAC;AAAA;AAAA,YACC,SAAQ;AAAA,YACR,SAAS;AAAA,YAER,YAAE,SAAS;AAAA;AAAA,QACd;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACC,SAAQ;AAAA,YACR,SAAS,MAAM,yBAAyB,KAAK;AAAA,YAE5C,YAAE,QAAQ;AAAA;AAAA,QACb;AAAA,SACF;AAAA,OACF;AAAA,KAEJ,GACF,GAGF;AAGJ;","names":[]}
1
+ {"version":3,"sources":["../../../../src/components-page/account-settings/email-and-auth/passkey-section.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY, INSTEAD EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\nimport { Button, Typography } from \"@stackframe/stack-ui\";\nimport { useState } from \"react\";\nimport { useStackApp } from \"../../..\";\nimport { useUser } from \"../../../lib/hooks\";\nimport { useTranslation } from \"../../../lib/translations\";\nimport { Section } from \"../section\";\n\nexport function PasskeySection(props?: {\n mockMode?: boolean,\n}) {\n const { t } = useTranslation();\n const user = useUser({ or: props?.mockMode ? 'return-null' : \"throw\" });\n\n // In mock mode, show a placeholder message\n if (props?.mockMode && !user) {\n return (\n <Section\n title={t(\"Passkey\")}\n description={t(\"Passkey management is not available in demo mode.\")}\n >\n <Typography variant='secondary'>{t(\"Passkey management is not available in demo mode.\")}</Typography>\n </Section>\n );\n }\n\n if (!user) {\n return null; // This shouldn't happen in non-mock mode due to throw\n }\n const stackApp = useStackApp();\n const project = stackApp.useProject();\n const contactChannels = user.useContactChannels();\n\n\n // passkey is enabled if there is a passkey\n const hasPasskey = user.passkeyAuthEnabled;\n\n const isLastAuth = user.passkeyAuthEnabled && !user.hasPassword && user.oauthProviders.length === 0 && !user.otpAuthEnabled;\n const [showConfirmationModal, setShowConfirmationModal] = useState(false);\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n const hasValidEmail = contactChannels.filter(x => x.type === 'email' && x.isVerified && x.usedForAuth).length > 0;\n\n if (!project.config.passkeyEnabled) {\n return null;\n }\n\n const handleDeletePasskey = async () => {\n await user.update({ passkeyAuthEnabled: false });\n setShowConfirmationModal(false);\n };\n\n\n const handleAddNewPasskey = async () => {\n await user.registerPasskey();\n };\n\n return (\n <>\n <Section title={t(\"Passkey\")} description={hasPasskey ? t(\"Passkey registered\") : t(\"Register a passkey\")}>\n <div className='flex md:justify-end gap-2'>\n {!hasValidEmail && (\n <Typography variant='secondary' type='label'>{t(\"To enable Passkey sign-in, please add a verified sign-in email.\")}</Typography>\n )}\n {hasValidEmail && hasPasskey && isLastAuth && (\n <Typography variant='secondary' type='label'>{t(\"Passkey sign-in is enabled and cannot be disabled as it is currently the only sign-in method\")}</Typography>\n )}\n {!hasPasskey && hasValidEmail && (\n <div>\n <Button onClick={handleAddNewPasskey} variant='secondary'>{t(\"Add new passkey\")}</Button>\n </div>\n )}\n {hasValidEmail && hasPasskey && !isLastAuth && !showConfirmationModal && (\n <Button\n variant='secondary'\n onClick={() => setShowConfirmationModal(true)}\n >\n {t(\"Delete Passkey\")}\n </Button>\n )}\n {hasValidEmail && hasPasskey && !isLastAuth && showConfirmationModal && (\n <div className='flex flex-col gap-2'>\n <Typography variant='destructive'>\n {t(\"Are you sure you want to disable Passkey sign-in? You will not be able to sign in with your passkey anymore.\")}\n </Typography>\n <div className='flex gap-2'>\n <Button\n variant='destructive'\n onClick={handleDeletePasskey}\n >\n {t(\"Disable\")}\n </Button>\n <Button\n variant='secondary'\n onClick={() => setShowConfirmationModal(false)}\n >\n {t(\"Cancel\")}\n </Button>\n </div>\n </div>\n )}\n </div>\n </Section>\n\n\n </>\n\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAIA,sBAAmC;AACnC,mBAAyB;AACzB,eAA4B;AAC5B,mBAAwB;AACxB,0BAA+B;AAC/B,qBAAwB;AAehB;AAbD,SAAS,eAAe,OAE5B;AACD,QAAM,EAAE,EAAE,QAAI,oCAAe;AAC7B,QAAM,WAAO,sBAAQ,EAAE,IAAI,OAAO,WAAW,gBAAgB,QAAQ,CAAC;AAGtE,MAAI,OAAO,YAAY,CAAC,MAAM;AAC5B,WACE;AAAA,MAAC;AAAA;AAAA,QACC,OAAO,EAAE,SAAS;AAAA,QAClB,aAAa,EAAE,mDAAmD;AAAA,QAElE,sDAAC,8BAAW,SAAQ,aAAa,YAAE,mDAAmD,GAAE;AAAA;AAAA,IAC1F;AAAA,EAEJ;AAEA,MAAI,CAAC,MAAM;AACT,WAAO;AAAA,EACT;AACA,QAAM,eAAW,sBAAY;AAC7B,QAAM,UAAU,SAAS,WAAW;AACpC,QAAM,kBAAkB,KAAK,mBAAmB;AAIhD,QAAM,aAAa,KAAK;AAExB,QAAM,aAAa,KAAK,sBAAsB,CAAC,KAAK,eAAe,KAAK,eAAe,WAAW,KAAK,CAAC,KAAK;AAC7G,QAAM,CAAC,uBAAuB,wBAAwB,QAAI,uBAAS,KAAK;AAGxE,QAAM,gBAAgB,gBAAgB,OAAO,OAAK,EAAE,SAAS,WAAW,EAAE,cAAc,EAAE,WAAW,EAAE,SAAS;AAEhH,MAAI,CAAC,QAAQ,OAAO,gBAAgB;AAClC,WAAO;AAAA,EACT;AAEA,QAAM,sBAAsB,YAAY;AACtC,UAAM,KAAK,OAAO,EAAE,oBAAoB,MAAM,CAAC;AAC/C,6BAAyB,KAAK;AAAA,EAChC;AAGA,QAAM,sBAAsB,YAAY;AACtC,UAAM,KAAK,gBAAgB;AAAA,EAC7B;AAEA,SACE,2EACE,sDAAC,0BAAQ,OAAO,EAAE,SAAS,GAAG,aAAa,aAAa,EAAE,oBAAoB,IAAI,EAAE,oBAAoB,GACtG,uDAAC,SAAI,WAAU,6BACZ;AAAA,KAAC,iBACA,4CAAC,8BAAW,SAAQ,aAAY,MAAK,SAAS,YAAE,iEAAiE,GAAE;AAAA,IAEpH,iBAAiB,cAAc,cAC9B,4CAAC,8BAAW,SAAQ,aAAY,MAAK,SAAS,YAAE,8FAA8F,GAAE;AAAA,IAEjJ,CAAC,cAAc,iBACd,4CAAC,SACC,sDAAC,0BAAO,SAAS,qBAAqB,SAAQ,aAAa,YAAE,iBAAiB,GAAE,GAClF;AAAA,IAED,iBAAiB,cAAc,CAAC,cAAc,CAAC,yBAC9C;AAAA,MAAC;AAAA;AAAA,QACC,SAAQ;AAAA,QACR,SAAS,MAAM,yBAAyB,IAAI;AAAA,QAE3C,YAAE,gBAAgB;AAAA;AAAA,IACrB;AAAA,IAED,iBAAiB,cAAc,CAAC,cAAc,yBAC7C,6CAAC,SAAI,WAAU,uBACb;AAAA,kDAAC,8BAAW,SAAQ,eACjB,YAAE,8GAA8G,GACnH;AAAA,MACA,6CAAC,SAAI,WAAU,cACb;AAAA;AAAA,UAAC;AAAA;AAAA,YACC,SAAQ;AAAA,YACR,SAAS;AAAA,YAER,YAAE,SAAS;AAAA;AAAA,QACd;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACC,SAAQ;AAAA,YACR,SAAS,MAAM,yBAAyB,KAAK;AAAA,YAE5C,YAAE,QAAQ;AAAA;AAAA,QACb;AAAA,SACF;AAAA,OACF;AAAA,KAEJ,GACF,GAGF;AAGJ;","names":[]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/components-page/account-settings/email-and-auth/password-section.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { yupResolver } from \"@hookform/resolvers/yup\";\nimport { getPasswordError } from '@stackframe/stack-shared/dist/helpers/password';\nimport { passwordSchema as schemaFieldsPasswordSchema, yupObject, yupString } from \"@stackframe/stack-shared/dist/schema-fields\";\nimport { runAsynchronously, runAsynchronouslyWithAlert } from \"@stackframe/stack-shared/dist/utils/promises\";\nimport { Button, Input, Label, PasswordInput, Typography } from \"@stackframe/stack-ui\";\nimport { useState } from \"react\";\nimport { useForm } from \"react-hook-form\";\nimport * as yup from \"yup\";\nimport { useStackApp } from '../../..';\nimport { FormWarningText } from \"../../../components/elements/form-warning\";\nimport { useUser } from \"../../../lib/hooks\";\nimport { useTranslation } from \"../../../lib/translations\";\nimport { Section } from \"../section\";\n\n\nexport function PasswordSection(props?: {\n mockMode?: boolean,\n}) {\n const { t } = useTranslation();\n const user = useUser({ or: props?.mockMode ? 'return-null' : \"throw\" });\n\n // In mock mode, show a placeholder message\n if (props?.mockMode && !user) {\n return (\n <Section\n title={t(\"Password\")}\n description={t(\"Password management is not available in demo mode.\")}\n >\n <Typography variant='secondary'>{t(\"Password management is not available in demo mode.\")}</Typography>\n </Section>\n );\n }\n\n if (!user) {\n return null; // This shouldn't happen in non-mock mode due to throw\n }\n const contactChannels = user.useContactChannels();\n const [changingPassword, setChangingPassword] = useState(false);\n const [loading, setLoading] = useState(false);\n const project = useStackApp().useProject();\n\n const passwordSchema = yupObject({\n oldPassword: user.hasPassword ? schemaFieldsPasswordSchema.defined().nonEmpty(t('Please enter your old password')) : yupString(),\n newPassword: schemaFieldsPasswordSchema.defined().nonEmpty(t('Please enter your password')).test({\n name: 'is-valid-password',\n test: (value, ctx) => {\n const error = getPasswordError(value);\n if (error) {\n return ctx.createError({ message: error.message });\n } else {\n return true;\n }\n }\n }),\n newPasswordRepeat: yupString().nullable().oneOf([yup.ref('newPassword'), \"\", null], t('Passwords do not match')).defined().nonEmpty(t('Please repeat your password'))\n });\n\n const { register, handleSubmit, setError, formState: { errors }, clearErrors, reset } = useForm({\n resolver: yupResolver(passwordSchema)\n });\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n const hasValidEmail = contactChannels.filter(x => x.type === 'email' && x.usedForAuth).length > 0;\n\n const onSubmit = async (data: yup.InferType<typeof passwordSchema>) => {\n setLoading(true);\n try {\n const { oldPassword, newPassword } = data;\n const error = user.hasPassword\n ? await user.updatePassword({ oldPassword: oldPassword!, newPassword })\n : await user.setPassword({ password: newPassword! });\n if (error) {\n setError('oldPassword', { type: 'manual', message: t('Incorrect password') });\n } else {\n reset();\n setChangingPassword(false);\n }\n } finally {\n setLoading(false);\n }\n };\n\n const registerPassword = register('newPassword');\n const registerPasswordRepeat = register('newPasswordRepeat');\n\n if (!project.config.credentialEnabled) {\n return null;\n }\n\n return (\n <Section\n title={t(\"Password\")}\n description={user.hasPassword ? t(\"Update your password\") : t(\"Set a password for your account\")}\n >\n <div className='flex flex-col gap-4'>\n {!changingPassword ? (\n hasValidEmail ? (\n <Button\n variant='secondary'\n onClick={() => setChangingPassword(true)}\n >\n {user.hasPassword ? t(\"Update password\") : t(\"Set password\")}\n </Button>\n ) : (\n <Typography variant='secondary' type='label'>{t(\"To set a password, please add a sign-in email.\")}</Typography>\n )\n ) : (\n <form\n onSubmit={e => runAsynchronouslyWithAlert(handleSubmit(onSubmit)(e))}\n noValidate\n >\n {user.hasPassword && (\n <>\n <Label htmlFor=\"old-password\" className=\"mb-1\">{t(\"Old password\")}</Label>\n <Input\n id=\"old-password\"\n type=\"password\"\n autoComplete=\"current-password\"\n {...register(\"oldPassword\")}\n />\n <FormWarningText text={errors.oldPassword?.message?.toString()} />\n </>\n )}\n\n <Label htmlFor=\"new-password\" className=\"mt-4 mb-1\">{t(\"New password\")}</Label>\n <PasswordInput\n id=\"new-password\"\n autoComplete=\"new-password\"\n {...registerPassword}\n onChange={(e) => {\n clearErrors('newPassword');\n clearErrors('newPasswordRepeat');\n runAsynchronously(registerPassword.onChange(e));\n }}\n />\n <FormWarningText text={errors.newPassword?.message?.toString()} />\n\n <Label htmlFor=\"repeat-password\" className=\"mt-4 mb-1\">{t(\"Repeat new password\")}</Label>\n <PasswordInput\n id=\"repeat-password\"\n autoComplete=\"new-password\"\n {...registerPasswordRepeat}\n onChange={(e) => {\n clearErrors('newPassword');\n clearErrors('newPasswordRepeat');\n runAsynchronously(registerPasswordRepeat.onChange(e));\n }}\n />\n <FormWarningText text={errors.newPasswordRepeat?.message?.toString()} />\n\n <div className=\"mt-6 flex gap-4\">\n <Button type=\"submit\" loading={loading}>\n {user.hasPassword ? t(\"Update Password\") : t(\"Set Password\")}\n </Button>\n <Button\n variant=\"secondary\"\n onClick={() => {\n setChangingPassword(false);\n reset();\n }}\n >\n {t(\"Cancel\")}\n </Button>\n </div>\n </form>\n )}\n </div>\n </Section>\n );\n}\n"],"mappings":"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","names":["schemaFieldsPasswordSchema"]}
1
+ {"version":3,"sources":["../../../../src/components-page/account-settings/email-and-auth/password-section.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY, INSTEAD EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\nimport { yupResolver } from \"@hookform/resolvers/yup\";\nimport { getPasswordError } from '@stackframe/stack-shared/dist/helpers/password';\nimport { passwordSchema as schemaFieldsPasswordSchema, yupObject, yupString } from \"@stackframe/stack-shared/dist/schema-fields\";\nimport { runAsynchronously, runAsynchronouslyWithAlert } from \"@stackframe/stack-shared/dist/utils/promises\";\nimport { Button, Input, Label, PasswordInput, Typography } from \"@stackframe/stack-ui\";\nimport { useState } from \"react\";\nimport { useForm } from \"react-hook-form\";\nimport * as yup from \"yup\";\nimport { useStackApp } from '../../..';\nimport { FormWarningText } from \"../../../components/elements/form-warning\";\nimport { useUser } from \"../../../lib/hooks\";\nimport { useTranslation } from \"../../../lib/translations\";\nimport { Section } from \"../section\";\n\n\nexport function PasswordSection(props?: {\n mockMode?: boolean,\n}) {\n const { t } = useTranslation();\n const user = useUser({ or: props?.mockMode ? 'return-null' : \"throw\" });\n\n // In mock mode, show a placeholder message\n if (props?.mockMode && !user) {\n return (\n <Section\n title={t(\"Password\")}\n description={t(\"Password management is not available in demo mode.\")}\n >\n <Typography variant='secondary'>{t(\"Password management is not available in demo mode.\")}</Typography>\n </Section>\n );\n }\n\n if (!user) {\n return null; // This shouldn't happen in non-mock mode due to throw\n }\n const contactChannels = user.useContactChannels();\n const [changingPassword, setChangingPassword] = useState(false);\n const [loading, setLoading] = useState(false);\n const project = useStackApp().useProject();\n\n const passwordSchema = yupObject({\n oldPassword: user.hasPassword ? schemaFieldsPasswordSchema.defined().nonEmpty(t('Please enter your old password')) : yupString(),\n newPassword: schemaFieldsPasswordSchema.defined().nonEmpty(t('Please enter your password')).test({\n name: 'is-valid-password',\n test: (value, ctx) => {\n const error = getPasswordError(value);\n if (error) {\n return ctx.createError({ message: error.message });\n } else {\n return true;\n }\n }\n }),\n newPasswordRepeat: yupString().nullable().oneOf([yup.ref('newPassword'), \"\", null], t('Passwords do not match')).defined().nonEmpty(t('Please repeat your password'))\n });\n\n const { register, handleSubmit, setError, formState: { errors }, clearErrors, reset } = useForm({\n resolver: yupResolver(passwordSchema)\n });\n\n // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition\n const hasValidEmail = contactChannels.filter(x => x.type === 'email' && x.usedForAuth).length > 0;\n\n const onSubmit = async (data: yup.InferType<typeof passwordSchema>) => {\n setLoading(true);\n try {\n const { oldPassword, newPassword } = data;\n const error = user.hasPassword\n ? await user.updatePassword({ oldPassword: oldPassword!, newPassword })\n : await user.setPassword({ password: newPassword! });\n if (error) {\n setError('oldPassword', { type: 'manual', message: t('Incorrect password') });\n } else {\n reset();\n setChangingPassword(false);\n }\n } finally {\n setLoading(false);\n }\n };\n\n const registerPassword = register('newPassword');\n const registerPasswordRepeat = register('newPasswordRepeat');\n\n if (!project.config.credentialEnabled) {\n return null;\n }\n\n return (\n <Section\n title={t(\"Password\")}\n description={user.hasPassword ? t(\"Update your password\") : t(\"Set a password for your account\")}\n >\n <div className='flex flex-col gap-4'>\n {!changingPassword ? (\n hasValidEmail ? (\n <Button\n variant='secondary'\n onClick={() => setChangingPassword(true)}\n >\n {user.hasPassword ? t(\"Update password\") : t(\"Set password\")}\n </Button>\n ) : (\n <Typography variant='secondary' type='label'>{t(\"To set a password, please add a sign-in email.\")}</Typography>\n )\n ) : (\n <form\n onSubmit={e => runAsynchronouslyWithAlert(handleSubmit(onSubmit)(e))}\n noValidate\n >\n {user.hasPassword && (\n <>\n <Label htmlFor=\"old-password\" className=\"mb-1\">{t(\"Old password\")}</Label>\n <Input\n id=\"old-password\"\n type=\"password\"\n autoComplete=\"current-password\"\n {...register(\"oldPassword\")}\n />\n <FormWarningText text={errors.oldPassword?.message?.toString()} />\n </>\n )}\n\n <Label htmlFor=\"new-password\" className=\"mt-4 mb-1\">{t(\"New password\")}</Label>\n <PasswordInput\n id=\"new-password\"\n autoComplete=\"new-password\"\n {...registerPassword}\n onChange={(e) => {\n clearErrors('newPassword');\n clearErrors('newPasswordRepeat');\n runAsynchronously(registerPassword.onChange(e));\n }}\n />\n <FormWarningText text={errors.newPassword?.message?.toString()} />\n\n <Label htmlFor=\"repeat-password\" className=\"mt-4 mb-1\">{t(\"Repeat new password\")}</Label>\n <PasswordInput\n id=\"repeat-password\"\n autoComplete=\"new-password\"\n {...registerPasswordRepeat}\n onChange={(e) => {\n clearErrors('newPassword');\n clearErrors('newPasswordRepeat');\n runAsynchronously(registerPasswordRepeat.onChange(e));\n }}\n />\n <FormWarningText text={errors.newPasswordRepeat?.message?.toString()} />\n\n <div className=\"mt-6 flex gap-4\">\n <Button type=\"submit\" loading={loading}>\n {user.hasPassword ? t(\"Update Password\") : t(\"Set Password\")}\n </Button>\n <Button\n variant=\"secondary\"\n onClick={() => {\n setChangingPassword(false);\n reset();\n }}\n >\n {t(\"Cancel\")}\n </Button>\n </div>\n </form>\n )}\n </div>\n </Section>\n );\n}\n"],"mappings":"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","names":["schemaFieldsPasswordSchema"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/components-page/account-settings/notifications/notifications-page.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { useUser } from \"../../../lib/hooks\";\nimport { useTranslation } from \"../../../lib/translations\";\nimport { PageLayout } from \"../page-layout\";\nimport { Switch } from \"@stackframe/stack-ui\";\nimport { Separator, Typography } from \"@stackframe/stack-ui\";\n\n\nexport function NotificationsPage() {\n const { t } = useTranslation();\n const user = useUser({ or: 'redirect' });\n const notificationCategories = user.useNotificationCategories();\n\n return (\n <PageLayout>\n <Separator />\n <div className='flex flex-col gap-2'>\n <div className='sm:flex-1 flex flex-col justify-center pb-2'>\n <Typography className=\"font-medium\">\n {t('Choose which emails you want to receive')}\n </Typography>\n </div>\n {notificationCategories.map((category) => (\n <div key={category.id} className=\"flex justify-start gap-4 items-center\">\n <Switch\n checked={category.enabled}\n onCheckedChange={(value) => void category.setEnabled(value)}\n disabled={!category.canDisable}\n />\n <Typography>{category.name}</Typography>\n {!category.canDisable && (\n <Typography variant='secondary' type='footnote'>\n (cannot be disabled)\n </Typography>\n )}\n </div>\n ))}\n </div>\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAIA,mBAAwB;AACxB,0BAA+B;AAC/B,yBAA2B;AAC3B,sBAAuB;AACvB,IAAAA,mBAAsC;AAUhC;AAPC,SAAS,oBAAoB;AAClC,QAAM,EAAE,EAAE,QAAI,oCAAe;AAC7B,QAAM,WAAO,sBAAQ,EAAE,IAAI,WAAW,CAAC;AACvC,QAAM,yBAAyB,KAAK,0BAA0B;AAE9D,SACE,6CAAC,iCACC;AAAA,gDAAC,8BAAU;AAAA,IACX,6CAAC,SAAI,WAAU,uBACb;AAAA,kDAAC,SAAI,WAAU,+CACb,sDAAC,+BAAW,WAAU,eACnB,YAAE,yCAAyC,GAC9C,GACF;AAAA,MACC,uBAAuB,IAAI,CAAC,aAC3B,6CAAC,SAAsB,WAAU,yCAC/B;AAAA;AAAA,UAAC;AAAA;AAAA,YACC,SAAS,SAAS;AAAA,YAClB,iBAAiB,CAAC,UAAU,KAAK,SAAS,WAAW,KAAK;AAAA,YAC1D,UAAU,CAAC,SAAS;AAAA;AAAA,QACtB;AAAA,QACA,4CAAC,+BAAY,mBAAS,MAAK;AAAA,QAC1B,CAAC,SAAS,cACT,4CAAC,+BAAW,SAAQ,aAAY,MAAK,YAAW,kCAEhD;AAAA,WAVM,SAAS,EAYnB,CACD;AAAA,OACH;AAAA,KACF;AAEJ;","names":["import_stack_ui"]}
1
+ {"version":3,"sources":["../../../../src/components-page/account-settings/notifications/notifications-page.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY, INSTEAD EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\nimport { useUser } from \"../../../lib/hooks\";\nimport { useTranslation } from \"../../../lib/translations\";\nimport { PageLayout } from \"../page-layout\";\nimport { Switch } from \"@stackframe/stack-ui\";\nimport { Separator, Typography } from \"@stackframe/stack-ui\";\n\n\nexport function NotificationsPage() {\n const { t } = useTranslation();\n const user = useUser({ or: 'redirect' });\n const notificationCategories = user.useNotificationCategories();\n\n return (\n <PageLayout>\n <Separator />\n <div className='flex flex-col gap-2'>\n <div className='sm:flex-1 flex flex-col justify-center pb-2'>\n <Typography className=\"font-medium\">\n {t('Choose which emails you want to receive')}\n </Typography>\n </div>\n {notificationCategories.map((category) => (\n <div key={category.id} className=\"flex justify-start gap-4 items-center\">\n <Switch\n checked={category.enabled}\n onCheckedChange={(value) => void category.setEnabled(value)}\n disabled={!category.canDisable}\n />\n <Typography>{category.name}</Typography>\n {!category.canDisable && (\n <Typography variant='secondary' type='footnote'>\n (cannot be disabled)\n </Typography>\n )}\n </div>\n ))}\n </div>\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAIA,mBAAwB;AACxB,0BAA+B;AAC/B,yBAA2B;AAC3B,sBAAuB;AACvB,IAAAA,mBAAsC;AAUhC;AAPC,SAAS,oBAAoB;AAClC,QAAM,EAAE,EAAE,QAAI,oCAAe;AAC7B,QAAM,WAAO,sBAAQ,EAAE,IAAI,WAAW,CAAC;AACvC,QAAM,yBAAyB,KAAK,0BAA0B;AAE9D,SACE,6CAAC,iCACC;AAAA,gDAAC,8BAAU;AAAA,IACX,6CAAC,SAAI,WAAU,uBACb;AAAA,kDAAC,SAAI,WAAU,+CACb,sDAAC,+BAAW,WAAU,eACnB,YAAE,yCAAyC,GAC9C,GACF;AAAA,MACC,uBAAuB,IAAI,CAAC,aAC3B,6CAAC,SAAsB,WAAU,yCAC/B;AAAA;AAAA,UAAC;AAAA;AAAA,YACC,SAAS,SAAS;AAAA,YAClB,iBAAiB,CAAC,UAAU,KAAK,SAAS,WAAW,KAAK;AAAA,YAC1D,UAAU,CAAC,SAAS;AAAA;AAAA,QACtB;AAAA,QACA,4CAAC,+BAAY,mBAAS,MAAK;AAAA,QAC1B,CAAC,SAAS,cACT,4CAAC,+BAAW,SAAQ,aAAY,MAAK,YAAW,kCAEhD;AAAA,WAVM,SAAS,EAYnB,CACD;AAAA,OACH;AAAA,KACF;AAEJ;","names":["import_stack_ui"]}
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components-page/account-settings/page-layout.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nexport function PageLayout(props: { children: React.ReactNode }) {\n return (\n <div className='flex flex-col gap-6'>\n {props.children}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAMI;AAFG,SAAS,WAAW,OAAsC;AAC/D,SACE,4CAAC,SAAI,WAAU,uBACZ,gBAAM,UACT;AAEJ;","names":[]}
1
+ {"version":3,"sources":["../../../src/components-page/account-settings/page-layout.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY, INSTEAD EDIT THE CORRESPONDING FILE IN packages/template\n//===========================================\nexport function PageLayout(props: { children: React.ReactNode }) {\n return (\n <div className='flex flex-col gap-6'>\n {props.children}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAMI;AAFG,SAAS,WAAW,OAAsC;AAC/D,SACE,4CAAC,SAAI,WAAU,uBACZ,gBAAM,UACT;AAEJ;","names":[]}