@stackframe/stack 2.8.11 → 2.8.16

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 (248) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/dist/components/api-key-dialogs.js +5 -4
  3. package/dist/components/api-key-dialogs.js.map +1 -1
  4. package/dist/components/credential-sign-in.js +4 -4
  5. package/dist/components/credential-sign-up.js +3 -3
  6. package/dist/components/elements/maybe-full-page.js +1 -1
  7. package/dist/components/elements/sidebar-layout.js +1 -1
  8. package/dist/components/magic-link-sign-in.js +6 -6
  9. package/dist/components/magic-link-sign-in.js.map +1 -1
  10. package/dist/components/message-cards/known-error-message-card.js +2 -2
  11. package/dist/components/message-cards/message-card.js +1 -1
  12. package/dist/components/message-cards/predefined-message-card.js +3 -3
  13. package/dist/components/oauth-button-group.js +2 -2
  14. package/dist/components/oauth-button.js +27 -16
  15. package/dist/components/oauth-button.js.map +1 -1
  16. package/dist/components/passkey-button.js +2 -2
  17. package/dist/components/profile-image-editor.js +87 -34
  18. package/dist/components/profile-image-editor.js.map +1 -1
  19. package/dist/components/selected-team-switcher.js +41 -9
  20. package/dist/components/selected-team-switcher.js.map +1 -1
  21. package/dist/components/{iframe-preventer.js → use-in-iframe.js} +9 -19
  22. package/dist/components/use-in-iframe.js.map +1 -0
  23. package/dist/components/user-button.js +41 -8
  24. package/dist/components/user-button.js.map +1 -1
  25. package/dist/components-page/account-settings/active-sessions/active-sessions-page.js +57 -12
  26. package/dist/components-page/account-settings/active-sessions/active-sessions-page.js.map +1 -1
  27. package/dist/components-page/account-settings/api-keys/api-keys-page.js +100 -12
  28. package/dist/components-page/account-settings/api-keys/api-keys-page.js.map +1 -1
  29. package/dist/components-page/account-settings/editable-text.js +1 -1
  30. package/dist/components-page/account-settings/email-and-auth/email-and-auth-page.js +12 -12
  31. package/dist/components-page/account-settings/email-and-auth/email-and-auth-page.js.map +1 -1
  32. package/dist/components-page/account-settings/email-and-auth/emails-section.js +15 -6
  33. package/dist/components-page/account-settings/email-and-auth/emails-section.js.map +1 -1
  34. package/dist/components-page/account-settings/email-and-auth/mfa-section.js +18 -5
  35. package/dist/components-page/account-settings/email-and-auth/mfa-section.js.map +1 -1
  36. package/dist/components-page/account-settings/email-and-auth/otp-section.js +18 -5
  37. package/dist/components-page/account-settings/email-and-auth/otp-section.js.map +1 -1
  38. package/dist/components-page/account-settings/email-and-auth/passkey-section.js +19 -6
  39. package/dist/components-page/account-settings/email-and-auth/passkey-section.js.map +1 -1
  40. package/dist/components-page/account-settings/email-and-auth/password-section.js +20 -7
  41. package/dist/components-page/account-settings/email-and-auth/password-section.js.map +1 -1
  42. package/dist/components-page/account-settings/profile-page/profile-page.js +18 -8
  43. package/dist/components-page/account-settings/profile-page/profile-page.js.map +1 -1
  44. package/dist/components-page/account-settings/settings/delete-account-section.js +19 -10
  45. package/dist/components-page/account-settings/settings/delete-account-section.js.map +1 -1
  46. package/dist/components-page/account-settings/settings/settings-page.js +6 -6
  47. package/dist/components-page/account-settings/settings/settings-page.js.map +1 -1
  48. package/dist/components-page/account-settings/settings/sign-out-section.js +15 -6
  49. package/dist/components-page/account-settings/settings/sign-out-section.js.map +1 -1
  50. package/dist/components-page/account-settings/teams/leave-team-section.js +3 -3
  51. package/dist/components-page/account-settings/teams/team-api-keys-section.js +9 -6
  52. package/dist/components-page/account-settings/teams/team-api-keys-section.js.map +1 -1
  53. package/dist/components-page/account-settings/teams/team-creation-page.js +19 -10
  54. package/dist/components-page/account-settings/teams/team-creation-page.js.map +1 -1
  55. package/dist/components-page/account-settings/teams/team-display-name-section.js +4 -4
  56. package/dist/components-page/account-settings/teams/team-member-invitation-section.js +4 -4
  57. package/dist/components-page/account-settings/teams/team-member-list-section.js +7 -4
  58. package/dist/components-page/account-settings/teams/team-member-list-section.js.map +1 -1
  59. package/dist/components-page/account-settings/teams/team-page.js +8 -8
  60. package/dist/components-page/account-settings/teams/team-profile-image-section.js +4 -4
  61. package/dist/components-page/account-settings/teams/team-profile-user-section.js +4 -4
  62. package/dist/components-page/account-settings.js +29 -21
  63. package/dist/components-page/account-settings.js.map +1 -1
  64. package/dist/components-page/auth-page.js +16 -17
  65. package/dist/components-page/auth-page.js.map +1 -1
  66. package/dist/components-page/cli-auth-confirm.js +3 -3
  67. package/dist/components-page/email-verification.js +6 -6
  68. package/dist/components-page/email-verification.js.map +1 -1
  69. package/dist/components-page/error-page.js +9 -9
  70. package/dist/components-page/error-page.js.map +1 -1
  71. package/dist/components-page/forgot-password.js +6 -6
  72. package/dist/components-page/magic-link-callback.js +7 -7
  73. package/dist/components-page/magic-link-callback.js.map +1 -1
  74. package/dist/components-page/mfa.js +190 -0
  75. package/dist/components-page/mfa.js.map +1 -0
  76. package/dist/components-page/oauth-callback.js +4 -4
  77. package/dist/components-page/password-reset.js +9 -9
  78. package/dist/components-page/password-reset.js.map +1 -1
  79. package/dist/components-page/sign-in.js +3 -2
  80. package/dist/components-page/sign-in.js.map +1 -1
  81. package/dist/components-page/sign-out.js +2 -2
  82. package/dist/components-page/sign-up.js +1 -1
  83. package/dist/components-page/stack-handler.js +25 -14
  84. package/dist/components-page/stack-handler.js.map +1 -1
  85. package/dist/components-page/team-creation.js +4 -4
  86. package/dist/components-page/team-invitation.js +6 -6
  87. package/dist/components-page/team-invitation.js.map +1 -1
  88. package/dist/esm/components/api-key-dialogs.js +5 -4
  89. package/dist/esm/components/api-key-dialogs.js.map +1 -1
  90. package/dist/esm/components/credential-sign-in.js +4 -4
  91. package/dist/esm/components/credential-sign-up.js +3 -3
  92. package/dist/esm/components/elements/maybe-full-page.js +1 -1
  93. package/dist/esm/components/elements/sidebar-layout.js +1 -1
  94. package/dist/esm/components/magic-link-sign-in.js +6 -6
  95. package/dist/esm/components/magic-link-sign-in.js.map +1 -1
  96. package/dist/esm/components/message-cards/known-error-message-card.js +2 -2
  97. package/dist/esm/components/message-cards/message-card.js +1 -1
  98. package/dist/esm/components/message-cards/predefined-message-card.js +3 -3
  99. package/dist/esm/components/oauth-button-group.js +2 -2
  100. package/dist/esm/components/oauth-button.js +28 -17
  101. package/dist/esm/components/oauth-button.js.map +1 -1
  102. package/dist/esm/components/passkey-button.js +2 -2
  103. package/dist/esm/components/profile-image-editor.js +86 -34
  104. package/dist/esm/components/profile-image-editor.js.map +1 -1
  105. package/dist/esm/components/selected-team-switcher.js +41 -9
  106. package/dist/esm/components/selected-team-switcher.js.map +1 -1
  107. package/dist/esm/components/use-in-iframe.js +18 -0
  108. package/dist/esm/components/use-in-iframe.js.map +1 -0
  109. package/dist/esm/components/user-button.js +41 -8
  110. package/dist/esm/components/user-button.js.map +1 -1
  111. package/dist/esm/components-page/account-settings/active-sessions/active-sessions-page.js +57 -12
  112. package/dist/esm/components-page/account-settings/active-sessions/active-sessions-page.js.map +1 -1
  113. package/dist/esm/components-page/account-settings/api-keys/api-keys-page.js +100 -12
  114. package/dist/esm/components-page/account-settings/api-keys/api-keys-page.js.map +1 -1
  115. package/dist/esm/components-page/account-settings/editable-text.js +1 -1
  116. package/dist/esm/components-page/account-settings/email-and-auth/email-and-auth-page.js +12 -12
  117. package/dist/esm/components-page/account-settings/email-and-auth/email-and-auth-page.js.map +1 -1
  118. package/dist/esm/components-page/account-settings/email-and-auth/emails-section.js +15 -6
  119. package/dist/esm/components-page/account-settings/email-and-auth/emails-section.js.map +1 -1
  120. package/dist/esm/components-page/account-settings/email-and-auth/mfa-section.js +18 -5
  121. package/dist/esm/components-page/account-settings/email-and-auth/mfa-section.js.map +1 -1
  122. package/dist/esm/components-page/account-settings/email-and-auth/otp-section.js +18 -5
  123. package/dist/esm/components-page/account-settings/email-and-auth/otp-section.js.map +1 -1
  124. package/dist/esm/components-page/account-settings/email-and-auth/passkey-section.js +19 -6
  125. package/dist/esm/components-page/account-settings/email-and-auth/passkey-section.js.map +1 -1
  126. package/dist/esm/components-page/account-settings/email-and-auth/password-section.js +20 -7
  127. package/dist/esm/components-page/account-settings/email-and-auth/password-section.js.map +1 -1
  128. package/dist/esm/components-page/account-settings/profile-page/profile-page.js +18 -8
  129. package/dist/esm/components-page/account-settings/profile-page/profile-page.js.map +1 -1
  130. package/dist/esm/components-page/account-settings/settings/delete-account-section.js +19 -10
  131. package/dist/esm/components-page/account-settings/settings/delete-account-section.js.map +1 -1
  132. package/dist/esm/components-page/account-settings/settings/settings-page.js +6 -6
  133. package/dist/esm/components-page/account-settings/settings/settings-page.js.map +1 -1
  134. package/dist/esm/components-page/account-settings/settings/sign-out-section.js +15 -6
  135. package/dist/esm/components-page/account-settings/settings/sign-out-section.js.map +1 -1
  136. package/dist/esm/components-page/account-settings/teams/leave-team-section.js +3 -3
  137. package/dist/esm/components-page/account-settings/teams/team-api-keys-section.js +9 -6
  138. package/dist/esm/components-page/account-settings/teams/team-api-keys-section.js.map +1 -1
  139. package/dist/esm/components-page/account-settings/teams/team-creation-page.js +19 -10
  140. package/dist/esm/components-page/account-settings/teams/team-creation-page.js.map +1 -1
  141. package/dist/esm/components-page/account-settings/teams/team-display-name-section.js +4 -4
  142. package/dist/esm/components-page/account-settings/teams/team-member-invitation-section.js +4 -4
  143. package/dist/esm/components-page/account-settings/teams/team-member-list-section.js +7 -4
  144. package/dist/esm/components-page/account-settings/teams/team-member-list-section.js.map +1 -1
  145. package/dist/esm/components-page/account-settings/teams/team-page.js +8 -8
  146. package/dist/esm/components-page/account-settings/teams/team-profile-image-section.js +4 -4
  147. package/dist/esm/components-page/account-settings/teams/team-profile-user-section.js +4 -4
  148. package/dist/esm/components-page/account-settings.js +29 -21
  149. package/dist/esm/components-page/account-settings.js.map +1 -1
  150. package/dist/esm/components-page/auth-page.js +16 -17
  151. package/dist/esm/components-page/auth-page.js.map +1 -1
  152. package/dist/esm/components-page/cli-auth-confirm.js +3 -3
  153. package/dist/esm/components-page/email-verification.js +6 -6
  154. package/dist/esm/components-page/email-verification.js.map +1 -1
  155. package/dist/esm/components-page/error-page.js +9 -9
  156. package/dist/esm/components-page/error-page.js.map +1 -1
  157. package/dist/esm/components-page/forgot-password.js +6 -6
  158. package/dist/esm/components-page/magic-link-callback.js +7 -7
  159. package/dist/esm/components-page/magic-link-callback.js.map +1 -1
  160. package/dist/esm/components-page/mfa.js +174 -0
  161. package/dist/esm/components-page/mfa.js.map +1 -0
  162. package/dist/esm/components-page/oauth-callback.js +4 -4
  163. package/dist/esm/components-page/password-reset.js +9 -9
  164. package/dist/esm/components-page/password-reset.js.map +1 -1
  165. package/dist/esm/components-page/sign-in.js +3 -2
  166. package/dist/esm/components-page/sign-in.js.map +1 -1
  167. package/dist/esm/components-page/sign-out.js +2 -2
  168. package/dist/esm/components-page/sign-up.js +1 -1
  169. package/dist/esm/components-page/stack-handler.js +25 -14
  170. package/dist/esm/components-page/stack-handler.js.map +1 -1
  171. package/dist/esm/components-page/team-creation.js +4 -4
  172. package/dist/esm/components-page/team-invitation.js +6 -6
  173. package/dist/esm/components-page/team-invitation.js.map +1 -1
  174. package/dist/esm/generated/global-css.js +1 -1
  175. package/dist/esm/generated/global-css.js.map +1 -1
  176. package/dist/esm/generated/quetzal-translations.js +3574 -2364
  177. package/dist/esm/generated/quetzal-translations.js.map +1 -1
  178. package/dist/esm/index.js +22 -22
  179. package/dist/esm/lib/auth.js +3 -3
  180. package/dist/esm/lib/auth.js.map +1 -1
  181. package/dist/esm/lib/cookie.js +1 -129
  182. package/dist/esm/lib/cookie.js.map +1 -1
  183. package/dist/esm/lib/hooks.js +1 -1
  184. package/dist/esm/lib/stack-app/apps/implementations/admin-app-impl.js +8 -8
  185. package/dist/esm/lib/stack-app/apps/implementations/admin-app-impl.js.map +1 -1
  186. package/dist/esm/lib/stack-app/apps/implementations/client-app-impl.js +57 -26
  187. package/dist/esm/lib/stack-app/apps/implementations/client-app-impl.js.map +1 -1
  188. package/dist/esm/lib/stack-app/apps/implementations/common.js +2 -1
  189. package/dist/esm/lib/stack-app/apps/implementations/common.js.map +1 -1
  190. package/dist/esm/lib/stack-app/apps/implementations/index.js +3 -3
  191. package/dist/esm/lib/stack-app/apps/implementations/server-app-impl.js +35 -17
  192. package/dist/esm/lib/stack-app/apps/implementations/server-app-impl.js.map +1 -1
  193. package/dist/esm/lib/stack-app/apps/index.js +3 -3
  194. package/dist/esm/lib/stack-app/apps/interfaces/admin-app.js +1 -1
  195. package/dist/esm/lib/stack-app/apps/interfaces/client-app.js +1 -1
  196. package/dist/esm/lib/stack-app/apps/interfaces/client-app.js.map +1 -1
  197. package/dist/esm/lib/stack-app/apps/interfaces/server-app.js +1 -1
  198. package/dist/esm/lib/stack-app/common.js.map +1 -1
  199. package/dist/esm/lib/stack-app/index.js +2 -2
  200. package/dist/esm/lib/stack-app/internal-api-keys/index.js.map +1 -1
  201. package/dist/esm/lib/stack-app/users/index.js.map +1 -1
  202. package/dist/esm/lib/translations.js +1 -1
  203. package/dist/esm/providers/stack-provider-client.js +2 -2
  204. package/dist/esm/providers/stack-provider.js +3 -3
  205. package/dist/esm/providers/theme-provider.js +3 -3
  206. package/dist/esm/providers/translation-provider.js +2 -2
  207. package/dist/esm/utils/browser-script.js +1 -1
  208. package/dist/generated/global-css.js +1 -1
  209. package/dist/generated/global-css.js.map +1 -1
  210. package/dist/generated/quetzal-translations.js +3574 -2364
  211. package/dist/generated/quetzal-translations.js.map +1 -1
  212. package/dist/index.d.mts +73 -2
  213. package/dist/index.d.ts +73 -2
  214. package/dist/index.js +23 -23
  215. package/dist/index.js.map +1 -1
  216. package/dist/lib/auth.js +3 -3
  217. package/dist/lib/auth.js.map +1 -1
  218. package/dist/lib/cookie.js +4 -132
  219. package/dist/lib/cookie.js.map +1 -1
  220. package/dist/lib/hooks.js +1 -1
  221. package/dist/lib/stack-app/apps/implementations/admin-app-impl.js +8 -8
  222. package/dist/lib/stack-app/apps/implementations/admin-app-impl.js.map +1 -1
  223. package/dist/lib/stack-app/apps/implementations/client-app-impl.js +57 -26
  224. package/dist/lib/stack-app/apps/implementations/client-app-impl.js.map +1 -1
  225. package/dist/lib/stack-app/apps/implementations/common.js +2 -1
  226. package/dist/lib/stack-app/apps/implementations/common.js.map +1 -1
  227. package/dist/lib/stack-app/apps/implementations/index.js +3 -3
  228. package/dist/lib/stack-app/apps/implementations/server-app-impl.js +35 -17
  229. package/dist/lib/stack-app/apps/implementations/server-app-impl.js.map +1 -1
  230. package/dist/lib/stack-app/apps/index.js +3 -3
  231. package/dist/lib/stack-app/apps/interfaces/admin-app.js +1 -1
  232. package/dist/lib/stack-app/apps/interfaces/client-app.js +1 -1
  233. package/dist/lib/stack-app/apps/interfaces/client-app.js.map +1 -1
  234. package/dist/lib/stack-app/apps/interfaces/server-app.js +1 -1
  235. package/dist/lib/stack-app/common.js.map +1 -1
  236. package/dist/lib/stack-app/index.js +2 -2
  237. package/dist/lib/stack-app/internal-api-keys/index.js.map +1 -1
  238. package/dist/lib/stack-app/users/index.js.map +1 -1
  239. package/dist/lib/translations.js +1 -1
  240. package/dist/providers/stack-provider-client.js +2 -2
  241. package/dist/providers/stack-provider.js +3 -3
  242. package/dist/providers/theme-provider.js +3 -3
  243. package/dist/providers/translation-provider.js +2 -2
  244. package/dist/utils/browser-script.js +1 -1
  245. package/package.json +5 -5
  246. package/dist/components/iframe-preventer.js.map +0 -1
  247. package/dist/esm/components/iframe-preventer.js +0 -28
  248. package/dist/esm/components/iframe-preventer.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/profile-image-editor.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { fileToBase64 } from '@stackframe/stack-shared/dist/utils/base64';\nimport { runAsynchronouslyWithAlert } from '@stackframe/stack-shared/dist/utils/promises';\nimport { Button, Slider, Typography } from '@stackframe/stack-ui';\nimport imageCompression from 'browser-image-compression';\nimport { Upload } from 'lucide-react';\nimport { ComponentProps, useRef, useState } from 'react';\nimport AvatarEditor from 'react-avatar-editor';\nimport { useTranslation } from '../lib/translations';\nimport { UserAvatar } from './elements/user-avatar';\n\nexport async function checkImageUrl(url: string){\n try {\n const res = await fetch(url, { method: 'HEAD' });\n const buff = await res.blob();\n return buff.type.startsWith('image/');\n } catch (e) {\n return false;\n }\n}\n\nexport function ProfileImageEditor(props: {\n user: NonNullable<ComponentProps<typeof UserAvatar>['user']>,\n onProfileImageUrlChange: (profileImageUrl: string | null) => void | Promise<void>,\n}) {\n const { t } = useTranslation();\n const cropRef = useRef<AvatarEditor>(null);\n const [slideValue, setSlideValue] = useState(1);\n const [rawUrl, setRawUrl] = useState<string | null>(null);\n const [error, setError] = useState<string | null>(null);\n\n function reset() {\n setSlideValue(1);\n setRawUrl(null);\n setError(null);\n }\n\n function upload() {\n const input = document.createElement('input');\n input.type = 'file';\n input.onchange = (e) => {\n const file = (e.target as HTMLInputElement).files?.[0];\n if (!file) return;\n runAsynchronouslyWithAlert(async () => {\n const rawUrl = await fileToBase64(file);\n if (await checkImageUrl(rawUrl)) {\n setRawUrl(rawUrl);\n setError(null);\n } else {\n setError(t('Invalid image'));\n }\n input.remove();\n });\n };\n input.click();\n }\n\n if (!rawUrl) {\n return <div className='flex flex-col'>\n <div className='cursor-pointer relative' onClick={upload}>\n <UserAvatar\n size={60}\n user={props.user}\n border\n />\n <div className='absolute top-0 left-0 h-[60px] w-[60px] bg-gray-500/20 backdrop-blur-sm items-center justify-center rounded-full flex opacity-0 hover:opacity-100 transition-opacity'>\n <div className='bg-background p-2 rounded-full'>\n <Upload className='h-5 w-5' />\n </div>\n </div>\n </div>\n {error && <Typography variant='destructive' type='label'>{error}</Typography>}\n </div>;\n }\n\n return (\n <div className='flex flex-col items-center gap-4'>\n <AvatarEditor\n ref={cropRef}\n image={rawUrl || props.user.profileImageUrl || \"\"}\n borderRadius={1000}\n color={[0, 0, 0, 0.72]}\n scale={slideValue}\n rotate={0}\n border={20}\n className='border'\n />\n <Slider\n min={1}\n max={5}\n step={0.1}\n defaultValue={[slideValue]}\n value={[slideValue]}\n onValueChange={(v) => setSlideValue(v[0])}\n />\n\n <div className='flex flex-row gap-2'>\n <Button\n onClick={async () => {\n if (cropRef.current && rawUrl) {\n const croppedUrl = cropRef.current.getImage().toDataURL('image/jpeg');\n const compressedFile = await imageCompression(\n await imageCompression.getFilefromDataUrl(croppedUrl, 'profile-image'),\n {\n maxSizeMB: 0.1,\n fileType: \"image/jpeg\",\n }\n );\n const compressedUrl = await imageCompression.getDataUrlFromFile(compressedFile);\n await props.onProfileImageUrlChange(compressedUrl);\n reset();\n }\n }}\n >\n {t('Save')}\n </Button>\n <Button\n variant=\"secondary\"\n onClick={reset}\n >\n {t('Cancel')}\n </Button>\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":["rawUrl","AvatarEditor","imageCompression"]}
1
+ {"version":3,"sources":["../../src/components/profile-image-editor.tsx"],"sourcesContent":["\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\nimport { fileToBase64 } from '@stackframe/stack-shared/dist/utils/base64';\nimport { runAsynchronouslyWithAlert } from '@stackframe/stack-shared/dist/utils/promises';\nimport { Button, Slider, Typography } from '@stackframe/stack-ui';\nimport imageCompression from 'browser-image-compression';\nimport { Upload } from 'lucide-react';\nimport { ComponentProps, useCallback, useState } from 'react';\nimport Cropper, { Area } from 'react-easy-crop';\nimport { useTranslation } from '../lib/translations';\nimport { UserAvatar } from './elements/user-avatar';\n\nexport async function checkImageUrl(url: string){\n try {\n const res = await fetch(url, { method: 'HEAD' });\n const buff = await res.blob();\n return buff.type.startsWith('image/');\n } catch (e) {\n return false;\n }\n}\n\nconst createImage = (url: string): Promise<HTMLImageElement> =>\n new Promise((resolve, reject) => {\n const image = new Image();\n image.addEventListener('load', () => resolve(image));\n image.addEventListener('error', (error) => reject(error));\n image.setAttribute('crossOrigin', 'anonymous');\n image.src = url;\n });\n\nexport async function getCroppedImg(imageSrc: string, pixelCrop: Area): Promise<string | null> {\n const image = await createImage(imageSrc);\n const canvas = document.createElement('canvas');\n const ctx = canvas.getContext('2d');\n\n if (!ctx) {\n return null;\n }\n\n const safeCrop = {\n x: Math.max(0, pixelCrop.x),\n y: Math.max(0, pixelCrop.y),\n width: Math.max(1, pixelCrop.width),\n height: Math.max(1, pixelCrop.height),\n };\n\n canvas.width = safeCrop.width;\n canvas.height = safeCrop.height;\n\n ctx.drawImage(\n image,\n safeCrop.x,\n safeCrop.y,\n safeCrop.width,\n safeCrop.height,\n 0,\n 0,\n safeCrop.width,\n safeCrop.height\n );\n\n return canvas.toDataURL('image/jpeg');\n}\n\nexport function ProfileImageEditor(props: {\n user: NonNullable<ComponentProps<typeof UserAvatar>['user']>,\n onProfileImageUrlChange: (profileImageUrl: string | null) => void | Promise<void>,\n}) {\n const { t } = useTranslation();\n const [rawUrl, setRawUrl] = useState<string | null>(null);\n const [error, setError] = useState<string | null>(null);\n const [crop, setCrop] = useState({ x: 0, y: 0 });\n const [zoom, setZoom] = useState(1);\n const [croppedAreaPixels, setCroppedAreaPixels] = useState<Area | null>(null);\n\n function reset() {\n setRawUrl(null);\n setError(null);\n setCrop({ x: 0, y: 0 });\n setZoom(1);\n setCroppedAreaPixels(null);\n }\n\n const onCropChange = useCallback((crop: { x: number, y: number }) => {\n setCrop(crop);\n }, []);\n\n const onCropComplete = useCallback((croppedArea: Area, croppedAreaPixels: Area) => {\n setCroppedAreaPixels(croppedAreaPixels);\n }, []);\n\n const onZoomChange = useCallback((zoom: number) => {\n setZoom(zoom);\n }, []);\n\n\n function upload() {\n const input = document.createElement('input');\n input.type = 'file';\n input.onchange = (e) => {\n const file = (e.target as HTMLInputElement).files?.[0];\n if (!file) return;\n runAsynchronouslyWithAlert(async () => {\n const rawUrl = await fileToBase64(file);\n if (await checkImageUrl(rawUrl)) {\n setRawUrl(rawUrl);\n setError(null);\n } else {\n setError(t('Invalid image'));\n }\n input.remove();\n });\n };\n input.click();\n }\n\n if (!rawUrl) {\n return <div className='flex flex-col'>\n <div className='cursor-pointer relative' onClick={upload}>\n <UserAvatar\n size={60}\n user={props.user}\n border\n />\n <div className='absolute top-0 left-0 h-[60px] w-[60px] bg-gray-500/20 backdrop-blur-sm items-center justify-center rounded-full flex opacity-0 hover:opacity-100 transition-opacity'>\n <div className='bg-background p-2 rounded-full'>\n <Upload className='h-5 w-5' />\n </div>\n </div>\n </div>\n {error && <Typography variant='destructive' type='label'>{error}</Typography>}\n </div>;\n }\n\n return (\n <div className='flex flex-col items-center gap-4'>\n <div className='relative w-64 h-64'>\n <Cropper\n image={rawUrl || props.user.profileImageUrl || \"\"}\n crop={crop}\n zoom={zoom}\n aspect={1}\n cropShape=\"round\"\n showGrid={false}\n onCropChange={onCropChange}\n onCropComplete={onCropComplete}\n onZoomChange={onZoomChange}\n />\n </div>\n <Slider\n min={1}\n max={3}\n step={0.1}\n value={[zoom]}\n onValueChange={(v) => onZoomChange(v[0])}\n />\n\n <div className='flex flex-row gap-2'>\n <Button\n onClick={async () => {\n if (rawUrl && croppedAreaPixels) {\n const croppedImageUrl = await getCroppedImg(rawUrl, croppedAreaPixels);\n if (croppedImageUrl) {\n const compressedFile = await imageCompression(\n await imageCompression.getFilefromDataUrl(croppedImageUrl, 'profile-image'),\n {\n maxSizeMB: 0.1,\n fileType: \"image/jpeg\",\n }\n );\n const compressedUrl = await imageCompression.getDataUrlFromFile(compressedFile);\n await props.onProfileImageUrlChange(compressedUrl);\n reset();\n } else {\n setError(t('Could not crop image.'));\n }\n }\n }}\n >\n {t('Save')}\n </Button>\n <Button\n variant=\"secondary\"\n onClick={reset}\n >\n {t('Cancel')}\n </Button>\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":["crop","croppedAreaPixels","zoom","rawUrl","Cropper","imageCompression"]}
@@ -29,9 +29,9 @@ var import_promises = require("@stackframe/stack-shared/dist/utils/promises");
29
29
  var import_stack_ui = require("@stackframe/stack-ui");
30
30
  var import_lucide_react = require("lucide-react");
31
31
  var import_react = require("react");
32
- var import__ = require("..");
33
- var import_translations = require("../lib/translations");
34
- var import_team_icon = require("./team-icon");
32
+ var import__ = require("../index.js");
33
+ var import_translations = require("../lib/translations.js");
34
+ var import_team_icon = require("./team-icon.js");
35
35
  var import_jsx_runtime = require("react/jsx-runtime");
36
36
  function SelectedTeamSwitcher(props) {
37
37
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react.Suspense, { fallback: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Fallback, {}), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Inner, { ...props }) });
@@ -41,23 +41,38 @@ function Fallback() {
41
41
  }
42
42
  function Inner(props) {
43
43
  const { t } = (0, import_translations.useTranslation)();
44
- const app = (0, import__.useStackApp)();
45
- const user = (0, import__.useUser)();
44
+ const appFromHook = (0, import__.useStackApp)();
45
+ const userFromHook = (0, import__.useUser)();
46
+ const app = props.mockUser ? {
47
+ useProject: () => props.mockProject || { config: { clientTeamCreationEnabled: false } },
48
+ useNavigate: () => () => {
49
+ },
50
+ // Mock navigate function
51
+ urls: { accountSettings: "/account-settings" }
52
+ } : appFromHook;
53
+ const user = props.mockUser ? {
54
+ selectedTeam: props.mockUser.selectedTeam,
55
+ useTeams: () => props.mockTeams || [],
56
+ setSelectedTeam: async () => {
57
+ }
58
+ // Mock function
59
+ } : userFromHook;
46
60
  const project = app.useProject();
47
61
  const navigate = app.useNavigate();
48
62
  const selectedTeam = user?.selectedTeam || props.selectedTeam;
49
63
  const rawTeams = user?.useTeams();
50
64
  const teams = (0, import_react.useMemo)(() => rawTeams?.sort((a, b) => b.id === selectedTeam?.id ? 1 : -1), [rawTeams, selectedTeam]);
51
65
  (0, import_react.useEffect)(() => {
52
- if (!props.noUpdateSelectedTeam && props.selectedTeam) {
66
+ if (!props.noUpdateSelectedTeam && props.selectedTeam && !props.mockUser) {
53
67
  (0, import_promises.runAsynchronouslyWithAlert)(user?.setSelectedTeam(props.selectedTeam));
54
68
  }
55
- }, [props.noUpdateSelectedTeam, props.selectedTeam]);
69
+ }, [props.noUpdateSelectedTeam, props.selectedTeam, props.mockUser]);
56
70
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
57
71
  import_stack_ui.Select,
58
72
  {
59
73
  value: selectedTeam?.id,
60
74
  onValueChange: (value) => {
75
+ if (props.mockUser) return;
61
76
  (0, import_promises.runAsynchronouslyWithAlert)(async () => {
62
77
  const team = teams?.find((team2) => team2.id === value);
63
78
  if (!team) {
@@ -77,7 +92,20 @@ function Inner(props) {
77
92
  user?.selectedTeam ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_stack_ui.SelectGroup, { children: [
78
93
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.SelectLabel, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center justify-between", children: [
79
94
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: t("Current team") }),
80
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Button, { variant: "ghost", size: "icon", className: "h-6 w-6", onClick: () => navigate(`${app.urls.accountSettings}#team-${user.selectedTeam?.id}`), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Settings, { className: "h-4 w-4" }) })
95
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
96
+ import_stack_ui.Button,
97
+ {
98
+ variant: "ghost",
99
+ size: "icon",
100
+ className: "h-6 w-6",
101
+ onClick: () => {
102
+ if (!props.mockUser) {
103
+ navigate(`${app.urls.accountSettings}#team-${user.selectedTeam?.id}`);
104
+ }
105
+ },
106
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Settings, { className: "h-4 w-4" })
107
+ }
108
+ )
81
109
  ] }) }),
