@vue-skuilder/common-ui 0.2.34 → 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 (207) 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/admin.css +1 -1
  8. package/dist/admin/common-ui-admin.es.js +1074 -473
  9. package/dist/admin/common-ui-admin.es.js.map +1 -1
  10. package/dist/admin/components/EChart.vue.d.ts +14 -0
  11. package/dist/admin/components/EChart.vue.d.ts.map +1 -0
  12. package/dist/admin/components/LearnerDiagnostics.vue.d.ts +3 -3
  13. package/dist/admin/components/LearnerDiagnostics.vue.d.ts.map +1 -1
  14. package/dist/admin/components/LearnerTagElo.vue.d.ts +14 -0
  15. package/dist/admin/components/LearnerTagElo.vue.d.ts.map +1 -0
  16. package/dist/admin/index.d.ts +1 -0
  17. package/dist/admin/index.d.ts.map +1 -1
  18. package/dist/assets/index.css +2 -1
  19. package/dist/components/CardBrowser.mjs +63 -0
  20. package/dist/components/CardBrowser.mjs.map +1 -0
  21. package/dist/components/CardBrowser.vue_vue_type_script_lang.mjs +50 -0
  22. package/dist/components/CardBrowser.vue_vue_type_script_lang.mjs.map +1 -0
  23. package/dist/components/CardHistoryViewer.mjs +70 -0
  24. package/dist/components/CardHistoryViewer.mjs.map +1 -0
  25. package/dist/components/CardHistoryViewer.vue_vue_type_script_lang.mjs +108 -0
  26. package/dist/components/CardHistoryViewer.vue_vue_type_script_lang.mjs.map +1 -0
  27. package/dist/components/CardSearch.mjs +26 -0
  28. package/dist/components/CardSearch.mjs.map +1 -0
  29. package/dist/components/CardSearch.vue_vue_type_script_lang.mjs +17 -0
  30. package/dist/components/CardSearch.vue_vue_type_script_lang.mjs.map +1 -0
  31. package/dist/components/CardSearchResults.mjs +32 -0
  32. package/dist/components/CardSearchResults.mjs.map +1 -0
  33. package/dist/components/CardSearchResults.vue_vue_type_script_lang.mjs +71 -0
  34. package/dist/components/CardSearchResults.vue_vue_type_script_lang.mjs.map +1 -0
  35. package/dist/components/CourseCardBrowser.mjs +175 -0
  36. package/dist/components/CourseCardBrowser.mjs.map +1 -0
  37. package/dist/components/CourseCardBrowser.vue.d.ts +3 -3
  38. package/dist/components/CourseCardBrowser.vue_vue_type_script_lang.mjs +173 -0
  39. package/dist/components/CourseCardBrowser.vue_vue_type_script_lang.mjs.map +1 -0
  40. package/dist/components/CourseInformation.mjs +132 -0
  41. package/dist/components/CourseInformation.mjs.map +1 -0
  42. package/dist/components/CourseInformation.vue.d.ts +3 -3
  43. package/dist/components/CourseInformation.vue_vue_type_script_lang.mjs +56 -0
  44. package/dist/components/CourseInformation.vue_vue_type_script_lang.mjs.map +1 -0
  45. package/dist/components/CourseTagFilterWidget.mjs +114 -0
  46. package/dist/components/CourseTagFilterWidget.mjs.map +1 -0
  47. package/dist/components/CourseTagFilterWidget.vue_vue_type_script_lang.mjs +65 -0
  48. package/dist/components/CourseTagFilterWidget.vue_vue_type_script_lang.mjs.map +1 -0
  49. package/dist/components/HeatMap.mjs +40 -0
  50. package/dist/components/HeatMap.mjs.map +1 -0
  51. package/dist/components/HeatMap.vue_vue_type_script_lang.mjs +211 -0
  52. package/dist/components/HeatMap.vue_vue_type_script_lang.mjs.map +1 -0
  53. package/dist/components/PaginatingToolbar.mjs +94 -0
  54. package/dist/components/PaginatingToolbar.mjs.map +1 -0
  55. package/dist/components/PaginatingToolbar.vue.d.ts +3 -3
  56. package/dist/components/PaginatingToolbar.vue_vue_type_script_lang.mjs +36 -0
  57. package/dist/components/PaginatingToolbar.vue_vue_type_script_lang.mjs.map +1 -0
  58. package/dist/components/SessionControllerDebug.mjs +130 -0
  59. package/dist/components/SessionControllerDebug.mjs.map +1 -0
  60. package/dist/components/SessionControllerDebug.vue_vue_type_script_lang.mjs +45 -0
  61. package/dist/components/SessionControllerDebug.vue_vue_type_script_lang.mjs.map +1 -0
  62. package/dist/components/SkMouseTrap.mjs +66 -0
  63. package/dist/components/SkMouseTrap.mjs.map +1 -0
  64. package/dist/components/SkMouseTrap.vue_vue_type_script_lang.mjs +31 -0
  65. package/dist/components/SkMouseTrap.vue_vue_type_script_lang.mjs.map +1 -0
  66. package/dist/components/SkMouseTrapToolTip.mjs +28 -0
  67. package/dist/components/SkMouseTrapToolTip.mjs.map +1 -0
  68. package/dist/components/SkMouseTrapToolTip.vue.d.ts +1 -1
  69. package/dist/components/SkMouseTrapToolTip.vue_vue_type_script_lang.mjs +91 -0
  70. package/dist/components/SkMouseTrapToolTip.vue_vue_type_script_lang.mjs.map +1 -0
  71. package/dist/components/SnackbarService.mjs +24 -0
  72. package/dist/components/SnackbarService.mjs.map +1 -0
  73. package/dist/components/SnackbarService.vue_vue_type_script_lang.mjs +34 -0
  74. package/dist/components/SnackbarService.vue_vue_type_script_lang.mjs.map +1 -0
  75. package/dist/components/SnackbarService2.mjs +40 -0
  76. package/dist/components/SnackbarService2.mjs.map +1 -0
  77. package/dist/components/StudySession.mjs +123 -0
  78. package/dist/components/StudySession.mjs.map +1 -0
  79. package/dist/components/StudySession.vue_vue_type_script_lang.mjs +280 -0
  80. package/dist/components/StudySession.vue_vue_type_script_lang.mjs.map +1 -0
  81. package/dist/components/StudySessionTimer.mjs +50 -0
  82. package/dist/components/StudySessionTimer.mjs.map +1 -0
  83. package/dist/components/StudySessionTimer.vue_vue_type_script_lang.mjs +43 -0
  84. package/dist/components/StudySessionTimer.vue_vue_type_script_lang.mjs.map +1 -0
  85. package/dist/components/TagsInput.mjs +45 -0
  86. package/dist/components/TagsInput.mjs.map +1 -0
  87. package/dist/components/TagsInput.vue_vue_type_script_lang.mjs +117 -0
  88. package/dist/components/TagsInput.vue_vue_type_script_lang.mjs.map +1 -0
  89. package/dist/components/UserTagPreferences.mjs +205 -0
  90. package/dist/components/UserTagPreferences.mjs.map +1 -0
  91. package/dist/components/UserTagPreferences.vue_vue_type_script_lang.mjs +139 -0
  92. package/dist/components/UserTagPreferences.vue_vue_type_script_lang.mjs.map +1 -0
  93. package/dist/components/auth/RequestPasswordReset.mjs +96 -0
  94. package/dist/components/auth/RequestPasswordReset.mjs.map +1 -0
  95. package/dist/components/auth/RequestPasswordReset.vue_vue_type_script_lang.mjs +47 -0
  96. package/dist/components/auth/RequestPasswordReset.vue_vue_type_script_lang.mjs.map +1 -0
  97. package/dist/components/auth/ResetPassword.mjs +129 -0
  98. package/dist/components/auth/ResetPassword.mjs.map +1 -0
  99. package/dist/components/auth/ResetPassword.vue_vue_type_script_lang.mjs +69 -0
  100. package/dist/components/auth/ResetPassword.vue_vue_type_script_lang.mjs.map +1 -0
  101. package/dist/components/auth/UserChip.mjs +9 -0
  102. package/dist/components/auth/UserChip.mjs.map +1 -0
  103. package/dist/components/auth/UserChip.vue_vue_type_script_setup_true_lang.mjs +213 -0
  104. package/dist/components/auth/UserChip.vue_vue_type_script_setup_true_lang.mjs.map +1 -0
  105. package/dist/components/auth/UserLogin.mjs +9 -0
  106. package/dist/components/auth/UserLogin.mjs.map +1 -0
  107. package/dist/components/auth/UserLogin.vue_vue_type_script_setup_true_lang.mjs +155 -0
  108. package/dist/components/auth/UserLogin.vue_vue_type_script_setup_true_lang.mjs.map +1 -0
  109. package/dist/components/auth/UserLoginAndRegistrationContainer.mjs +9 -0
  110. package/dist/components/auth/UserLoginAndRegistrationContainer.mjs.map +1 -0
  111. package/dist/components/auth/UserLoginAndRegistrationContainer.vue_vue_type_script_setup_true_lang.mjs +126 -0
  112. package/dist/components/auth/UserLoginAndRegistrationContainer.vue_vue_type_script_setup_true_lang.mjs.map +1 -0
  113. package/dist/components/auth/UserRegistration.mjs +147 -0
  114. package/dist/components/auth/UserRegistration.mjs.map +1 -0
  115. package/dist/components/auth/UserRegistration.vue_vue_type_script_lang.mjs +130 -0
  116. package/dist/components/auth/UserRegistration.vue_vue_type_script_lang.mjs.map +1 -0
  117. package/dist/components/auth/VerifyEmail.mjs +118 -0
  118. package/dist/components/auth/VerifyEmail.mjs.map +1 -0
  119. package/dist/components/auth/VerifyEmail.vue_vue_type_script_lang.mjs +59 -0
  120. package/dist/components/auth/VerifyEmail.vue_vue_type_script_lang.mjs.map +1 -0
  121. package/dist/components/cardRendering/AudioAutoPlayer.mjs +9 -0
  122. package/dist/components/cardRendering/AudioAutoPlayer.mjs.map +1 -0
  123. package/dist/components/cardRendering/AudioAutoPlayer.vue_vue_type_script_setup_true_lang.mjs +69 -0
  124. package/dist/components/cardRendering/AudioAutoPlayer.vue_vue_type_script_setup_true_lang.mjs.map +1 -0
  125. package/dist/components/cardRendering/CardLoader.mjs +31 -0
  126. package/dist/components/cardRendering/CardLoader.mjs.map +1 -0
  127. package/dist/components/cardRendering/CardLoader.vue_vue_type_script_lang.mjs +75 -0
  128. package/dist/components/cardRendering/CardLoader.vue_vue_type_script_lang.mjs.map +1 -0
  129. package/dist/components/cardRendering/CardViewer.mjs +39 -0
  130. package/dist/components/cardRendering/CardViewer.mjs.map +1 -0
  131. package/dist/components/cardRendering/CardViewer.vue_vue_type_script_lang.mjs +66 -0
  132. package/dist/components/cardRendering/CardViewer.vue_vue_type_script_lang.mjs.map +1 -0
  133. package/dist/components/cardRendering/CodeBlockRenderer.mjs +8 -0
  134. package/dist/components/cardRendering/CodeBlockRenderer.mjs.map +1 -0
  135. package/dist/components/cardRendering/CodeBlockRenderer.vue_vue_type_script_setup_true_lang.mjs +44 -0
  136. package/dist/components/cardRendering/CodeBlockRenderer.vue_vue_type_script_setup_true_lang.mjs.map +1 -0
  137. package/dist/components/cardRendering/MarkdownRenderer.mjs +21 -0
  138. package/dist/components/cardRendering/MarkdownRenderer.mjs.map +1 -0
  139. package/dist/components/cardRendering/MarkdownRenderer.vue_vue_type_script_lang.mjs +24 -0
  140. package/dist/components/cardRendering/MarkdownRenderer.vue_vue_type_script_lang.mjs.map +1 -0
  141. package/dist/components/cardRendering/MarkdownRendererHelpers.mjs +79 -0
  142. package/dist/components/cardRendering/MarkdownRendererHelpers.mjs.map +1 -0
  143. package/dist/components/cardRendering/MdTokenRenderer.mjs +9 -0
  144. package/dist/components/cardRendering/MdTokenRenderer.mjs.map +1 -0
  145. package/dist/components/cardRendering/MdTokenRenderer.vue_vue_type_script_setup_true_lang.mjs +176 -0
  146. package/dist/components/cardRendering/MdTokenRenderer.vue_vue_type_script_setup_true_lang.mjs.map +1 -0
  147. package/dist/components/studentInputs/BaseUserInput.mjs +54 -0
  148. package/dist/components/studentInputs/BaseUserInput.mjs.map +1 -0
  149. package/dist/components/studentInputs/MultipleChoiceOption.mjs +26 -0
  150. package/dist/components/studentInputs/MultipleChoiceOption.mjs.map +1 -0
  151. package/dist/components/studentInputs/MultipleChoiceOption.vue_vue_type_script_lang.mjs +76 -0
  152. package/dist/components/studentInputs/MultipleChoiceOption.vue_vue_type_script_lang.mjs.map +1 -0
  153. package/dist/components/studentInputs/RadioMultipleChoice.mjs +33 -0
  154. package/dist/components/studentInputs/RadioMultipleChoice.mjs.map +1 -0
  155. package/dist/components/studentInputs/RadioMultipleChoice.vue_vue_type_script_lang.mjs +105 -0
  156. package/dist/components/studentInputs/RadioMultipleChoice.vue_vue_type_script_lang.mjs.map +1 -0
  157. package/dist/components/studentInputs/TrueFalse.mjs +19 -0
  158. package/dist/components/studentInputs/TrueFalse.mjs.map +1 -0
  159. package/dist/components/studentInputs/TrueFalse.vue_vue_type_script_lang.mjs +21 -0
  160. package/dist/components/studentInputs/TrueFalse.vue_vue_type_script_lang.mjs.map +1 -0
  161. package/dist/components/studentInputs/UserInputNumber.mjs +33 -0
  162. package/dist/components/studentInputs/UserInputNumber.mjs.map +1 -0
  163. package/dist/components/studentInputs/UserInputNumber.vue_vue_type_script_lang.mjs +25 -0
  164. package/dist/components/studentInputs/UserInputNumber.vue_vue_type_script_lang.mjs.map +1 -0
  165. package/dist/components/studentInputs/UserInputString.mjs +22 -0
  166. package/dist/components/studentInputs/UserInputString.mjs.map +1 -0
  167. package/dist/components/studentInputs/UserInputString.vue_vue_type_script_lang.mjs +23 -0
  168. package/dist/components/studentInputs/UserInputString.vue_vue_type_script_lang.mjs.map +1 -0
  169. package/dist/components/studentInputs/fillInInput.mjs +25 -0
  170. package/dist/components/studentInputs/fillInInput.mjs.map +1 -0
  171. package/dist/components/studentInputs/fillInInput.vue_vue_type_script_lang.mjs +26 -0
  172. package/dist/components/studentInputs/fillInInput.vue_vue_type_script_lang.mjs.map +1 -0
  173. package/dist/composables/CompositionViewable.mjs +65 -0
  174. package/dist/composables/CompositionViewable.mjs.map +1 -0
  175. package/dist/composables/Displayable.mjs +36 -0
  176. package/dist/composables/Displayable.mjs.map +1 -0
  177. package/dist/composables/useAuthUI.mjs +38 -0
  178. package/dist/composables/useAuthUI.mjs.map +1 -0
  179. package/dist/composables/useEntitlements.mjs +58 -0
  180. package/dist/composables/useEntitlements.mjs.map +1 -0
  181. package/dist/index.mjs +49 -0
  182. package/dist/plugins/pinia.mjs +12 -0
  183. package/dist/plugins/pinia.mjs.map +1 -0
  184. package/dist/services/authAPI.mjs +159 -0
  185. package/dist/services/authAPI.mjs.map +1 -0
  186. package/dist/stores/useAuthStore.mjs +68 -0
  187. package/dist/stores/useAuthStore.mjs.map +1 -0
  188. package/dist/stores/useCardPreviewModeStore.mjs +20 -0
  189. package/dist/stores/useCardPreviewModeStore.mjs.map +1 -0
  190. package/dist/stores/useConfigStore.mjs +60 -0
  191. package/dist/stores/useConfigStore.mjs.map +1 -0
  192. package/dist/utils/SkldrMouseTrap.mjs +48 -0
  193. package/dist/utils/SkldrMouseTrap.mjs.map +1 -0
  194. package/dist/utils/passwordValidation.mjs +12 -0
  195. package/dist/utils/passwordValidation.mjs.map +1 -0
  196. package/package.json +8 -8
  197. package/src/admin/components/EChart.vue +59 -0
  198. package/src/admin/components/LearnerDiagnostics.vue +8 -4
  199. package/src/admin/components/LearnerTagElo.vue +563 -0
  200. package/src/admin/index.ts +1 -0
  201. package/dist/MarkdownRenderer-U_iPiA2x.mjs +0 -4966
  202. package/dist/MarkdownRenderer-U_iPiA2x.mjs.map +0 -1
  203. package/dist/assets/MarkdownRenderer.css +0 -1
  204. package/dist/common-ui.es.js +0 -5090
  205. package/dist/common-ui.es.js.map +0 -1
  206. package/dist/common-ui.umd.js +0 -77
  207. package/dist/common-ui.umd.js.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UserChip.vue_vue_type_script_setup_true_lang.mjs","names":[],"sources":["../../../src/components/auth/UserChip.vue"],"sourcesContent":["<template>\n <v-badge :content=\"items.length\" :model-value=\"hasNewItems\" color=\"accent\" location=\"end top\">\n <v-menu location=\"bottom end\" transition=\"scale-transition\">\n <template #activator=\"{ props }\">\n <v-chip v-bind=\"props\" class=\"ma-2\">\n <v-avatar start class=\"bg-primary\">\n <v-icon>mdi-school</v-icon>\n </v-avatar>\n {{ username }}\n </v-chip>\n </template>\n\n <v-list>\n <v-list-item v-for=\"item in items\" :key=\"item\" @click=\"dismiss(item)\">\n <v-list-item-title>{{ item }}</v-list-item-title>\n </v-list-item>\n\n <v-divider v-if=\"items.length\" />\n\n <v-list-item @click=\"gotoStats\">\n <template #prepend>\n <v-icon>mdi-trending-up</v-icon>\n </template>\n <v-list-item-title>Stats</v-list-item-title>\n </v-list-item>\n\n <v-list-item @click=\"gotoSettings\">\n <template #prepend>\n <v-icon>mdi-cog</v-icon>\n </template>\n <v-list-item-title>Settings</v-list-item-title>\n </v-list-item>\n\n <v-list-item v-if=\"authUIConfig.showLogout\" @click=\"logout\">\n <template #prepend>\n <v-icon>mdi-logout</v-icon>\n </template>\n <v-list-item-title>{{ authUIConfig.logoutLabel }}</v-list-item-title>\n </v-list-item>\n\n <v-list-item v-if=\"authUIConfig.showResetData\" @click=\"showResetDialog = true\">\n <template #prepend>\n <v-icon>mdi-delete-sweep</v-icon>\n </template>\n <v-list-item-title>{{ authUIConfig.resetLabel }}</v-list-item-title>\n </v-list-item>\n </v-list>\n </v-menu>\n </v-badge>\n\n <!-- Reset Confirmation Dialog -->\n <v-dialog v-model=\"showResetDialog\" max-width=\"500px\" persistent>\n <v-card>\n <v-card-title class=\"text-h5 d-flex align-center\">\n <v-icon color=\"warning\" class=\"mr-3\">mdi-alert-circle</v-icon>\n Reset All User Data\n </v-card-title>\n \n <v-card-text>\n <p class=\"mb-4\">This will permanently delete:</p>\n <ul class=\"mb-4\">\n <li>All course progress and history</li>\n <li>Scheduled card reviews</li>\n <li>Course registrations</li>\n <li>User preferences</li>\n </ul>\n <p class=\"mb-4 text-error font-weight-bold\">This cannot be undone.</p>\n \n <v-text-field\n v-model=\"confirmationText\"\n label='Type \"reset\" to confirm'\n outlined\n dense\n @keyup.enter=\"isConfirmationValid && executeReset()\"\n />\n </v-card-text>\n \n <v-card-actions>\n <v-spacer />\n <v-btn text @click=\"resetDialogState\">Cancel</v-btn>\n <v-btn \n color=\"error\" \n :disabled=\"!isConfirmationValid\"\n @click=\"executeReset\"\n >\n Reset All Data\n </v-btn>\n </v-card-actions>\n </v-card>\n </v-dialog>\n</template>\n\n<script lang=\"ts\" setup>\nimport { ref, computed, onMounted } from 'vue';\nimport { useRouter } from 'vue-router';\nimport { getCurrentUser, useAuthStore } from '../../stores/useAuthStore';\nimport { useConfigStore } from '../../stores/useConfigStore';\nimport { useAuthUI } from '../../composables/useAuthUI';\n\n// Define props (even if not used, prevents warnings)\ndefineProps<{\n showLoginButton?: boolean;\n redirectToPath?: string;\n}>();\n\nconst router = useRouter();\nconst authStore = useAuthStore();\nconst configStore = useConfigStore();\nconst authUI = useAuthUI();\n\nconst username = ref('');\nconst items = ref<string[]>([]);\n\n// Reset confirmation dialog state\nconst showResetDialog = ref(false);\nconst confirmationText = ref('');\n\nconst isConfirmationValid = computed(() => confirmationText.value === 'reset');\n\nconst resetDialogState = () => {\n confirmationText.value = '';\n showResetDialog.value = false;\n};\n\nconst hasNewItems = computed(() => items.value.length > 0);\n\nconst authUIConfig = computed(() => {\n const configValue = authUI.config.value;\n const fallback = {\n showLoginRegistration: true,\n showLogout: true,\n showResetData: false,\n logoutLabel: 'Log out',\n resetLabel: '',\n };\n \n return configValue || fallback;\n});\n\nonMounted(async () => {\n const user = await getCurrentUser();\n username.value = user.getUsername();\n \n // Detect sync strategy for conditional UI\n await authUI.detectSyncStrategy();\n});\n\nconst gotoSettings = async () => {\n router.push(`/u/${(await getCurrentUser()).getUsername()}`);\n};\n\nconst gotoStats = async () => {\n router.push(`/u/${(await getCurrentUser()).getUsername()}/stats`);\n};\n\nconst dismiss = (item: string) => {\n const index = items.value.indexOf(item);\n items.value.splice(index, 1);\n};\n\nconst logout = async () => {\n const res = await authStore._user!.logout();\n if (res.ok) {\n authStore.loginAndRegistration = {\n init: true,\n loggedIn: false,\n regDialogOpen: false,\n loginDialogOpen: false,\n };\n\n configStore.resetDefaults();\n router.push('/home');\n }\n};\n\nconst executeReset = async () => {\n try {\n await authStore.resetUserData();\n configStore.resetDefaults();\n \n // Close dialog and navigate to home\n resetDialogState();\n router.push('/home');\n } catch (error) {\n console.error('Failed to reset user data:', error);\n // Could add a snackbar notification here\n }\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>"],"mappings":";;;;;;;;;;;;;;EAyGA,IAAM,IAAS,GAAW,EACpB,IAAY,GAAc,EAC1B,IAAc,GAAgB,EAC9B,IAAS,GAAW,EAEpB,IAAW,EAAI,GAAG,EAClB,IAAQ,EAAc,EAAE,CAAC,EAGzB,IAAkB,EAAI,GAAM,EAC5B,IAAmB,EAAI,GAAG,EAE1B,IAAsB,QAAe,EAAiB,UAAU,QAAQ,EAExE,yBAAyB;AAE7B,GADA,EAAiB,QAAQ,IACzB,EAAgB,QAAQ;KAGpB,IAAc,QAAe,EAAM,MAAM,SAAS,EAAE,EAEpD,IAAe,QACC,EAAO,OAAO,SACjB;GACf,uBAAuB;GACvB,YAAY;GACZ,eAAe;GACf,aAAa;GACb,YAAY;GACb,CAGD;AAEF,IAAU,YAAY;AAKpB,GAHA,EAAS,SADI,MAAM,GAAgB,EACb,aAAa,EAGnC,MAAM,EAAO,oBAAoB;IACjC;EAEF,IAAM,eAAe,YAAY;AAC/B,KAAO,KAAK,OAAO,MAAM,GAAgB,EAAE,aAAa,GAAG;KAGvD,YAAY,YAAY;AAC5B,KAAO,KAAK,OAAO,MAAM,GAAgB,EAAE,aAAa,CAAC,QAAQ;KAG7D,WAAW,MAAiB;GAChC,IAAM,IAAQ,EAAM,MAAM,QAAQ,EAAK;AACvC,KAAM,MAAM,OAAO,GAAO,EAAE;KAGxB,SAAS,YAAY;AAEzB,IADY,MAAM,EAAU,MAAO,QAAQ,EACnC,OACN,EAAU,uBAAuB;IAC/B,MAAM;IACN,UAAU;IACV,eAAe;IACf,iBAAiB;IAClB,EAED,EAAY,eAAe,EAC3B,EAAO,KAAK,QAAQ;KAIlB,eAAe,YAAY;AAC/B,OAAI;AAMF,IALA,MAAM,EAAU,eAAe,EAC/B,EAAY,eAAe,EAG3B,kBAAkB,EAClB,EAAO,KAAK,QAAQ;YACb,GAAO;AACd,YAAQ,MAAM,8BAA8B,EAAM"}
