@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.
- package/dist/_virtual/_@oxc-project_runtime@0.115.0/helpers/defineProperty.mjs +13 -0
- package/dist/_virtual/_@oxc-project_runtime@0.115.0/helpers/toPrimitive.mjs +15 -0
- package/dist/_virtual/_@oxc-project_runtime@0.115.0/helpers/toPropertyKey.mjs +10 -0
- package/dist/_virtual/_@oxc-project_runtime@0.115.0/helpers/typeof.mjs +12 -0
- package/dist/_virtual/_plugin-vue_export-helper.mjs +9 -0
- package/dist/_virtual/_rolldown/runtime.mjs +7 -0
- package/dist/admin/common-ui-admin.es.js +259 -23774
- package/dist/admin/common-ui-admin.es.js.map +1 -1
- package/dist/assets/index.css +2 -1
- package/dist/components/CardBrowser.mjs +63 -0
- package/dist/components/CardBrowser.mjs.map +1 -0
- package/dist/components/CardBrowser.vue_vue_type_script_lang.mjs +50 -0
- package/dist/components/CardBrowser.vue_vue_type_script_lang.mjs.map +1 -0
- package/dist/components/CardHistoryViewer.mjs +70 -0
- package/dist/components/CardHistoryViewer.mjs.map +1 -0
- package/dist/components/CardHistoryViewer.vue_vue_type_script_lang.mjs +108 -0
- package/dist/components/CardHistoryViewer.vue_vue_type_script_lang.mjs.map +1 -0
- package/dist/components/CardSearch.mjs +26 -0
- package/dist/components/CardSearch.mjs.map +1 -0
- package/dist/components/CardSearch.vue_vue_type_script_lang.mjs +17 -0
- package/dist/components/CardSearch.vue_vue_type_script_lang.mjs.map +1 -0
- package/dist/components/CardSearchResults.mjs +32 -0
- package/dist/components/CardSearchResults.mjs.map +1 -0
- package/dist/components/CardSearchResults.vue_vue_type_script_lang.mjs +71 -0
- package/dist/components/CardSearchResults.vue_vue_type_script_lang.mjs.map +1 -0
- package/dist/components/CourseCardBrowser.mjs +175 -0
- package/dist/components/CourseCardBrowser.mjs.map +1 -0
- package/dist/components/CourseCardBrowser.vue_vue_type_script_lang.mjs +173 -0
- package/dist/components/CourseCardBrowser.vue_vue_type_script_lang.mjs.map +1 -0
- package/dist/components/CourseInformation.mjs +132 -0
- package/dist/components/CourseInformation.mjs.map +1 -0
- package/dist/components/CourseInformation.vue_vue_type_script_lang.mjs +56 -0
- package/dist/components/CourseInformation.vue_vue_type_script_lang.mjs.map +1 -0
- package/dist/components/CourseTagFilterWidget.mjs +114 -0
- package/dist/components/CourseTagFilterWidget.mjs.map +1 -0
- package/dist/components/CourseTagFilterWidget.vue_vue_type_script_lang.mjs +65 -0
- package/dist/components/CourseTagFilterWidget.vue_vue_type_script_lang.mjs.map +1 -0
- package/dist/components/HeatMap.mjs +40 -0
- package/dist/components/HeatMap.mjs.map +1 -0
- package/dist/components/HeatMap.vue_vue_type_script_lang.mjs +211 -0
- package/dist/components/HeatMap.vue_vue_type_script_lang.mjs.map +1 -0
- package/dist/components/PaginatingToolbar.mjs +94 -0
- package/dist/components/PaginatingToolbar.mjs.map +1 -0
- package/dist/components/PaginatingToolbar.vue_vue_type_script_lang.mjs +36 -0
- package/dist/components/PaginatingToolbar.vue_vue_type_script_lang.mjs.map +1 -0
- package/dist/components/SessionControllerDebug.mjs +130 -0
- package/dist/components/SessionControllerDebug.mjs.map +1 -0
- package/dist/components/SessionControllerDebug.vue_vue_type_script_lang.mjs +45 -0
- package/dist/components/SessionControllerDebug.vue_vue_type_script_lang.mjs.map +1 -0
- package/dist/components/SkMouseTrap.mjs +66 -0
- package/dist/components/SkMouseTrap.mjs.map +1 -0
- package/dist/components/SkMouseTrap.vue_vue_type_script_lang.mjs +31 -0
- package/dist/components/SkMouseTrap.vue_vue_type_script_lang.mjs.map +1 -0
- package/dist/components/SkMouseTrapToolTip.mjs +28 -0
- package/dist/components/SkMouseTrapToolTip.mjs.map +1 -0
- package/dist/components/SkMouseTrapToolTip.vue_vue_type_script_lang.mjs +91 -0
- package/dist/components/SkMouseTrapToolTip.vue_vue_type_script_lang.mjs.map +1 -0
- package/dist/components/SnackbarService.mjs +24 -0
- package/dist/components/SnackbarService.mjs.map +1 -0
- package/dist/components/SnackbarService.vue_vue_type_script_lang.mjs +34 -0
- package/dist/components/SnackbarService.vue_vue_type_script_lang.mjs.map +1 -0
- package/dist/components/SnackbarService2.mjs +40 -0
- package/dist/components/SnackbarService2.mjs.map +1 -0
- package/dist/components/StudySession.mjs +123 -0
- package/dist/components/StudySession.mjs.map +1 -0
- package/dist/components/StudySession.vue_vue_type_script_lang.mjs +280 -0
- package/dist/components/StudySession.vue_vue_type_script_lang.mjs.map +1 -0
- package/dist/components/StudySessionTimer.mjs +50 -0
- package/dist/components/StudySessionTimer.mjs.map +1 -0
- package/dist/components/StudySessionTimer.vue_vue_type_script_lang.mjs +43 -0
- package/dist/components/StudySessionTimer.vue_vue_type_script_lang.mjs.map +1 -0
- package/dist/components/TagsInput.mjs +45 -0
- package/dist/components/TagsInput.mjs.map +1 -0
- package/dist/components/TagsInput.vue_vue_type_script_lang.mjs +117 -0
- package/dist/components/TagsInput.vue_vue_type_script_lang.mjs.map +1 -0
- package/dist/components/UserTagPreferences.mjs +205 -0
- package/dist/components/UserTagPreferences.mjs.map +1 -0
- package/dist/components/UserTagPreferences.vue_vue_type_script_lang.mjs +139 -0
- package/dist/components/UserTagPreferences.vue_vue_type_script_lang.mjs.map +1 -0
- package/dist/components/auth/RequestPasswordReset.mjs +96 -0
- package/dist/components/auth/RequestPasswordReset.mjs.map +1 -0
- package/dist/components/auth/RequestPasswordReset.vue_vue_type_script_lang.mjs +47 -0
- package/dist/components/auth/RequestPasswordReset.vue_vue_type_script_lang.mjs.map +1 -0
- package/dist/components/auth/ResetPassword.mjs +129 -0
- package/dist/components/auth/ResetPassword.mjs.map +1 -0
- package/dist/components/auth/ResetPassword.vue_vue_type_script_lang.mjs +69 -0
- package/dist/components/auth/ResetPassword.vue_vue_type_script_lang.mjs.map +1 -0
- package/dist/components/auth/UserChip.mjs +9 -0
- package/dist/components/auth/UserChip.mjs.map +1 -0
- package/dist/components/auth/UserChip.vue_vue_type_script_setup_true_lang.mjs +213 -0
- package/dist/components/auth/UserChip.vue_vue_type_script_setup_true_lang.mjs.map +1 -0
- package/dist/components/auth/UserLogin.mjs +9 -0
- package/dist/components/auth/UserLogin.mjs.map +1 -0
- package/dist/components/auth/UserLogin.vue_vue_type_script_setup_true_lang.mjs +155 -0
- package/dist/components/auth/UserLogin.vue_vue_type_script_setup_true_lang.mjs.map +1 -0
- package/dist/components/auth/UserLoginAndRegistrationContainer.mjs +9 -0
- package/dist/components/auth/UserLoginAndRegistrationContainer.mjs.map +1 -0
- package/dist/components/auth/UserLoginAndRegistrationContainer.vue_vue_type_script_setup_true_lang.mjs +126 -0
- package/dist/components/auth/UserLoginAndRegistrationContainer.vue_vue_type_script_setup_true_lang.mjs.map +1 -0
- package/dist/components/auth/UserRegistration.mjs +147 -0
- package/dist/components/auth/UserRegistration.mjs.map +1 -0
- package/dist/components/auth/UserRegistration.vue_vue_type_script_lang.mjs +130 -0
- package/dist/components/auth/UserRegistration.vue_vue_type_script_lang.mjs.map +1 -0
- package/dist/components/auth/VerifyEmail.mjs +118 -0
- package/dist/components/auth/VerifyEmail.mjs.map +1 -0
- package/dist/components/auth/VerifyEmail.vue_vue_type_script_lang.mjs +59 -0
- package/dist/components/auth/VerifyEmail.vue_vue_type_script_lang.mjs.map +1 -0
- package/dist/components/cardRendering/AudioAutoPlayer.mjs +9 -0
- package/dist/components/cardRendering/AudioAutoPlayer.mjs.map +1 -0
- package/dist/components/cardRendering/AudioAutoPlayer.vue_vue_type_script_setup_true_lang.mjs +69 -0
- package/dist/components/cardRendering/AudioAutoPlayer.vue_vue_type_script_setup_true_lang.mjs.map +1 -0
- package/dist/components/cardRendering/CardLoader.mjs +31 -0
- package/dist/components/cardRendering/CardLoader.mjs.map +1 -0
- package/dist/components/cardRendering/CardLoader.vue_vue_type_script_lang.mjs +75 -0
- package/dist/components/cardRendering/CardLoader.vue_vue_type_script_lang.mjs.map +1 -0
- package/dist/components/cardRendering/CardViewer.mjs +39 -0
- package/dist/components/cardRendering/CardViewer.mjs.map +1 -0
- package/dist/components/cardRendering/CardViewer.vue_vue_type_script_lang.mjs +66 -0
- package/dist/components/cardRendering/CardViewer.vue_vue_type_script_lang.mjs.map +1 -0
- package/dist/components/cardRendering/CodeBlockRenderer.mjs +8 -0
- package/dist/components/cardRendering/CodeBlockRenderer.mjs.map +1 -0
- package/dist/components/cardRendering/CodeBlockRenderer.vue_vue_type_script_setup_true_lang.mjs +44 -0
- package/dist/components/cardRendering/CodeBlockRenderer.vue_vue_type_script_setup_true_lang.mjs.map +1 -0
- package/dist/components/cardRendering/MarkdownRenderer.mjs +21 -0
- package/dist/components/cardRendering/MarkdownRenderer.mjs.map +1 -0
- package/dist/components/cardRendering/MarkdownRenderer.vue_vue_type_script_lang.mjs +24 -0
- package/dist/components/cardRendering/MarkdownRenderer.vue_vue_type_script_lang.mjs.map +1 -0
- package/dist/components/cardRendering/MarkdownRendererHelpers.mjs +79 -0
- package/dist/components/cardRendering/MarkdownRendererHelpers.mjs.map +1 -0
- package/dist/components/cardRendering/MdTokenRenderer.mjs +9 -0
- package/dist/components/cardRendering/MdTokenRenderer.mjs.map +1 -0
- package/dist/components/cardRendering/MdTokenRenderer.vue_vue_type_script_setup_true_lang.mjs +176 -0
- package/dist/components/cardRendering/MdTokenRenderer.vue_vue_type_script_setup_true_lang.mjs.map +1 -0
- package/dist/components/studentInputs/BaseUserInput.mjs +54 -0
- package/dist/components/studentInputs/BaseUserInput.mjs.map +1 -0
- package/dist/components/studentInputs/MultipleChoiceOption.mjs +26 -0
- package/dist/components/studentInputs/MultipleChoiceOption.mjs.map +1 -0
- package/dist/components/studentInputs/MultipleChoiceOption.vue_vue_type_script_lang.mjs +76 -0
- package/dist/components/studentInputs/MultipleChoiceOption.vue_vue_type_script_lang.mjs.map +1 -0
- package/dist/components/studentInputs/RadioMultipleChoice.mjs +33 -0
- package/dist/components/studentInputs/RadioMultipleChoice.mjs.map +1 -0
- package/dist/components/studentInputs/RadioMultipleChoice.vue_vue_type_script_lang.mjs +105 -0
- package/dist/components/studentInputs/RadioMultipleChoice.vue_vue_type_script_lang.mjs.map +1 -0
- package/dist/components/studentInputs/TrueFalse.mjs +19 -0
- package/dist/components/studentInputs/TrueFalse.mjs.map +1 -0
- package/dist/components/studentInputs/TrueFalse.vue_vue_type_script_lang.mjs +21 -0
- package/dist/components/studentInputs/TrueFalse.vue_vue_type_script_lang.mjs.map +1 -0
- package/dist/components/studentInputs/UserInputNumber.mjs +33 -0
- package/dist/components/studentInputs/UserInputNumber.mjs.map +1 -0
- package/dist/components/studentInputs/UserInputNumber.vue_vue_type_script_lang.mjs +25 -0
- package/dist/components/studentInputs/UserInputNumber.vue_vue_type_script_lang.mjs.map +1 -0
- package/dist/components/studentInputs/UserInputString.mjs +22 -0
- package/dist/components/studentInputs/UserInputString.mjs.map +1 -0
- package/dist/components/studentInputs/UserInputString.vue_vue_type_script_lang.mjs +23 -0
- package/dist/components/studentInputs/UserInputString.vue_vue_type_script_lang.mjs.map +1 -0
- package/dist/components/studentInputs/fillInInput.mjs +25 -0
- package/dist/components/studentInputs/fillInInput.mjs.map +1 -0
- package/dist/components/studentInputs/fillInInput.vue_vue_type_script_lang.mjs +26 -0
- package/dist/components/studentInputs/fillInInput.vue_vue_type_script_lang.mjs.map +1 -0
- package/dist/composables/CompositionViewable.mjs +65 -0
- package/dist/composables/CompositionViewable.mjs.map +1 -0
- package/dist/composables/Displayable.mjs +36 -0
- package/dist/composables/Displayable.mjs.map +1 -0
- package/dist/composables/useAuthUI.mjs +38 -0
- package/dist/composables/useAuthUI.mjs.map +1 -0
- package/dist/composables/useEntitlements.mjs +58 -0
- package/dist/composables/useEntitlements.mjs.map +1 -0
- package/dist/index.mjs +49 -0
- package/dist/plugins/pinia.mjs +12 -0
- package/dist/plugins/pinia.mjs.map +1 -0
- package/dist/services/authAPI.mjs +159 -0
- package/dist/services/authAPI.mjs.map +1 -0
- package/dist/stores/useAuthStore.mjs +68 -0
- package/dist/stores/useAuthStore.mjs.map +1 -0
- package/dist/stores/useCardPreviewModeStore.mjs +20 -0
- package/dist/stores/useCardPreviewModeStore.mjs.map +1 -0
- package/dist/stores/useConfigStore.mjs +60 -0
- package/dist/stores/useConfigStore.mjs.map +1 -0
- package/dist/utils/SkldrMouseTrap.mjs +48 -0
- package/dist/utils/SkldrMouseTrap.mjs.map +1 -0
- package/dist/utils/passwordValidation.mjs +12 -0
- package/dist/utils/passwordValidation.mjs.map +1 -0
- package/package.json +7 -8
- package/dist/MarkdownRenderer-U_iPiA2x.mjs +0 -4966
- package/dist/MarkdownRenderer-U_iPiA2x.mjs.map +0 -1
- package/dist/assets/MarkdownRenderer.css +0 -1
- package/dist/common-ui.es.js +0 -5090
- package/dist/common-ui.es.js.map +0 -1
- package/dist/common-ui.umd.js +0 -77
- package/dist/common-ui.umd.js.map +0 -1
package/dist/assets/index.css
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
.tooltip[data-v-ca46239a]{color:#fff;background-color:#000c;border-radius:3px;padding:5px;font-size:12px}.sk-mousetrap-tooltip-wrapper[data-v-5d6fb09c]{display:inline-block;position:relative}.sk-mousetrap-tooltip[data-v-5d6fb09c]{color:#fff;white-space:nowrap;pointer-events:none;z-index:9999;background-color:#000c;border-radius:4px;padding:2px 6px;font-size:12px;position:absolute}.sk-mt-tooltip-top[data-v-5d6fb09c]{margin-bottom:5px;bottom:100%;left:50%;transform:translate(-50%)}.sk-mt-tooltip-bottom[data-v-5d6fb09c]{margin-top:5px;top:100%;left:50%;transform:translate(-50%)}.sk-mt-tooltip-left[data-v-5d6fb09c]{margin-right:5px;top:50%;right:100%;transform:translateY(-50%)}.sk-mt-tooltip-right[data-v-5d6fb09c]{margin-left:5px;top:50%;left:100%;transform:translateY(-50%)}.sk-mousetrap-highlight-glow[data-v-5d6fb09c]{transition:box-shadow .25s;box-shadow:0 0 8px 2px #1976d299}.sk-mousetrap-highlight-scale[data-v-5d6fb09c]{transition:transform .25s;transform:scale(1.03)}.sk-mousetrap-highlight-border[data-v-5d6fb09c]{outline-offset:2px;border-radius:4px;outline:2px solid #1976d2cc;transition:outline .25s,outline-offset .25s}.fade-enter-active[data-v-5d6fb09c],.fade-leave-active[data-v-5d6fb09c]{transition:opacity .25s}.fade-enter-from[data-v-5d6fb09c],.fade-leave-to[data-v-5d6fb09c]{opacity:0}.pageSelect[data-v-a75fea7e]{max-width:60px}.selected-card[data-v-285e13bd]{background-color:#e0f2f7;border-left:4px solid #2196f3}.cursor-pointer[data-v-285e13bd]{cursor:pointer}.error-message[data-v-a4bdfdfb]{color:#f44336;background-color:#ffebee;border-radius:4px;margin:8px 0;padding:16px}.negative-interval[data-v-a4bdfdfb]{color:#f44336;background-color:#ffebee;border-radius:3px;padding:2px 4px;font-weight:700}.invalid-timestamp[data-v-a4bdfdfb]{color:#f44336;font-weight:700}.card-history-viewer[data-v-a4bdfdfb]{margin:16px 0}.timer-container[data-v-5e104ccc]{cursor:pointer;display:inline-flex}.cardView[data-v-2d892b82]{border-radius:8px;padding:15px}.session-debug[data-v-641ac170]{max-height:400px;font-family:Courier New,monospace;font-size:.75rem;overflow-y:auto}.debug-section[data-v-641ac170]{background-color:#ffffff0d;border-radius:4px;min-height:120px;padding:8px}.debug-header[data-v-641ac170]{border-bottom:1px solid #ffffff1a;align-items:center;margin-bottom:4px;padding-bottom:4px;display:flex}.debug-stats[data-v-641ac170]{gap:8px;margin-bottom:8px;display:flex}.debug-items[data-v-641ac170]{margin-top:4px;padding-left:8px}.debug-item[data-v-641ac170]{border-left:2px solid #fff3;margin-bottom:2px;padding:2px 0 2px 6px}.footer-controls[data-v-ad5e0df0]{background-color:var(--v-background);z-index:100;position:fixed;bottom:0}.footer-right[data-v-ad5e0df0]{background-color:var(--v-background);z-index:100;position:fixed;bottom:0;right:0}.correct[data-v-ad5e0df0]{animation:1.25s ease-out varFade-ad5e0df0}.incorrect[data-v-ad5e0df0]{animation:1.25s ease-out purpleFade-ad5e0df0}a[data-v-ad5e0df0]{text-decoration:underline}.StudySession[data-v-ad5e0df0],.session-finished-area[data-v-ad5e0df0]{flex-direction:column;flex:1;min-height:0;display:flex}.card-transition-container[data-v-ad5e0df0]{flex-direction:column;flex:1;justify-content:center;min-height:0;display:flex;position:relative;overflow:hidden}@keyframes varFade-ad5e0df0{0%{box-shadow:rgba(var(--r), var(--g), 0, .25) 0px 7px 8px -4px, rgba(var(--r), var(--g), 0, .25) 0px 12px 17px 2px, rgba(var(--r), var(--g), 0, .25) 0px 5px 22px 4px}to{box-shadow:0 0 #00960000}}@keyframes greenFade-ad5e0df0{0%{box-shadow:0 7px 8px -4px #00960040,0 12px 17px 2px #00960040,0 5px 22px 4px #00960040}to{box-shadow:0 0 #00960000}}@keyframes purpleFade-ad5e0df0{0%{box-shadow:0 7px 8px -4px #73004b40,0 12px 17px 2px #73004b40,0 5px 22px 4px #73004b40}to{box-shadow:0 0 #73004b00}}.component-fade-enter-active,.component-fade-leave-active{transition:opacity .2s}.component-fade-enter-from,.component-fade-leave-to{opacity:0}.card-slide-leave-active{will-change:transform, opacity;pointer-events:none;height:-moz-fit-content;height:fit-content;margin:auto;animation:.4s cubic-bezier(.4,0,.7,.2) both cardBackOutLeft;top:0;bottom:0;left:0;right:0;position:absolute!important}.card-slide-leave-active *{transition:none!important;animation-play-state:paused!important}.card-slide-enter-active{will-change:transform, opacity;animation:.25s ease-out .1s both cardSlideInRight}@keyframes cardBackOutLeft{0%{opacity:1;transform:translate(0)scale(1)}40%{opacity:.8;transform:translate(0)scale(.88)}to{opacity:0;transform:translate(-100%)scale(.88)}}@keyframes cardSlideInRight{0%{opacity:0;transform:translate(60%)}to{opacity:1;transform:translate(0)}}.card-scale-leave-active{transition:transform .15s ease-in,opacity .15s ease-in}.card-scale-enter-active{transition:transform .2s cubic-bezier(.34,1.4,.64,1),opacity .15s ease-out}.card-scale-leave-to{opacity:0;transform:scale(.92)}.card-scale-enter-from{opacity:0;transform:scale(1.03)}.choice[data-v-96de7172]{text-align:center;border-radius:4px;min-width:75px;margin:10px;padding:5px 15px;transition:all .2s ease-in-out;display:inline-block}.selected[data-v-96de7172]{z-index:1;transform:translateY(-10px)scale(1.15)}.not-selected[data-v-96de7172]{z-index:0}.userInput[data-v-a56dcd1c]{text-align:center;border:none;border-bottom:1px #000}.cardView[data-v-93f758b5]{border-radius:8px;padding:15px}.component-fade-enter-active[data-v-93f758b5],.component-fade-leave-active[data-v-93f758b5]{transition:opacity .3s}.component-fade-enter[data-v-93f758b5],.component-fade-leave-to[data-v-93f758b5]{opacity:0}.component-fade-enter-active[data-v-9a38a213],.component-fade-leave-active[data-v-9a38a213]{transition:opacity .5s}.component-fade-enter[data-v-9a38a213],.component-fade-leave-to[data-v-9a38a213]{opacity:0}.login[data-v-764d1a76]{max-width:650px}.component-fade-enter-active[data-v-5a55bdf0],.component-fade-leave-active[data-v-5a55bdf0]{transition:opacity .5s}.component-fade-enter[data-v-5a55bdf0],.component-fade-leave-to[data-v-5a55bdf0]{opacity:0}.vue-tags-input[data-v-f7373dc0]{max-width:100%}.autocomplete-item[data-v-f7373dc0]{align-items:center;padding:5px;display:flex}.autocomplete-item.is-active[data-v-f7373dc0]{background-color:#e8e8e8}.autocomplete-item.is-active>.tag-name[data-v-f7373dc0]{color:#fff;background-color:#5c6bc0}.tag-name[data-v-f7373dc0]{color:#333;background-color:#e0e0e0;border-radius:3px;margin-right:5px;padding:2px 6px;font-weight:700}.tag-snippet[data-v-f7373dc0]{color:#666;font-size:.9em}.autocomplete-item.is-active .tag-snippet[data-v-f7373dc0]{color:#333}.component-fade-enter-active[data-v-5ecc9d21],.component-fade-leave-active[data-v-5ecc9d21]{transition:opacity .5s}.component-fade-enter[data-v-5ecc9d21],.component-fade-leave-to[data-v-5ecc9d21]{opacity:0}.component-scale-enter-active[data-v-5ecc9d21],.component-scale-leave-active[data-v-5ecc9d21]{max-height:auto;transform-origin:top;transition:transform .3s,max-height .3s;transform:scale(1)}.component-scale-enter[data-v-5ecc9d21],.component-fade-leave-to[data-v-5ecc9d21]{max-height:0;overflow:hidden;transform:scaleY(0)}.component-fade-enter-active[data-v-38e9efe1],.component-fade-leave-active[data-v-38e9efe1]{transition:opacity .5s}.component-fade-enter[data-v-38e9efe1],.component-fade-leave-to[data-v-38e9efe1]{opacity:0}.component-scale-enter-active[data-v-38e9efe1],.component-scale-leave-active[data-v-38e9efe1]{max-height:auto;transform-origin:top;transition:transform .3s,max-height .3s;transform:scale(1)}.component-scale-enter[data-v-38e9efe1],.component-fade-leave-to[data-v-38e9efe1]{max-height:0;overflow:hidden;transform:scaleY(0)}.course-tag-filter-widget[data-v-e1ba963b]{width:100%}.filter-summary[data-v-e1ba963b]{color:rgba(var(--v-theme-on-surface), .7)}
|
|
1
|
+
.cardView[data-v-2d892b82]{border-radius:8px;padding:15px}.error-message[data-v-a4bdfdfb]{color:#f44336;background-color:#ffebee;border-radius:4px;margin:8px 0;padding:16px}.negative-interval[data-v-a4bdfdfb]{color:#f44336;background-color:#ffebee;border-radius:3px;padding:2px 4px;font-weight:700}.invalid-timestamp[data-v-a4bdfdfb]{color:#f44336;font-weight:700}.card-history-viewer[data-v-a4bdfdfb]{margin:16px 0}.selected-card[data-v-285e13bd]{background-color:#e0f2f7;border-left:4px solid #2196f3}.cursor-pointer[data-v-285e13bd]{cursor:pointer}.pageSelect[data-v-a75fea7e]{max-width:60px}.vue-tags-input[data-v-f7373dc0]{max-width:100%}.autocomplete-item[data-v-f7373dc0]{align-items:center;padding:5px;display:flex}.autocomplete-item.is-active[data-v-f7373dc0]{background-color:#e8e8e8}.autocomplete-item.is-active>.tag-name[data-v-f7373dc0]{color:#fff;background-color:#5c6bc0}.tag-name[data-v-f7373dc0]{color:#333;background-color:#e0e0e0;border-radius:3px;margin-right:5px;padding:2px 6px;font-weight:700}.tag-snippet[data-v-f7373dc0]{color:#666;font-size:.9em}.autocomplete-item.is-active .tag-snippet[data-v-f7373dc0]{color:#333}.cardView[data-v-93f758b5]{border-radius:8px;padding:15px}.component-fade-enter-active[data-v-93f758b5],.component-fade-leave-active[data-v-93f758b5]{transition:opacity .3s}.component-fade-enter[data-v-93f758b5],.component-fade-leave-to[data-v-93f758b5]{opacity:0}.component-fade-enter-active[data-v-5ecc9d21],.component-fade-leave-active[data-v-5ecc9d21]{transition:opacity .5s}.component-fade-enter[data-v-5ecc9d21],.component-fade-leave-to[data-v-5ecc9d21]{opacity:0}.component-scale-enter-active[data-v-5ecc9d21],.component-scale-leave-active[data-v-5ecc9d21]{max-height:auto;transform-origin:top;transition:transform .3s,max-height .3s;transform:scale(1)}.component-scale-enter[data-v-5ecc9d21],.component-fade-leave-to[data-v-5ecc9d21]{max-height:0;overflow:hidden;transform:scaleY(0)}.component-fade-enter-active[data-v-38e9efe1],.component-fade-leave-active[data-v-38e9efe1]{transition:opacity .5s}.component-fade-enter[data-v-38e9efe1],.component-fade-leave-to[data-v-38e9efe1]{opacity:0}.component-scale-enter-active[data-v-38e9efe1],.component-scale-leave-active[data-v-38e9efe1]{max-height:auto;transform-origin:top;transition:transform .3s,max-height .3s;transform:scale(1)}.component-scale-enter[data-v-38e9efe1],.component-fade-leave-to[data-v-38e9efe1]{max-height:0;overflow:hidden;transform:scaleY(0)}.course-tag-filter-widget[data-v-e1ba963b]{width:100%}.filter-summary[data-v-e1ba963b]{color:rgba(var(--v-theme-on-surface), .7)}.tooltip[data-v-ca46239a]{color:#fff;background-color:#000c;border-radius:3px;padding:5px;font-size:12px}.session-debug[data-v-641ac170]{max-height:400px;font-family:Courier New,monospace;font-size:.75rem;overflow-y:auto}.debug-section[data-v-641ac170]{background-color:#ffffff0d;border-radius:4px;min-height:120px;padding:8px}.debug-header[data-v-641ac170]{border-bottom:1px solid #ffffff1a;align-items:center;margin-bottom:4px;padding-bottom:4px;display:flex}.debug-stats[data-v-641ac170]{gap:8px;margin-bottom:8px;display:flex}.debug-items[data-v-641ac170]{margin-top:4px;padding-left:8px}.debug-item[data-v-641ac170]{border-left:2px solid #fff3;margin-bottom:2px;padding:2px 0 2px 6px}.sk-mousetrap-tooltip-wrapper[data-v-5d6fb09c]{display:inline-block;position:relative}.sk-mousetrap-tooltip[data-v-5d6fb09c]{color:#fff;white-space:nowrap;pointer-events:none;z-index:9999;background-color:#000c;border-radius:4px;padding:2px 6px;font-size:12px;position:absolute}.sk-mt-tooltip-top[data-v-5d6fb09c]{margin-bottom:5px;bottom:100%;left:50%;transform:translate(-50%)}.sk-mt-tooltip-bottom[data-v-5d6fb09c]{margin-top:5px;top:100%;left:50%;transform:translate(-50%)}.sk-mt-tooltip-left[data-v-5d6fb09c]{margin-right:5px;top:50%;right:100%;transform:translateY(-50%)}.sk-mt-tooltip-right[data-v-5d6fb09c]{margin-left:5px;top:50%;left:100%;transform:translateY(-50%)}.sk-mousetrap-highlight-glow[data-v-5d6fb09c]{transition:box-shadow .25s;box-shadow:0 0 8px 2px #1976d299}.sk-mousetrap-highlight-scale[data-v-5d6fb09c]{transition:transform .25s;transform:scale(1.03)}.sk-mousetrap-highlight-border[data-v-5d6fb09c]{outline-offset:2px;border-radius:4px;outline:2px solid #1976d2cc;transition:outline .25s,outline-offset .25s}.fade-enter-active[data-v-5d6fb09c],.fade-leave-active[data-v-5d6fb09c]{transition:opacity .25s}.fade-enter-from[data-v-5d6fb09c],.fade-leave-to[data-v-5d6fb09c]{opacity:0}.timer-container[data-v-5e104ccc]{cursor:pointer;display:inline-flex}.footer-controls[data-v-ad5e0df0]{background-color:var(--v-background);z-index:100;position:fixed;bottom:0}.footer-right[data-v-ad5e0df0]{background-color:var(--v-background);z-index:100;position:fixed;bottom:0;right:0}.correct[data-v-ad5e0df0]{animation:1.25s ease-out varFade-ad5e0df0}.incorrect[data-v-ad5e0df0]{animation:1.25s ease-out purpleFade-ad5e0df0}a[data-v-ad5e0df0]{text-decoration:underline}.StudySession[data-v-ad5e0df0],.session-finished-area[data-v-ad5e0df0]{flex-direction:column;flex:1;min-height:0;display:flex}.card-transition-container[data-v-ad5e0df0]{flex-direction:column;flex:1;justify-content:center;min-height:0;display:flex;position:relative;overflow:hidden}@keyframes varFade-ad5e0df0{0%{box-shadow:rgba(var(--r), var(--g), 0, .25) 0px 7px 8px -4px, rgba(var(--r), var(--g), 0, .25) 0px 12px 17px 2px, rgba(var(--r), var(--g), 0, .25) 0px 5px 22px 4px}to{box-shadow:0 0 #00960000}}@keyframes greenFade-ad5e0df0{0%{box-shadow:0 7px 8px -4px #00960040,0 12px 17px 2px #00960040,0 5px 22px 4px #00960040}to{box-shadow:0 0 #00960000}}@keyframes purpleFade-ad5e0df0{0%{box-shadow:0 7px 8px -4px #73004b40,0 12px 17px 2px #73004b40,0 5px 22px 4px #73004b40}to{box-shadow:0 0 #73004b00}}.component-fade-enter-active,.component-fade-leave-active{transition:opacity .2s}.component-fade-enter-from,.component-fade-leave-to{opacity:0}.card-slide-leave-active{will-change:transform, opacity;pointer-events:none;height:-moz-fit-content;height:fit-content;margin:auto;animation:.4s cubic-bezier(.4,0,.7,.2) both cardBackOutLeft;top:0;bottom:0;left:0;right:0;position:absolute!important}.card-slide-leave-active *{transition:none!important;animation-play-state:paused!important}.card-slide-enter-active{will-change:transform, opacity;animation:.25s ease-out .1s both cardSlideInRight}@keyframes cardBackOutLeft{0%{opacity:1;transform:translate(0)scale(1)}40%{opacity:.8;transform:translate(0)scale(.88)}to{opacity:0;transform:translate(-100%)scale(.88)}}@keyframes cardSlideInRight{0%{opacity:0;transform:translate(60%)}to{opacity:1;transform:translate(0)}}.card-scale-leave-active{transition:transform .15s ease-in,opacity .15s ease-in}.card-scale-enter-active{transition:transform .2s cubic-bezier(.34,1.4,.64,1),opacity .15s ease-out}.card-scale-leave-to{opacity:0;transform:scale(.92)}.card-scale-enter-from{opacity:0;transform:scale(1.03)}.component-fade-enter-active[data-v-9a38a213],.component-fade-leave-active[data-v-9a38a213]{transition:opacity .5s}.component-fade-enter[data-v-9a38a213],.component-fade-leave-to[data-v-9a38a213]{opacity:0}.login[data-v-764d1a76]{max-width:650px}.component-fade-enter-active[data-v-5a55bdf0],.component-fade-leave-active[data-v-5a55bdf0]{transition:opacity .5s}.component-fade-enter[data-v-5a55bdf0],.component-fade-leave-to[data-v-5a55bdf0]{opacity:0}.playing[data-v-e1a0f62c]{z-index:1;animation:.85s ease-in-out infinite alternate pulse-e1a0f62c}@keyframes pulse-e1a0f62c{0%{transform:scale(1)}to{transform:scale(1.15)}}pre code.hljs{padding:1em;display:block;overflow-x:auto}code.hljs{padding:3px 5px}.hljs{color:#24292e;background:#fff}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#d73a49}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#6f42c1}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-variable,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id{color:#005cc5}.hljs-regexp,.hljs-string,.hljs-meta .hljs-string{color:#032f62}.hljs-built_in,.hljs-symbol{color:#e36209}.hljs-comment,.hljs-code,.hljs-formula{color:#6a737d}.hljs-name,.hljs-quote,.hljs-selector-tag,.hljs-selector-pseudo{color:#22863a}.hljs-subst{color:#24292e}.hljs-section{color:#005cc5;font-weight:700}.hljs-bullet{color:#735c0f}.hljs-emphasis{color:#24292e;font-style:italic}.hljs-strong{color:#24292e;font-weight:700}.hljs-addition{color:#22863a;background-color:#f0fff4}.hljs-deletion{color:#b31d28;background-color:#ffeef0}.code-block-wrapper{background-color:#f6f8fa;border-radius:4px;margin:1rem 0;position:relative;overflow:auto}.language-indicator{color:#666;z-index:1;text-transform:lowercase;background-color:#ffffffb3;border-radius:4px;padding:2px 8px;font-family:monospace;font-size:.75rem;position:absolute;top:8px;right:10px}.user-input-container[data-v-aa14961f]{vertical-align:baseline;min-width:6em;display:inline-block}.user-input-string[data-v-aa14961f]{color:currentColor;font-family:inherit;font-size:inherit;line-height:inherit;text-align:center;background:0 0;border:none;border-bottom:1px solid;outline:none;width:100%;margin:0;padding:0;display:inline-block}.user-input-string[data-v-aa14961f]:focus{border-bottom:2px solid}#input[data-v-486ac035]{text-align:center;min-width:4em;display:inline-block}.underline[data-v-486ac035]{text-decoration:underline;-webkit-text-decoration-style:solid 14px;text-decoration-style:solid 14px}blockquote[data-v-de3eebd6]{border-left:3px solid teal;padding-left:8px}.codespan[data-v-de3eebd6]{margin-left:1px;margin-right:1px;padding-left:3px;padding-right:3px}p[data-v-de3eebd6]{margin-top:15px;margin-bottom:15px}.choice[data-v-96de7172]{text-align:center;border-radius:4px;min-width:75px;margin:10px;padding:5px 15px;transition:all .2s ease-in-out;display:inline-block}.selected[data-v-96de7172]{z-index:1;transform:translateY(-10px)scale(1.15)}.not-selected[data-v-96de7172]{z-index:0}.userInput[data-v-a56dcd1c]{text-align:center;border:none;border-bottom:1px #000}
|
|
2
|
+
/*$vite$:1*/
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import "../_virtual/_rolldown/runtime.mjs";
|
|
2
|
+
import e from "../_virtual/_plugin-vue_export-helper.mjs";
|
|
3
|
+
import t from "./CardBrowser.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, resolveComponent as l, toDisplayString as u, withCtx as d } from "vue";
|
|
5
|
+
//#region src/components/CardBrowser.vue
|
|
6
|
+
var f = {
|
|
7
|
+
key: 0,
|
|
8
|
+
class: "text-subtitle-1 pa-2"
|
|
9
|
+
};
|
|
10
|
+
function _sfc_render(e, t, p, m, h, g) {
|
|
11
|
+
let _ = l("CardViewer"), v = l("v-icon"), y = l("v-btn"), b = l("v-row");
|
|
12
|
+
return c(), n(b, {
|
|
13
|
+
column: "",
|
|
14
|
+
align: "center",
|
|
15
|
+
justify: "center"
|
|
16
|
+
}, {
|
|
17
|
+
default: d(() => [
|
|
18
|
+
s(_, {
|
|
19
|
+
view: e.views[e.viewIndex],
|
|
20
|
+
data: e.data,
|
|
21
|
+
course_id: "[browsing]",
|
|
22
|
+
card_id: "[browsing]"
|
|
23
|
+
}, null, 8, ["view", "data"]),
|
|
24
|
+
t[2] || (t[2] = a("br", null, null, -1)),
|
|
25
|
+
t[3] || (t[3] = a("br", null, null, -1)),
|
|
26
|
+
e.suppressSpinner ? r("", !0) : (c(), i("div", f, [
|
|
27
|
+
e.spinner ? (c(), n(y, {
|
|
28
|
+
key: 0,
|
|
29
|
+
icon: "",
|
|
30
|
+
variant: "outlined",
|
|
31
|
+
color: "primary",
|
|
32
|
+
onClick: e.decrementView
|
|
33
|
+
}, {
|
|
34
|
+
default: d(() => [s(v, null, {
|
|
35
|
+
default: d(() => t[0] || (t[0] = [o("mdi-chevron-left")])),
|
|
36
|
+
_: 1
|
|
37
|
+
})]),
|
|
38
|
+
_: 1
|
|
39
|
+
}, 8, ["onClick"])) : r("", !0),
|
|
40
|
+
o(" " + u(e.views[e.viewIndex].name) + " ", 1),
|
|
41
|
+
e.spinner ? (c(), n(y, {
|
|
42
|
+
key: 1,
|
|
43
|
+
icon: "",
|
|
44
|
+
variant: "outlined",
|
|
45
|
+
color: "primary",
|
|
46
|
+
onClick: e.incrementView
|
|
47
|
+
}, {
|
|
48
|
+
default: d(() => [s(v, null, {
|
|
49
|
+
default: d(() => t[1] || (t[1] = [o("mdi-chevron-right")])),
|
|
50
|
+
_: 1
|
|
51
|
+
})]),
|
|
52
|
+
_: 1
|
|
53
|
+
}, 8, ["onClick"])) : r("", !0)
|
|
54
|
+
]))
|
|
55
|
+
]),
|
|
56
|
+
_: 1
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
var p = /* @__PURE__ */ e(t, [["render", _sfc_render]]);
|
|
60
|
+
//#endregion
|
|
61
|
+
export { p as default };
|
|
62
|
+
|
|
63
|
+
//# sourceMappingURL=CardBrowser.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CardBrowser.mjs","names":[],"sources":["../../src/components/CardBrowser.vue"],"sourcesContent":["<template>\n <v-row column align=\"center\" justify=\"center\">\n <CardViewer :view=\"views[viewIndex]\" :data=\"data\" :course_id=\"'[browsing]'\" :card_id=\"'[browsing]'\" />\n <br /><br />\n <div v-if=\"!suppressSpinner\" class=\"text-subtitle-1 pa-2\">\n <v-btn v-if=\"spinner\" icon variant=\"outlined\" color=\"primary\" @click=\"decrementView\">\n <v-icon>mdi-chevron-left</v-icon>\n </v-btn>\n {{ views[viewIndex].name }}\n <v-btn v-if=\"spinner\" icon variant=\"outlined\" color=\"primary\" @click=\"incrementView\">\n <v-icon>mdi-chevron-right</v-icon>\n </v-btn>\n </div>\n </v-row>\n</template>\n\n<script lang=\"ts\">\nimport { ViewData } from '@vue-skuilder/common';\nimport { defineComponent, PropType } from 'vue';\nimport { ViewComponent } from '../composables/Displayable';\nimport CardViewer from './cardRendering/CardViewer.vue';\nimport { useCardPreviewModeStore } from '../stores/useCardPreviewModeStore';\n\nexport default defineComponent({\n name: 'CardBrowser',\n\n components: {\n CardViewer,\n },\n\n props: {\n views: {\n type: Array as PropType<Array<ViewComponent>>,\n required: true,\n },\n data: {\n type: Array as PropType<ViewData[]>,\n required: true,\n },\n suppressSpinner: {\n type: Boolean,\n default: false,\n },\n },\n\n data() {\n return {\n viewIndex: 0,\n previewMode: useCardPreviewModeStore(),\n };\n },\n\n computed: {\n spinner(): boolean {\n return this.views.length > 1;\n },\n },\n\n created() {\n console.log(`[CardBrowser] Card browser created. Cards now in 'prewviewMode'`);\n this.previewMode.setPreviewMode(true);\n },\n\n unmounted() {\n console.log(`[CardBrowser] Card browser unmounted. Cards no longer in 'prewviewMode'`);\n this.previewMode.setPreviewMode(false);\n },\n\n methods: {\n incrementView() {\n this.viewIndex++;\n this.viewIndex = (this.viewIndex + this.views.length) % this.views.length;\n },\n\n decrementView() {\n this.viewIndex--;\n this.viewIndex = (this.viewIndex + this.views.length) % this.views.length;\n },\n },\n});\n</script>\n"],"mappings":";;;;;;CAAA,KAAA;CAIiC,OAAM;;;;aAHrC,EAYQ,GAAA;EAZD,QAAA;EAAO,OAAM;EAAS,SAAQ;;EADvC,SAAA,QAE0G;GAAtG,EAAsG,GAAA;IAAzF,MAAM,EAAA,MAAM,EAAA;IAAa,MAAM,EAAA;IAAO,WAAW;IAAe,SAAS;;mBACtF,EAAM,MAAA,MAAA,MAAA,GAAA;mBAAA,EAAM,MAAA,MAAA,MAAA,GAAA;GACA,EAAA,kBAJhB,EAAA,IAAA,GAAA,IAIgB,GAAA,EAAZ,EAQM,OARN,GAQM;IAPS,EAAA,WAAA,GAAA,EAAb,EAEQ,GAAA;KAPd,KAAA;KAK4B,MAAA;KAAK,SAAQ;KAAW,OAAM;KAAW,SAAO,EAAA;;KAL5E,SAAA,QAMyC,CAAjC,EAAiC,GAAA,MAAA;MANzC,SAAA,QAMgC,EAAA,OAAA,EAAA,KAAA,CANhC,EAMgB,mBAAgB,CAAA,EAAA;MANhC,GAAA;;KAAA,GAAA;0BAAA,EAAA,IAAA,GAAA;IAAA,EAOc,MACR,EAAG,EAAA,MAAM,EAAA,WAAW,KAAI,GAAG,KAC3B,EAAA;IAAa,EAAA,WAAA,GAAA,EAAb,EAEQ,GAAA;KAXd,KAAA;KAS4B,MAAA;KAAK,SAAQ;KAAW,OAAM;KAAW,SAAO,EAAA;;KAT5E,SAAA,QAU0C,CAAlC,EAAkC,GAAA,MAAA;MAV1C,SAAA,QAUiC,EAAA,OAAA,EAAA,KAAA,CAVjC,EAUgB,oBAAiB,CAAA,EAAA;MAVjC,GAAA;;KAAA,GAAA;0BAAA,EAAA,IAAA,GAAA;;;EAAA,GAAA"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import "../_virtual/_rolldown/runtime.mjs";
|
|
2
|
+
import e from "./cardRendering/CardViewer.mjs";
|
|
3
|
+
import { useCardPreviewModeStore as t } from "../stores/useCardPreviewModeStore.mjs";
|
|
4
|
+
import { defineComponent as n } from "vue";
|
|
5
|
+
//#region src/components/CardBrowser.vue?vue&type=script&lang.ts
|
|
6
|
+
var r = n({
|
|
7
|
+
name: "CardBrowser",
|
|
8
|
+
components: { CardViewer: e },
|
|
9
|
+
props: {
|
|
10
|
+
views: {
|
|
11
|
+
type: Array,
|
|
12
|
+
required: !0
|
|
13
|
+
},
|
|
14
|
+
data: {
|
|
15
|
+
type: Array,
|
|
16
|
+
required: !0
|
|
17
|
+
},
|
|
18
|
+
suppressSpinner: {
|
|
19
|
+
type: Boolean,
|
|
20
|
+
default: !1
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
data() {
|
|
24
|
+
return {
|
|
25
|
+
viewIndex: 0,
|
|
26
|
+
previewMode: t()
|
|
27
|
+
};
|
|
28
|
+
},
|
|
29
|
+
computed: { spinner() {
|
|
30
|
+
return this.views.length > 1;
|
|
31
|
+
} },
|
|
32
|
+
created() {
|
|
33
|
+
console.log("[CardBrowser] Card browser created. Cards now in 'prewviewMode'"), this.previewMode.setPreviewMode(!0);
|
|
34
|
+
},
|
|
35
|
+
unmounted() {
|
|
36
|
+
console.log("[CardBrowser] Card browser unmounted. Cards no longer in 'prewviewMode'"), this.previewMode.setPreviewMode(!1);
|
|
37
|
+
},
|
|
38
|
+
methods: {
|
|
39
|
+
incrementView() {
|
|
40
|
+
this.viewIndex++, this.viewIndex = (this.viewIndex + this.views.length) % this.views.length;
|
|
41
|
+
},
|
|
42
|
+
decrementView() {
|
|
43
|
+
this.viewIndex--, this.viewIndex = (this.viewIndex + this.views.length) % this.views.length;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
//#endregion
|
|
48
|
+
export { r as default };
|
|
49
|
+
|
|
50
|
+
//# sourceMappingURL=CardBrowser.vue_vue_type_script_lang.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CardBrowser.vue_vue_type_script_lang.mjs","names":[],"sources":["../../src/components/CardBrowser.vue"],"sourcesContent":["<template>\n <v-row column align=\"center\" justify=\"center\">\n <CardViewer :view=\"views[viewIndex]\" :data=\"data\" :course_id=\"'[browsing]'\" :card_id=\"'[browsing]'\" />\n <br /><br />\n <div v-if=\"!suppressSpinner\" class=\"text-subtitle-1 pa-2\">\n <v-btn v-if=\"spinner\" icon variant=\"outlined\" color=\"primary\" @click=\"decrementView\">\n <v-icon>mdi-chevron-left</v-icon>\n </v-btn>\n {{ views[viewIndex].name }}\n <v-btn v-if=\"spinner\" icon variant=\"outlined\" color=\"primary\" @click=\"incrementView\">\n <v-icon>mdi-chevron-right</v-icon>\n </v-btn>\n </div>\n </v-row>\n</template>\n\n<script lang=\"ts\">\nimport { ViewData } from '@vue-skuilder/common';\nimport { defineComponent, PropType } from 'vue';\nimport { ViewComponent } from '../composables/Displayable';\nimport CardViewer from './cardRendering/CardViewer.vue';\nimport { useCardPreviewModeStore } from '../stores/useCardPreviewModeStore';\n\nexport default defineComponent({\n name: 'CardBrowser',\n\n components: {\n CardViewer,\n },\n\n props: {\n views: {\n type: Array as PropType<Array<ViewComponent>>,\n required: true,\n },\n data: {\n type: Array as PropType<ViewData[]>,\n required: true,\n },\n suppressSpinner: {\n type: Boolean,\n default: false,\n },\n },\n\n data() {\n return {\n viewIndex: 0,\n previewMode: useCardPreviewModeStore(),\n };\n },\n\n computed: {\n spinner(): boolean {\n return this.views.length > 1;\n },\n },\n\n created() {\n console.log(`[CardBrowser] Card browser created. Cards now in 'prewviewMode'`);\n this.previewMode.setPreviewMode(true);\n },\n\n unmounted() {\n console.log(`[CardBrowser] Card browser unmounted. Cards no longer in 'prewviewMode'`);\n this.previewMode.setPreviewMode(false);\n },\n\n methods: {\n incrementView() {\n this.viewIndex++;\n this.viewIndex = (this.viewIndex + this.views.length) % this.views.length;\n },\n\n decrementView() {\n this.viewIndex--;\n this.viewIndex = (this.viewIndex + this.views.length) % this.views.length;\n },\n },\n});\n</script>\n"],"mappings":";;;;;AAuBA,IAAA,IAAe,EAAgB;CAC7B,MAAM;CAEN,YAAY,EACV,YAAA,GACD;CAED,OAAO;EACL,OAAO;GACL,MAAM;GACN,UAAU;GACX;EACD,MAAM;GACJ,MAAM;GACN,UAAU;GACX;EACD,iBAAiB;GACf,MAAM;GACN,SAAS;GACV;EACF;CAED,OAAO;AACL,SAAO;GACL,WAAW;GACX,aAAa,GAAyB;GACvC;;CAGH,UAAU,EACR,UAAmB;AACjB,SAAO,KAAK,MAAM,SAAS;IAE9B;CAED,UAAU;AAER,EADA,QAAQ,IAAI,kEAAkE,EAC9E,KAAK,YAAY,eAAe,GAAK;;CAGvC,YAAY;AAEV,EADA,QAAQ,IAAI,0EAA0E,EACtF,KAAK,YAAY,eAAe,GAAM;;CAGxC,SAAS;EACP,gBAAgB;AAEd,GADA,KAAK,aACL,KAAK,aAAa,KAAK,YAAY,KAAK,MAAM,UAAU,KAAK,MAAM;;EAGrE,gBAAgB;AAEd,GADA,KAAK,aACL,KAAK,aAAa,KAAK,YAAY,KAAK,MAAM,UAAU,KAAK,MAAM;;EAEtE;CACF,CAAC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import "../_virtual/_rolldown/runtime.mjs";
|
|
2
|
+
import e from "../_virtual/_plugin-vue_export-helper.mjs";
|
|
3
|
+
import t from "./CardHistoryViewer.vue_vue_type_script_lang.mjs";
|
|
4
|
+
/* empty css */
|
|
5
|
+
import { createCommentVNode as n, createElementBlock as r, createTextVNode as i, createVNode as a, openBlock as o, resolveComponent as s, toDisplayString as c, withCtx as l } from "vue";
|
|
6
|
+
//#region src/components/CardHistoryViewer.vue
|
|
7
|
+
var u = { class: "card-history-viewer" }, d = { key: 0 }, f = { key: 1 }, p = {
|
|
8
|
+
key: 2,
|
|
9
|
+
class: "error-message"
|
|
10
|
+
}, m = { key: 3 };
|
|
11
|
+
function _sfc_render(e, t, h, g, _, v) {
|
|
12
|
+
let y = s("v-card-title"), b = s("v-list-item-title"), x = s("v-list-item-subtitle"), S = s("v-list-item-content"), C = s("v-list-item"), w = s("v-card"), T = s("v-data-table");
|
|
13
|
+
return o(), r("div", u, [e.userId ? (o(), r("h3", d, "Card History for User: " + c(e.userId), 1)) : n("", !0), e.loading ? (o(), r("div", f, "Loading...")) : e.error ? (o(), r("div", p, c(e.error), 1)) : e.cardHistory ? (o(), r("div", m, [a(w, { class: "mb-4" }, {
|
|
14
|
+
default: l(() => [
|
|
15
|
+
a(y, null, {
|
|
16
|
+
default: l(() => t[0] || (t[0] = [i("Summary")])),
|
|
17
|
+
_: 1
|
|
18
|
+
}),
|
|
19
|
+
a(C, null, {
|
|
20
|
+
default: l(() => [a(S, null, {
|
|
21
|
+
default: l(() => [a(b, null, {
|
|
22
|
+
default: l(() => [i("Best Interval: " + c(e.cardHistory.bestInterval) + " seconds (" + c(e.bestIntervalHumanized) + ")", 1)]),
|
|
23
|
+
_: 1
|
|
24
|
+
}), a(x, null, {
|
|
25
|
+
default: l(() => t[1] || (t[1] = [i("The to-date largest interval between successful card reviews.")])),
|
|
26
|
+
_: 1
|
|
27
|
+
})]),
|
|
28
|
+
_: 1
|
|
29
|
+
})]),
|
|
30
|
+
_: 1
|
|
31
|
+
}),
|
|
32
|
+
a(C, null, {
|
|
33
|
+
default: l(() => [a(S, null, {
|
|
34
|
+
default: l(() => [a(b, null, {
|
|
35
|
+
default: l(() => [i("Lapses: " + c(e.cardHistory.lapses), 1)]),
|
|
36
|
+
_: 1
|
|
37
|
+
}), a(x, null, {
|
|
38
|
+
default: l(() => t[2] || (t[2] = [i("The number of times that a card has been failed in review.")])),
|
|
39
|
+
_: 1
|
|
40
|
+
})]),
|
|
41
|
+
_: 1
|
|
42
|
+
})]),
|
|
43
|
+
_: 1
|
|
44
|
+
}),
|
|
45
|
+
a(C, null, {
|
|
46
|
+
default: l(() => [a(S, null, {
|
|
47
|
+
default: l(() => [a(b, null, {
|
|
48
|
+
default: l(() => [i("Streak: " + c(e.cardHistory.streak), 1)]),
|
|
49
|
+
_: 1
|
|
50
|
+
}), a(x, null, {
|
|
51
|
+
default: l(() => t[3] || (t[3] = [i("The number of consecutive successful impressions on this card.")])),
|
|
52
|
+
_: 1
|
|
53
|
+
})]),
|
|
54
|
+
_: 1
|
|
55
|
+
})]),
|
|
56
|
+
_: 1
|
|
57
|
+
})
|
|
58
|
+
]),
|
|
59
|
+
_: 1
|
|
60
|
+
}), a(T, {
|
|
61
|
+
headers: e.headers,
|
|
62
|
+
items: e.history,
|
|
63
|
+
class: "elevation-1"
|
|
64
|
+
}, null, 8, ["headers", "items"])])) : n("", !0)]);
|
|
65
|
+
}
|
|
66
|
+
var h = /* @__PURE__ */ e(t, [["render", _sfc_render], ["__scopeId", "data-v-a4bdfdfb"]]);
|
|
67
|
+
//#endregion
|
|
68
|
+
export { h as default };
|
|
69
|
+
|
|
70
|
+
//# sourceMappingURL=CardHistoryViewer.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CardHistoryViewer.mjs","names":[],"sources":["../../src/components/CardHistoryViewer.vue"],"sourcesContent":["<template>\n <div class=\"card-history-viewer\">\n <h3 v-if=\"userId\">Card History for User: {{ userId }}</h3>\n <div v-if=\"loading\">Loading...</div>\n <div v-else-if=\"error\" class=\"error-message\">{{ error }}</div>\n <div v-else-if=\"cardHistory\">\n <v-card class=\"mb-4\">\n <v-card-title>Summary</v-card-title>\n <v-list-item>\n <v-list-item-content>\n <v-list-item-title\n >Best Interval: {{ cardHistory.bestInterval }} seconds ({{ bestIntervalHumanized }})</v-list-item-title\n >\n <v-list-item-subtitle>The to-date largest interval between successful card reviews.</v-list-item-subtitle>\n </v-list-item-content>\n </v-list-item>\n <v-list-item>\n <v-list-item-content>\n <v-list-item-title>Lapses: {{ cardHistory.lapses }}</v-list-item-title>\n <v-list-item-subtitle>The number of times that a card has been failed in review.</v-list-item-subtitle>\n </v-list-item-content>\n </v-list-item>\n <v-list-item>\n <v-list-item-content>\n <v-list-item-title>Streak: {{ cardHistory.streak }}</v-list-item-title>\n <v-list-item-subtitle>The number of consecutive successful impressions on this card.</v-list-item-subtitle>\n </v-list-item-content>\n </v-list-item>\n </v-card>\n <v-data-table :headers=\"headers\" :items=\"history\" class=\"elevation-1\"></v-data-table>\n </div>\n </div>\n</template>\n\n<script lang=\"ts\">\nimport { defineComponent, PropType } from 'vue';\nimport { UserDBInterface, UserDBReader, CardHistory, CardRecord, getCardHistoryID } from '@vue-skuilder/db';\nimport moment, { Moment } from 'moment';\n\ninterface FormattedRecord extends CardRecord {\n formattedTimeStamp: string;\n intervalFromPrevious?: number;\n userFriendlyInterval?: string;\n timeSpentSeconds: number;\n}\n\nexport default defineComponent({\n name: 'CardHistoryViewer',\n props: {\n cardId: {\n type: String,\n required: true,\n },\n courseId: {\n type: String,\n required: true,\n },\n userId: {\n type: String,\n required: true,\n },\n userDB: {\n type: Object as PropType<UserDBReader>,\n required: true,\n },\n },\n data() {\n return {\n history: [] as FormattedRecord[],\n cardHistory: null as CardHistory<CardRecord> | null,\n bestIntervalHumanized: '',\n loading: false,\n error: null as string | null,\n headers: [\n { title: 'Timestamp', key: 'formattedTimeStamp' },\n { title: 'Interval', key: 'userFriendlyInterval' },\n { title: 'Time Spent (s)', key: 'timeSpentSeconds' },\n { title: 'Correct?', key: 'isCorrect' },\n { title: 'Performance', key: 'performance' },\n { title: 'Prior Attempts', key: 'priorAttemps' },\n { title: 'User Answer', key: 'userAnswer' },\n ],\n };\n },\n watch: {\n cardId: {\n immediate: true,\n handler(newCardId) {\n if (newCardId && this.userDB) {\n this.fetchHistory();\n }\n },\n },\n userDB: {\n handler(newUserDB) {\n if (newUserDB && this.cardId) {\n this.fetchHistory();\n }\n },\n },\n },\n methods: {\n async fetchHistory() {\n this.loading = true;\n this.error = null;\n try {\n const cardHistoryID = getCardHistoryID(this.courseId, this.cardId);\n const historyDoc: CardHistory<CardRecord> = await this.userDB.get(cardHistoryID);\n this.cardHistory = historyDoc;\n this.bestIntervalHumanized = moment.duration(historyDoc.bestInterval, 'seconds').humanize();\n\n // Sort records by timestamp and format them\n const sortedRecords = [...historyDoc.records].sort(\n (a, b) => moment(a.timeStamp).valueOf() - moment(b.timeStamp).valueOf()\n );\n\n this.history = sortedRecords.map((record, index) => {\n const currentTime = moment(record.timeStamp);\n const formatted: FormattedRecord = {\n ...record,\n formattedTimeStamp: currentTime.format('YYYY-MM-DD HH:mm:ss'),\n timeSpentSeconds: Math.round(record.timeSpent / 1000),\n isCorrect: (record as any).isCorrect,\n performance: (record as any).performance,\n priorAttemps: (record as any).priorAttemps,\n userAnswer: (record as any).userAnswer,\n };\n\n // Calculate interval from previous record\n if (index > 0) {\n const previousTime = moment(sortedRecords[index - 1].timeStamp);\n const intervalSeconds = currentTime.diff(previousTime, 'seconds', true);\n formatted.intervalFromPrevious = Math.round(intervalSeconds * 100) / 100;\n formatted.userFriendlyInterval = `${formatted.intervalFromPrevious} sec (${moment.duration(intervalSeconds, 'seconds').humanize()})`;\n }\n\n return formatted;\n });\n } catch (e) {\n this.error = 'Error fetching card history.';\n console.error(e);\n } finally {\n this.loading = false;\n }\n },\n },\n});\n</script>\n\n<style scoped>\n.error-message {\n color: #f44336;\n padding: 16px;\n background-color: #ffebee;\n border-radius: 4px;\n margin: 8px 0;\n}\n\n.negative-interval {\n color: #f44336;\n font-weight: bold;\n background-color: #ffebee;\n padding: 2px 4px;\n border-radius: 3px;\n}\n\n.invalid-timestamp {\n color: #f44336;\n font-weight: bold;\n}\n\n.card-history-viewer {\n margin: 16px 0;\n}\n</style>\n"],"mappings":";;;;;;UACO,OAAM,uBAAqB,QADlC,KAAA,GAAA,QAAA,KAAA,GAAA;CAAA,KAAA;CAI2B,OAAM;SAJjC,KAAA,GAAA;;;aACE,EA8BM,OA9BN,GA8BM,CA7BM,EAAA,UAAA,GAAA,EAAV,EAA0D,MAF9D,GAEsB,4BAAuB,EAAG,EAAA,OAAM,EAAA,EAAA,IAFtD,EAAA,IAAA,GAAA,EAGe,EAAA,WAAA,GAAA,EAAX,EAAoC,OAHxC,GAGwB,aAAU,IACd,EAAA,SAAA,GAAA,EAAhB,EAA8D,OAA9D,GAA8D,EAAd,EAAA,MAAK,EAAA,EAAA,IACrC,EAAA,eAAA,GAAA,EAAhB,EAyBM,OA9BV,GAAA,CAMM,EAsBS,GAAA,EAtBD,OAAM,QAAM,EAAA;EAN1B,SAAA,QAO4C;GAApC,EAAoC,GAAA,MAAA;IAP5C,SAAA,QAO6B,EAAA,OAAA,EAAA,KAAA,CAP7B,EAOsB,UAAO,CAAA,EAAA;IAP7B,GAAA;;GAQQ,EAOc,GAAA,MAAA;IAftB,SAAA,QAcgC,CALtB,EAKsB,GAAA,MAAA;KAdhC,SAAA,QAYa,CAFD,EAEC,GAAA,MAAA;MAZb,SAAA,QAW8B,CAX9B,EAWe,oBAAe,EAAG,EAAA,YAAY,aAAY,GAAG,eAAU,EAAG,EAAA,sBAAqB,GAAG,KAAC,EAAA,CAAA,CAAA;MAXlG,GAAA;SAaY,EAA0G,GAAA,MAAA;MAbtH,SAAA,QAa+F,EAAA,OAAA,EAAA,KAAA,CAb/F,EAakC,gEAA6D,CAAA,EAAA;MAb/F,GAAA;;KAAA,GAAA;;IAAA,GAAA;;GAgBQ,EAKc,GAAA,MAAA;IArBtB,SAAA,QAoBgC,CAHtB,EAGsB,GAAA,MAAA;KApBhC,SAAA,QAkBmF,CAAvE,EAAuE,GAAA,MAAA;MAlBnF,SAAA,QAkBuC,CAlBvC,EAkB+B,aAAQ,EAAG,EAAA,YAAY,OAAM,EAAA,EAAA,CAAA,CAAA;MAlB5D,GAAA;SAmBY,EAAuG,GAAA,MAAA;MAnBnH,SAAA,QAmB4F,EAAA,OAAA,EAAA,KAAA,CAnB5F,EAmBkC,6DAA0D,CAAA,EAAA;MAnB5F,GAAA;;KAAA,GAAA;;IAAA,GAAA;;GAsBQ,EAKc,GAAA,MAAA;IA3BtB,SAAA,QA0BgC,CAHtB,EAGsB,GAAA,MAAA;KA1BhC,SAAA,QAwBmF,CAAvE,EAAuE,GAAA,MAAA;MAxBnF,SAAA,QAwBuC,CAxBvC,EAwB+B,aAAQ,EAAG,EAAA,YAAY,OAAM,EAAA,EAAA,CAAA,CAAA;MAxB5D,GAAA;SAyBY,EAA2G,GAAA,MAAA;MAzBvH,SAAA,QAyBgG,EAAA,OAAA,EAAA,KAAA,CAzBhG,EAyBkC,iEAA8D,CAAA,EAAA;MAzBhG,GAAA;;KAAA,GAAA;;IAAA,GAAA;;;EAAA,GAAA;KA6BM,EAAqF,GAAA;EAAtE,SAAS,EAAA;EAAU,OAAO,EAAA;EAAS,OAAM;wCA7B9D,EAAA,IAAA,GAAA,CAAA,CAAA"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import "../_virtual/_rolldown/runtime.mjs";
|
|
2
|
+
import { defineComponent as e } from "vue";
|
|
3
|
+
import t from "moment";
|
|
4
|
+
import { getCardHistoryID as n } from "@vue-skuilder/db";
|
|
5
|
+
//#region src/components/CardHistoryViewer.vue?vue&type=script&lang.ts
|
|
6
|
+
var r = e({
|
|
7
|
+
name: "CardHistoryViewer",
|
|
8
|
+
props: {
|
|
9
|
+
cardId: {
|
|
10
|
+
type: String,
|
|
11
|
+
required: !0
|
|
12
|
+
},
|
|
13
|
+
courseId: {
|
|
14
|
+
type: String,
|
|
15
|
+
required: !0
|
|
16
|
+
},
|
|
17
|
+
userId: {
|
|
18
|
+
type: String,
|
|
19
|
+
required: !0
|
|
20
|
+
},
|
|
21
|
+
userDB: {
|
|
22
|
+
type: Object,
|
|
23
|
+
required: !0
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
data() {
|
|
27
|
+
return {
|
|
28
|
+
history: [],
|
|
29
|
+
cardHistory: null,
|
|
30
|
+
bestIntervalHumanized: "",
|
|
31
|
+
loading: !1,
|
|
32
|
+
error: null,
|
|
33
|
+
headers: [
|
|
34
|
+
{
|
|
35
|
+
title: "Timestamp",
|
|
36
|
+
key: "formattedTimeStamp"
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
title: "Interval",
|
|
40
|
+
key: "userFriendlyInterval"
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
title: "Time Spent (s)",
|
|
44
|
+
key: "timeSpentSeconds"
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
title: "Correct?",
|
|
48
|
+
key: "isCorrect"
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
title: "Performance",
|
|
52
|
+
key: "performance"
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
title: "Prior Attempts",
|
|
56
|
+
key: "priorAttemps"
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
title: "User Answer",
|
|
60
|
+
key: "userAnswer"
|
|
61
|
+
}
|
|
62
|
+
]
|
|
63
|
+
};
|
|
64
|
+
},
|
|
65
|
+
watch: {
|
|
66
|
+
cardId: {
|
|
67
|
+
immediate: !0,
|
|
68
|
+
handler(e) {
|
|
69
|
+
e && this.userDB && this.fetchHistory();
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
userDB: { handler(e) {
|
|
73
|
+
e && this.cardId && this.fetchHistory();
|
|
74
|
+
} }
|
|
75
|
+
},
|
|
76
|
+
methods: { async fetchHistory() {
|
|
77
|
+
this.loading = !0, this.error = null;
|
|
78
|
+
try {
|
|
79
|
+
let e = n(this.courseId, this.cardId), r = await this.userDB.get(e);
|
|
80
|
+
this.cardHistory = r, this.bestIntervalHumanized = t.duration(r.bestInterval, "seconds").humanize();
|
|
81
|
+
let i = [...r.records].sort((e, n) => t(e.timeStamp).valueOf() - t(n.timeStamp).valueOf());
|
|
82
|
+
this.history = i.map((e, n) => {
|
|
83
|
+
let r = t(e.timeStamp), a = {
|
|
84
|
+
...e,
|
|
85
|
+
formattedTimeStamp: r.format("YYYY-MM-DD HH:mm:ss"),
|
|
86
|
+
timeSpentSeconds: Math.round(e.timeSpent / 1e3),
|
|
87
|
+
isCorrect: e.isCorrect,
|
|
88
|
+
performance: e.performance,
|
|
89
|
+
priorAttemps: e.priorAttemps,
|
|
90
|
+
userAnswer: e.userAnswer
|
|
91
|
+
};
|
|
92
|
+
if (n > 0) {
|
|
93
|
+
let e = t(i[n - 1].timeStamp), o = r.diff(e, "seconds", !0);
|
|
94
|
+
a.intervalFromPrevious = Math.round(o * 100) / 100, a.userFriendlyInterval = `${a.intervalFromPrevious} sec (${t.duration(o, "seconds").humanize()})`;
|
|
95
|
+
}
|
|
96
|
+
return a;
|
|
97
|
+
});
|
|
98
|
+
} catch (e) {
|
|
99
|
+
this.error = "Error fetching card history.", console.error(e);
|
|
100
|
+
} finally {
|
|
101
|
+
this.loading = !1;
|
|
102
|
+
}
|
|
103
|
+
} }
|
|
104
|
+
});
|
|
105
|
+
//#endregion
|
|
106
|
+
export { r as default };
|
|
107
|
+
|
|
108
|
+
//# sourceMappingURL=CardHistoryViewer.vue_vue_type_script_lang.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CardHistoryViewer.vue_vue_type_script_lang.mjs","names":[],"sources":["../../src/components/CardHistoryViewer.vue"],"sourcesContent":["<template>\n <div class=\"card-history-viewer\">\n <h3 v-if=\"userId\">Card History for User: {{ userId }}</h3>\n <div v-if=\"loading\">Loading...</div>\n <div v-else-if=\"error\" class=\"error-message\">{{ error }}</div>\n <div v-else-if=\"cardHistory\">\n <v-card class=\"mb-4\">\n <v-card-title>Summary</v-card-title>\n <v-list-item>\n <v-list-item-content>\n <v-list-item-title\n >Best Interval: {{ cardHistory.bestInterval }} seconds ({{ bestIntervalHumanized }})</v-list-item-title\n >\n <v-list-item-subtitle>The to-date largest interval between successful card reviews.</v-list-item-subtitle>\n </v-list-item-content>\n </v-list-item>\n <v-list-item>\n <v-list-item-content>\n <v-list-item-title>Lapses: {{ cardHistory.lapses }}</v-list-item-title>\n <v-list-item-subtitle>The number of times that a card has been failed in review.</v-list-item-subtitle>\n </v-list-item-content>\n </v-list-item>\n <v-list-item>\n <v-list-item-content>\n <v-list-item-title>Streak: {{ cardHistory.streak }}</v-list-item-title>\n <v-list-item-subtitle>The number of consecutive successful impressions on this card.</v-list-item-subtitle>\n </v-list-item-content>\n </v-list-item>\n </v-card>\n <v-data-table :headers=\"headers\" :items=\"history\" class=\"elevation-1\"></v-data-table>\n </div>\n </div>\n</template>\n\n<script lang=\"ts\">\nimport { defineComponent, PropType } from 'vue';\nimport { UserDBInterface, UserDBReader, CardHistory, CardRecord, getCardHistoryID } from '@vue-skuilder/db';\nimport moment, { Moment } from 'moment';\n\ninterface FormattedRecord extends CardRecord {\n formattedTimeStamp: string;\n intervalFromPrevious?: number;\n userFriendlyInterval?: string;\n timeSpentSeconds: number;\n}\n\nexport default defineComponent({\n name: 'CardHistoryViewer',\n props: {\n cardId: {\n type: String,\n required: true,\n },\n courseId: {\n type: String,\n required: true,\n },\n userId: {\n type: String,\n required: true,\n },\n userDB: {\n type: Object as PropType<UserDBReader>,\n required: true,\n },\n },\n data() {\n return {\n history: [] as FormattedRecord[],\n cardHistory: null as CardHistory<CardRecord> | null,\n bestIntervalHumanized: '',\n loading: false,\n error: null as string | null,\n headers: [\n { title: 'Timestamp', key: 'formattedTimeStamp' },\n { title: 'Interval', key: 'userFriendlyInterval' },\n { title: 'Time Spent (s)', key: 'timeSpentSeconds' },\n { title: 'Correct?', key: 'isCorrect' },\n { title: 'Performance', key: 'performance' },\n { title: 'Prior Attempts', key: 'priorAttemps' },\n { title: 'User Answer', key: 'userAnswer' },\n ],\n };\n },\n watch: {\n cardId: {\n immediate: true,\n handler(newCardId) {\n if (newCardId && this.userDB) {\n this.fetchHistory();\n }\n },\n },\n userDB: {\n handler(newUserDB) {\n if (newUserDB && this.cardId) {\n this.fetchHistory();\n }\n },\n },\n },\n methods: {\n async fetchHistory() {\n this.loading = true;\n this.error = null;\n try {\n const cardHistoryID = getCardHistoryID(this.courseId, this.cardId);\n const historyDoc: CardHistory<CardRecord> = await this.userDB.get(cardHistoryID);\n this.cardHistory = historyDoc;\n this.bestIntervalHumanized = moment.duration(historyDoc.bestInterval, 'seconds').humanize();\n\n // Sort records by timestamp and format them\n const sortedRecords = [...historyDoc.records].sort(\n (a, b) => moment(a.timeStamp).valueOf() - moment(b.timeStamp).valueOf()\n );\n\n this.history = sortedRecords.map((record, index) => {\n const currentTime = moment(record.timeStamp);\n const formatted: FormattedRecord = {\n ...record,\n formattedTimeStamp: currentTime.format('YYYY-MM-DD HH:mm:ss'),\n timeSpentSeconds: Math.round(record.timeSpent / 1000),\n isCorrect: (record as any).isCorrect,\n performance: (record as any).performance,\n priorAttemps: (record as any).priorAttemps,\n userAnswer: (record as any).userAnswer,\n };\n\n // Calculate interval from previous record\n if (index > 0) {\n const previousTime = moment(sortedRecords[index - 1].timeStamp);\n const intervalSeconds = currentTime.diff(previousTime, 'seconds', true);\n formatted.intervalFromPrevious = Math.round(intervalSeconds * 100) / 100;\n formatted.userFriendlyInterval = `${formatted.intervalFromPrevious} sec (${moment.duration(intervalSeconds, 'seconds').humanize()})`;\n }\n\n return formatted;\n });\n } catch (e) {\n this.error = 'Error fetching card history.';\n console.error(e);\n } finally {\n this.loading = false;\n }\n },\n },\n});\n</script>\n\n<style scoped>\n.error-message {\n color: #f44336;\n padding: 16px;\n background-color: #ffebee;\n border-radius: 4px;\n margin: 8px 0;\n}\n\n.negative-interval {\n color: #f44336;\n font-weight: bold;\n background-color: #ffebee;\n padding: 2px 4px;\n border-radius: 3px;\n}\n\n.invalid-timestamp {\n color: #f44336;\n font-weight: bold;\n}\n\n.card-history-viewer {\n margin: 16px 0;\n}\n</style>\n"],"mappings":";;;;;AA8CA,IAAA,IAAe,EAAgB;CAC7B,MAAM;CACN,OAAO;EACL,QAAQ;GACN,MAAM;GACN,UAAU;GACX;EACD,UAAU;GACR,MAAM;GACN,UAAU;GACX;EACD,QAAQ;GACN,MAAM;GACN,UAAU;GACX;EACD,QAAQ;GACN,MAAM;GACN,UAAU;GACX;EACF;CACD,OAAO;AACL,SAAO;GACL,SAAS,EAAC;GACV,aAAa;GACb,uBAAuB;GACvB,SAAS;GACT,OAAO;GACP,SAAS;IACP;KAAE,OAAO;KAAa,KAAK;KAAsB;IACjD;KAAE,OAAO;KAAY,KAAK;KAAwB;IAClD;KAAE,OAAO;KAAkB,KAAK;KAAoB;IACpD;KAAE,OAAO;KAAY,KAAK;KAAa;IACvC;KAAE,OAAO;KAAe,KAAK;KAAe;IAC5C;KAAE,OAAO;KAAkB,KAAK;KAAgB;IAChD;KAAE,OAAO;KAAe,KAAK;KAAc;IAC5C;GACF;;CAEH,OAAO;EACL,QAAQ;GACN,WAAW;GACX,QAAQ,GAAW;AACjB,IAAI,KAAa,KAAK,UACpB,KAAK,cAAc;;GAGxB;EACD,QAAQ,EACN,QAAQ,GAAW;AACjB,GAAI,KAAa,KAAK,UACpB,KAAK,cAAc;KAGxB;EACF;CACD,SAAS,EACP,MAAM,eAAe;AAEnB,EADA,KAAK,UAAU,IACf,KAAK,QAAQ;AACb,MAAI;GACF,IAAM,IAAgB,EAAiB,KAAK,UAAU,KAAK,OAAO,EAC5D,IAAsC,MAAM,KAAK,OAAO,IAAI,EAAc;AAEhF,GADA,KAAK,cAAc,GACnB,KAAK,wBAAwB,EAAO,SAAS,EAAW,cAAc,UAAU,CAAC,UAAU;GAG3F,IAAM,IAAgB,CAAC,GAAG,EAAW,QAAQ,CAAC,MAC3C,GAAG,MAAM,EAAO,EAAE,UAAU,CAAC,SAAQ,GAAI,EAAO,EAAE,UAAU,CAAC,SAAQ,CACvE;AAED,QAAK,UAAU,EAAc,KAAK,GAAQ,MAAU;IAClD,IAAM,IAAc,EAAO,EAAO,UAAU,EACtC,IAA6B;KACjC,GAAG;KACH,oBAAoB,EAAY,OAAO,sBAAsB;KAC7D,kBAAkB,KAAK,MAAM,EAAO,YAAY,IAAK;KACrD,WAAY,EAAe;KAC3B,aAAc,EAAe;KAC7B,cAAe,EAAe;KAC9B,YAAa,EAAe;KAC7B;AAGD,QAAI,IAAQ,GAAG;KACb,IAAM,IAAe,EAAO,EAAc,IAAQ,GAAG,UAAU,EACzD,IAAkB,EAAY,KAAK,GAAc,WAAW,GAAK;AAEvE,KADA,EAAU,uBAAuB,KAAK,MAAM,IAAkB,IAAG,GAAI,KACrE,EAAU,uBAAuB,GAAG,EAAU,qBAAoB,QAAS,EAAO,SAAS,GAAiB,UAAU,CAAC,UAAU,CAAC;;AAGpI,WAAO;KACP;WACK,GAAG;AAEV,GADA,KAAK,QAAQ,gCACb,QAAQ,MAAM,EAAE;YACR;AACR,QAAK,UAAU;;IAGpB;CACF,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import "../_virtual/_rolldown/runtime.mjs";
|
|
2
|
+
import e from "../_virtual/_plugin-vue_export-helper.mjs";
|
|
3
|
+
import t from "./CardSearch.vue_vue_type_script_lang.mjs";
|
|
4
|
+
import { createElementBlock as n, createVNode as r, openBlock as i, resolveComponent as a, withKeys as o } from "vue";
|
|
5
|
+
//#region src/components/CardSearch.vue
|
|
6
|
+
var s = { class: "card-search" };
|
|
7
|
+
function _sfc_render(e, t, c, l, u, d) {
|
|
8
|
+
let f = a("v-text-field");
|
|
9
|
+
return i(), n("div", s, [r(f, {
|
|
10
|
+
modelValue: e.query,
|
|
11
|
+
"onUpdate:modelValue": t[0] || (t[0] = (t) => e.query = t),
|
|
12
|
+
label: "Search for cards...",
|
|
13
|
+
"append-icon": "mdi-magnify",
|
|
14
|
+
"onClick:append": e.search,
|
|
15
|
+
onKeydown: o(e.search, ["enter"])
|
|
16
|
+
}, null, 8, [
|
|
17
|
+
"modelValue",
|
|
18
|
+
"onClick:append",
|
|
19
|
+
"onKeydown"
|
|
20
|
+
])]);
|
|
21
|
+
}
|
|
22
|
+
var c = /* @__PURE__ */ e(t, [["render", _sfc_render]]);
|
|
23
|
+
//#endregion
|
|
24
|
+
export { c as default };
|
|
25
|
+
|
|
26
|
+
//# sourceMappingURL=CardSearch.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CardSearch.mjs","names":[],"sources":["../../src/components/CardSearch.vue"],"sourcesContent":["<template>\n <div class=\"card-search\">\n <v-text-field\n v-model=\"query\"\n label=\"Search for cards...\"\n append-icon=\"mdi-magnify\"\n @click:append=\"search\"\n @keydown.enter=\"search\"\n ></v-text-field>\n </div>\n</template>\n\n<script lang=\"ts\">\nimport { defineComponent } from 'vue';\n\nexport default defineComponent({\n name: 'CardSearch',\n emits: {\n search: (query: string) => typeof query === 'string'\n },\n data() {\n return {\n query: '',\n };\n },\n methods: {\n search() {\n this.$emit('search', this.query);\n },\n },\n});\n</script>\n"],"mappings":";;;;;UACO,OAAM,eAAa;;;aAAxB,EAQM,OARN,GAQM,CAPJ,EAMgB,GAAA;EARpB,YAGe,EAAA;EAHf,uBAAA,EAAA,OAAA,EAAA,MAAA,MAAA,EAGe,QAAK;EACd,OAAM;EACN,eAAY;EACX,kBAAc,EAAA;EACd,WAPP,EAOsB,EAAA,QAAM,CAAA,QAAA,CAAA"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import "../_virtual/_rolldown/runtime.mjs";
|
|
2
|
+
import { defineComponent as e } from "vue";
|
|
3
|
+
//#region src/components/CardSearch.vue?vue&type=script&lang.ts
|
|
4
|
+
var t = e({
|
|
5
|
+
name: "CardSearch",
|
|
6
|
+
emits: { search: (e) => typeof e == "string" },
|
|
7
|
+
data() {
|
|
8
|
+
return { query: "" };
|
|
9
|
+
},
|
|
10
|
+
methods: { search() {
|
|
11
|
+
this.$emit("search", this.query);
|
|
12
|
+
} }
|
|
13
|
+
});
|
|
14
|
+
//#endregion
|
|
15
|
+
export { t as default };
|
|
16
|
+
|
|
17
|
+
//# sourceMappingURL=CardSearch.vue_vue_type_script_lang.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CardSearch.vue_vue_type_script_lang.mjs","names":[],"sources":["../../src/components/CardSearch.vue"],"sourcesContent":["<template>\n <div class=\"card-search\">\n <v-text-field\n v-model=\"query\"\n label=\"Search for cards...\"\n append-icon=\"mdi-magnify\"\n @click:append=\"search\"\n @keydown.enter=\"search\"\n ></v-text-field>\n </div>\n</template>\n\n<script lang=\"ts\">\nimport { defineComponent } from 'vue';\n\nexport default defineComponent({\n name: 'CardSearch',\n emits: {\n search: (query: string) => typeof query === 'string'\n },\n data() {\n return {\n query: '',\n };\n },\n methods: {\n search() {\n this.$emit('search', this.query);\n },\n },\n});\n</script>\n"],"mappings":";;;AAeA,IAAA,IAAe,EAAgB;CAC7B,MAAM;CACN,OAAO,EACL,SAAS,MAAkB,OAAO,KAAU,UAC7C;CACD,OAAO;AACL,SAAO,EACL,OAAO,IACR;;CAEH,SAAS,EACP,SAAS;AACP,OAAK,MAAM,UAAU,KAAK,MAAM;IAEnC;CACF,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import "../_virtual/_rolldown/runtime.mjs";
|
|
2
|
+
import e from "../_virtual/_plugin-vue_export-helper.mjs";
|
|
3
|
+
import t from "./CardSearchResults.vue_vue_type_script_lang.mjs";
|
|
4
|
+
/* empty css */
|
|
5
|
+
import { Fragment as n, createBlock as r, createElementBlock as i, createTextVNode as a, createVNode as o, normalizeClass as s, openBlock as c, renderList as l, resolveComponent as u, toDisplayString as d, withCtx as f } from "vue";
|
|
6
|
+
//#region src/components/CardSearchResults.vue
|
|
7
|
+
var p = { class: "card-search-results" }, m = { key: 0 }, h = { key: 1 }, g = { key: 2 };
|
|
8
|
+
function _sfc_render(e, t, _, v, y, b) {
|
|
9
|
+
let x = u("v-list-item-title"), S = u("v-list-item-subtitle"), C = u("v-list-item"), w = u("v-list");
|
|
10
|
+
return c(), i("div", p, [e.loading ? (c(), i("div", m, "Loading...")) : e.error ? (c(), i("div", h, d(e.error), 1)) : (c(), i("div", g, [o(w, null, {
|
|
11
|
+
default: f(() => [(c(!0), i(n, null, l(e.cards, (t) => (c(), r(C, {
|
|
12
|
+
key: t._id,
|
|
13
|
+
onClick: (n) => e.selectCard(t),
|
|
14
|
+
class: s([{ "selected-card": t._id === e.selectedCardId }, "cursor-pointer"])
|
|
15
|
+
}, {
|
|
16
|
+
default: f(() => [o(x, null, {
|
|
17
|
+
default: f(() => [a(d(t._id), 1)]),
|
|
18
|
+
_: 2
|
|
19
|
+
}, 1024), o(S, null, {
|
|
20
|
+
default: f(() => [a("Course: " + d(t.courseId), 1)]),
|
|
21
|
+
_: 2
|
|
22
|
+
}, 1024)]),
|
|
23
|
+
_: 2
|
|
24
|
+
}, 1032, ["onClick", "class"]))), 128))]),
|
|
25
|
+
_: 1
|
|
26
|
+
})]))]);
|
|
27
|
+
}
|
|
28
|
+
var _ = /* @__PURE__ */ e(t, [["render", _sfc_render], ["__scopeId", "data-v-285e13bd"]]);
|
|
29
|
+
//#endregion
|
|
30
|
+
export { _ as default };
|
|
31
|
+
|
|
32
|
+
//# sourceMappingURL=CardSearchResults.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CardSearchResults.mjs","names":[],"sources":["../../src/components/CardSearchResults.vue"],"sourcesContent":["<template>\n <div class=\"card-search-results\">\n <div v-if=\"loading\">Loading...</div>\n <div v-else-if=\"error\">{{ error }}</div>\n <div v-else>\n <v-list>\n <v-list-item \n v-for=\"card in cards\" \n :key=\"card._id\"\n @click=\"selectCard(card)\"\n :class=\"{'selected-card': card._id === selectedCardId}\"\n class=\"cursor-pointer\"\n >\n <v-list-item-title>{{ card._id }}</v-list-item-title>\n <v-list-item-subtitle>Course: {{ card.courseId }}</v-list-item-subtitle>\n </v-list-item>\n </v-list>\n </div>\n </div>\n</template>\n\n<script lang=\"ts\">\nimport { defineComponent, PropType } from 'vue';\nimport { DataLayerProvider, CardData } from '@vue-skuilder/db';\n\ninterface CardWithCourse extends CardData {\n courseId: string;\n}\n\nexport default defineComponent({\n name: 'CardSearchResults',\n emits: {\n 'card-selected': (payload: { cardId: string; courseId: string }) => \n typeof payload.cardId === 'string' && typeof payload.courseId === 'string'\n },\n props: {\n query: {\n type: String,\n required: true,\n },\n dataLayer: {\n type: Object as PropType<DataLayerProvider>,\n required: true,\n },\n courseFilter: {\n type: String as PropType<string | null>,\n default: null,\n },\n },\n data() {\n return {\n cards: [] as CardWithCourse[],\n loading: false,\n error: null as string | null,\n selectedCardId: null as string | null,\n };\n },\n watch: {\n query: {\n immediate: true,\n handler(newQuery) {\n if (newQuery) {\n this.fetchResults(newQuery);\n }\n },\n },\n },\n methods: {\n async fetchResults(query: string) {\n this.loading = true;\n this.error = null;\n try {\n let courseIds: string[] = [];\n \n // Get course IDs efficiently\n if (this.courseFilter) {\n // Single course search - no need to fetch all courses\n courseIds = [this.courseFilter];\n console.log(`Filtering search to course: ${this.courseFilter}`);\n } else {\n // Get all course IDs without expensive config lookups\n const { CourseLookup } = await import('@vue-skuilder/db');\n const lookupCourses = await CourseLookup.allCourseWare();\n courseIds = lookupCourses.map(c => c._id).filter(Boolean);\n console.log(`Searching across all ${courseIds.length} courses`);\n }\n \n const allCards: CardWithCourse[] = [];\n\n for (const courseId of courseIds) {\n const courseDB = this.dataLayer.getCourseDB(courseId);\n const cards = await courseDB.searchCards(query);\n \n for (const card of cards) {\n allCards.push({\n ...card,\n courseId: courseId,\n });\n }\n }\n this.cards = allCards;\n console.log(`Search completed: found ${allCards.length} cards across ${courseIds.length} courses`);\n } catch (e) {\n this.error = 'Error fetching search results.';\n console.error('Search error:', e);\n } finally {\n this.loading = false;\n }\n },\n \n selectCard(card: CardWithCourse) {\n this.selectedCardId = card._id;\n this.$emit('card-selected', { cardId: card._id, courseId: card.courseId });\n },\n },\n});\n</script>\n\n<style scoped>\n.selected-card {\n background-color: #e0f2f7; /* Light blue background */\n border-left: 4px solid #2196f3; /* Blue left border */\n}\n\n.cursor-pointer {\n cursor: pointer;\n}\n</style>\n"],"mappings":";;;;;;UACO,OAAM,uBAAqB,QADlC,KAAA,GAAA,QAAA,KAAA,GAAA,QAAA,KAAA,GAAA;;;aACE,EAiBM,OAjBN,GAiBM,CAhBO,EAAA,WAAA,GAAA,EAAX,EAAoC,OAFxC,GAEwB,aAAU,IACd,EAAA,SAAA,GAAA,EAAhB,EAAwC,OAH5C,GAAA,EAG8B,EAAA,MAAK,EAAA,EAAA,KAAA,GAAA,EAC/B,EAaM,OAjBV,GAAA,CAKM,EAWS,GAAA,MAAA;EAhBf,SAAA,QAO+B,EAAA,EAAA,GAAA,EADvB,EASc,GAAA,MAftB,EAOyB,EAAA,QAAR,YADT,EASc,GAAA;GAPX,KAAK,EAAK;GACV,UAAK,MAAE,EAAA,WAAW,EAAI;GACtB,OAVX,EAAA,CAAA,EAAA,iBAUoC,EAAK,QAAQ,EAAA,gBAAc,EAC/C,iBAAgB,CAAA;;GAXhC,SAAA,QAa+D,CAArD,EAAqD,GAAA,MAAA;IAb/D,SAAA,QAa2C,CAb3C,EAAA,EAagC,EAAK,IAAG,EAAA,EAAA,CAAA,CAAA;IAbxC,GAAA;aAcU,EAAwE,GAAA,MAAA;IAdlF,SAAA,QAcwC,CAdxC,EAcgC,aAAQ,EAAG,EAAK,SAAQ,EAAA,EAAA,CAAA,CAAA;IAdxD,GAAA;;GAAA,GAAA;;EAAA,GAAA"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import "../_virtual/_rolldown/runtime.mjs";
|
|
2
|
+
import { defineComponent as e } from "vue";
|
|
3
|
+
//#region src/components/CardSearchResults.vue?vue&type=script&lang.ts
|
|
4
|
+
var t = e({
|
|
5
|
+
name: "CardSearchResults",
|
|
6
|
+
emits: { "card-selected": (e) => typeof e.cardId == "string" && typeof e.courseId == "string" },
|
|
7
|
+
props: {
|
|
8
|
+
query: {
|
|
9
|
+
type: String,
|
|
10
|
+
required: !0
|
|
11
|
+
},
|
|
12
|
+
dataLayer: {
|
|
13
|
+
type: Object,
|
|
14
|
+
required: !0
|
|
15
|
+
},
|
|
16
|
+
courseFilter: {
|
|
17
|
+
type: String,
|
|
18
|
+
default: null
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
data() {
|
|
22
|
+
return {
|
|
23
|
+
cards: [],
|
|
24
|
+
loading: !1,
|
|
25
|
+
error: null,
|
|
26
|
+
selectedCardId: null
|
|
27
|
+
};
|
|
28
|
+
},
|
|
29
|
+
watch: { query: {
|
|
30
|
+
immediate: !0,
|
|
31
|
+
handler(e) {
|
|
32
|
+
e && this.fetchResults(e);
|
|
33
|
+
}
|
|
34
|
+
} },
|
|
35
|
+
methods: {
|
|
36
|
+
async fetchResults(e) {
|
|
37
|
+
this.loading = !0, this.error = null;
|
|
38
|
+
try {
|
|
39
|
+
let t = [];
|
|
40
|
+
if (this.courseFilter) t = [this.courseFilter], console.log(`Filtering search to course: ${this.courseFilter}`);
|
|
41
|
+
else {
|
|
42
|
+
let { CourseLookup: e } = await import("@vue-skuilder/db");
|
|
43
|
+
t = (await e.allCourseWare()).map((e) => e._id).filter(Boolean), console.log(`Searching across all ${t.length} courses`);
|
|
44
|
+
}
|
|
45
|
+
let n = [];
|
|
46
|
+
for (let r of t) {
|
|
47
|
+
let t = await this.dataLayer.getCourseDB(r).searchCards(e);
|
|
48
|
+
for (let e of t) n.push({
|
|
49
|
+
...e,
|
|
50
|
+
courseId: r
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
this.cards = n, console.log(`Search completed: found ${n.length} cards across ${t.length} courses`);
|
|
54
|
+
} catch (e) {
|
|
55
|
+
this.error = "Error fetching search results.", console.error("Search error:", e);
|
|
56
|
+
} finally {
|
|
57
|
+
this.loading = !1;
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
selectCard(e) {
|
|
61
|
+
this.selectedCardId = e._id, this.$emit("card-selected", {
|
|
62
|
+
cardId: e._id,
|
|
63
|
+
courseId: e.courseId
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
//#endregion
|
|
69
|
+
export { t as default };
|
|
70
|
+
|
|
71
|
+
//# sourceMappingURL=CardSearchResults.vue_vue_type_script_lang.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CardSearchResults.vue_vue_type_script_lang.mjs","names":[],"sources":["../../src/components/CardSearchResults.vue"],"sourcesContent":["<template>\n <div class=\"card-search-results\">\n <div v-if=\"loading\">Loading...</div>\n <div v-else-if=\"error\">{{ error }}</div>\n <div v-else>\n <v-list>\n <v-list-item \n v-for=\"card in cards\" \n :key=\"card._id\"\n @click=\"selectCard(card)\"\n :class=\"{'selected-card': card._id === selectedCardId}\"\n class=\"cursor-pointer\"\n >\n <v-list-item-title>{{ card._id }}</v-list-item-title>\n <v-list-item-subtitle>Course: {{ card.courseId }}</v-list-item-subtitle>\n </v-list-item>\n </v-list>\n </div>\n </div>\n</template>\n\n<script lang=\"ts\">\nimport { defineComponent, PropType } from 'vue';\nimport { DataLayerProvider, CardData } from '@vue-skuilder/db';\n\ninterface CardWithCourse extends CardData {\n courseId: string;\n}\n\nexport default defineComponent({\n name: 'CardSearchResults',\n emits: {\n 'card-selected': (payload: { cardId: string; courseId: string }) => \n typeof payload.cardId === 'string' && typeof payload.courseId === 'string'\n },\n props: {\n query: {\n type: String,\n required: true,\n },\n dataLayer: {\n type: Object as PropType<DataLayerProvider>,\n required: true,\n },\n courseFilter: {\n type: String as PropType<string | null>,\n default: null,\n },\n },\n data() {\n return {\n cards: [] as CardWithCourse[],\n loading: false,\n error: null as string | null,\n selectedCardId: null as string | null,\n };\n },\n watch: {\n query: {\n immediate: true,\n handler(newQuery) {\n if (newQuery) {\n this.fetchResults(newQuery);\n }\n },\n },\n },\n methods: {\n async fetchResults(query: string) {\n this.loading = true;\n this.error = null;\n try {\n let courseIds: string[] = [];\n \n // Get course IDs efficiently\n if (this.courseFilter) {\n // Single course search - no need to fetch all courses\n courseIds = [this.courseFilter];\n console.log(`Filtering search to course: ${this.courseFilter}`);\n } else {\n // Get all course IDs without expensive config lookups\n const { CourseLookup } = await import('@vue-skuilder/db');\n const lookupCourses = await CourseLookup.allCourseWare();\n courseIds = lookupCourses.map(c => c._id).filter(Boolean);\n console.log(`Searching across all ${courseIds.length} courses`);\n }\n \n const allCards: CardWithCourse[] = [];\n\n for (const courseId of courseIds) {\n const courseDB = this.dataLayer.getCourseDB(courseId);\n const cards = await courseDB.searchCards(query);\n \n for (const card of cards) {\n allCards.push({\n ...card,\n courseId: courseId,\n });\n }\n }\n this.cards = allCards;\n console.log(`Search completed: found ${allCards.length} cards across ${courseIds.length} courses`);\n } catch (e) {\n this.error = 'Error fetching search results.';\n console.error('Search error:', e);\n } finally {\n this.loading = false;\n }\n },\n \n selectCard(card: CardWithCourse) {\n this.selectedCardId = card._id;\n this.$emit('card-selected', { cardId: card._id, courseId: card.courseId });\n },\n },\n});\n</script>\n\n<style scoped>\n.selected-card {\n background-color: #e0f2f7; /* Light blue background */\n border-left: 4px solid #2196f3; /* Blue left border */\n}\n\n.cursor-pointer {\n cursor: pointer;\n}\n</style>\n"],"mappings":";;;AA6BA,IAAA,IAAe,EAAgB;CAC7B,MAAM;CACN,OAAO,EACL,kBAAkB,MAChB,OAAO,EAAQ,UAAW,YAAY,OAAO,EAAQ,YAAa,UACrE;CACD,OAAO;EACL,OAAO;GACL,MAAM;GACN,UAAU;GACX;EACD,WAAW;GACT,MAAM;GACN,UAAU;GACX;EACD,cAAc;GACZ,MAAM;GACN,SAAS;GACV;EACF;CACD,OAAO;AACL,SAAO;GACL,OAAO,EAAC;GACR,SAAS;GACT,OAAO;GACP,gBAAgB;GACjB;;CAEH,OAAO,EACL,OAAO;EACL,WAAW;EACX,QAAQ,GAAU;AAChB,GAAI,KACF,KAAK,aAAa,EAAS;;EAGhC,EACF;CACD,SAAS;EACP,MAAM,aAAa,GAAe;AAEhC,GADA,KAAK,UAAU,IACf,KAAK,QAAQ;AACb,OAAI;IACF,IAAI,IAAsB,EAAE;AAG5B,QAAI,KAAK,aAGP,CADA,IAAY,CAAC,KAAK,aAAa,EAC/B,QAAQ,IAAI,+BAA+B,KAAK,eAAe;SAC1D;KAEL,IAAM,EAAE,oBAAiB,MAAM,OAAO;AAGtC,KADA,KADsB,MAAM,EAAa,eAAe,EAC9B,KAAI,MAAK,EAAE,IAAI,CAAC,OAAO,QAAQ,EACzD,QAAQ,IAAI,wBAAwB,EAAU,OAAM,UAAW;;IAGjE,IAAM,IAA6B,EAAE;AAErC,SAAK,IAAM,KAAY,GAAW;KAEhC,IAAM,IAAQ,MADG,KAAK,UAAU,YAAY,EAAS,CACxB,YAAY,EAAM;AAE/C,UAAK,IAAM,KAAQ,EACjB,GAAS,KAAK;MACZ,GAAG;MACO;MACX,CAAC;;AAIN,IADA,KAAK,QAAQ,GACb,QAAQ,IAAI,2BAA2B,EAAS,OAAM,gBAAiB,EAAU,OAAM,UAAW;YAC3F,GAAG;AAEV,IADA,KAAK,QAAQ,kCACb,QAAQ,MAAM,iBAAiB,EAAE;aACzB;AACR,SAAK,UAAU;;;EAInB,WAAW,GAAsB;AAE/B,GADA,KAAK,iBAAiB,EAAK,KAC3B,KAAK,MAAM,iBAAiB;IAAE,QAAQ,EAAK;IAAK,UAAU,EAAK;IAAU,CAAC;;EAE7E;CACF,CAAC"}
|