82
110
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.SelectItem, { value: user.selectedTeam.id, children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex items-center gap-2", children: [
83
111
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_team_icon.TeamIcon, { team: user.selectedTeam }),
@@ -96,7 +124,11 @@ function Inner(props) {
96
124
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
97
125
  import_stack_ui.Button,
98
126
  {
99
- onClick: () => navigate(`${app.urls.accountSettings}#team-creation`),
127
+ onClick: () => {
128
+ if (!props.mockUser) {
129
+ navigate(`${app.urls.accountSettings}#team-creation`);
130
+ }
131
+ },
100
132
  className: "w-full",
101
133
  variant: "ghost",
102
134
  children: [
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/selected-team-switcher.tsx"],"sourcesContent":["'use client';\n\n\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\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} from \"@stackframe/stack-ui\";\nimport { PlusCircle, Settings } from \"lucide-react\";\nimport { Suspense, useEffect, useMemo } from \"react\";\nimport { Team, useStackApp, useUser } from \"..\";\nimport { useTranslation } from \"../lib/translations\";\nimport { TeamIcon } from \"./team-icon\";\n\ntype SelectedTeamSwitcherProps = {\n urlMap?: (team: Team) => string,\n selectedTeam?: Team,\n noUpdateSelectedTeam?: boolean,\n};\n\nexport function SelectedTeamSwitcher(props: SelectedTeamSwitcherProps) {\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(props: SelectedTeamSwitcherProps) {\n const { t } = useTranslation();\n const app = useStackApp();\n const user = useUser();\n const project = app.useProject();\n const navigate = app.useNavigate();\n const selectedTeam = user?.selectedTeam || props.selectedTeam;\n const rawTeams = user?.useTeams();\n const teams = useMemo(() => rawTeams?.sort((a, b) => b.id === selectedTeam?.id ? 1 : -1), [rawTeams, selectedTeam]);\n\n useEffect(() => {\n if (!props.noUpdateSelectedTeam && props.selectedTeam) {\n runAsynchronouslyWithAlert(user?.setSelectedTeam(props.selectedTeam));\n }\n }, [props.noUpdateSelectedTeam, props.selectedTeam]);\n\n return (\n <Select\n value={selectedTeam?.id}\n onValueChange={(value) => {\n runAsynchronouslyWithAlert(async () => {\n const team = teams?.find(team => team.id === value);\n if (!team) {\n throw new Error('Team not found, this should not happen');\n }\n\n if (!props.noUpdateSelectedTeam) {\n await user?.setSelectedTeam(team);\n }\n if (props.urlMap) {\n navigate(props.urlMap(team));\n }\n });\n }}\n >\n <SelectTrigger className=\"stack-scope max-w-64\">\n <SelectValue placeholder=\"Select team\"/>\n </SelectTrigger>\n <SelectContent className=\"stack-scope\">\n {user?.selectedTeam ? <SelectGroup>\n <SelectLabel>\n <div className=\"flex items-center justify-between\">\n <span>\n {t('Current team')}\n </span>\n <Button variant='ghost' size='icon' className=\"h-6 w-6\" onClick={() => navigate(`${app.urls.accountSettings}#team-${user.selectedTeam?.id}`)}>\n <Settings className=\"h-4 w-4\"/>\n </Button>\n </div>\n </SelectLabel>\n <SelectItem value={user.selectedTeam.id}>\n <div className=\"flex items-center gap-2\">\n <TeamIcon team={user.selectedTeam} />\n <Typography className=\"max-w-40 truncate\">{user.selectedTeam.displayName}</Typography>\n </div>\n </SelectItem>\n </SelectGroup> : undefined}\n\n {teams?.length ?\n <SelectGroup>\n <SelectLabel>{t('Other teams')}</SelectLabel>\n {teams.filter(team => team.id !== user?.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} />\n <Typography className=\"max-w-64 truncate\">{team.displayName}</Typography>\n </div>\n </SelectItem>\n ))}\n </SelectGroup> :\n <SelectGroup>\n <SelectLabel>{t('No teams yet')}</SelectLabel>\n </SelectGroup>}\n\n {project.config.clientTeamCreationEnabled && <>\n <SelectSeparator/>\n <div>\n <Button\n onClick={() => navigate(`${app.urls.accountSettings}#team-creation`)}\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/selected-team-switcher.tsx"],"sourcesContent":["'use client';\n\n\n//===========================================\n// THIS FILE IS AUTO-GENERATED FROM TEMPLATE. DO NOT EDIT IT DIRECTLY\n//===========================================\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} from \"@stackframe/stack-ui\";\nimport { PlusCircle, Settings } from \"lucide-react\";\nimport { Suspense, useEffect, 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 SelectedTeamSwitcherProps = {\n urlMap?: (team: Team) => string,\n selectedTeam?: Team,\n noUpdateSelectedTeam?: boolean,\n // Mock data props\n mockUser?: {\n selectedTeam?: MockTeam,\n },\n mockTeams?: MockTeam[],\n mockProject?: {\n config: {\n clientTeamCreationEnabled: boolean,\n },\n },\n};\n\nexport function SelectedTeamSwitcher(props: SelectedTeamSwitcherProps) {\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(props: SelectedTeamSwitcherProps) {\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.selectedTeam,\n useTeams: () => props.mockTeams || [],\n setSelectedTeam: async () => {}, // Mock function\n } : userFromHook;\n\n const project = app.useProject();\n const navigate = app.useNavigate();\n const selectedTeam = user?.selectedTeam || props.selectedTeam;\n const rawTeams = user?.useTeams();\n const teams = useMemo(() => rawTeams?.sort((a, b) => b.id === selectedTeam?.id ? 1 : -1), [rawTeams, selectedTeam]);\n\n useEffect(() => {\n if (!props.noUpdateSelectedTeam && props.selectedTeam && !props.mockUser) {\n runAsynchronouslyWithAlert(user?.setSelectedTeam(props.selectedTeam));\n }\n }, [props.noUpdateSelectedTeam, props.selectedTeam, props.mockUser]);\n\n return (\n <Select\n value={selectedTeam?.id}\n onValueChange={(value) => {\n // Skip actual navigation/updates in mock mode\n if (props.mockUser) return;\n\n runAsynchronouslyWithAlert(async () => {\n const team = teams?.find(team => team.id === value);\n if (!team) {\n throw new Error('Team not found, this should not happen');\n }\n\n if (!props.noUpdateSelectedTeam) {\n await user?.setSelectedTeam(team as Team);\n }\n if (props.urlMap) {\n navigate(props.urlMap(team as Team));\n }\n });\n }}\n >\n <SelectTrigger className=\"stack-scope max-w-64\">\n <SelectValue placeholder=\"Select team\"/>\n </SelectTrigger>\n <SelectContent className=\"stack-scope\">\n {user?.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 // Skip navigation in mock mode\n if (!props.mockUser) {\n navigate(`${app.urls.accountSettings}#team-${user.selectedTeam?.id}`);\n }\n }}\n >\n <Settings className=\"h-4 w-4\"/>\n </Button>\n </div>\n </SelectLabel>\n <SelectItem value={user.selectedTeam.id}>\n <div className=\"flex items-center gap-2\">\n <TeamIcon team={user.selectedTeam as Team} />\n <Typography className=\"max-w-40 truncate\">{user.selectedTeam.displayName}</Typography>\n </div>\n </SelectItem>\n </SelectGroup> : undefined}\n\n {teams?.length ?\n <SelectGroup>\n <SelectLabel>{t('Other teams')}</SelectLabel>\n {teams.filter(team => team.id !== user?.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> :\n <SelectGroup>\n <SelectLabel>{t('No teams yet')}</SelectLabel>\n </SelectGroup>}\n\n {project.config.clientTeamCreationEnabled && <>\n <SelectSeparator/>\n <div>\n <Button\n onClick={() => {\n // Skip navigation in mock mode\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"]}
@@ -19,34 +19,24 @@ var __copyProps = (to, from, except, desc) => {
19
19
  };