@@ -0,0 +1,9 @@
1
+ import e from "../../_virtual/_plugin-vue_export-helper.mjs";
2
+ import t from "./UserLogin.vue_vue_type_script_setup_true_lang.mjs";
3
+ /* empty css */
4
+ //#region src/components/auth/UserLogin.vue
5
+ var n = /* @__PURE__ */ e(t, [["__scopeId", "data-v-764d1a76"]]);
6
+ //#endregion
7
+ export { n as default };
8
+
9
+ //# sourceMappingURL=UserLogin.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UserLogin.mjs","names":[],"sources":["../../../src/components/auth/UserLogin.vue"],"sourcesContent":["<template>\n <v-card>\n <v-card-title v-if=\"!loginRoute\" class=\"text-h5 bg-grey-lighten-2\">Log In</v-card-title>\n\n <v-card-text>\n <v-form onsubmit=\"return false;\" @submit.prevent=\"login\">\n <v-text-field\n id=\"\"\n v-model=\"identifier\"\n autofocus\n name=\"username\"\n label=\"Username or email\"\n prepend-icon=\"mdi-account-circle\"\n ></v-text-field>\n <v-text-field\n v-model=\"password\"\n prepend-icon=\"mdi-lock\"\n name=\"password\"\n hover=\"Show password input\"\n label=\"Enter your password\"\n hint=\"\"\n min=\"0\"\n :append-icon=\"passwordVisible ? 'mdi-eye-off' : 'mdi-eye'\"\n :type=\"passwordVisible ? 'text' : 'password'\"\n @click:append=\"() => (passwordVisible = !passwordVisible)\"\n ></v-text-field>\n\n <v-snackbar v-model=\"badLoginAttempt\" location=\"bottom right\" :timeout=\"errorTimeout\">\n {{ errorMessage }}\n <v-btn color=\"pink\" variant=\"text\" @click=\"badLoginAttempt = false\">Close</v-btn>\n </v-snackbar>\n\n <div class=\"d-flex flex-column align-start\">\n <div class=\"mb-2\">\n <v-btn class=\"mr-2\" type=\"submit\" :loading=\"awaitingResponse\" :color=\"buttonStatus.color\">\n <v-icon start>mdi-lock-open</v-icon>\n Log In\n </v-btn>\n <router-link v-if=\"loginRoute\" to=\"signup\">\n <v-btn variant=\"text\">Create New Account</v-btn>\n </router-link>\n <v-btn v-else variant=\"text\" @click=\"toggle\">Create New Account</v-btn>\n </div>\n\n <slot name=\"forgot-password\">\n <!-- Default: always show forgot password link -->\n <a\n href=\"#\"\n class=\"text-caption text-decoration-none\"\n @click.prevent=\"handleForgotPassword\"\n >\n Forgot password?\n </a>\n </slot>\n </div>\n </v-form>\n </v-card-text>\n </v-card>\n</template>\n\n<script lang=\"ts\" setup>\nimport { ref, computed } from 'vue';\nimport { useRouter, useRoute } from 'vue-router';\nimport { alertUser } from '../SnackbarService';\nimport { log } from '@vue-skuilder/common';\nimport { Status } from '@vue-skuilder/common';\nimport { User } from '@vue-skuilder/db';\nimport { getCurrentUser, useAuthStore } from '../../stores/useAuthStore';\nimport { useConfigStore } from '../../stores/useConfigStore';\nimport { resolveLoginIdentifier } from '../../services/authAPI';\n\n// Define props\ninterface Props {\n redirectTo?: string;\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n redirectTo: '/study'\n});\n\n// Define emits for toggle, cleanup, and forgot password\nconst emit = defineEmits<{\n toggle: [];\n loginSuccess: [redirectPath: string];\n forgotPassword: [];\n}>();\n\nconst router = useRouter();\nconst route = useRoute();\nconst authStore = useAuthStore();\nconst configStore = useConfigStore();\n\n// Holds a username OR an email address (see login()).\nconst identifier = ref('');\nconst password = ref('');\nconst passwordVisible = ref(false);\nconst awaitingResponse = ref(false);\nconst badLoginAttempt = ref(false);\nconst errorTimeout = ref(7000);\nconst errorMessage = ref('Username or password was incorrect.');\n\n// Shown when the email→username resolver is unreachable (route missing / 5xx /\n// network) rather than when credentials are actually wrong. Telling the two\n// apart matters: \"wrong password\" sends the user to re-check what is in fact\n// correct, and hides a real backend outage.\nconst BAD_CREDENTIALS_MSG = 'Username or password was incorrect.';\nconst LOGIN_UNAVAILABLE_MSG =\n 'Login is temporarily unavailable. Please try again in a moment.';\nconst user = ref<User | undefined>(undefined);\n\nconst loginRoute = computed(() => route.name === 'login');\n\nconst buttonStatus = computed(() => ({\n color: badLoginAttempt.value ? 'error' : 'success',\n text: badLoginAttempt.value ? 'Try again' : 'Log In',\n}));\n\nconst initBadLogin = (message: string = BAD_CREDENTIALS_MSG) => {\n errorMessage.value = message;\n badLoginAttempt.value = true;\n\n alertUser({\n text: message,\n status: Status.error,\n timeout: errorTimeout.value,\n });\n setTimeout(() => {\n badLoginAttempt.value = false;\n }, errorTimeout.value);\n};\n\nconst login = async () => {\n awaitingResponse.value = true;\n log('Starting login attempt');\n log(`Login attempt for identifier: ${identifier.value}`);\n\n try {\n log('Attempting to get User instance');\n // #172 starting point - why is the pre-existing _user being referenced here?\n user.value = await getCurrentUser();\n log('Got User instance, attempting login');\n\n // Couch _session authenticates by username only. When the identifier is an\n // email, resolve it to the account username first — server-side and gated\n // on the correct password, so it can't be used to enumerate accounts. A\n // plain username is passed straight through.\n let loginName = identifier.value;\n if (identifier.value.includes('@')) {\n const resolved = await resolveLoginIdentifier(identifier.value, password.value);\n if (!resolved.ok || !resolved.username) {\n // 401 is the server's uniform \"real auth failure\" code; anything else\n // (404 = route not deployed, 5xx, or no status = network) means the\n // resolver is unavailable, not that the credentials were wrong.\n if (resolved.status === 401) {\n log('Email identifier did not resolve: invalid credentials');\n initBadLogin();\n } else {\n log(`Email login unavailable (resolver error: ${resolved.error ?? 'network'})`);\n initBadLogin(LOGIN_UNAVAILABLE_MSG);\n }\n awaitingResponse.value = false;\n return;\n }\n loginName = resolved.username;\n }\n\n await user.value.login(loginName, password.value);\n log('Login successful');\n\n // load user config\n log('Initializing user config');\n configStore.init();\n log('User config initialized');\n\n // set login state\n log('Setting authentication state');\n authStore.loginAndRegistration.loggedIn = true;\n log(`Authentication state set, redirecting to: ${props.redirectTo}`);\n \n // Emit success event for cleanup\n emit('loginSuccess', props.redirectTo);\n \n router.push(props.redirectTo);\n log('Login and redirect complete');\n } catch (e) {\n // entry #186\n log('Login attempt failed');\n log(`Login error details: ${JSON.stringify(e)}`);\n console.log(`login error: ${JSON.stringify(e)}`);\n // - differentiate response\n // - return better message to UI\n log('Initiating bad login feedback');\n initBadLogin();\n }\n\n log('Resetting awaiting response state');\n awaitingResponse.value = false;\n};\n\n\nconst toggle = () => {\n log('Toggling registration / login forms.');\n emit('toggle');\n};\n\nconst handleForgotPassword = () => {\n log('Forgot password clicked');\n // If on login route, navigate directly\n if (loginRoute.value) {\n router.push('/request-reset');\n } else {\n // Otherwise, emit event for parent to handle (e.g., modal context)\n emit('forgotPassword');\n }\n};\n</script>\n\n<style lang=\"css\" scoped>\n.login {\n max-width: 650px;\n}\n</style>\n"],"mappings":""}
@@ -0,0 +1,155 @@
1
+ import "../../_virtual/_rolldown/runtime.mjs";
2
+ import { alertUser as e } from "../SnackbarService.mjs";
3
+ import { resolveLoginIdentifier as t } from "../../services/authAPI.mjs";
4
+ import { getCurrentUser as n, useAuthStore as r } from "../../stores/useAuthStore.mjs";
5
+ import { useConfigStore as i } from "../../stores/useConfigStore.mjs";
6
+ import { computed as a, createBlock as o, createCommentVNode as s, createElementVNode as c, createTextVNode as l, createVNode as u, defineComponent as d, openBlock as f, ref as p, renderSlot as m, resolveComponent as h, toDisplayString as g, withCtx as _, withModifiers as v } from "vue";
7
+ import { Status as y, log as b } from "@vue-skuilder/common";
8
+ import { useRoute as x, useRouter as S } from "vue-router";
9
+ //#region src/components/auth/UserLogin.vue?vue&type=script&setup=true&lang.ts
10
+ var C = { class: "d-flex flex-column align-start" }, w = { class: "mb-2" }, T = "Username or password was incorrect.", E = "Login is temporarily unavailable. Please try again in a moment.", D = /* @__PURE__ */ d({
11
+ __name: "UserLogin",
12
+ props: { redirectTo: { default: "/study" } },
13
+ emits: [
14
+ "toggle",
15
+ "loginSuccess",
16
+ "forgotPassword"
17
+ ],
18
+ setup(d, { emit: D }) {
19
+ let O = d, k = D, A = S(), j = x(), M = r(), N = i(), P = p(""), F = p(""), I = p(!1), L = p(!1), R = p(!1), z = p(7e3), B = p("Username or password was incorrect."), V = p(void 0), H = a(() => j.name === "login"), U = a(() => ({
20
+ color: R.value ? "error" : "success",
21
+ text: R.value ? "Try again" : "Log In"
22
+ })), initBadLogin = (t = T) => {
23
+ B.value = t, R.value = !0, e({
24
+ text: t,
25
+ status: y.error,
26
+ timeout: z.value
27
+ }), setTimeout(() => {
28
+ R.value = !1;
29
+ }, z.value);
30
+ }, login = async () => {
31
+ L.value = !0, b("Starting login attempt"), b(`Login attempt for identifier: ${P.value}`);
32
+ try {
33
+ b("Attempting to get User instance"), V.value = await n(), b("Got User instance, attempting login");
34
+ let e = P.value;
35
+ if (P.value.includes("@")) {
36
+ let n = await t(P.value, F.value);
37
+ if (!n.ok || !n.username) {
38
+ n.status === 401 ? (b("Email identifier did not resolve: invalid credentials"), initBadLogin()) : (b(`Email login unavailable (resolver error: ${n.error ?? "network"})`), initBadLogin(E)), L.value = !1;
39
+ return;
40
+ }
41
+ e = n.username;
42
+ }
43
+ await V.value.login(e, F.value), b("Login successful"), b("Initializing user config"), N.init(), b("User config initialized"), b("Setting authentication state"), M.loginAndRegistration.loggedIn = !0, b(`Authentication state set, redirecting to: ${O.redirectTo}`), k("loginSuccess", O.redirectTo), A.push(O.redirectTo), b("Login and redirect complete");
44
+ } catch (e) {
45
+ b("Login attempt failed"), b(`Login error details: ${JSON.stringify(e)}`), console.log(`login error: ${JSON.stringify(e)}`), b("Initiating bad login feedback"), initBadLogin();
46
+ }
47
+ b("Resetting awaiting response state"), L.value = !1;
48
+ }, toggle = () => {
49
+ b("Toggling registration / login forms."), k("toggle");
50
+ }, handleForgotPassword = () => {
51
+ b("Forgot password clicked"), H.value ? A.push("/request-reset") : k("forgotPassword");
52
+ };
53
+ return (e, t) => {
54
+ let n = h("v-card-title"), r = h("v-text-field"), i = h("v-btn"), a = h("v-snackbar"), d = h("v-icon"), p = h("router-link"), y = h("v-form"), b = h("v-card-text"), x = h("v-card");
55
+ return f(), o(x, null, {
56
+ default: _(() => [H.value ? s("", !0) : (f(), o(n, {
57
+ key: 0,
58
+ class: "text-h5 bg-grey-lighten-2"
59
+ }, {
60
+ default: _(() => t[5] || (t[5] = [l("Log In")])),
61
+ _: 1
62
+ })), u(b, null, {
63
+ default: _(() => [u(y, {
64
+ onsubmit: "return false;",
65
+ onSubmit: v(login, ["prevent"])
66
+ }, {
67
+ default: _(() => [
68
+ u(r, {
69
+ id: "",
70
+ modelValue: P.value,
71
+ "onUpdate:modelValue": t[0] || (t[0] = (e) => P.value = e),
72
+ autofocus: "",
73
+ name: "username",
74
+ label: "Username or email",
75
+ "prepend-icon": "mdi-account-circle"
76
+ }, null, 8, ["modelValue"]),
77
+ u(r, {
78
+ modelValue: F.value,
79
+ "onUpdate:modelValue": t[1] || (t[1] = (e) => F.value = e),
80
+ "prepend-icon": "mdi-lock",
81
+ name: "password",
82
+ hover: "Show password input",
83
+ label: "Enter your password",
84
+ hint: "",
85
+ min: "0",
86
+ "append-icon": I.value ? "mdi-eye-off" : "mdi-eye",
87
+ type: I.value ? "text" : "password",
88
+ "onClick:append": t[2] || (t[2] = () => I.value = !I.value)
89
+ }, null, 8, [
90
+ "modelValue",
91
+ "append-icon",
92
+ "type"
93
+ ]),
94
+ u(a, {
95
+ modelValue: R.value,
96
+ "onUpdate:modelValue": t[4] || (t[4] = (e) => R.value = e),
97
+ location: "bottom right",
98
+ timeout: z.value
99
+ }, {
100
+ default: _(() => [l(g(B.value) + " ", 1), u(i, {
101
+ color: "pink",
102
+ variant: "text",
103
+ onClick: t[3] || (t[3] = (e) => R.value = !1)
104
+ }, {
105
+ default: _(() => t[6] || (t[6] = [l("Close")])),
106
+ _: 1
107
+ })]),
108
+ _: 1
109
+ }, 8, ["modelValue", "timeout"]),
110
+ c("div", C, [c("div", w, [u(i, {
111
+ class: "mr-2",
112
+ type: "submit",
113
+ loading: L.value,
114
+ color: U.value.color
115
+ }, {
116
+ default: _(() => [u(d, { start: "" }, {
117
+ default: _(() => t[7] || (t[7] = [l("mdi-lock-open")])),
118
+ _: 1
119
+ }), t[8] || (t[8] = l(" Log In "))]),
120
+ _: 1
121
+ }, 8, ["loading", "color"]), H.value ? (f(), o(p, {
122
+ key: 0,
123
+ to: "signup"
124
+ }, {
125
+ default: _(() => [u(i, { variant: "text" }, {
126
+ default: _(() => t[9] || (t[9] = [l("Create New Account")])),
127
+ _: 1
128
+ })]),
129
+ _: 1
130
+ })) : (f(), o(i, {
131
+ key: 1,
132
+ variant: "text",
133
+ onClick: toggle
134
+ }, {
135
+ default: _(() => t[10] || (t[10] = [l("Create New Account")])),
136
+ _: 1
137
+ }))]), m(e.$slots, "forgot-password", {}, () => [c("a", {
138
+ href: "#",
139
+ class: "text-caption text-decoration-none",
140
+ onClick: v(handleForgotPassword, ["prevent"])
141
+ }, " Forgot password? ")], !0)])
142
+ ]),
143
+ _: 3
144
+ })]),
145
+ _: 3
146
+ })]),
147
+ _: 3
148
+ });
149
+ };
150
+ }
151
+ });
152
+ //#endregion
153
+ export { D as default };
154
+
155
+ //# sourceMappingURL=UserLogin.vue_vue_type_script_setup_true_lang.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UserLogin.vue_vue_type_script_setup_true_lang.mjs","names":[],"sources":["../../../src/components/auth/UserLogin.vue"],"sourcesContent":["<template>\n <v-card>\n <v-card-title v-if=\"!loginRoute\" class=\"text-h5 bg-grey-lighten-2\">Log In</v-card-title>\n\n <v-card-text>\n <v-form onsubmit=\"return false;\" @submit.prevent=\"login\">\n <v-text-field\n id=\"\"\n v-model=\"identifier\"\n autofocus\n name=\"username\"\n label=\"Username or email\"\n prepend-icon=\"mdi-account-circle\"\n ></v-text-field>\n <v-text-field\n v-model=\"password\"\n prepend-icon=\"mdi-lock\"\n name=\"password\"\n hover=\"Show password input\"\n label=\"Enter your password\"\n hint=\"\"\n min=\"0\"\n :append-icon=\"passwordVisible ? 'mdi-eye-off' : 'mdi-eye'\"\n :type=\"passwordVisible ? 'text' : 'password'\"\n @click:append=\"() => (passwordVisible = !passwordVisible)\"\n ></v-text-field>\n\n <v-snackbar v-model=\"badLoginAttempt\" location=\"bottom right\" :timeout=\"errorTimeout\">\n {{ errorMessage }}\n <v-btn color=\"pink\" variant=\"text\" @click=\"badLoginAttempt = false\">Close</v-btn>\n </v-snackbar>\n\n <div class=\"d-flex flex-column align-start\">\n <div class=\"mb-2\">\n <v-btn class=\"mr-2\" type=\"submit\" :loading=\"awaitingResponse\" :color=\"buttonStatus.color\">\n <v-icon start>mdi-lock-open</v-icon>\n Log In\n </v-btn>\n <router-link v-if=\"loginRoute\" to=\"signup\">\n <v-btn variant=\"text\">Create New Account</v-btn>\n </router-link>\n <v-btn v-else variant=\"text\" @click=\"toggle\">Create New Account</v-btn>\n </div>\n\n <slot name=\"forgot-password\">\n <!-- Default: always show forgot password link -->\n <a\n href=\"#\"\n class=\"text-caption text-decoration-none\"\n @click.prevent=\"handleForgotPassword\"\n >\n Forgot password?\n </a>\n </slot>\n </div>\n </v-form>\n </v-card-text>\n </v-card>\n</template>\n\n<script lang=\"ts\" setup>\nimport { ref, computed } from 'vue';\nimport { useRouter, useRoute } from 'vue-router';\nimport { alertUser } from '../SnackbarService';\nimport { log } from '@vue-skuilder/common';\nimport { Status } from '@vue-skuilder/common';\nimport { User } from '@vue-skuilder/db';\nimport { getCurrentUser, useAuthStore } from '../../stores/useAuthStore';\nimport { useConfigStore } from '../../stores/useConfigStore';\nimport { resolveLoginIdentifier } from '../../services/authAPI';\n\n// Define props\ninterface Props {\n redirectTo?: string;\n}\n\nconst props = withDefaults(defineProps<Props>(), {\n redirectTo: '/study'\n});\n\n// Define emits for toggle, cleanup, and forgot password\nconst emit = defineEmits<{\n toggle: [];\n loginSuccess: [redirectPath: string];\n forgotPassword: [];\n}>();\n\nconst router = useRouter();\nconst route = useRoute();\nconst authStore = useAuthStore();\nconst configStore = useConfigStore();\n\n// Holds a username OR an email address (see login()).\nconst identifier = ref('');\nconst password = ref('');\nconst passwordVisible = ref(false);\nconst awaitingResponse = ref(false);\nconst badLoginAttempt = ref(false);\nconst errorTimeout = ref(7000);\nconst errorMessage = ref('Username or password was incorrect.');\n\n// Shown when the email→username resolver is unreachable (route missing / 5xx /\n// network) rather than when credentials are actually wrong. Telling the two\n// apart matters: \"wrong password\" sends the user to re-check what is in fact\n// correct, and hides a real backend outage.\nconst BAD_CREDENTIALS_MSG = 'Username or password was incorrect.';\nconst LOGIN_UNAVAILABLE_MSG =\n 'Login is temporarily unavailable. Please try again in a moment.';\nconst user = ref<User | undefined>(undefined);\n\nconst loginRoute = computed(() => route.name === 'login');\n\nconst buttonStatus = computed(() => ({\n color: badLoginAttempt.value ? 'error' : 'success',\n text: badLoginAttempt.value ? 'Try again' : 'Log In',\n}));\n\nconst initBadLogin = (message: string = BAD_CREDENTIALS_MSG) => {\n errorMessage.value = message;\n badLoginAttempt.value = true;\n\n alertUser({\n text: message,\n status: Status.error,\n timeout: errorTimeout.value,\n });\n setTimeout(() => {\n badLoginAttempt.value = false;\n }, errorTimeout.value);\n};\n\nconst login = async () => {\n awaitingResponse.value = true;\n log('Starting login attempt');\n log(`Login attempt for identifier: ${identifier.value}`);\n\n try {\n log('Attempting to get User instance');\n // #172 starting point - why is the pre-existing _user being referenced here?\n user.value = await getCurrentUser();\n log('Got User instance, attempting login');\n\n // Couch _session authenticates by username only. When the identifier is an\n // email, resolve it to the account username first — server-side and gated\n // on the correct password, so it can't be used to enumerate accounts. A\n // plain username is passed straight through.\n let loginName = identifier.value;\n if (identifier.value.includes('@')) {\n const resolved = await resolveLoginIdentifier(identifier.value, password.value);\n if (!resolved.ok || !resolved.username) {\n // 401 is the server's uniform \"real auth failure\" code; anything else\n // (404 = route not deployed, 5xx, or no status = network) means the\n // resolver is unavailable, not that the credentials were wrong.\n if (resolved.status === 401) {\n log('Email identifier did not resolve: invalid credentials');\n initBadLogin();\n } else {\n log(`Email login unavailable (resolver error: ${resolved.error ?? 'network'})`);\n initBadLogin(LOGIN_UNAVAILABLE_MSG);\n }\n awaitingResponse.value = false;\n return;\n }\n loginName = resolved.username;\n }\n\n await user.value.login(loginName, password.value);\n log('Login successful');\n\n // load user config\n log('Initializing user config');\n configStore.init();\n log('User config initialized');\n\n // set login state\n log('Setting authentication state');\n authStore.loginAndRegistration.loggedIn = true;\n log(`Authentication state set, redirecting to: ${props.redirectTo}`);\n \n // Emit success event for cleanup\n emit('loginSuccess', props.redirectTo);\n \n router.push(props.redirectTo);\n log('Login and redirect complete');\n } catch (e) {\n // entry #186\n log('Login attempt failed');\n log(`Login error details: ${JSON.stringify(e)}`);\n console.log(`login error: ${JSON.stringify(e)}`);\n // - differentiate response\n // - return better message to UI\n log('Initiating bad login feedback');\n initBadLogin();\n }\n\n log('Resetting awaiting response state');\n awaitingResponse.value = false;\n};\n\n\nconst toggle = () => {\n log('Toggling registration / login forms.');\n emit('toggle');\n};\n\nconst handleForgotPassword = () => {\n log('Forgot password clicked');\n // If on login route, navigate directly\n if (loginRoute.value) {\n router.push('/request-reset');\n } else {\n // Otherwise, emit event for parent to handle (e.g., modal context)\n emit('forgotPassword');\n }\n};\n</script>\n\n<style lang=\"css\" scoped>\n.login {\n max-width: 650px;\n}\n</style>\n"],"mappings":";;;;;;;;;4EAyGM,IAAsB,uCACtB,IACJ;;;;;;;;;EA/BF,IAAM,IAAQ,GAKR,IAAO,GAMP,IAAS,GAAW,EACpB,IAAQ,GAAU,EAClB,IAAY,GAAc,EAC1B,IAAc,GAAgB,EAG9B,IAAa,EAAI,GAAG,EACpB,IAAW,EAAI,GAAG,EAClB,IAAkB,EAAI,GAAM,EAC5B,IAAmB,EAAI,GAAM,EAC7B,IAAkB,EAAI,GAAM,EAC5B,IAAe,EAAI,IAAK,EACxB,IAAe,EAAI,sCAAsC,EASzD,IAAO,EAAsB,KAAA,EAAU,EAEvC,IAAa,QAAe,EAAM,SAAS,QAAQ,EAEnD,IAAe,SAAgB;GACnC,OAAO,EAAgB,QAAQ,UAAU;GACzC,MAAM,EAAgB,QAAQ,cAAc;GAC7C,EAAE,EAEG,gBAAgB,IAAkB,MAAwB;AAS9D,GARA,EAAa,QAAQ,GACrB,EAAgB,QAAQ,IAExB,EAAU;IACR,MAAM;IACN,QAAQ,EAAO;IACf,SAAS,EAAa;IACvB,CAAC,EACF,iBAAiB;AACf,MAAgB,QAAQ;MACvB,EAAa,MAAM;KAGlB,QAAQ,YAAY;AAGxB,GAFA,EAAiB,QAAQ,IACzB,EAAI,yBAAyB,EAC7B,EAAI,iCAAiC,EAAW,QAAQ;AAExD,OAAI;AAIF,IAHA,EAAI,kCAAkC,EAEtC,EAAK,QAAQ,MAAM,GAAgB,EACnC,EAAI,sCAAsC;IAM1C,IAAI,IAAY,EAAW;AAC3B,QAAI,EAAW,MAAM,SAAS,IAAI,EAAE;KAClC,IAAM,IAAW,MAAM,EAAuB,EAAW,OAAO,EAAS,MAAM;AAC/E,SAAI,CAAC,EAAS,MAAM,CAAC,EAAS,UAAU;AAWtC,MAPI,EAAS,WAAW,OACtB,EAAI,wDAAwD,EAC5D,cAAc,KAEd,EAAI,4CAA4C,EAAS,SAAS,UAAU,GAAG,EAC/E,aAAa,EAAsB,GAErC,EAAiB,QAAQ;AACzB;;AAEF,SAAY,EAAS;;AAoBvB,IAjBA,MAAM,EAAK,MAAM,MAAM,GAAW,EAAS,MAAM,EACjD,EAAI,mBAAmB,EAGvB,EAAI,2BAA2B,EAC/B,EAAY,MAAM,EAClB,EAAI,0BAA0B,EAG9B,EAAI,+BAA+B,EACnC,EAAU,qBAAqB,WAAW,IAC1C,EAAI,6CAA6C,EAAM,aAAa,EAGpE,EAAK,gBAAgB,EAAM,WAAW,EAEtC,EAAO,KAAK,EAAM,WAAW,EAC7B,EAAI,8BAA8B;YAC3B,GAAG;AAQV,IANA,EAAI,uBAAuB,EAC3B,EAAI,wBAAwB,KAAK,UAAU,EAAE,GAAG,EAChD,QAAQ,IAAI,gBAAgB,KAAK,UAAU,EAAE,GAAG,EAGhD,EAAI,gCAAgC,EACpC,cAAc;;AAIhB,GADA,EAAI,oCAAoC,EACxC,EAAiB,QAAQ;KAIrB,eAAe;AAEnB,GADA,EAAI,uCAAuC,EAC3C,EAAK,SAAS;KAGV,6BAA6B;AAGjC,GAFA,EAAI,0BAA0B,EAE1B,EAAW,QACb,EAAO,KAAK,iBAAiB,GAG7B,EAAK,iBAAiB"}
@@ -0,0 +1,9 @@
1
+ import e from "../../_virtual/_plugin-vue_export-helper.mjs";
2
+ import t from "./UserLoginAndRegistrationContainer.vue_vue_type_script_setup_true_lang.mjs";
3
+ /* empty css */
4
+ //#region src/components/auth/UserLoginAndRegistrationContainer.vue
5
+ var n = /* @__PURE__ */ e(t, [["__scopeId", "data-v-5a55bdf0"]]);
6
+ //#endregion
7
+ export { n as default };
8
+
9
+ //# sourceMappingURL=UserLoginAndRegistrationContainer.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UserLoginAndRegistrationContainer.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":""}
@@ -0,0 +1,126 @@
1
+ import "../../_virtual/_rolldown/runtime.mjs";
2
+ import { useAuthStore as e } from "../../stores/useAuthStore.mjs";
3
+ import { useAuthUI as t } from "../../composables/useAuthUI.mjs";
4
+ import n from "./UserChip.mjs";
5
+ import r from "./UserLogin.mjs";
6
+ import i from "./UserRegistration.mjs";
7
+ import a from "./RequestPasswordReset.mjs";
8
+ import { Transition as o, computed as s, createBlock as c, createCommentVNode as l, createElementBlock as u, createTextVNode as d, createVNode as f, defineComponent as p, mergeProps as m, onMounted as h, openBlock as g, ref as _, resolveComponent as v, withCtx as y } from "vue";
9
+ import { GuestUsername as b } from "@vue-skuilder/db";
10
+ import { useRoute as x } from "vue-router";
11
+ //#region src/components/auth/UserLoginAndRegistrationContainer.vue?vue&type=script&setup=true&lang.ts
12
+ var S = { key: 0 }, C = { key: "login-buttons" }, w = { key: "user-chip" }, T = /* @__PURE__ */ p({
13
+ __name: "UserLoginAndRegistrationContainer",
14
+ props: {
15
+ showLoginButton: { type: Boolean },
16
+ redirectToPath: {},
17
+ showRegistration: { type: Boolean },
18
+ onSignupSuccess: { type: Function }
19
+ },
20
+ emits: ["signup-success"],
21
+ setup(p, { emit: T }) {
22
+ let E = p, D = T, O = x(), k = e(), A = t();
23
+ h(async () => {
24
+ await A.detectSyncStrategy();
25
+ });
26
+ let j = s(() => {
27
+ if (!O.name || typeof O.name != "string") return !0;
28
+ let e = O.name.toLowerCase();
29
+ return !(e === "login" || e === "signup");
30
+ }), M = s(() => k.onLoadComplete), N = s(() => k._user ? k._user.getUsername().startsWith(b) : !k.loginAndRegistration.loggedIn), P = s(() => ({
31
+ ...A.config.value || {
32
+ showLoginRegistration: !0,
33
+ showLogout: !0,
34
+ showResetData: !1,
35
+ logoutLabel: "Log out",
36
+ resetLabel: ""
37
+ },
38
+ ...E.showRegistration !== void 0 && { showLoginRegistration: E.showRegistration }
39
+ })), F = s({
40
+ get: () => k.loginAndRegistration.regDialogOpen,
41
+ set: (e) => {
42
+ k.loginAndRegistration.regDialogOpen = e;
43
+ }
44
+ }), I = s({
45
+ get: () => k.loginAndRegistration.loginDialogOpen,
46
+ set: (e) => {
47
+ k.loginAndRegistration.loginDialogOpen = e;
48
+ }
49
+ }), L = _(!1), toggle = () => {
50
+ if (F.value && I.value) throw Error("Registration / Login dialogs both activated.");
51
+ if (F.value === I.value) throw Error("Registration / Login dialogs toggled while both were dormant.");
52
+ F.value = !F.value, I.value = !I.value;
53
+ }, openResetDialog = () => {
54
+ I.value = !1, L.value = !0;
55
+ }, closeResetDialog = () => {
56
+ L.value = !1;
57
+ }, handleSignupSuccess = (e) => {
58
+ F.value = !1, E.onSignupSuccess && E.onSignupSuccess(e), D("signup-success", e);
59
+ };
60
+ return (e, t) => {
61
+ let s = v("v-btn"), p = v("v-dialog");
62
+ return M.value && j.value ? (g(), u("div", S, [f(o, {
63
+ name: "component-fade",
64
+ mode: "out-in"
65
+ }, {
66
+ default: y(() => [N.value ? (g(), u("div", C, [
67
+ P.value.showLoginRegistration ? (g(), c(p, {
68
+ key: 0,
69
+ modelValue: F.value,
70
+ "onUpdate:modelValue": t[0] || (t[0] = (e) => F.value = e),
71
+ width: "500px"
72
+ }, {
73
+ activator: y(({ props: e }) => [f(s, m({
74
+ class: "mr-2",
75
+ size: "small",
76
+ color: "success"
77
+ }, e), {
78
+ default: y(() => t[3] || (t[3] = [d("Sign Up")])),
79
+ _: 2
80
+ }, 1040)]),
81
+ default: y(() => [f(i, {
82
+ onToggle: toggle,
83
+ onSignupSuccess: handleSignupSuccess,
84
+ "on-signup-success": handleSignupSuccess
85
+ })]),
86
+ _: 1
87
+ }, 8, ["modelValue"])) : l("", !0),
88
+ f(p, {
89
+ modelValue: I.value,
90
+ "onUpdate:modelValue": t[1] || (t[1] = (e) => I.value = e),
91
+ width: "500px"
92
+ }, {
93
+ activator: y(({ props: e }) => [f(s, m({
94
+ size: "small",
95
+ color: "success"
96
+ }, e), {
97
+ default: y(() => t[4] || (t[4] = [d("Log In")])),
98
+ _: 2
99
+ }, 1040)]),
100
+ default: y(() => [f(r, {
101
+ onToggle: toggle,
102
+ onForgotPassword: openResetDialog
103
+ })]),
104
+ _: 1
105
+ }, 8, ["modelValue"]),
106
+ f(p, {
107
+ modelValue: L.value,
108
+ "onUpdate:modelValue": t[2] || (t[2] = (e) => L.value = e),
109
+ width: "500px"
110
+ }, {
111
+ default: y(() => [f(a, {
112
+ onCancel: closeResetDialog,
113
+ onSuccess: closeResetDialog
114
+ })]),
115
+ _: 1
116
+ }, 8, ["modelValue"])
117
+ ])) : (g(), u("div", w, [f(n)]))]),
118
+ _: 1
119
+ })])) : l("", !0);
120
+ };
121
+ }
122
+ });
123
+ //#endregion
124
+ export { T as default };
125
+
126
+ //# sourceMappingURL=UserLoginAndRegistrationContainer.vue_vue_type_script_setup_true_lang.mjs.map
@@ -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":";;;;;;;aACE,EA2ES,GAAA,MAAA;EA5EX,SAAA,QAEsF,CAAlF,EAAkF,GAAA,EAApE,OAAM,6BAA2B,EAAA;GAFnD,SAAA,QAEuE,EAAA,OAAA,EAAA,KAAA,CAFvE,EAEoD,sBAAmB,CAAA,EAAA;GAFvE,GAAA;MAII,EAuEc,GAAA,MAAA;GA3ElB,SAAA,QA0Ee,CArET,EAqES,GAAA,EArEA,UALf,EAK+B,EAAA,YAAU,CAAA,UAAA,CAAA,EAAA,EAAA;IALzC,SAAA,QAewB;KAThB,EASgB,GAAA;MAfxB,YAOmB,EAAA;MAPnB,uBAAA,EAAA,OAAA,EAAA,MAAA,MAAA,EAOmB,QAAK;MACd,MAAK;MACL,OAAM;MACN,MAAK;MACL,gBAAa;MACZ,OAAO,EAAA;MACP,MAAM,EAAA;MACN,QAAM,EAAA;;;;;;;KAET,EAUgB,GAAA;MATd,IAAG;MACH,KAAI;MAlBd,YAmBmB,EAAA;MAnBnB,uBAAA,EAAA,OAAA,EAAA,MAAA,MAAA,EAmBmB,WAAQ;MACjB,MAAK;MACL,OAAM;MACN,gBAAa;MACZ,OAAO,EAAA;MACP,MAAM,EAAA;MACN,QAAM,EAAA;;;;;;;KAET,EAYgB,GAAA;MAvCxB,YA4BmB,EAAA;MA5BnB,uBAAA,EAAA,OAAA,EAAA,MAAA,MAAA,EA4BmB,WAAQ;MACjB,gBAAa;MACb,MAAK;MACL,OAAM;MACN,OAAM;MACL,MAAM,EAAA;MACN,OAAK,CAAA,CAAI,EAAA;MACV,KAAI;MACH,eAAa,EAAA,kBAAe,gBAAA;MAC5B,MAAM,EAAA,kBAAe,SAAA;MACrB,kBAAY,EAAA,OAAA,EAAA,WAAS,EAAA,kBAAe,CAAI,EAAA;;;;;;;;KAE3C,EAUgB,GAAA;MAlDxB,YAyCmB,EAAA;MAzCnB,uBAAA,EAAA,OAAA,EAAA,MAAA,MAAA,EAyCmB,kBAAe;MACxB,gBAAa;MACb,MAAK;MACL,OAAM;MACN,OAAM;MACL,UAAU,EAAA,aAAQ,MAAA,CAAA,CAAa,EAAA;MAC/B,MAAM,EAAA;MACP,KAAI;MACH,MAAM,EAAA,kBAAe,SAAA;;;;;;;KAOxB,EAGa,GAAA;MA3DrB,YAwD6B,EAAA;MAxD7B,uBAAA,EAAA,OAAA,EAAA,MAAA,MAAA,EAwD6B,kBAAe;MAAE,UAAS;MAAgB,SAAS;;MAxDhF,SAAA,QA0DU,CAAA,EAAA,OAAA,EAAA,KA1DV,EAwDsF,wCAE5E,GAAA,EAAmF,GAAA;OAA5E,OAAM;OAAO,SAAQ;OAAQ,SAAK,EAAA,OAAA,EAAA,MAAA,MAAE,EAAA,kBAAe;;OA1DpE,SAAA,QA0DqF,EAAA,OAAA,EAAA,KAAA,CA1DrF,EA0D8E,UAAO,CAAA,EAAA;OA1DrF,GAAA;;MAAA,GAAA;;KA4DQ,EASQ,GAAA;MARN,OAAM;MACN,MAAK;MACJ,SAAS,EAAA;MACT,OAAO,EAAA,aAAa;MACpB,UAAQ,CAAA,CAAI,EAAA,iBAAiB,EAAA,aAAa,EAAA;;MAjErD,SAAA,QAmE8C,CAApC,EAAoC,GAAA,EAA5B,OAAA,IAAK,EAAA;OAnEvB,SAAA,QAmEqC,EAAA,QAAA,EAAA,MAAA,CAnErC,EAmEwB,gBAAa,CAAA,EAAA;OAnErC,GAAA;4BAAA,EAmE8C,mBAEtC,EAAA,CAAA;MArER,GAAA;;;;;;KAsE2B,EAAA,qBAAA,GAAA,EAAnB,EAEc,GAAA;MAxEtB,KAAA;MAsE8C,IAAG;;MAtEjD,SAAA,QAuE8C,CAApC,EAAoC,GAAA,EAA7B,SAAQ,QAAM,EAAA;OAvE/B,SAAA,QAuEsC,EAAA,QAAA,EAAA,MAAA,CAvEtC,EAuEgC,SAAM,CAAA,EAAA;OAvEtC,GAAA;;MAAA,GAAA;iBAyEQ,EAA6D,GAAA;MAzErE,KAAA;MAyEsB,SAAQ;MAAQ,SAAO,EAAA;;MAzE7C,SAAA,QAyE6D,EAAA,QAAA,EAAA,MAAA,CAzE7D,EAyEqD,WAAQ,CAAA,EAAA;MAzE7D,GAAA;;;IAAA,GAAA;;GAAA,GAAA;;EAAA,GAAA"}