@vue-skuilder/common-ui 0.2.35 → 0.2.36

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 (190) hide show
  1. package/dist/_virtual/_@oxc-project_runtime@0.115.0/helpers/defineProperty.mjs +13 -0
  2. package/dist/_virtual/_@oxc-project_runtime@0.115.0/helpers/toPrimitive.mjs +15 -0
  3. package/dist/_virtual/_@oxc-project_runtime@0.115.0/helpers/toPropertyKey.mjs +10 -0
  4. package/dist/_virtual/_@oxc-project_runtime@0.115.0/helpers/typeof.mjs +12 -0
  5. package/dist/_virtual/_plugin-vue_export-helper.mjs +9 -0
  6. package/dist/_virtual/_rolldown/runtime.mjs +7 -0
  7. package/dist/admin/common-ui-admin.es.js +259 -23774
  8. package/dist/admin/common-ui-admin.es.js.map +1 -1
  9. package/dist/assets/index.css +2 -1
  10. package/dist/components/CardBrowser.mjs +63 -0
  11. package/dist/components/CardBrowser.mjs.map +1 -0
  12. package/dist/components/CardBrowser.vue_vue_type_script_lang.mjs +50 -0
  13. package/dist/components/CardBrowser.vue_vue_type_script_lang.mjs.map +1 -0
  14. package/dist/components/CardHistoryViewer.mjs +70 -0
  15. package/dist/components/CardHistoryViewer.mjs.map +1 -0
  16. package/dist/components/CardHistoryViewer.vue_vue_type_script_lang.mjs +108 -0
  17. package/dist/components/CardHistoryViewer.vue_vue_type_script_lang.mjs.map +1 -0
  18. package/dist/components/CardSearch.mjs +26 -0
  19. package/dist/components/CardSearch.mjs.map +1 -0
  20. package/dist/components/CardSearch.vue_vue_type_script_lang.mjs +17 -0
  21. package/dist/components/CardSearch.vue_vue_type_script_lang.mjs.map +1 -0
  22. package/dist/components/CardSearchResults.mjs +32 -0
  23. package/dist/components/CardSearchResults.mjs.map +1 -0
  24. package/dist/components/CardSearchResults.vue_vue_type_script_lang.mjs +71 -0
  25. package/dist/components/CardSearchResults.vue_vue_type_script_lang.mjs.map +1 -0
  26. package/dist/components/CourseCardBrowser.mjs +175 -0
  27. package/dist/components/CourseCardBrowser.mjs.map +1 -0
  28. package/dist/components/CourseCardBrowser.vue_vue_type_script_lang.mjs +173 -0
  29. package/dist/components/CourseCardBrowser.vue_vue_type_script_lang.mjs.map +1 -0
  30. package/dist/components/CourseInformation.mjs +132 -0
  31. package/dist/components/CourseInformation.mjs.map +1 -0
  32. package/dist/components/CourseInformation.vue_vue_type_script_lang.mjs +56 -0
  33. package/dist/components/CourseInformation.vue_vue_type_script_lang.mjs.map +1 -0
  34. package/dist/components/CourseTagFilterWidget.mjs +114 -0
  35. package/dist/components/CourseTagFilterWidget.mjs.map +1 -0
  36. package/dist/components/CourseTagFilterWidget.vue_vue_type_script_lang.mjs +65 -0
  37. package/dist/components/CourseTagFilterWidget.vue_vue_type_script_lang.mjs.map +1 -0
  38. package/dist/components/HeatMap.mjs +40 -0
  39. package/dist/components/HeatMap.mjs.map +1 -0
  40. package/dist/components/HeatMap.vue_vue_type_script_lang.mjs +211 -0
  41. package/dist/components/HeatMap.vue_vue_type_script_lang.mjs.map +1 -0
  42. package/dist/components/PaginatingToolbar.mjs +94 -0
  43. package/dist/components/PaginatingToolbar.mjs.map +1 -0
  44. package/dist/components/PaginatingToolbar.vue_vue_type_script_lang.mjs +36 -0
  45. package/dist/components/PaginatingToolbar.vue_vue_type_script_lang.mjs.map +1 -0
  46. package/dist/components/SessionControllerDebug.mjs +130 -0
  47. package/dist/components/SessionControllerDebug.mjs.map +1 -0
  48. package/dist/components/SessionControllerDebug.vue_vue_type_script_lang.mjs +45 -0
  49. package/dist/components/SessionControllerDebug.vue_vue_type_script_lang.mjs.map +1 -0
  50. package/dist/components/SkMouseTrap.mjs +66 -0
  51. package/dist/components/SkMouseTrap.mjs.map +1 -0
  52. package/dist/components/SkMouseTrap.vue_vue_type_script_lang.mjs +31 -0
  53. package/dist/components/SkMouseTrap.vue_vue_type_script_lang.mjs.map +1 -0
  54. package/dist/components/SkMouseTrapToolTip.mjs +28 -0
  55. package/dist/components/SkMouseTrapToolTip.mjs.map +1 -0
  56. package/dist/components/SkMouseTrapToolTip.vue_vue_type_script_lang.mjs +91 -0
  57. package/dist/components/SkMouseTrapToolTip.vue_vue_type_script_lang.mjs.map +1 -0
  58. package/dist/components/SnackbarService.mjs +24 -0
  59. package/dist/components/SnackbarService.mjs.map +1 -0
  60. package/dist/components/SnackbarService.vue_vue_type_script_lang.mjs +34 -0
  61. package/dist/components/SnackbarService.vue_vue_type_script_lang.mjs.map +1 -0
  62. package/dist/components/SnackbarService2.mjs +40 -0
  63. package/dist/components/SnackbarService2.mjs.map +1 -0
  64. package/dist/components/StudySession.mjs +123 -0
  65. package/dist/components/StudySession.mjs.map +1 -0
  66. package/dist/components/StudySession.vue_vue_type_script_lang.mjs +280 -0
  67. package/dist/components/StudySession.vue_vue_type_script_lang.mjs.map +1 -0
  68. package/dist/components/StudySessionTimer.mjs +50 -0
  69. package/dist/components/StudySessionTimer.mjs.map +1 -0
  70. package/dist/components/StudySessionTimer.vue_vue_type_script_lang.mjs +43 -0
  71. package/dist/components/StudySessionTimer.vue_vue_type_script_lang.mjs.map +1 -0
  72. package/dist/components/TagsInput.mjs +45 -0
  73. package/dist/components/TagsInput.mjs.map +1 -0
  74. package/dist/components/TagsInput.vue_vue_type_script_lang.mjs +117 -0
  75. package/dist/components/TagsInput.vue_vue_type_script_lang.mjs.map +1 -0
  76. package/dist/components/UserTagPreferences.mjs +205 -0
  77. package/dist/components/UserTagPreferences.mjs.map +1 -0
  78. package/dist/components/UserTagPreferences.vue_vue_type_script_lang.mjs +139 -0
  79. package/dist/components/UserTagPreferences.vue_vue_type_script_lang.mjs.map +1 -0
  80. package/dist/components/auth/RequestPasswordReset.mjs +96 -0
  81. package/dist/components/auth/RequestPasswordReset.mjs.map +1 -0
  82. package/dist/components/auth/RequestPasswordReset.vue_vue_type_script_lang.mjs +47 -0
  83. package/dist/components/auth/RequestPasswordReset.vue_vue_type_script_lang.mjs.map +1 -0
  84. package/dist/components/auth/ResetPassword.mjs +129 -0
  85. package/dist/components/auth/ResetPassword.mjs.map +1 -0
  86. package/dist/components/auth/ResetPassword.vue_vue_type_script_lang.mjs +69 -0
  87. package/dist/components/auth/ResetPassword.vue_vue_type_script_lang.mjs.map +1 -0
  88. package/dist/components/auth/UserChip.mjs +9 -0
  89. package/dist/components/auth/UserChip.mjs.map +1 -0
  90. package/dist/components/auth/UserChip.vue_vue_type_script_setup_true_lang.mjs +213 -0
  91. package/dist/components/auth/UserChip.vue_vue_type_script_setup_true_lang.mjs.map +1 -0
  92. package/dist/components/auth/UserLogin.mjs +9 -0
  93. package/dist/components/auth/UserLogin.mjs.map +1 -0
  94. package/dist/components/auth/UserLogin.vue_vue_type_script_setup_true_lang.mjs +155 -0
  95. package/dist/components/auth/UserLogin.vue_vue_type_script_setup_true_lang.mjs.map +1 -0
  96. package/dist/components/auth/UserLoginAndRegistrationContainer.mjs +9 -0
  97. package/dist/components/auth/UserLoginAndRegistrationContainer.mjs.map +1 -0
  98. package/dist/components/auth/UserLoginAndRegistrationContainer.vue_vue_type_script_setup_true_lang.mjs +126 -0
  99. package/dist/components/auth/UserLoginAndRegistrationContainer.vue_vue_type_script_setup_true_lang.mjs.map +1 -0
  100. package/dist/components/auth/UserRegistration.mjs +147 -0
  101. package/dist/components/auth/UserRegistration.mjs.map +1 -0
  102. package/dist/components/auth/UserRegistration.vue_vue_type_script_lang.mjs +130 -0
  103. package/dist/components/auth/UserRegistration.vue_vue_type_script_lang.mjs.map +1 -0
  104. package/dist/components/auth/VerifyEmail.mjs +118 -0
  105. package/dist/components/auth/VerifyEmail.mjs.map +1 -0
  106. package/dist/components/auth/VerifyEmail.vue_vue_type_script_lang.mjs +59 -0
  107. package/dist/components/auth/VerifyEmail.vue_vue_type_script_lang.mjs.map +1 -0
  108. package/dist/components/cardRendering/AudioAutoPlayer.mjs +9 -0
  109. package/dist/components/cardRendering/AudioAutoPlayer.mjs.map +1 -0
  110. package/dist/components/cardRendering/AudioAutoPlayer.vue_vue_type_script_setup_true_lang.mjs +69 -0
  111. package/dist/components/cardRendering/AudioAutoPlayer.vue_vue_type_script_setup_true_lang.mjs.map +1 -0
  112. package/dist/components/cardRendering/CardLoader.mjs +31 -0
  113. package/dist/components/cardRendering/CardLoader.mjs.map +1 -0
  114. package/dist/components/cardRendering/CardLoader.vue_vue_type_script_lang.mjs +75 -0
  115. package/dist/components/cardRendering/CardLoader.vue_vue_type_script_lang.mjs.map +1 -0
  116. package/dist/components/cardRendering/CardViewer.mjs +39 -0
  117. package/dist/components/cardRendering/CardViewer.mjs.map +1 -0
  118. package/dist/components/cardRendering/CardViewer.vue_vue_type_script_lang.mjs +66 -0
  119. package/dist/components/cardRendering/CardViewer.vue_vue_type_script_lang.mjs.map +1 -0
  120. package/dist/components/cardRendering/CodeBlockRenderer.mjs +8 -0
  121. package/dist/components/cardRendering/CodeBlockRenderer.mjs.map +1 -0
  122. package/dist/components/cardRendering/CodeBlockRenderer.vue_vue_type_script_setup_true_lang.mjs +44 -0
  123. package/dist/components/cardRendering/CodeBlockRenderer.vue_vue_type_script_setup_true_lang.mjs.map +1 -0
  124. package/dist/components/cardRendering/MarkdownRenderer.mjs +21 -0
  125. package/dist/components/cardRendering/MarkdownRenderer.mjs.map +1 -0
  126. package/dist/components/cardRendering/MarkdownRenderer.vue_vue_type_script_lang.mjs +24 -0
  127. package/dist/components/cardRendering/MarkdownRenderer.vue_vue_type_script_lang.mjs.map +1 -0
  128. package/dist/components/cardRendering/MarkdownRendererHelpers.mjs +79 -0
  129. package/dist/components/cardRendering/MarkdownRendererHelpers.mjs.map +1 -0
  130. package/dist/components/cardRendering/MdTokenRenderer.mjs +9 -0
  131. package/dist/components/cardRendering/MdTokenRenderer.mjs.map +1 -0
  132. package/dist/components/cardRendering/MdTokenRenderer.vue_vue_type_script_setup_true_lang.mjs +176 -0
  133. package/dist/components/cardRendering/MdTokenRenderer.vue_vue_type_script_setup_true_lang.mjs.map +1 -0
  134. package/dist/components/studentInputs/BaseUserInput.mjs +54 -0
  135. package/dist/components/studentInputs/BaseUserInput.mjs.map +1 -0
  136. package/dist/components/studentInputs/MultipleChoiceOption.mjs +26 -0
  137. package/dist/components/studentInputs/MultipleChoiceOption.mjs.map +1 -0
  138. package/dist/components/studentInputs/MultipleChoiceOption.vue_vue_type_script_lang.mjs +76 -0
  139. package/dist/components/studentInputs/MultipleChoiceOption.vue_vue_type_script_lang.mjs.map +1 -0
  140. package/dist/components/studentInputs/RadioMultipleChoice.mjs +33 -0
  141. package/dist/components/studentInputs/RadioMultipleChoice.mjs.map +1 -0
  142. package/dist/components/studentInputs/RadioMultipleChoice.vue_vue_type_script_lang.mjs +105 -0
  143. package/dist/components/studentInputs/RadioMultipleChoice.vue_vue_type_script_lang.mjs.map +1 -0
  144. package/dist/components/studentInputs/TrueFalse.mjs +19 -0
  145. package/dist/components/studentInputs/TrueFalse.mjs.map +1 -0
  146. package/dist/components/studentInputs/TrueFalse.vue_vue_type_script_lang.mjs +21 -0
  147. package/dist/components/studentInputs/TrueFalse.vue_vue_type_script_lang.mjs.map +1 -0
  148. package/dist/components/studentInputs/UserInputNumber.mjs +33 -0
  149. package/dist/components/studentInputs/UserInputNumber.mjs.map +1 -0
  150. package/dist/components/studentInputs/UserInputNumber.vue_vue_type_script_lang.mjs +25 -0
  151. package/dist/components/studentInputs/UserInputNumber.vue_vue_type_script_lang.mjs.map +1 -0
  152. package/dist/components/studentInputs/UserInputString.mjs +22 -0
  153. package/dist/components/studentInputs/UserInputString.mjs.map +1 -0
  154. package/dist/components/studentInputs/UserInputString.vue_vue_type_script_lang.mjs +23 -0
  155. package/dist/components/studentInputs/UserInputString.vue_vue_type_script_lang.mjs.map +1 -0
  156. package/dist/components/studentInputs/fillInInput.mjs +25 -0
  157. package/dist/components/studentInputs/fillInInput.mjs.map +1 -0
  158. package/dist/components/studentInputs/fillInInput.vue_vue_type_script_lang.mjs +26 -0
  159. package/dist/components/studentInputs/fillInInput.vue_vue_type_script_lang.mjs.map +1 -0
  160. package/dist/composables/CompositionViewable.mjs +65 -0
  161. package/dist/composables/CompositionViewable.mjs.map +1 -0
  162. package/dist/composables/Displayable.mjs +36 -0
  163. package/dist/composables/Displayable.mjs.map +1 -0
  164. package/dist/composables/useAuthUI.mjs +38 -0
  165. package/dist/composables/useAuthUI.mjs.map +1 -0
  166. package/dist/composables/useEntitlements.mjs +58 -0
  167. package/dist/composables/useEntitlements.mjs.map +1 -0
  168. package/dist/index.mjs +49 -0
  169. package/dist/plugins/pinia.mjs +12 -0
  170. package/dist/plugins/pinia.mjs.map +1 -0
  171. package/dist/services/authAPI.mjs +159 -0
  172. package/dist/services/authAPI.mjs.map +1 -0
  173. package/dist/stores/useAuthStore.mjs +68 -0
  174. package/dist/stores/useAuthStore.mjs.map +1 -0
  175. package/dist/stores/useCardPreviewModeStore.mjs +20 -0
  176. package/dist/stores/useCardPreviewModeStore.mjs.map +1 -0
  177. package/dist/stores/useConfigStore.mjs +60 -0
  178. package/dist/stores/useConfigStore.mjs.map +1 -0
  179. package/dist/utils/SkldrMouseTrap.mjs +48 -0
  180. package/dist/utils/SkldrMouseTrap.mjs.map +1 -0
  181. package/dist/utils/passwordValidation.mjs +12 -0
  182. package/dist/utils/passwordValidation.mjs.map +1 -0
  183. package/package.json +7 -8
  184. package/dist/MarkdownRenderer-U_iPiA2x.mjs +0 -4966
  185. package/dist/MarkdownRenderer-U_iPiA2x.mjs.map +0 -1
  186. package/dist/assets/MarkdownRenderer.css +0 -1
  187. package/dist/common-ui.es.js +0 -5090
  188. package/dist/common-ui.es.js.map +0 -1
  189. package/dist/common-ui.umd.js +0 -77
  190. package/dist/common-ui.umd.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UserLoginAndRegistrationContainer.vue_vue_type_script_setup_true_lang.mjs","names":[],"sources":["../../../src/components/auth/UserLoginAndRegistrationContainer.vue"],"sourcesContent":["<template>\n <div v-if=\"userReady && display\">\n <transition name=\"component-fade\" mode=\"out-in\">\n <div v-if=\"guestMode\" key=\"login-buttons\">\n <!-- Registration button: only if showLoginRegistration is true -->\n <v-dialog v-model=\"regDialog\" width=\"500px\" v-if=\"authUIConfig.showLoginRegistration\">\n <template #activator=\"{ props }\">\n <v-btn class=\"mr-2\" size=\"small\" color=\"success\" v-bind=\"props\">Sign Up</v-btn>\n </template>\n <UserRegistration\n @toggle=\"toggle\"\n @signup-success=\"handleSignupSuccess\"\n :on-signup-success=\"handleSignupSuccess\"\n />\n </v-dialog>\n \n <!-- Login button: always show in guest mode -->\n <v-dialog v-model=\"loginDialog\" width=\"500px\">\n <template #activator=\"{ props }\">\n <v-btn size=\"small\" color=\"success\" v-bind=\"props\">Log In</v-btn>\n </template>\n <UserLogin @toggle=\"toggle\" @forgot-password=\"openResetDialog\" />\n </v-dialog>\n\n <!-- Password reset dialog: opened from login via \"Forgot password?\" -->\n <v-dialog v-model=\"resetDialog\" width=\"500px\">\n <RequestPasswordReset @cancel=\"closeResetDialog\" @success=\"closeResetDialog\" />\n </v-dialog>\n </div>\n <div v-else key=\"user-chip\">\n <user-chip />\n </div>\n </transition>\n </div>\n</template>\n\n<script lang=\"ts\" setup>\nimport { computed, onMounted, ref } from 'vue';\nimport { useRoute } from 'vue-router';\nimport UserLogin from './UserLogin.vue';\nimport UserRegistration from './UserRegistration.vue';\nimport RequestPasswordReset from './RequestPasswordReset.vue';\nimport UserChip from './UserChip.vue';\nimport { useAuthStore } from '../../stores/useAuthStore';\nimport { useAuthUI } from '../../composables/useAuthUI';\nimport { GuestUsername } from '@vue-skuilder/db';\n\n// Define props\nconst props = defineProps<{\n showLoginButton?: boolean;\n redirectToPath?: string;\n showRegistration?: boolean;\n onSignupSuccess?: (payload: { username: string }) => void;\n}>();\n\n// Define emits (keeping for backward compatibility if needed)\nconst emit = defineEmits<{\n 'signup-success': [payload: { username: string }]\n}>();\n\nconst route = useRoute();\nconst authStore = useAuthStore();\nconst authUI = useAuthUI();\n\n// Initialize auth UI detection\nonMounted(async () => {\n await authUI.detectSyncStrategy();\n});\n\nconst display = computed(() => {\n if (!route.name || typeof route.name !== 'string') {\n return true;\n }\n const routeName = route.name.toLowerCase();\n return !(routeName === 'login' || routeName === 'signup');\n});\n\nconst userReady = computed(() => authStore.onLoadComplete);\n\nconst guestMode = computed(() => {\n if (authStore._user) {\n return authStore._user.getUsername().startsWith(GuestUsername);\n }\n return !authStore.loginAndRegistration.loggedIn;\n});\n\nconst authUIConfig = computed(() => {\n const baseConfig = authUI.config.value || {\n showLoginRegistration: true,\n showLogout: true, \n showResetData: false,\n logoutLabel: 'Log out',\n resetLabel: '',\n };\n \n // Apply prop overrides\n return {\n ...baseConfig,\n ...(props.showRegistration !== undefined && { showLoginRegistration: props.showRegistration })\n };\n});\n\nconst regDialog = computed({\n get: () => authStore.loginAndRegistration.regDialogOpen,\n set: (value: boolean) => {\n authStore.loginAndRegistration.regDialogOpen = value;\n },\n});\n\nconst loginDialog = computed({\n get: () => authStore.loginAndRegistration.loginDialogOpen,\n set: (value: boolean) => {\n authStore.loginAndRegistration.loginDialogOpen = value;\n },\n});\n\n// Password reset dialog state (local, not in auth store)\nconst resetDialog = ref(false);\n\nconst toggle = () => {\n if (regDialog.value && loginDialog.value) {\n throw new Error('Registration / Login dialogs both activated.');\n } else if (regDialog.value === loginDialog.value) {\n throw new Error('Registration / Login dialogs toggled while both were dormant.');\n } else {\n regDialog.value = !regDialog.value;\n loginDialog.value = !loginDialog.value;\n }\n};\n\nconst openResetDialog = () => {\n loginDialog.value = false; // Close login dialog\n resetDialog.value = true; // Open reset dialog\n};\n\nconst closeResetDialog = () => {\n resetDialog.value = false;\n};\n\nconst handleSignupSuccess = (payload: { username: string }) => {\n // Close the registration dialog\n regDialog.value = false;\n\n // Call the callback prop if provided (preferred method)\n if (props.onSignupSuccess) {\n props.onSignupSuccess(payload);\n }\n\n // Also emit event for backward compatibility\n emit('signup-success', payload);\n};\n</script>\n\n<style scoped>\n.component-fade-enter-active,\n.component-fade-leave-active {\n transition: opacity 0.5s ease;\n}\n.component-fade-enter,\n.component-fade-leave-to {\n opacity: 0;\n}\n</style>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;EAgDA,IAAM,IAAQ,GAQR,IAAO,GAIP,IAAQ,GAAU,EAClB,IAAY,GAAc,EAC1B,IAAS,GAAW;AAG1B,IAAU,YAAY;AACpB,SAAM,EAAO,oBAAoB;IACjC;EAEF,IAAM,IAAU,QAAe;AAC7B,OAAI,CAAC,EAAM,QAAQ,OAAO,EAAM,QAAS,SACvC,QAAO;GAET,IAAM,IAAY,EAAM,KAAK,aAAa;AAC1C,UAAO,EAAE,MAAc,WAAW,MAAc;IAChD,EAEI,IAAY,QAAe,EAAU,eAAe,EAEpD,IAAY,QACZ,EAAU,QACL,EAAU,MAAM,aAAa,CAAC,WAAW,EAAc,GAEzD,CAAC,EAAU,qBAAqB,SACvC,EAEI,IAAe,SAUZ;GACL,GAViB,EAAO,OAAO,SAAS;IACxC,uBAAuB;IACvB,YAAY;IACZ,eAAe;IACf,aAAa;IACb,YAAY;IACb;GAKC,GAAI,EAAM,qBAAqB,KAAA,KAAa,EAAE,uBAAuB,EAAM,kBAAkB;GAC9F,EACD,EAEI,IAAY,EAAS;GACzB,WAAW,EAAU,qBAAqB;GAC1C,MAAM,MAAmB;AACvB,MAAU,qBAAqB,gBAAgB;;GAElD,CAAC,EAEI,IAAc,EAAS;GAC3B,WAAW,EAAU,qBAAqB;GAC1C,MAAM,MAAmB;AACvB,MAAU,qBAAqB,kBAAkB;;GAEpD,CAAC,EAGI,IAAc,EAAI,GAAM,EAExB,eAAe;AACnB,OAAI,EAAU,SAAS,EAAY,MACjC,OAAU,MAAM,+CAA+C;OACtD,EAAU,UAAU,EAAY,MACzC,OAAU,MAAM,gEAAgE;AAGhF,GADA,EAAU,QAAQ,CAAC,EAAU,OAC7B,EAAY,QAAQ,CAAC,EAAY;KAI/B,wBAAwB;AAE5B,GADA,EAAY,QAAQ,IACpB,EAAY,QAAQ;KAGhB,yBAAyB;AAC7B,KAAY,QAAQ;KAGhB,uBAAuB,MAAkC;AAU7D,GARA,EAAU,QAAQ,IAGd,EAAM,mBACR,EAAM,gBAAgB,EAAQ,EAIhC,EAAK,kBAAkB,EAAQ"}