20
20
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
21
21
 
22
- // src/components/iframe-preventer.tsx
23
- var iframe_preventer_exports = {};
24
- __export(iframe_preventer_exports, {
25
- IframePreventer: () => IframePreventer
22
+ // src/components/use-in-iframe.tsx
23
+ var use_in_iframe_exports = {};
24
+ __export(use_in_iframe_exports, {
25
+ useInIframe: () => useInIframe
26
26
  });
27
- module.exports = __toCommonJS(iframe_preventer_exports);
27
+ module.exports = __toCommonJS(use_in_iframe_exports);
28
28
  var import_react = require("react");
29
- var import_jsx_runtime = require("react/jsx-runtime");
30
- function IframePreventer({ children }) {
29
+ function useInIframe() {
31
30
  const [isIframe, setIsIframe] = (0, import_react.useState)(false);
32
31
  (0, import_react.useEffect)(() => {
33
32
  if (window.self !== window.top) {
34
33
  setIsIframe(true);
35
34
  }
36
35
  }, []);
37
- if (isIframe) {
38
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
39
- "Stack Auth components may not run in an ",
40
- "<",
41
- "iframe",
42
- ">",
43
- "."
44
- ] });
45
- }
46
- return children;
36
+ return isIframe;
47
37
  }
48
38
  // Annotate the CommonJS export names for ESM import in node:
49
39
  0 && (module.exports = {
50
- IframePreventer
40
+ useInIframe
51
41
  });
52
- //# sourceMappingURL=iframe-preventer.js.map
42
+ //# sourceMappingURL=use-in-iframe.js.map
@@ -0,0 +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":[]}
@@ -29,9 +29,9 @@ var import_promises = require("@stackframe/stack-shared/dist/utils/promises");
29
29
  var import_stack_ui = require("@stackframe/stack-ui");
30
30
  var import_lucide_react = require("lucide-react");
31
31
  var import_react = require("react");
32
- var import__ = require("..");
33
- var import_translations = require("../lib/translations");
34
- var import_user_avatar = require("./elements/user-avatar");
32
+ var import__ = require("../index.js");
33
+ var import_translations = require("../lib/translations.js");
34
+ var import_user_avatar = require("./elements/user-avatar.js");
35
35
  var import_jsx_runtime = require("react/jsx-runtime");
36
36
  function Item(props) {
37
37
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.DropdownMenuItem, { onClick: () => (0, import_promises.runAsynchronouslyWithAlert)(props.onClick), children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex gap-2 items-center", children: [
@@ -43,7 +43,16 @@ function UserButton(props) {
43
43
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react.Suspense, { fallback: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Skeleton, { className: "h-[34px] w-[34px] rounded-full stack-scope" }), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(UserButtonInner, { ...props }) });
44
44
  }
45
45
  function UserButtonInner(props) {
46
- const user = (0, import__.useUser)();
46
+ const userFromHook = (0, import__.useUser)();
47
+ const user = props.mockUser ? {
48
+ displayName: props.mockUser.displayName || "Mock User",
49
+ primaryEmail: props.mockUser.primaryEmail || "mock@example.com",
50
+ profileImageUrl: props.mockUser.profileImageUrl,
51
+ signOut: () => {
52
+ console.log("Mock sign out - no action taken in demo mode");
53
+ return Promise.resolve();
54
+ }
55
+ } : userFromHook;
47
56
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(UserButtonInnerInner, { ...props, user });
48
57
  }
49
58
  function UserButtonInnerInner(props) {
@@ -73,7 +82,13 @@ function UserButtonInnerInner(props) {
73
82
  Item,
74
83
  {
75
84
  text: t("Account settings"),
76
- onClick: async () => await app.redirectToAccountSettings(),
85
+ onClick: async () => {
86
+ if (props.mockUser) {
87
+ console.log("Mock account settings - no navigation in demo mode");
88
+ } else {
89
+ await app.redirectToAccountSettings();
90
+ }
91
+ },
77
92
  icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.CircleUser, { ...iconProps })
78
93
  }
79
94
  ),
@@ -81,7 +96,13 @@ function UserButtonInnerInner(props) {
81
96
  Item,
82
97
  {
83
98
  text: t("Sign in"),
84
- onClick: async () => await app.redirectToSignIn(),
99
+ onClick: async () => {
100
+ if (props.mockUser) {
101
+ console.log("Mock sign in - no navigation in demo mode");
102
+ } else {
103
+ await app.redirectToSignIn();
104
+ }
105
+ },
85
106
  icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.LogIn, { ...iconProps })
86
107
  }