@@ -0,0 +1,147 @@
1
+ import "../../_virtual/_rolldown/runtime.mjs";
2
+ import e from "../../_virtual/_plugin-vue_export-helper.mjs";
3
+ import t from "./UserRegistration.vue_vue_type_script_lang.mjs";
4
+ import { createBlock as n, createTextVNode as r, createVNode as i, openBlock as a, resolveComponent as o, withCtx as s, withModifiers as c } from "vue";
5
+ //#region src/components/auth/UserRegistration.vue
6
+ function _sfc_render(e, t, l, u, d, f) {
7
+ let p = o("v-card-title"), m = o("v-text-field"), h = o("v-btn"), g = o("v-snackbar"), _ = o("v-icon"), v = o("router-link"), y = o("v-form"), b = o("v-card-text"), x = o("v-card");
8
+ return a(), n(x, null, {
9
+ default: s(() => [i(p, { class: "text-h5 bg-grey-lighten-2" }, {
10
+ default: s(() => t[7] || (t[7] = [r(" Create an Account ")])),
11
+ _: 1
12
+ }), i(b, null, {
13
+ default: s(() => [i(y, { onSubmit: c(e.createUser, ["prevent"]) }, {
14
+ default: s(() => [
15
+ i(m, {
16
+ modelValue: e.email,
17
+ "onUpdate:modelValue": t[0] || (t[0] = (t) => e.email = t),
18
+ name: "email",
19
+ label: "Email address",
20
+ type: "email",
21
+ "prepend-icon": "mdi-email",
22
+ error: e.emailError,
23
+ hint: e.emailHint,
24
+ onBlur: e.validateEmail
25
+ }, null, 8, [
26
+ "modelValue",
27
+ "error",
28
+ "hint",
29
+ "onBlur"
30
+ ]),
31
+ i(m, {
32
+ id: "",
33
+ ref: "userNameTextField",
34
+ modelValue: e.username,
35
+ "onUpdate:modelValue": t[1] || (t[1] = (t) => e.username = t),
36
+ name: "username",
37
+ label: "Choose a Username",
38
+ "prepend-icon": "mdi-account-circle",
39
+ error: e.usernameError,
40
+ hint: e.usernameHint,
41
+ onBlur: e.validateUsername
42
+ }, null, 8, [
43
+ "modelValue",
44
+ "error",
45
+ "hint",
46
+ "onBlur"
47
+ ]),
48
+ i(m, {
49
+ modelValue: e.password,
50
+ "onUpdate:modelValue": t[2] || (t[2] = (t) => e.password = t),
51
+ "prepend-icon": "mdi-lock",
52
+ name: "password",
53
+ hover: "Show password",
54
+ label: "Create a password",
55
+ hint: e.passwordError,
56
+ error: !!e.passwordError,
57
+ min: "4",
58
+ "append-icon": e.passwordVisible ? "mdi-eye-off" : "mdi-eye",
59
+ type: e.passwordVisible ? "text" : "password",
60
+ "onClick:append": t[3] || (t[3] = () => e.passwordVisible = !e.passwordVisible)
61
+ }, null, 8, [
62
+ "modelValue",
63
+ "hint",
64
+ "error",
65
+ "append-icon",
66
+ "type"
67
+ ]),
68
+ i(m, {
69
+ modelValue: e.retypedPassword,
70
+ "onUpdate:modelValue": t[4] || (t[4] = (t) => e.retypedPassword = t),
71
+ "prepend-icon": "mdi-lock",
72
+ name: "retypedPassword",
73
+ hover: "Show password",
74
+ label: "Retype your password",
75
+ disabled: e.password === "" || !!e.passwordError,
76
+ hint: e.passwordRetypeError,
77
+ min: "4",
78
+ type: e.passwordVisible ? "text" : "password"
79
+ }, null, 8, [
80
+ "modelValue",
81
+ "disabled",
82
+ "hint",
83
+ "type"
84
+ ]),
85
+ i(g, {
86
+ modelValue: e.badLoginAttempt,
87
+ "onUpdate:modelValue": t[6] || (t[6] = (t) => e.badLoginAttempt = t),
88
+ location: "bottom right",
89
+ timeout: 5e3
90
+ }, {
91
+ default: s(() => [t[9] || (t[9] = r(" Username or password was incorrect. ")), i(h, {
92
+ color: "pink",
93
+ variant: "text",
94
+ onClick: t[5] || (t[5] = (t) => e.badLoginAttempt = !1)
95
+ }, {
96
+ default: s(() => t[8] || (t[8] = [r(" Close ")])),
97
+ _: 1
98
+ })]),
99
+ _: 1
100
+ }, 8, ["modelValue"]),
101
+ i(h, {
102
+ class: "mr-2 my-2",
103
+ type: "submit",
104
+ loading: e.awaitingResponse,
105
+ color: e.buttonStatus.color,
106
+ disabled: !!e.passwordError || e.password !== e.retypedPassword
107
+ }, {
108
+ default: s(() => [i(_, { start: "" }, {
109
+ default: s(() => t[10] || (t[10] = [r("mdi-lock-open")])),
110
+ _: 1
111
+ }), t[11] || (t[11] = r(" Create Account "))]),
112
+ _: 1
113
+ }, 8, [
114
+ "loading",
115
+ "color",
116
+ "disabled"
117
+ ]),
118
+ e.registrationRoute ? (a(), n(v, {
119
+ key: 0,
120
+ to: "login"
121
+ }, {
122
+ default: s(() => [i(h, { variant: "text" }, {
123
+ default: s(() => t[12] || (t[12] = [r("Log In")])),
124
+ _: 1
125
+ })]),
126
+ _: 1
127
+ })) : (a(), n(h, {
128
+ key: 1,
129
+ variant: "text",
130
+ onClick: e.toggle
131
+ }, {
132
+ default: s(() => t[13] || (t[13] = [r(" Log In ")])),
133
+ _: 1
134
+ }, 8, ["onClick"]))
135
+ ]),
136
+ _: 1
137
+ }, 8, ["onSubmit"])]),
138
+ _: 1
139
+ })]),
140
+ _: 1
141
+ });
142
+ }
143
+ var l = /* @__PURE__ */ e(t, [["render", _sfc_render]]);
144
+ //#endregion
145
+ export { l as default };
146
+
147
+ //# sourceMappingURL=UserRegistration.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UserRegistration.mjs","names":[],"sources":["../../../src/components/auth/UserRegistration.vue"],"sourcesContent":["<template>\n <v-card>\n <v-card-title class=\"text-h5 bg-grey-lighten-2\"> Create an Account </v-card-title>\n\n <v-card-text>\n <v-form @submit.prevent=\"createUser\">\n <v-text-field\n v-model=\"email\"\n name=\"email\"\n label=\"Email address\"\n type=\"email\"\n prepend-icon=\"mdi-email\"\n :error=\"emailError\"\n :hint=\"emailHint\"\n @blur=\"validateEmail\"\n ></v-text-field>\n <v-text-field\n id=\"\"\n ref=\"userNameTextField\"\n v-model=\"username\"\n name=\"username\"\n label=\"Choose a Username\"\n prepend-icon=\"mdi-account-circle\"\n :error=\"usernameError\"\n :hint=\"usernameHint\"\n @blur=\"validateUsername\"\n ></v-text-field>\n <v-text-field\n v-model=\"password\"\n prepend-icon=\"mdi-lock\"\n name=\"password\"\n hover=\"Show password\"\n label=\"Create a password\"\n :hint=\"passwordError\"\n :error=\"!!passwordError\"\n min=\"4\"\n :append-icon=\"passwordVisible ? 'mdi-eye-off' : 'mdi-eye'\"\n :type=\"passwordVisible ? 'text' : 'password'\"\n @click:append=\"() => (passwordVisible = !passwordVisible)\"\n ></v-text-field>\n <v-text-field\n v-model=\"retypedPassword\"\n prepend-icon=\"mdi-lock\"\n name=\"retypedPassword\"\n hover=\"Show password\"\n label=\"Retype your password\"\n :disabled=\"password === '' || !!passwordError\"\n :hint=\"passwordRetypeError\"\n min=\"4\"\n :type=\"passwordVisible ? 'text' : 'password'\"\n ></v-text-field>\n\n <!-- <v-checkbox label=\"Student\" v-model=\"student\" ></v-checkbox>\n <v-checkbox label=\"Teacher\" v-model=\"teacher\" ></v-checkbox>\n <v-checkbox label=\"Author\" v-model=\"author\" ></v-checkbox> -->\n\n <v-snackbar v-model=\"badLoginAttempt\" location=\"bottom right\" :timeout=\"5000\">\n Username or password was incorrect.\n <v-btn color=\"pink\" variant=\"text\" @click=\"badLoginAttempt = false\"> Close </v-btn>\n </v-snackbar>\n <v-btn\n class=\"mr-2 my-2\"\n type=\"submit\"\n :loading=\"awaitingResponse\"\n :color=\"buttonStatus.color\"\n :disabled=\"!!passwordError || password !== retypedPassword\"\n >\n <v-icon start>mdi-lock-open</v-icon>\n Create Account\n </v-btn>\n <router-link v-if=\"registrationRoute\" to=\"login\">\n <v-btn variant=\"text\">Log In</v-btn>\n </router-link>\n <v-btn v-else variant=\"text\" @click=\"toggle\"> Log In </v-btn>\n </v-form>\n </v-card-text>\n </v-card>\n</template>\n\n<script lang=\"ts\">\nimport { defineComponent, PropType } from 'vue';\nimport { UserDBInterface } from '@vue-skuilder/db';\nimport { alertUser } from '../SnackbarService';\nimport { Status, log } from '@vue-skuilder/common';\nimport { getCurrentUser, useAuthStore } from '../../stores/useAuthStore';\nimport { sendVerificationEmail } from '../../services/authAPI';\nimport { validatePassword } from '../../utils/passwordValidation';\n\nexport default defineComponent({\n name: 'UserRegistration',\n\n props: {\n onSignupSuccess: {\n type: Function as PropType<(payload: { username: string }) => void>,\n required: false,\n },\n },\n\n emits: ['toggle', 'signup-success'],\n\n data() {\n return {\n email: '',\n username: '',\n password: '',\n retypedPassword: '',\n passwordVisible: false,\n emailError: false,\n emailHint: '',\n usernameValidationInProgress: false,\n usernameError: false,\n usernameHint: '',\n awaitingResponse: false,\n badLoginAttempt: false,\n userSecret: '',\n secret: 'goons',\n user: null as UserDBInterface | null,\n roles: ['Student', 'Teacher', 'Author'] as string[],\n student: true,\n teacher: false,\n author: false,\n authStore: useAuthStore(),\n };\n },\n\n computed: {\n registrationRoute(): boolean {\n return typeof this.$route.name === 'string' && this.$route.name.toLowerCase() === 'signup';\n },\n buttonStatus() {\n return {\n color: this.badLoginAttempt ? 'error' : 'success',\n text: this.badLoginAttempt ? 'Try again' : 'Log In',\n };\n },\n passwordError(): string {\n return validatePassword(this.password);\n },\n passwordRetypeError(): string {\n console.log('[RTE]');\n if (this.password !== this.retypedPassword) {\n return 'Passwords must match.';\n } else {\n return '';\n }\n },\n },\n\n async created() {\n this.user = await getCurrentUser();\n },\n\n methods: {\n toggle() {\n log('Toggling registration / login forms.');\n this.$emit('toggle');\n },\n\n validateEmail() {\n this.emailError = false;\n // Basic email validation\n const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n if (this.email && !emailRegex.test(this.email)) {\n this.emailError = true;\n this.emailHint = 'Please enter a valid email address';\n } else {\n this.emailHint = '';\n }\n },\n\n validateUsername() {\n this.usernameError = false;\n },\n\n async createUser() {\n this.awaitingResponse = true;\n\n // Validate password before proceeding\n if (this.passwordError) {\n alertUser({\n text: this.passwordError,\n status: Status.error,\n });\n this.awaitingResponse = false;\n return;\n }\n\n log(`\nUser creation\n-------------\n\nName: ${this.username}\nStudent: ${this.student}\nTeacher: ${this.teacher}\nAuthor: ${this.author}\n`);\n if (this.password === this.retypedPassword) {\n if (!this.user) {\n console.error('ERROR: No user object available');\n return;\n }\n\n this.user\n .createAccount(this.username, this.password)\n .then(async (resp: any) => {\n if (resp.status === Status.ok) {\n // Account created successfully via PouchDB\n this.authStore.loginAndRegistration.loggedIn = true;\n this.authStore.loginAndRegistration.init = false;\n this.authStore.loginAndRegistration.init = true;\n\n // Send verification email if an address was provided. The address\n // is persisted to the _users doc by send-verification (the single\n // source of truth); no separate CONFIG.email copy is kept.\n if (this.email) {\n try {\n // Trigger verification email send with current origin\n const origin = typeof window !== 'undefined' ? window.location.origin : undefined;\n const verificationResult = await sendVerificationEmail(this.username, this.email, origin);\n if (verificationResult.ok) {\n alertUser({\n text: 'Account created! Please check your email to verify your account.',\n status: Status.ok,\n });\n } else {\n log(`Warning: Failed to send verification email: ${verificationResult.error}`);\n // Continue anyway - user can still use the account\n }\n } catch (emailError) {\n console.error('Email save/send error:', emailError);\n log(`Warning: Failed to save email or send verification: ${emailError}`);\n // Continue anyway - account was created successfully\n }\n }\n\n // Emit signup success event for parent to handle\n // Use the username we just created rather than calling getCurrentUser() again\n // which can fail if the auth state hasn't fully synchronized\n\n // Call callback prop if provided (preferred method)\n if (this.onSignupSuccess) {\n console.log('[UserRegistration] Calling onSignupSuccess callback');\n this.onSignupSuccess({ username: this.username });\n }\n\n // Also emit for backward compatibility\n this.$emit('signup-success', { username: this.username });\n } else {\n if (resp.error === 'This username is taken!') {\n this.usernameError = true;\n this.usernameHint = 'Try a different name.';\n (this.$refs.userNameTextField as HTMLInputElement).focus();\n alertUser({\n text: `The name ${this.username} is taken!`,\n status: resp.status,\n });\n } else {\n alertUser({\n text: resp.error,\n status: resp.status,\n });\n }\n }\n })\n .catch((e) => {\n console.error('[UserRegistration] .catch() called with error:', e);\n if (e) {\n const errorText = e?.message || e?.error || e?.toString() || 'Account creation failed';\n alertUser({\n text: errorText,\n status: Status.error,\n });\n }\n });\n this.awaitingResponse = false;\n } else {\n alertUser({\n text: 'Passwords do not match.',\n status: Status.error,\n });\n this.awaitingResponse = false;\n }\n },\n },\n});\n</script>\n"],"mappings":"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"}
@@ -0,0 +1,130 @@
1
+ import "../../_virtual/_rolldown/runtime.mjs";
2
+ import { alertUser as e } from "../SnackbarService.mjs";
3
+ import { sendVerificationEmail as t } from "../../services/authAPI.mjs";
4
+ import { getCurrentUser as n, useAuthStore as r } from "../../stores/useAuthStore.mjs";
5
+ import { validatePassword as i } from "../../utils/passwordValidation.mjs";
6
+ import { defineComponent as a } from "vue";
7
+ import { Status as o, log as s } from "@vue-skuilder/common";
8
+ //#region src/components/auth/UserRegistration.vue?vue&type=script&lang.ts
9
+ var c = a({
10
+ name: "UserRegistration",
11
+ props: { onSignupSuccess: {
12
+ type: Function,
13
+ required: !1
14
+ } },
15
+ emits: ["toggle", "signup-success"],
16
+ data() {
17
+ return {
18
+ email: "",
19
+ username: "",
20
+ password: "",
21
+ retypedPassword: "",
22
+ passwordVisible: !1,
23
+ emailError: !1,
24
+ emailHint: "",
25
+ usernameValidationInProgress: !1,
26
+ usernameError: !1,
27
+ usernameHint: "",
28
+ awaitingResponse: !1,
29
+ badLoginAttempt: !1,
30
+ userSecret: "",
31
+ secret: "goons",
32
+ user: null,
33
+ roles: [
34
+ "Student",
35
+ "Teacher",
36
+ "Author"
37
+ ],
38
+ student: !0,
39
+ teacher: !1,
40
+ author: !1,
41
+ authStore: r()
42
+ };
43
+ },
44
+ computed: {
45
+ registrationRoute() {
46
+ return typeof this.$route.name == "string" && this.$route.name.toLowerCase() === "signup";
47
+ },
48
+ buttonStatus() {
49
+ return {
50
+ color: this.badLoginAttempt ? "error" : "success",
51
+ text: this.badLoginAttempt ? "Try again" : "Log In"
52
+ };
53
+ },
54
+ passwordError() {
55
+ return i(this.password);
56
+ },
57
+ passwordRetypeError() {
58
+ return console.log("[RTE]"), this.password === this.retypedPassword ? "" : "Passwords must match.";
59
+ }
60
+ },
61
+ async created() {
62
+ this.user = await n();
63
+ },
64
+ methods: {
65
+ toggle() {
66
+ s("Toggling registration / login forms."), this.$emit("toggle");
67
+ },
68
+ validateEmail() {
69
+ this.emailError = !1, this.email && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(this.email) ? (this.emailError = !0, this.emailHint = "Please enter a valid email address") : this.emailHint = "";
70
+ },
71
+ validateUsername() {
72
+ this.usernameError = !1;
73
+ },
74
+ async createUser() {
75
+ if (this.awaitingResponse = !0, this.passwordError) {
76
+ e({
77
+ text: this.passwordError,
78
+ status: o.error
79
+ }), this.awaitingResponse = !1;
80
+ return;
81
+ }
82
+ if (s(`
83
+ User creation
84
+ -------------
85
+
86
+ Name: ${this.username}
87
+ Student: ${this.student}
88
+ Teacher: ${this.teacher}
89
+ Author: ${this.author}
90
+ `), this.password === this.retypedPassword) {
91
+ if (!this.user) {
92
+ console.error("ERROR: No user object available");
93
+ return;
94
+ }
95
+ this.user.createAccount(this.username, this.password).then(async (n) => {
96
+ if (n.status === o.ok) {
97
+ if (this.authStore.loginAndRegistration.loggedIn = !0, this.authStore.loginAndRegistration.init = !1, this.authStore.loginAndRegistration.init = !0, this.email) try {
98
+ let n = typeof window < "u" ? window.location.origin : void 0, r = await t(this.username, this.email, n);
99
+ r.ok ? e({
100
+ text: "Account created! Please check your email to verify your account.",
101
+ status: o.ok
102
+ }) : s(`Warning: Failed to send verification email: ${r.error}`);
103
+ } catch (e) {
104
+ console.error("Email save/send error:", e), s(`Warning: Failed to save email or send verification: ${e}`);
105
+ }
106
+ this.onSignupSuccess && (console.log("[UserRegistration] Calling onSignupSuccess callback"), this.onSignupSuccess({ username: this.username })), this.$emit("signup-success", { username: this.username });
107
+ } else n.error === "This username is taken!" ? (this.usernameError = !0, this.usernameHint = "Try a different name.", this.$refs.userNameTextField.focus(), e({
108
+ text: `The name ${this.username} is taken!`,
109
+ status: n.status
110
+ })) : e({
111
+ text: n.error,
112
+ status: n.status
113
+ });
114
+ }).catch((t) => {
115
+ console.error("[UserRegistration] .catch() called with error:", t), t && e({
116
+ text: t?.message || t?.error || t?.toString() || "Account creation failed",
117
+ status: o.error
118
+ });
119
+ }), this.awaitingResponse = !1;
120
+ } else e({
121
+ text: "Passwords do not match.",
122
+ status: o.error
123
+ }), this.awaitingResponse = !1;
124
+ }
125
+ }
126
+ });
127
+ //#endregion
128
+ export { c as default };
129
+
130
+ //# sourceMappingURL=UserRegistration.vue_vue_type_script_lang.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UserRegistration.vue_vue_type_script_lang.mjs","names":[],"sources":["../../../src/components/auth/UserRegistration.vue"],"sourcesContent":["<template>\n <v-card>\n <v-card-title class=\"text-h5 bg-grey-lighten-2\"> Create an Account </v-card-title>\n\n <v-card-text>\n <v-form @submit.prevent=\"createUser\">\n <v-text-field\n v-model=\"email\"\n name=\"email\"\n label=\"Email address\"\n type=\"email\"\n prepend-icon=\"mdi-email\"\n :error=\"emailError\"\n :hint=\"emailHint\"\n @blur=\"validateEmail\"\n ></v-text-field>\n <v-text-field\n id=\"\"\n ref=\"userNameTextField\"\n v-model=\"username\"\n name=\"username\"\n label=\"Choose a Username\"\n prepend-icon=\"mdi-account-circle\"\n :error=\"usernameError\"\n :hint=\"usernameHint\"\n @blur=\"validateUsername\"\n ></v-text-field>\n <v-text-field\n v-model=\"password\"\n prepend-icon=\"mdi-lock\"\n name=\"password\"\n hover=\"Show password\"\n label=\"Create a password\"\n :hint=\"passwordError\"\n :error=\"!!passwordError\"\n min=\"4\"\n :append-icon=\"passwordVisible ? 'mdi-eye-off' : 'mdi-eye'\"\n :type=\"passwordVisible ? 'text' : 'password'\"\n @click:append=\"() => (passwordVisible = !passwordVisible)\"\n ></v-text-field>\n <v-text-field\n v-model=\"retypedPassword\"\n prepend-icon=\"mdi-lock\"\n name=\"retypedPassword\"\n hover=\"Show password\"\n label=\"Retype your password\"\n :disabled=\"password === '' || !!passwordError\"\n :hint=\"passwordRetypeError\"\n min=\"4\"\n :type=\"passwordVisible ? 'text' : 'password'\"\n ></v-text-field>\n\n <!-- <v-checkbox label=\"Student\" v-model=\"student\" ></v-checkbox>\n <v-checkbox label=\"Teacher\" v-model=\"teacher\" ></v-checkbox>\n <v-checkbox label=\"Author\" v-model=\"author\" ></v-checkbox> -->\n\n <v-snackbar v-model=\"badLoginAttempt\" location=\"bottom right\" :timeout=\"5000\">\n Username or password was incorrect.\n <v-btn color=\"pink\" variant=\"text\" @click=\"badLoginAttempt = false\"> Close </v-btn>\n </v-snackbar>\n <v-btn\n class=\"mr-2 my-2\"\n type=\"submit\"\n :loading=\"awaitingResponse\"\n :color=\"buttonStatus.color\"\n :disabled=\"!!passwordError || password !== retypedPassword\"\n >\n <v-icon start>mdi-lock-open</v-icon>\n Create Account\n </v-btn>\n <router-link v-if=\"registrationRoute\" to=\"login\">\n <v-btn variant=\"text\">Log In</v-btn>\n </router-link>\n <v-btn v-else variant=\"text\" @click=\"toggle\"> Log In </v-btn>\n </v-form>\n </v-card-text>\n </v-card>\n</template>\n\n<script lang=\"ts\">\nimport { defineComponent, PropType } from 'vue';\nimport { UserDBInterface } from '@vue-skuilder/db';\nimport { alertUser } from '../SnackbarService';\nimport { Status, log } from '@vue-skuilder/common';\nimport { getCurrentUser, useAuthStore } from '../../stores/useAuthStore';\nimport { sendVerificationEmail } from '../../services/authAPI';\nimport { validatePassword } from '../../utils/passwordValidation';\n\nexport default defineComponent({\n name: 'UserRegistration',\n\n props: {\n onSignupSuccess: {\n type: Function as PropType<(payload: { username: string }) => void>,\n required: false,\n },\n },\n\n emits: ['toggle', 'signup-success'],\n\n data() {\n return {\n email: '',\n username: '',\n password: '',\n retypedPassword: '',\n passwordVisible: false,\n emailError: false,\n emailHint: '',\n usernameValidationInProgress: false,\n usernameError: false,\n usernameHint: '',\n awaitingResponse: false,\n badLoginAttempt: false,\n userSecret: '',\n secret: 'goons',\n user: null as UserDBInterface | null,\n roles: ['Student', 'Teacher', 'Author'] as string[],\n student: true,\n teacher: false,\n author: false,\n authStore: useAuthStore(),\n };\n },\n\n computed: {\n registrationRoute(): boolean {\n return typeof this.$route.name === 'string' && this.$route.name.toLowerCase() === 'signup';\n },\n buttonStatus() {\n return {\n color: this.badLoginAttempt ? 'error' : 'success',\n text: this.badLoginAttempt ? 'Try again' : 'Log In',\n };\n },\n passwordError(): string {\n return validatePassword(this.password);\n },\n passwordRetypeError(): string {\n console.log('[RTE]');\n if (this.password !== this.retypedPassword) {\n return 'Passwords must match.';\n } else {\n return '';\n }\n },\n },\n\n async created() {\n this.user = await getCurrentUser();\n },\n\n methods: {\n toggle() {\n log('Toggling registration / login forms.');\n this.$emit('toggle');\n },\n\n validateEmail() {\n this.emailError = false;\n // Basic email validation\n const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n if (this.email && !emailRegex.test(this.email)) {\n this.emailError = true;\n this.emailHint = 'Please enter a valid email address';\n } else {\n this.emailHint = '';\n }\n },\n\n validateUsername() {\n this.usernameError = false;\n },\n\n async createUser() {\n this.awaitingResponse = true;\n\n // Validate password before proceeding\n if (this.passwordError) {\n alertUser({\n text: this.passwordError,\n status: Status.error,\n });\n this.awaitingResponse = false;\n return;\n }\n\n log(`\nUser creation\n-------------\n\nName: ${this.username}\nStudent: ${this.student}\nTeacher: ${this.teacher}\nAuthor: ${this.author}\n`);\n if (this.password === this.retypedPassword) {\n if (!this.user) {\n console.error('ERROR: No user object available');\n return;\n }\n\n this.user\n .createAccount(this.username, this.password)\n .then(async (resp: any) => {\n if (resp.status === Status.ok) {\n // Account created successfully via PouchDB\n this.authStore.loginAndRegistration.loggedIn = true;\n this.authStore.loginAndRegistration.init = false;\n this.authStore.loginAndRegistration.init = true;\n\n // Send verification email if an address was provided. The address\n // is persisted to the _users doc by send-verification (the single\n // source of truth); no separate CONFIG.email copy is kept.\n if (this.email) {\n try {\n // Trigger verification email send with current origin\n const origin = typeof window !== 'undefined' ? window.location.origin : undefined;\n const verificationResult = await sendVerificationEmail(this.username, this.email, origin);\n if (verificationResult.ok) {\n alertUser({\n text: 'Account created! Please check your email to verify your account.',\n status: Status.ok,\n });\n } else {\n log(`Warning: Failed to send verification email: ${verificationResult.error}`);\n // Continue anyway - user can still use the account\n }\n } catch (emailError) {\n console.error('Email save/send error:', emailError);\n log(`Warning: Failed to save email or send verification: ${emailError}`);\n // Continue anyway - account was created successfully\n }\n }\n\n // Emit signup success event for parent to handle\n // Use the username we just created rather than calling getCurrentUser() again\n // which can fail if the auth state hasn't fully synchronized\n\n // Call callback prop if provided (preferred method)\n if (this.onSignupSuccess) {\n console.log('[UserRegistration] Calling onSignupSuccess callback');\n this.onSignupSuccess({ username: this.username });\n }\n\n // Also emit for backward compatibility\n this.$emit('signup-success', { username: this.username });\n } else {\n if (resp.error === 'This username is taken!') {\n this.usernameError = true;\n this.usernameHint = 'Try a different name.';\n (this.$refs.userNameTextField as HTMLInputElement).focus();\n alertUser({\n text: `The name ${this.username} is taken!`,\n status: resp.status,\n });\n } else {\n alertUser({\n text: resp.error,\n status: resp.status,\n });\n }\n }\n })\n .catch((e) => {\n console.error('[UserRegistration] .catch() called with error:', e);\n if (e) {\n const errorText = e?.message || e?.error || e?.toString() || 'Account creation failed';\n alertUser({\n text: errorText,\n status: Status.error,\n });\n }\n });\n this.awaitingResponse = false;\n } else {\n alertUser({\n text: 'Passwords do not match.',\n status: Status.error,\n });\n this.awaitingResponse = false;\n }\n },\n },\n});\n</script>\n"],"mappings":"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"}
@@ -0,0 +1,118 @@
1
+ import "../../_virtual/_rolldown/runtime.mjs";
2
+ import e from "../../_virtual/_plugin-vue_export-helper.mjs";
3
+ import t from "./VerifyEmail.vue_vue_type_script_lang.mjs";
4
+ import { createBlock as n, createCommentVNode as r, createElementBlock as i, createElementVNode as a, createTextVNode as o, createVNode as s, openBlock as c, renderSlot as l, resolveComponent as u, toDisplayString as d, withCtx as f } from "vue";
5
+ //#region src/components/auth/VerifyEmail.vue
6
+ var p = {
7
+ key: 0,
8
+ class: "text-center"
9
+ }, m = {
10
+ key: 1,
11
+ class: "text-center"
12
+ }, h = { key: 0 }, g = {
13
+ key: 2,
14
+ class: "text-center"
15
+ }, _ = {
16
+ key: 3,
17
+ class: "text-center"
18
+ };
19
+ function _sfc_render(e, t, v, y, b, x) {
20
+ let S = u("v-card-title"), C = u("v-progress-circular"), w = u("v-icon"), T = u("v-card-text"), E = u("v-spacer"), D = u("v-btn"), O = u("v-card-actions"), k = u("v-card"), A = u("v-col"), j = u("v-row"), M = u("v-container");
21
+ return c(), n(M, {
22
+ class: "fill-height",
23
+ fluid: ""
24
+ }, {
25
+ default: f(() => [s(j, {
26
+ align: "center",
27
+ justify: "center"
28
+ }, {
29
+ default: f(() => [s(A, {
30
+ cols: "12",
31
+ sm: "8",
32
+ md: "6"
33
+ }, {
34
+ default: f(() => [s(k, null, {
35
+ default: f(() => [
36
+ s(S, { class: "text-h5 bg-grey-lighten-2" }, {
37
+ default: f(() => t[2] || (t[2] = [o(" Email Verification ")])),
38
+ _: 1
39
+ }),
40
+ s(T, { class: "pa-6" }, {
41
+ default: f(() => [e.isVerifying ? (c(), i("div", p, [s(C, {
42
+ indeterminate: "",
43
+ color: "primary",
44
+ size: "64",
45
+ class: "mb-4"
46
+ }), t[3] || (t[3] = a("p", null, "Verifying your email...", -1))])) : e.verificationStatus === "success" ? (c(), i("div", m, [
47
+ s(w, {
48
+ color: "success",
49
+ size: "64",
50
+ class: "mb-4"
51
+ }, {
52
+ default: f(() => t[4] || (t[4] = [o("mdi-check-circle")])),
53
+ _: 1
54
+ }),
55
+ t[5] || (t[5] = a("h3", { class: "mb-2" }, "Email Verified!", -1)),
56
+ t[6] || (t[6] = a("p", null, "Your account has been successfully verified.", -1)),
57
+ e.username ? (c(), i("p", h, "Welcome, " + d(e.username) + "!", 1)) : r("", !0)
58
+ ])) : e.verificationStatus === "error" ? (c(), i("div", g, [
59
+ s(w, {
60
+ color: "error",
61
+ size: "64",
62
+ class: "mb-4"
63
+ }, {
64
+ default: f(() => t[7] || (t[7] = [o("mdi-alert-circle")])),
65
+ _: 1
66
+ }),
67
+ t[8] || (t[8] = a("h3", { class: "mb-2" }, "Verification Failed", -1)),
68
+ a("p", null, d(e.errorMessage), 1)
69
+ ])) : (c(), i("div", _, [
70
+ s(w, {
71
+ color: "warning",
72
+ size: "64",
73
+ class: "mb-4"
74
+ }, {
75
+ default: f(() => t[9] || (t[9] = [o("mdi-help-circle")])),
76
+ _: 1
77
+ }),
78
+ t[10] || (t[10] = a("h3", { class: "mb-2" }, "No Verification Token", -1)),
79
+ t[11] || (t[11] = a("p", null, "Please use the link from your verification email.", -1))
80
+ ]))]),
81
+ _: 1
82
+ }),
83
+ s(O, null, {
84
+ default: f(() => [s(E), l(e.$slots, "actions", {
85
+ status: e.verificationStatus,
86
+ username: e.username
87
+ }, () => [e.verificationStatus === "success" ? (c(), n(D, {
88
+ key: 0,
89
+ color: "primary",
90
+ onClick: t[0] || (t[0] = (t) => e.$emit("verified", e.username))
91
+ }, {
92
+ default: f(() => t[12] || (t[12] = [o(" Continue ")])),
93
+ _: 1
94
+ })) : e.verificationStatus === "error" ? (c(), n(D, {
95
+ key: 1,
96
+ variant: "text",
97
+ onClick: t[1] || (t[1] = (t) => e.$emit("error", e.errorMessage))
98
+ }, {
99
+ default: f(() => t[13] || (t[13] = [o(" Close ")])),
100
+ _: 1
101
+ })) : r("", !0)])]),
102
+ _: 3
103
+ })
104
+ ]),
105
+ _: 3
106
+ })]),
107
+ _: 3
108
+ })]),
109
+ _: 3
110
+ })]),
111
+ _: 3
112
+ });
113
+ }
114
+ var v = /* @__PURE__ */ e(t, [["render", _sfc_render]]);
115
+ //#endregion
116
+ export { v as default };
117
+
118
+ //# sourceMappingURL=VerifyEmail.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"VerifyEmail.mjs","names":[],"sources":["../../../src/components/auth/VerifyEmail.vue"],"sourcesContent":["<template>\n <v-container class=\"fill-height\" fluid>\n <v-row align=\"center\" justify=\"center\">\n <v-col cols=\"12\" sm=\"8\" md=\"6\">\n <v-card>\n <v-card-title class=\"text-h5 bg-grey-lighten-2\">\n Email Verification\n </v-card-title>\n\n <v-card-text class=\"pa-6\">\n <!-- Loading state -->\n <div v-if=\"isVerifying\" class=\"text-center\">\n <v-progress-circular\n indeterminate\n color=\"primary\"\n size=\"64\"\n class=\"mb-4\"\n ></v-progress-circular>\n <p>Verifying your email...</p>\n </div>\n\n <!-- Success state -->\n <div v-else-if=\"verificationStatus === 'success'\" class=\"text-center\">\n <v-icon color=\"success\" size=\"64\" class=\"mb-4\">mdi-check-circle</v-icon>\n <h3 class=\"mb-2\">Email Verified!</h3>\n <p>Your account has been successfully verified.</p>\n <p v-if=\"username\">Welcome, {{ username }}!</p>\n </div>\n\n <!-- Error state -->\n <div v-else-if=\"verificationStatus === 'error'\" class=\"text-center\">\n <v-icon color=\"error\" size=\"64\" class=\"mb-4\">mdi-alert-circle</v-icon>\n <h3 class=\"mb-2\">Verification Failed</h3>\n <p>{{ errorMessage }}</p>\n </div>\n\n <!-- No token state -->\n <div v-else class=\"text-center\">\n <v-icon color=\"warning\" size=\"64\" class=\"mb-4\">mdi-help-circle</v-icon>\n <h3 class=\"mb-2\">No Verification Token</h3>\n <p>Please use the link from your verification email.</p>\n </div>\n </v-card-text>\n\n <v-card-actions>\n <v-spacer></v-spacer>\n <slot name=\"actions\" :status=\"verificationStatus\" :username=\"username\">\n <!-- Default action - external apps can override with their own navigation -->\n <v-btn\n v-if=\"verificationStatus === 'success'\"\n color=\"primary\"\n @click=\"$emit('verified', username)\"\n >\n Continue\n </v-btn>\n <v-btn\n v-else-if=\"verificationStatus === 'error'\"\n variant=\"text\"\n @click=\"$emit('error', errorMessage)\"\n >\n Close\n </v-btn>\n </slot>\n </v-card-actions>\n </v-card>\n </v-col>\n </v-row>\n </v-container>\n</template>\n\n<script lang=\"ts\">\nimport { defineComponent } from 'vue';\nimport { verifyEmail } from '../../services/authAPI';\nimport { alertUser } from '../SnackbarService';\nimport { Status } from '@vue-skuilder/common';\n\nexport default defineComponent({\n name: 'VerifyEmail',\n\n emits: ['verified', 'error'],\n\n props: {\n /**\n * Verification token. If not provided, will try to read from URL query params.\n */\n token: {\n type: String,\n default: null,\n },\n },\n\n data() {\n return {\n isVerifying: false,\n verificationStatus: null as 'success' | 'error' | null,\n errorMessage: '',\n username: '',\n };\n },\n\n async mounted() {\n await this.performVerification();\n },\n\n methods: {\n async performVerification() {\n // Get token from prop or URL query params\n const token = this.token || this.getTokenFromURL();\n\n if (!token) {\n this.verificationStatus = 'error';\n this.errorMessage = 'No verification token provided';\n return;\n }\n\n this.isVerifying = true;\n\n try {\n const result = await verifyEmail(token);\n\n if (result.ok) {\n this.verificationStatus = 'success';\n this.username = result.username || '';\n alertUser({\n text: 'Email verified successfully!',\n status: Status.ok,\n });\n } else {\n this.verificationStatus = 'error';\n this.errorMessage = result.error || 'Verification failed';\n alertUser({\n text: result.error || 'Verification failed',\n status: Status.error,\n });\n }\n } catch (error) {\n this.verificationStatus = 'error';\n this.errorMessage = 'An unexpected error occurred';\n alertUser({\n text: 'An unexpected error occurred',\n status: Status.error,\n });\n } finally {\n this.isVerifying = false;\n }\n },\n\n getTokenFromURL(): string | null {\n if (typeof window === 'undefined') return null;\n\n const params = new URLSearchParams(window.location.search);\n return params.get('token');\n },\n },\n});\n</script>\n"],"mappings":";;;;;;CAAA,KAAA;CAWoC,OAAM;;CAX1C,KAAA;CAsB8D,OAAM;SAtBpE,KAAA,GAAA;CAAA,KAAA;CA8B4D,OAAM;;CA9BlE,KAAA;CAqCwB,OAAM;;;;aApC5B,EAkEc,GAAA;EAlED,OAAM;EAAc,OAAA;;EADnC,SAAA,QAkEY,CAhER,EAgEQ,GAAA;GAhED,OAAM;GAAS,SAAQ;;GAFlC,SAAA,QAiEc,CA9DR,EA8DQ,GAAA;IA9DD,MAAK;IAAK,IAAG;IAAI,IAAG;;IAHjC,SAAA,QAgEiB,CA5DT,EA4DS,GAAA,MAAA;KAhEjB,SAAA,QAOyB;MAFf,EAEe,GAAA,EAFD,OAAM,6BAA2B,EAAA;OALzD,SAAA,QAOU,EAAA,OAAA,EAAA,KAAA,CAPV,EAK0D,uBAEhD,CAAA,EAAA;OAPV,GAAA;;MASU,EAiCc,GAAA,EAjCD,OAAM,QAAM,EAAA;OATnC,SAAA,QAmBkB,CARK,EAAA,eAAA,GAAA,EAAX,EAQM,OARN,GAQM,CAPJ,EAKuB,GAAA;QAJrB,eAAA;QACA,OAAM;QACN,MAAK;QACL,OAAM;2BAER,EAA8B,KAAA,MAA3B,2BAAuB,GAAA,EAAA,CAAA,IAIZ,EAAA,uBAAkB,aAAA,GAAA,EAAlC,EAKM,OALN,GAKM;QAJJ,EAAwE,GAAA;SAAhE,OAAM;SAAU,MAAK;SAAK,OAAM;;SAvBtD,SAAA,QAuB6E,EAAA,OAAA,EAAA,KAAA,CAvB7E,EAuB6D,mBAAgB,CAAA,EAAA;SAvB7E,GAAA;;wBAwBc,EAAqC,MAAA,EAAjC,OAAM,QAAM,EAAC,mBAAe,GAAA;wBAChC,EAAmD,KAAA,MAAhD,gDAA4C,GAAA;QACtC,EAAA,YAAA,GAAA,EAAT,EAA+C,KA1B7D,GA0BiC,cAAS,EAAG,EAAA,SAAQ,GAAG,KAAC,EAAA,IA1BzD,EAAA,IAAA,GAAA;aA8B4B,EAAA,uBAAkB,WAAA,GAAA,EAAlC,EAIM,OAJN,GAIM;QAHJ,EAAsE,GAAA;SAA9D,OAAM;SAAQ,MAAK;SAAK,OAAM;;SA/BpD,SAAA,QA+B2E,EAAA,OAAA,EAAA,KAAA,CA/B3E,EA+B2D,mBAAgB,CAAA,EAAA;SA/B3E,GAAA;;wBAgCc,EAAyC,MAAA,EAArC,OAAM,QAAM,EAAC,uBAAmB,GAAA;QACpC,EAAyB,KAAA,MAAA,EAAnB,EAAA,aAAY,EAAA,EAAA;mBAIpB,EAIM,OAJN,GAIM;QAHJ,EAAuE,GAAA;SAA/D,OAAM;SAAU,MAAK;SAAK,OAAM;;SAtCtD,SAAA,QAsC4E,EAAA,OAAA,EAAA,KAAA,CAtC5E,EAsC6D,kBAAe,CAAA,EAAA;SAtC5E,GAAA;;0BAuCc,EAA2C,MAAA,EAAvC,OAAM,QAAM,EAAC,yBAAqB,GAAA;0BACtC,EAAwD,KAAA,MAArD,qDAAiD,GAAA;;OAxClE,GAAA;;MA4CU,EAmBiB,GAAA,MAAA;OA/D3B,SAAA,QA6CiC,CAArB,EAAqB,EAAA,EACrB,EAgBO,EAAA,QAAA,WAAA;QAhBe,QAAQ,EAAA;QAAqB,UAAU,EAAA;gBAgBtD,CAbG,EAAA,uBAAkB,aAAA,GAAA,EAD1B,EAMQ,GAAA;QAtDtB,KAAA;QAkDgB,OAAM;QACL,SAAK,EAAA,OAAA,EAAA,MAAA,MAAE,EAAA,MAAK,YAAa,EAAA,SAAQ;;QAnDlD,SAAA,QAsDc,EAAA,QAAA,EAAA,MAAA,CAtDd,EAoDe,aAED,CAAA,EAAA;QAtDd,GAAA;aAwD2B,EAAA,uBAAkB,WAAA,GAAA,EAD/B,EAMQ,GAAA;QA7DtB,KAAA;QAyDgB,SAAQ;QACP,SAAK,EAAA,OAAA,EAAA,MAAA,MAAE,EAAA,MAAK,SAAU,EAAA,aAAY;;QA1DnD,SAAA,QA6Dc,EAAA,QAAA,EAAA,MAAA,CA7Dd,EA2De,UAED,CAAA,EAAA;QA7Dd,GAAA;aAAA,EAAA,IAAA,GAAA,CAAA,CAAA,CAAA,CAAA;OAAA,GAAA;;;KAAA,GAAA;;IAAA,GAAA;;GAAA,GAAA;;EAAA,GAAA"}
@@ -0,0 +1,59 @@
1
+ import "../../_virtual/_rolldown/runtime.mjs";
2
+ import { alertUser as e } from "../SnackbarService.mjs";
3
+ import { verifyEmail as t } from "../../services/authAPI.mjs";
4
+ import { defineComponent as n } from "vue";
5
+ import { Status as r } from "@vue-skuilder/common";
6
+ //#region src/components/auth/VerifyEmail.vue?vue&type=script&lang.ts
7
+ var i = n({
8
+ name: "VerifyEmail",
9
+ emits: ["verified", "error"],
10
+ props: { token: {
11
+ type: String,
12
+ default: null
13
+ } },
14
+ data() {
15
+ return {
16
+ isVerifying: !1,
17
+ verificationStatus: null,
18
+ errorMessage: "",
19
+ username: ""
20
+ };
21
+ },
22
+ async mounted() {
23
+ await this.performVerification();
24
+ },
25
+ methods: {
26
+ async performVerification() {
27
+ let n = this.token || this.getTokenFromURL();
28
+ if (!n) {
29
+ this.verificationStatus = "error", this.errorMessage = "No verification token provided";
30
+ return;
31
+ }
32
+ this.isVerifying = !0;
33
+ try {
34
+ let i = await t(n);
35
+ i.ok ? (this.verificationStatus = "success", this.username = i.username || "", e({
36
+ text: "Email verified successfully!",
37
+ status: r.ok
38
+ })) : (this.verificationStatus = "error", this.errorMessage = i.error || "Verification failed", e({
39
+ text: i.error || "Verification failed",
40
+ status: r.error
41
+ }));
42
+ } catch {
43
+ this.verificationStatus = "error", this.errorMessage = "An unexpected error occurred", e({
44
+ text: "An unexpected error occurred",
45
+ status: r.error
46
+ });
47
+ } finally {
48
+ this.isVerifying = !1;
49
+ }
50
+ },
51
+ getTokenFromURL() {
52
+ return typeof window > "u" ? null : new URLSearchParams(window.location.search).get("token");
53
+ }
54
+ }
55
+ });
56
+ //#endregion
57
+ export { i as default };
58
+
59
+ //# sourceMappingURL=VerifyEmail.vue_vue_type_script_lang.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"VerifyEmail.vue_vue_type_script_lang.mjs","names":[],"sources":["../../../src/components/auth/VerifyEmail.vue"],"sourcesContent":["<template>\n <v-container class=\"fill-height\" fluid>\n <v-row align=\"center\" justify=\"center\">\n <v-col cols=\"12\" sm=\"8\" md=\"6\">\n <v-card>\n <v-card-title class=\"text-h5 bg-grey-lighten-2\">\n Email Verification\n </v-card-title>\n\n <v-card-text class=\"pa-6\">\n <!-- Loading state -->\n <div v-if=\"isVerifying\" class=\"text-center\">\n <v-progress-circular\n indeterminate\n color=\"primary\"\n size=\"64\"\n class=\"mb-4\"\n ></v-progress-circular>\n <p>Verifying your email...</p>\n </div>\n\n <!-- Success state -->\n <div v-else-if=\"verificationStatus === 'success'\" class=\"text-center\">\n <v-icon color=\"success\" size=\"64\" class=\"mb-4\">mdi-check-circle</v-icon>\n <h3 class=\"mb-2\">Email Verified!</h3>\n <p>Your account has been successfully verified.</p>\n <p v-if=\"username\">Welcome, {{ username }}!</p>\n </div>\n\n <!-- Error state -->\n <div v-else-if=\"verificationStatus === 'error'\" class=\"text-center\">\n <v-icon color=\"error\" size=\"64\" class=\"mb-4\">mdi-alert-circle</v-icon>\n <h3 class=\"mb-2\">Verification Failed</h3>\n <p>{{ errorMessage }}</p>\n </div>\n\n <!-- No token state -->\n <div v-else class=\"text-center\">\n <v-icon color=\"warning\" size=\"64\" class=\"mb-4\">mdi-help-circle</v-icon>\n <h3 class=\"mb-2\">No Verification Token</h3>\n <p>Please use the link from your verification email.</p>\n </div>\n </v-card-text>\n\n <v-card-actions>\n <v-spacer></v-spacer>\n <slot name=\"actions\" :status=\"verificationStatus\" :username=\"username\">\n <!-- Default action - external apps can override with their own navigation -->\n <v-btn\n v-if=\"verificationStatus === 'success'\"\n color=\"primary\"\n @click=\"$emit('verified', username)\"\n >\n Continue\n </v-btn>\n <v-btn\n v-else-if=\"verificationStatus === 'error'\"\n variant=\"text\"\n @click=\"$emit('error', errorMessage)\"\n >\n Close\n </v-btn>\n </slot>\n </v-card-actions>\n </v-card>\n </v-col>\n </v-row>\n </v-container>\n</template>\n\n<script lang=\"ts\">\nimport { defineComponent } from 'vue';\nimport { verifyEmail } from '../../services/authAPI';\nimport { alertUser } from '../SnackbarService';\nimport { Status } from '@vue-skuilder/common';\n\nexport default defineComponent({\n name: 'VerifyEmail',\n\n emits: ['verified', 'error'],\n\n props: {\n /**\n * Verification token. If not provided, will try to read from URL query params.\n */\n token: {\n type: String,\n default: null,\n },\n },\n\n data() {\n return {\n isVerifying: false,\n verificationStatus: null as 'success' | 'error' | null,\n errorMessage: '',\n username: '',\n };\n },\n\n async mounted() {\n await this.performVerification();\n },\n\n methods: {\n async performVerification() {\n // Get token from prop or URL query params\n const token = this.token || this.getTokenFromURL();\n\n if (!token) {\n this.verificationStatus = 'error';\n this.errorMessage = 'No verification token provided';\n return;\n }\n\n this.isVerifying = true;\n\n try {\n const result = await verifyEmail(token);\n\n if (result.ok) {\n this.verificationStatus = 'success';\n this.username = result.username || '';\n alertUser({\n text: 'Email verified successfully!',\n status: Status.ok,\n });\n } else {\n this.verificationStatus = 'error';\n this.errorMessage = result.error || 'Verification failed';\n alertUser({\n text: result.error || 'Verification failed',\n status: Status.error,\n });\n }\n } catch (error) {\n this.verificationStatus = 'error';\n this.errorMessage = 'An unexpected error occurred';\n alertUser({\n text: 'An unexpected error occurred',\n status: Status.error,\n });\n } finally {\n this.isVerifying = false;\n }\n },\n\n getTokenFromURL(): string | null {\n if (typeof window === 'undefined') return null;\n\n const params = new URLSearchParams(window.location.search);\n return params.get('token');\n },\n },\n});\n</script>\n"],"mappings":";;;;;;AA4EA,IAAA,IAAe,EAAgB;CAC7B,MAAM;CAEN,OAAO,CAAC,YAAY,QAAQ;CAE5B,OAAO,EAIL,OAAO;EACL,MAAM;EACN,SAAS;EACV,EACF;CAED,OAAO;AACL,SAAO;GACL,aAAa;GACb,oBAAoB;GACpB,cAAc;GACd,UAAU;GACX;;CAGH,MAAM,UAAU;AACd,QAAM,KAAK,qBAAqB;;CAGlC,SAAS;EACP,MAAM,sBAAsB;GAE1B,IAAM,IAAQ,KAAK,SAAS,KAAK,iBAAiB;AAElD,OAAI,CAAC,GAAO;AAEV,IADA,KAAK,qBAAqB,SAC1B,KAAK,eAAe;AACpB;;AAGF,QAAK,cAAc;AAEnB,OAAI;IACF,IAAM,IAAS,MAAM,EAAY,EAAM;AAEvC,IAAI,EAAO,MACT,KAAK,qBAAqB,WAC1B,KAAK,WAAW,EAAO,YAAY,IACnC,EAAU;KACR,MAAM;KACN,QAAQ,EAAO;KAChB,CAAC,KAEF,KAAK,qBAAqB,SAC1B,KAAK,eAAe,EAAO,SAAS,uBACpC,EAAU;KACR,MAAM,EAAO,SAAS;KACtB,QAAQ,EAAO;KAChB,CAAC;WAEU;AAGd,IAFA,KAAK,qBAAqB,SAC1B,KAAK,eAAe,gCACpB,EAAU;KACR,MAAM;KACN,QAAQ,EAAO;KAChB,CAAC;aACM;AACR,SAAK,cAAc;;;EAIvB,kBAAiC;AAI/B,UAHI,OAAO,SAAW,MAAoB,OAE3B,IAAI,gBAAgB,OAAO,SAAS,OAAO,CAC5C,IAAI,QAAQ;;EAE7B;CACF,CAAC"}
@@ -0,0 +1,9 @@
1
+ import e from "../../_virtual/_plugin-vue_export-helper.mjs";
2
+ import t from "./AudioAutoPlayer.vue_vue_type_script_setup_true_lang.mjs";
3
+ /* empty css */
4
+ //#region src/components/cardRendering/AudioAutoPlayer.vue
5
+ var n = /* @__PURE__ */ e(t, [["__scopeId", "data-v-e1a0f62c"]]);
6
+ //#endregion
7
+ export { n as default };
8
+
9
+ //# sourceMappingURL=AudioAutoPlayer.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AudioAutoPlayer.mjs","names":[],"sources":["../../../src/components/cardRendering/AudioAutoPlayer.vue"],"sourcesContent":["<template>\n <v-btn size=\"large\" icon :color=\"playing ? 'primary lighten-3' : 'primary'\" :class=\"{ playing }\" @click=\"play\">\n <v-icon>mdi-volume-high</v-icon>\n </v-btn>\n</template>\n\n<script setup lang=\"ts\">\nimport { ref, onBeforeUnmount, onMounted, getCurrentInstance } from 'vue';\nimport { SkldrMouseTrap } from '../../utils/SkldrMouseTrap';\n\nconst props = defineProps<{\n src: string | string[];\n}>();\n\nconst audioElems = ref<HTMLAudioElement[]>([]);\nconst playTimeouts = ref<NodeJS.Timer[]>([]);\nconst playing = ref(false);\n\nlet staticLOCK: any = null;\nconst playbackGap = 500;\n\nconst stop = () => {\n playing.value = false;\n setTimeout(() => {\n staticLOCK = null;\n }, playbackGap);\n\n playTimeouts.value.forEach(clearTimeout);\n\n console.log(`Audio stopping...`);\n audioElems.value.forEach((audio) => {\n if (!audio.paused) {\n audio.pause();\n audio.currentTime = 0;\n }\n });\n};\n\nconst downloadFinished = (i: number): boolean => {\n try {\n return !isNaN(audioElems.value[i].duration);\n } catch (e) {\n throw new Error('AudioPlayer does not have an element at this index:', e);\n }\n};\n\nconst playByIndex = (n: number) => {\n if (downloadFinished(n)) {\n audioElems.value[n].play();\n\n if (n + 1 < audioElems.value.length) {\n const delay = (audioElems.value[n].duration + 0.7) * 1000;\n playTimeouts.value.push(\n setTimeout(() => {\n if (playing.value) {\n playByIndex(n + 1);\n }\n }, delay)\n );\n } else {\n setTimeout(() => {\n playing.value = false;\n }, audioElems.value[n].duration * 1000);\n setTimeout(() => {\n staticLOCK = null;\n }, audioElems.value[n].duration * 1000 + playbackGap);\n }\n } else {\n setTimeout(playByIndex, 100, n);\n }\n};\n\nconst play = () => {\n if (staticLOCK === null || staticLOCK === getCurrentInstance()) {\n staticLOCK = getCurrentInstance();\n playing.value = true;\n playByIndex(0);\n } else {\n setTimeout(play, 100);\n }\n};\n\nonMounted(() => {\n staticLOCK = null;\n\n if (typeof props.src === 'string') {\n audioElems.value.push(new Audio(props.src));\n } else {\n props.src.forEach((url) => {\n audioElems.value.push(new Audio(url));\n });\n }\n\n // Define hotkeys\n const hotkeys = [\n {\n hotkey: 'up',\n callback: play,\n command: 'Replay Audio',\n },\n ];\n \n // Add bindings\n SkldrMouseTrap.addBinding(hotkeys);\n\n play();\n});\n\nonBeforeUnmount(() => {\n // Clean up hotkey bindings\n SkldrMouseTrap.removeBinding('up');\n stop();\n});\n</script>\n\n<style scoped>\n.playing {\n /* transform: rotate(3deg) scale(1.15); */\n animation: 0.85s ease-in-out infinite alternate pulse;\n z-index: 1;\n}\n\n@keyframes pulse {\n 0% {\n transform: scale(1);\n }\n 100% {\n transform: scale(1.15);\n }\n}\n</style>\n"],"mappings":""}