87
108
  ),
@@ -89,7 +110,13 @@ function UserButtonInnerInner(props) {
89
110
  Item,
90
111
  {
91
112
  text: t("Sign up"),
92
- onClick: async () => await app.redirectToSignUp(),
113
+ onClick: async () => {
114
+ if (props.mockUser) {
115
+ console.log("Mock sign up - no navigation in demo mode");
116
+ } else {
117
+ await app.redirectToSignUp();
118
+ }
119
+ },
93
120
  icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.UserPlus, { ...iconProps })
94
121
  }
95
122
  ),
@@ -106,7 +133,13 @@ function UserButtonInnerInner(props) {
106
133
  Item,
107
134
  {
108
135
  text: t("Sign out"),
109
- onClick: () => user.signOut(),
136
+ onClick: async () => {
137
+ if (props.mockUser) {
138
+ console.log("Mock sign out - no action taken in demo mode");
139
+ } else {
140
+ await user.signOut();
141
+ }
142
+ },
110
143
  icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.LogOut, { ...iconProps })
111
144
  }
112
145
  )
@@ -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};\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 user = useUser();\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\">\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\">\n <Typography className=\"max-w-40 truncate\">{user.displayName}</Typography>\n <Typography className=\"max-w-40 truncate\" variant=\"secondary\" type='label'>{user.primaryEmail}</Typography>\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 () => await app.redirectToAccountSettings()}\n icon={<CircleUser {...iconProps} />}\n />}\n {!user && <Item\n text={t('Sign in')}\n onClick={async () => await app.redirectToSignIn()}\n icon={<LogIn {...iconProps} />}\n />}\n {!user && <Item\n text={t('Sign up')}\n onClick={async () => await app.redirectToSignUp()}\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={() => user.signOut()}\n icon={<LogOut {...iconProps} />}\n />}\n </DropdownMenuContent>\n </DropdownMenu>\n );\n}\n"],"mappings":"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","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\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\">\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\">\n <Typography className=\"max-w-40 truncate\">{user.displayName}</Typography>\n <Typography className=\"max-w-40 truncate\" variant=\"secondary\" type='label'>{user.primaryEmail}</Typography>\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":[]}
@@ -28,29 +28,70 @@ var import_errors = require("@stackframe/stack-shared/dist/utils/errors");
28
28
  var import_promises = require("@stackframe/stack-shared/dist/utils/promises");
29
29
  var import_stack_ui = require("@stackframe/stack-ui");
30
30
  var import_react = require("react");
31
- var import_hooks = require("../../../lib/hooks");
32
- var import_translations = require("../../../lib/translations");
33
- var import_page_layout = require("../page-layout");
31
+ var import_hooks = require("../../../lib/hooks.js");
32
+ var import_translations = require("../../../lib/translations.js");
33
+ var import_page_layout = require("../page-layout.js");
34
34
  var import_jsx_runtime = require("react/jsx-runtime");
35
- function ActiveSessionsPage() {
35
+ function ActiveSessionsPage(props) {
36
36
  const { t } = (0, import_translations.useTranslation)();
37
- const user = (0, import_hooks.useUser)({ or: "throw" });
38
- const [isLoading, setIsLoading] = (0, import_react.useState)(true);
37
+ const userFromHook = (0, import_hooks.useUser)({ or: props?.mockSessions || props?.mockMode ? "return-null" : "throw" });
38
+ const [isLoading, setIsLoading] = (0, import_react.useState)(!props?.mockSessions);
39
39
  const [isRevokingAll, setIsRevokingAll] = (0, import_react.useState)(false);
40
40
  const [sessions, setSessions] = (0, import_react.useState)([]);
41
41
  const [showConfirmRevokeAll, setShowConfirmRevokeAll] = (0, import_react.useState)(false);
42
+ const mockSessionsData = props?.mockSessions ? props.mockSessions.map((session) => ({
43
+ id: session.id,
44
+ isCurrentSession: session.isCurrentSession,
45
+ isImpersonation: session.isImpersonation || false,
46
+ createdAt: session.createdAt,
47
+ lastUsedAt: session.lastUsedAt,
48
+ geoInfo: session.geoInfo
49
+ })) : [
50
+ {
51
+ id: "current-session",
52
+ isCurrentSession: true,
53
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
54
+ lastUsedAt: (/* @__PURE__ */ new Date()).toISOString(),
55
+ geoInfo: { ip: "192.168.1.1", cityName: "San Francisco" }
56
+ },
57
+ {
58
+ id: "mobile-session",
59
+ isCurrentSession: false,
60
+ createdAt: new Date(Date.now() - 864e5).toISOString(),
61
+ // 1 day ago
62
+ lastUsedAt: new Date(Date.now() - 72e5).toISOString(),
63
+ // 2 hours ago
64
+ geoInfo: { ip: "10.0.0.1", cityName: "New York" }
65
+ }
66
+ ];
42
67
  (0, import_react.useEffect)(() => {
68
+ if (props?.mockSessions) {
69
+ setSessions(mockSessionsData);
70
+ setIsLoading(false);
71
+ return;
72
+ }
73
+ if (props?.mockMode && !userFromHook) {
74
+ setSessions(mockSessionsData);
75
+ setIsLoading(false);
76
+ return;
77
+ }
78
+ if (!userFromHook) return;
43
79
  (0, import_promises.runAsynchronously)(async () => {
44
80
  setIsLoading(true);
45
- const sessionsData = await user.getActiveSessions();
81
+ const sessionsData = await userFromHook.getActiveSessions();
46
82
  const enhancedSessions = sessionsData;
47
83
  setSessions(enhancedSessions);
48
84
  setIsLoading(false);
49
85
  });
50
- }, [user]);
86
+ }, [userFromHook, props?.mockSessions]);
51
87
  const handleRevokeSession = async (sessionId) => {
88
+ if (props?.mockSessions) {
89
+ setSessions((prev) => prev.filter((session) => session.id !== sessionId));
90
+ return;
91
+ }
92
+ if (!userFromHook) return;
52
93
  try {
53
- await user.revokeSession(sessionId);
94
+ await userFromHook.revokeSession(sessionId);
54
95
  setSessions((prev) => prev.filter((session) => session.id !== sessionId));
55
96
  } catch (error) {
56
97
  (0, import_errors.captureError)("Failed to revoke session", { sessionId, error });
@@ -60,9 +101,13 @@ function ActiveSessionsPage() {
60
101
  const handleRevokeAllSessions = async () => {
61
102
  setIsRevokingAll(true);
62
103
  try {
63
- const deletionPromises = sessions.filter((session) => !session.isCurrentSession).map((session) => user.revokeSession(session.id));
64
- await Promise.all(deletionPromises);
65
- setSessions((prevSessions) => prevSessions.filter((session) => session.isCurrentSession));
104
+ if (props?.mockSessions) {
105
+ setSessions((prevSessions) => prevSessions.filter((session) => session.isCurrentSession));
106
+ } else if (userFromHook) {
107
+ const deletionPromises = sessions.filter((session) => !session.isCurrentSession).map((session) => userFromHook.revokeSession(session.id));
108
+ await Promise.all(deletionPromises);
109
+ setSessions((prevSessions) => prevSessions.filter((session) => session.isCurrentSession));
110
+ }
66
111
  } catch (error) {
67
112
  (0, import_errors.captureError)("Failed to revoke all sessions", { error, sessionIds: sessions.map((session) => session.id) });
68
113
  throw error;
@@ -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() {\n const { t } = useTranslation();\n const user = useUser({ or: \"throw\" });\n const [isLoading, setIsLoading] = useState(true);\n const [isRevokingAll, setIsRevokingAll] = useState(false);\n const [sessions, setSessions] = useState<ActiveSession[]>([]);\n const [showConfirmRevokeAll, setShowConfirmRevokeAll] = useState(false);\n\n // Fetch sessions when component mounts\n useEffect(() => {\n runAsynchronously(async () => {\n setIsLoading(true);\n const sessionsData = await user.getActiveSessions();\n const enhancedSessions = sessionsData;\n setSessions(enhancedSessions);\n setIsLoading(false);\n });\n }, [user]);\n\n const handleRevokeSession = async (sessionId: string) => {\n try {\n await user.revokeSession(sessionId);\n\n // Remove the session from the list\n setSessions(prev => prev.filter(session => session.id !== sessionId));\n } catch (error) {\n captureError(\"Failed to revoke session\", { sessionId ,error });\n throw error;\n }\n };\n\n const handleRevokeAllSessions = async () => {\n setIsRevokingAll(true);\n try {\n const deletionPromises = sessions\n .filter(session => !session.isCurrentSession)\n .map(session => user.revokeSession(session.id));\n await Promise.all(deletionPromises);\n setSessions(prevSessions => prevSessions.filter(session => session.isCurrentSession));\n } catch (error) {\n captureError(\"Failed to revoke all sessions\", { 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\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(\"Failed to revoke session\", { 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(\"Failed to revoke all sessions\", { 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":[]}