@vue-skuilder/common-ui 0.1.4 → 0.1.6

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 (65) hide show
  1. package/dist/assets/index.css +2 -2
  2. package/dist/common-ui.es.js +1471 -295
  3. package/dist/common-ui.es.js.map +1 -1
  4. package/dist/common-ui.umd.js +2 -2
  5. package/dist/common-ui.umd.js.map +1 -1
  6. package/dist/components/HeatMap.types.d.ts +1 -0
  7. package/dist/components/HeatMap.types.d.ts.map +1 -0
  8. package/dist/components/PaginatingToolbar.types.d.ts +1 -0
  9. package/dist/components/PaginatingToolbar.types.d.ts.map +1 -0
  10. package/dist/components/SkMouseTrap.types.d.ts +1 -0
  11. package/dist/components/SkMouseTrap.types.d.ts.map +1 -0
  12. package/dist/components/SkMouseTrapToolTip.types.d.ts +1 -0
  13. package/dist/components/SkMouseTrapToolTip.types.d.ts.map +1 -0
  14. package/dist/components/SnackbarService.d.ts +1 -0
  15. package/dist/components/SnackbarService.d.ts.map +1 -0
  16. package/dist/components/StudySession.types.d.ts +1 -0
  17. package/dist/components/StudySession.types.d.ts.map +1 -0
  18. package/dist/components/auth/index.d.ts +1 -0
  19. package/dist/components/auth/index.d.ts.map +1 -0
  20. package/dist/components/cardRendering/MarkdownRendererHelpers.d.ts +1 -0
  21. package/dist/components/cardRendering/MarkdownRendererHelpers.d.ts.map +1 -0
  22. package/dist/components/studentInputs/BaseUserInput.d.ts +1 -0
  23. package/dist/components/studentInputs/BaseUserInput.d.ts.map +1 -0
  24. package/dist/components/studentInputs/RadioMultipleChoice.types.d.ts +1 -0
  25. package/dist/components/studentInputs/RadioMultipleChoice.types.d.ts.map +1 -0
  26. package/dist/composables/CompositionViewable.d.ts +1 -0
  27. package/dist/composables/CompositionViewable.d.ts.map +1 -0
  28. package/dist/composables/Displayable.d.ts +1 -0
  29. package/dist/composables/Displayable.d.ts.map +1 -0
  30. package/dist/composables/__tests__/useAuthUI.test.d.ts +2 -0
  31. package/dist/composables/__tests__/useAuthUI.test.d.ts.map +1 -0
  32. package/dist/composables/index.d.ts +2 -0
  33. package/dist/composables/index.d.ts.map +1 -0
  34. package/dist/composables/useAuthUI.d.ts +15 -0
  35. package/dist/composables/useAuthUI.d.ts.map +1 -0
  36. package/dist/index.d.ts +5 -0
  37. package/dist/index.d.ts.map +1 -0
  38. package/dist/plugins/pinia.d.ts +1 -0
  39. package/dist/plugins/pinia.d.ts.map +1 -0
  40. package/dist/stores/useAuthStore.d.ts +21 -0
  41. package/dist/stores/useAuthStore.d.ts.map +1 -0
  42. package/dist/stores/useCardPreviewModeStore.d.ts +1 -0
  43. package/dist/stores/useCardPreviewModeStore.d.ts.map +1 -0
  44. package/dist/stores/useConfigStore.d.ts +1 -0
  45. package/dist/stores/useConfigStore.d.ts.map +1 -0
  46. package/dist/utils/SkldrMouseTrap.d.ts +1 -0
  47. package/dist/utils/SkldrMouseTrap.d.ts.map +1 -0
  48. package/package.json +8 -3
  49. package/src/components/CardBrowser.vue +81 -0
  50. package/src/components/CourseCardBrowser.vue +384 -0
  51. package/src/components/CourseInformation.vue +194 -0
  52. package/src/components/PaginatingToolbar.vue +1 -1
  53. package/src/components/SnackbarService.vue +1 -3
  54. package/src/components/StudySession.vue +52 -23
  55. package/src/components/TagsInput.vue +247 -0
  56. package/src/components/auth/UserChip.vue +146 -58
  57. package/src/components/auth/UserLoginAndRegistrationContainer.vue +17 -2
  58. package/src/components/cardRendering/MarkdownRendererHelpers.ts +2 -2
  59. package/src/components/studentInputs/BaseUserInput.ts +0 -1
  60. package/src/composables/__tests__/useAuthUI.test.ts +103 -0
  61. package/src/composables/index.ts +1 -0
  62. package/src/composables/useAuthUI.ts +67 -0
  63. package/src/index.ts +8 -0
  64. package/src/plugins/pinia.ts +1 -1
  65. package/src/stores/useAuthStore.ts +19 -0
@@ -1,11 +1,12 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
3
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
- import { defineComponent, createElementBlock, openBlock, createCommentVNode, Fragment, renderList, normalizeStyle, toDisplayString, resolveComponent, createBlock, withCtx, createVNode, createTextVNode, createElementVNode, mergeProps, ref, computed, watch, onMounted, onBeforeUnmount, normalizeClass, renderSlot, Transition, resolveDynamicComponent, defineAsyncComponent, withKeys, withDirectives, vModelText, getCurrentInstance, h, markRaw, withModifiers } from "vue";
4
+ import { defineComponent, createElementBlock, openBlock, createCommentVNode, Fragment, renderList, normalizeStyle, toDisplayString, resolveComponent, createBlock, withCtx, createVNode, createTextVNode, createElementVNode, mergeProps, ref, computed, watch, onMounted, onBeforeUnmount, normalizeClass, renderSlot, Transition, resolveDynamicComponent, markRaw, defineAsyncComponent, withKeys, withDirectives, vModelText, getCurrentInstance, h, withModifiers, vShow } from "vue";
5
5
  import { Status, FieldType, isCourseElo, toCourseElo, displayableDataToViewData, adjustCourseScores, log } from "@vue-skuilder/common";
6
6
  import { docIsDeleted, isReview, newInterval, isQuestionRecord, getStudySource, SessionController, getDataLayer, GuestUsername } from "@vue-skuilder/db";
7
7
  import { defineStore, setActivePinia } from "pinia";
8
8
  import { useRouter, useRoute } from "vue-router";
9
+ import { VueTagsInput } from "@vojtechlanka/vue-tags-input";
9
10
  //! moment.js
10
11
  //! version : 2.30.1
11
12
  //! authors : Tim Wood, Iskren Chernev, Moment.js contributors
@@ -4000,7 +4001,7 @@ hooks.HTML5_FMT = {
4000
4001
  MONTH: "YYYY-MM"
4001
4002
  // <input type="month" />
4002
4003
  };
4003
- const _sfc_main$l = defineComponent({
4004
+ const _sfc_main$q = defineComponent({
4004
4005
  name: "HeatMap",
4005
4006
  props: {
4006
4007
  // Accept activity records directly as a prop
@@ -4245,10 +4246,10 @@ const _export_sfc = (sfc, props) => {
4245
4246
  }
4246
4247
  return target;
4247
4248
  };
4248
- const _hoisted_1$b = ["width", "height"];
4249
- const _hoisted_2$4 = ["transform"];
4250
- const _hoisted_3$2 = ["y", "width", "height", "fill", "onMouseover"];
4251
- function _sfc_render$h(_ctx, _cache, $props, $setup, $data, $options) {
4249
+ const _hoisted_1$f = ["width", "height"];
4250
+ const _hoisted_2$7 = ["transform"];
4251
+ const _hoisted_3$5 = ["y", "width", "height", "fill", "onMouseover"];
4252
+ function _sfc_render$k(_ctx, _cache, $props, $setup, $data, $options) {
4252
4253
  return openBlock(), createElementBlock("div", null, [
4253
4254
  (openBlock(), createElementBlock("svg", {
4254
4255
  width: _ctx.width,
@@ -4269,11 +4270,11 @@ function _sfc_render$h(_ctx, _cache, $props, $setup, $data, $options) {
4269
4270
  fill: _ctx.getColor(day.count),
4270
4271
  onMouseover: ($event) => _ctx.showTooltip(day, $event),
4271
4272
  onMouseout: _cache[0] || (_cache[0] = (...args) => _ctx.hideTooltip && _ctx.hideTooltip(...args))
4272
- }, null, 40, _hoisted_3$2);
4273
+ }, null, 40, _hoisted_3$5);
4273
4274
  }), 128))
4274
- ], 8, _hoisted_2$4);
4275
+ ], 8, _hoisted_2$7);
4275
4276
  }), 128))
4276
- ], 8, _hoisted_1$b)),
4277
+ ], 8, _hoisted_1$f)),
4277
4278
  _ctx.tooltipData ? (openBlock(), createElementBlock("div", {
4278
4279
  key: 0,
4279
4280
  class: "tooltip",
@@ -4281,7 +4282,7 @@ function _sfc_render$h(_ctx, _cache, $props, $setup, $data, $options) {
4281
4282
  }, toDisplayString(_ctx.tooltipData.count) + " review" + toDisplayString(_ctx.tooltipData.count !== 1 ? "s" : "") + " on " + toDisplayString(_ctx.toDateString(_ctx.tooltipData.date)), 5)) : createCommentVNode("", true)
4282
4283
  ]);
4283
4284
  }
4284
- const HeatMap = /* @__PURE__ */ _export_sfc(_sfc_main$l, [["render", _sfc_render$h], ["__scopeId", "data-v-ca46239a"]]);
4285
+ const HeatMap = /* @__PURE__ */ _export_sfc(_sfc_main$q, [["render", _sfc_render$k], ["__scopeId", "data-v-ca46239a"]]);
4285
4286
  function getDefaultExportFromCjs(x) {
4286
4287
  return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
4287
4288
  }
@@ -4861,7 +4862,7 @@ const _SkldrMouseTrap = class _SkldrMouseTrap {
4861
4862
  };
4862
4863
  __publicField(_SkldrMouseTrap, "_instance");
4863
4864
  let SkldrMouseTrap = _SkldrMouseTrap;
4864
- const _sfc_main$k = defineComponent({
4865
+ const _sfc_main$p = defineComponent({
4865
4866
  name: "SkMouseTrap",
4866
4867
  props: {
4867
4868
  refreshInterval: {
@@ -4891,8 +4892,8 @@ const _sfc_main$k = defineComponent({
4891
4892
  }
4892
4893
  }
4893
4894
  });
4894
- const _hoisted_1$a = { class: "text-caption ml-2" };
4895
- function _sfc_render$g(_ctx, _cache, $props, $setup, $data, $options) {
4895
+ const _hoisted_1$e = { class: "text-caption ml-2" };
4896
+ function _sfc_render$j(_ctx, _cache, $props, $setup, $data, $options) {
4896
4897
  const _component_v_icon = resolveComponent("v-icon");
4897
4898
  const _component_v_btn = resolveComponent("v-btn");
4898
4899
  const _component_v_toolbar_title = resolveComponent("v-toolbar-title");
@@ -4961,7 +4962,7 @@ function _sfc_render$g(_ctx, _cache, $props, $setup, $data, $options) {
4961
4962
  _: 2
4962
4963
  }, 1024),
4963
4964
  createVNode(_component_v_spacer),
4964
- createElementVNode("span", _hoisted_1$a, toDisplayString(hk.command), 1)
4965
+ createElementVNode("span", _hoisted_1$e, toDisplayString(hk.command), 1)
4965
4966
  ]),
4966
4967
  _: 2
4967
4968
  }, 1024);
@@ -4976,8 +4977,8 @@ function _sfc_render$g(_ctx, _cache, $props, $setup, $data, $options) {
4976
4977
  _: 1
4977
4978
  })) : createCommentVNode("", true);
4978
4979
  }
4979
- const SkMouseTrap = /* @__PURE__ */ _export_sfc(_sfc_main$k, [["render", _sfc_render$g]]);
4980
- const _sfc_main$j = defineComponent({
4980
+ const SkMouseTrap = /* @__PURE__ */ _export_sfc(_sfc_main$p, [["render", _sfc_render$j]]);
4981
+ const _sfc_main$o = defineComponent({
4981
4982
  name: "SkMouseTrapToolTip",
4982
4983
  props: {
4983
4984
  hotkey: {
@@ -5126,7 +5127,7 @@ const _sfc_main$j = defineComponent({
5126
5127
  };
5127
5128
  }
5128
5129
  });
5129
- function _sfc_render$f(_ctx, _cache, $props, $setup, $data, $options) {
5130
+ function _sfc_render$i(_ctx, _cache, $props, $setup, $data, $options) {
5130
5131
  return openBlock(), createElementBlock("div", {
5131
5132
  class: normalizeClass(["sk-mousetrap-tooltip-wrapper", [
5132
5133
  _ctx.isControlKeyPressed && !_ctx.disabled && _ctx.highlightEffect !== "none" ? `sk-mousetrap-highlight-${_ctx.highlightEffect}` : ""
@@ -5150,7 +5151,7 @@ function _sfc_render$f(_ctx, _cache, $props, $setup, $data, $options) {
5150
5151
  })
5151
5152
  ], 2);
5152
5153
  }
5153
- const SkMouseTrapToolTip = /* @__PURE__ */ _export_sfc(_sfc_main$j, [["render", _sfc_render$f], ["__scopeId", "data-v-5d6fb09c"]]);
5154
+ const SkMouseTrapToolTip = /* @__PURE__ */ _export_sfc(_sfc_main$o, [["render", _sfc_render$i], ["__scopeId", "data-v-5d6fb09c"]]);
5154
5155
  const SnackbarServiceModule = /* @__PURE__ */ (() => {
5155
5156
  let _instance = null;
5156
5157
  return {
@@ -5174,7 +5175,7 @@ const SnackbarServiceModule = /* @__PURE__ */ (() => {
5174
5175
  };
5175
5176
  })();
5176
5177
  const { setInstance, alertUser } = SnackbarServiceModule;
5177
- const SnackbarService$1 = defineComponent({
5178
+ const _sfc_main$n = defineComponent({
5178
5179
  name: "SnackbarService",
5179
5180
  data() {
5180
5181
  return {
@@ -5212,8 +5213,8 @@ const SnackbarService$1 = defineComponent({
5212
5213
  }
5213
5214
  }
5214
5215
  });
5215
- const _hoisted_1$9 = { class: "d-flex align-center justify-space-between w-100" };
5216
- function _sfc_render$e(_ctx, _cache, $props, $setup, $data, $options) {
5216
+ const _hoisted_1$d = { class: "d-flex align-center justify-space-between w-100" };
5217
+ function _sfc_render$h(_ctx, _cache, $props, $setup, $data, $options) {
5217
5218
  const _component_v_icon = resolveComponent("v-icon");
5218
5219
  const _component_v_btn = resolveComponent("v-btn");
5219
5220
  const _component_v_snackbar = resolveComponent("v-snackbar");
@@ -5228,7 +5229,7 @@ function _sfc_render$e(_ctx, _cache, $props, $setup, $data, $options) {
5228
5229
  color: _ctx.getColor(snack)
5229
5230
  }, {
5230
5231
  default: withCtx(() => [
5231
- createElementVNode("div", _hoisted_1$9, [
5232
+ createElementVNode("div", _hoisted_1$d, [
5232
5233
  createElementVNode("span", null, toDisplayString(snack.text), 1),
5233
5234
  createVNode(_component_v_btn, {
5234
5235
  icon: "",
@@ -5252,8 +5253,8 @@ function _sfc_render$e(_ctx, _cache, $props, $setup, $data, $options) {
5252
5253
  }), 128))
5253
5254
  ]);
5254
5255
  }
5255
- const SnackbarService = /* @__PURE__ */ _export_sfc(SnackbarService$1, [["render", _sfc_render$e]]);
5256
- const _sfc_main$i = defineComponent({
5256
+ const SnackbarService = /* @__PURE__ */ _export_sfc(_sfc_main$n, [["render", _sfc_render$h]]);
5257
+ const _sfc_main$m = defineComponent({
5257
5258
  name: "PaginatingToolbar",
5258
5259
  props: {
5259
5260
  pages: {
@@ -5277,11 +5278,12 @@ const _sfc_main$i = defineComponent({
5277
5278
  },
5278
5279
  emits: ["first", "prev", "next", "last", "set-page"]
5279
5280
  });
5280
- const _hoisted_1$8 = {
5281
+ const _hoisted_1$c = {
5281
5282
  key: 0,
5282
- class: "ms-2 text-subtitle-2"
5283
+ class: "ms-2 text-subtitle-2",
5284
+ "data-cy": "paginating-toolbar-subtitle"
5283
5285
  };
5284
- function _sfc_render$d(_ctx, _cache, $props, $setup, $data, $options) {
5286
+ function _sfc_render$g(_ctx, _cache, $props, $setup, $data, $options) {
5285
5287
  const _component_v_toolbar_title = resolveComponent("v-toolbar-title");
5286
5288
  const _component_v_spacer = resolveComponent("v-spacer");
5287
5289
  const _component_v_icon = resolveComponent("v-icon");
@@ -5293,7 +5295,7 @@ function _sfc_render$d(_ctx, _cache, $props, $setup, $data, $options) {
5293
5295
  createVNode(_component_v_toolbar_title, null, {
5294
5296
  default: withCtx(() => [
5295
5297
  createElementVNode("span", null, toDisplayString(_ctx.title), 1),
5296
- _ctx.subtitle ? (openBlock(), createElementBlock("span", _hoisted_1$8, toDisplayString(_ctx.subtitle), 1)) : createCommentVNode("", true)
5298
+ _ctx.subtitle ? (openBlock(), createElementBlock("span", _hoisted_1$c, toDisplayString(_ctx.subtitle), 1)) : createCommentVNode("", true)
5297
5299
  ]),
5298
5300
  _: 1
5299
5301
  }),
@@ -5385,7 +5387,7 @@ function _sfc_render$d(_ctx, _cache, $props, $setup, $data, $options) {
5385
5387
  _: 1
5386
5388
  });
5387
5389
  }
5388
- const PaginatingToolbar = /* @__PURE__ */ _export_sfc(_sfc_main$i, [["render", _sfc_render$d], ["__scopeId", "data-v-39413af8"]]);
5390
+ const PaginatingToolbar = /* @__PURE__ */ _export_sfc(_sfc_main$m, [["render", _sfc_render$g], ["__scopeId", "data-v-a75fea7e"]]);
5389
5391
  function useViewable(props, emit, componentName) {
5390
5392
  const startTime = ref(hooks.utc());
5391
5393
  const hotKeys = ref([]);
@@ -5554,7 +5556,7 @@ class Question extends Displayable {
5554
5556
  with 7 * 4 = 21
5555
5557
  */
5556
5558
  }
5557
- const _sfc_main$h = defineComponent({
5559
+ const _sfc_main$l = defineComponent({
5558
5560
  name: "StudySessionTimer",
5559
5561
  props: {
5560
5562
  /**
@@ -5608,7 +5610,7 @@ const _sfc_main$h = defineComponent({
5608
5610
  };
5609
5611
  }
5610
5612
  });
5611
- function _sfc_render$c(_ctx, _cache, $props, $setup, $data, $options) {
5613
+ function _sfc_render$f(_ctx, _cache, $props, $setup, $data, $options) {
5612
5614
  const _component_v_icon = resolveComponent("v-icon");
5613
5615
  const _component_v_btn = resolveComponent("v-btn");
5614
5616
  const _component_v_progress_circular = resolveComponent("v-progress-circular");
@@ -5662,8 +5664,8 @@ function _sfc_render$c(_ctx, _cache, $props, $setup, $data, $options) {
5662
5664
  _: 1
5663
5665
  });
5664
5666
  }
5665
- const StudySessionTimer = /* @__PURE__ */ _export_sfc(_sfc_main$h, [["render", _sfc_render$c], ["__scopeId", "data-v-5960940a"]]);
5666
- const _sfc_main$g = defineComponent({
5667
+ const StudySessionTimer = /* @__PURE__ */ _export_sfc(_sfc_main$l, [["render", _sfc_render$f], ["__scopeId", "data-v-5960940a"]]);
5668
+ const _sfc_main$k = defineComponent({
5667
5669
  name: "CardViewer",
5668
5670
  ref: {},
5669
5671
  props: {
@@ -5717,7 +5719,7 @@ const _sfc_main$g = defineComponent({
5717
5719
  }
5718
5720
  }
5719
5721
  });
5720
- function _sfc_render$b(_ctx, _cache, $props, $setup, $data, $options) {
5722
+ function _sfc_render$e(_ctx, _cache, $props, $setup, $data, $options) {
5721
5723
  const _component_v_card = resolveComponent("v-card");
5722
5724
  return openBlock(), createBlock(_component_v_card, { elevation: "12" }, {
5723
5725
  default: withCtx(() => [
@@ -5741,7 +5743,7 @@ function _sfc_render$b(_ctx, _cache, $props, $setup, $data, $options) {
5741
5743
  _: 1
5742
5744
  });
5743
5745
  }
5744
- const CardViewer = /* @__PURE__ */ _export_sfc(_sfc_main$g, [["render", _sfc_render$b], ["__scopeId", "data-v-a180fe1c"]]);
5746
+ const CardViewer = /* @__PURE__ */ _export_sfc(_sfc_main$k, [["render", _sfc_render$e], ["__scopeId", "data-v-a180fe1c"]]);
5745
5747
  var module$1 = {};
5746
5748
  (function main(global, module2, isWorker, workerSize) {
5747
5749
  var canUseWorker = !!(global.Worker && global.Blob && global.Promise && global.OffscreenCanvas && global.OffscreenCanvasRenderingContext2D && global.HTMLCanvasElement && global.HTMLCanvasElement.prototype.transferControlToOffscreen && global.URL && global.URL.createObjectURL);
@@ -6438,7 +6440,7 @@ var module$1 = {};
6438
6440
  }(), module$1, false);
6439
6441
  const confetti = module$1.exports;
6440
6442
  module$1.exports.create;
6441
- const _sfc_main$f = defineComponent({
6443
+ const _sfc_main$j = defineComponent({
6442
6444
  name: "StudySession",
6443
6445
  ref: {},
6444
6446
  components: {
@@ -6582,23 +6584,25 @@ const _sfc_main$f = defineComponent({
6582
6584
  try {
6583
6585
  console.log(`[StudySession] starting study session w/ sources: ${JSON.stringify(this.contentSources)}`);
6584
6586
  console.log("[StudySession] Beginning preparation process");
6585
- this.sessionContentSources = (await Promise.all(
6586
- this.contentSources.map(async (s) => {
6587
- try {
6588
- return await getStudySource(s, this.user);
6589
- } catch (e) {
6590
- console.error(`Failed to load study source: ${s.type}/${s.id}`, e);
6591
- return null;
6592
- }
6593
- })
6594
- )).filter((s) => s !== null);
6587
+ this.sessionContentSources = markRaw(
6588
+ (await Promise.all(
6589
+ this.contentSources.map(async (s) => {
6590
+ try {
6591
+ return await getStudySource(s, this.user);
6592
+ } catch (e) {
6593
+ console.error(`Failed to load study source: ${s.type}/${s.id}`, e);
6594
+ return null;
6595
+ }
6596
+ })
6597
+ )).filter((s) => s !== null)
6598
+ );
6595
6599
  this.timeRemaining = this.sessionTimeLimit * 60;
6596
6600
  sessionClassroomDBs = await Promise.all(
6597
6601
  this.contentSources.filter((s) => s.type === "classroom").map(async (c) => await this.dataLayer.getClassroomDB(c.id, "student"))
6598
6602
  );
6599
- sessionClassroomDBs.forEach((db) => {
6603
+ sessionClassroomDBs.forEach((_db) => {
6600
6604
  });
6601
- this.sessionController = new SessionController(this.sessionContentSources, 60 * this.sessionTimeLimit);
6605
+ this.sessionController = markRaw(new SessionController(this.sessionContentSources, 60 * this.sessionTimeLimit));
6602
6606
  this.sessionController.sessionRecord = this.sessionRecord;
6603
6607
  await this.sessionController.prepareSession();
6604
6608
  this.intervalHandler = setInterval(this.tick, 1e3);
@@ -6728,12 +6732,15 @@ const _sfc_main$f = defineComponent({
6728
6732
  if (cardElo && userElo) {
6729
6733
  const eloUpdate = adjustCourseScores(userElo, cardElo, userScore);
6730
6734
  this.userCourseRegDoc.courses.find((c) => c.courseID === course_id).elo = eloUpdate.userElo;
6731
- Promise.all([
6735
+ const results = await Promise.allSettled([
6732
6736
  this.user.updateUserElo(course_id, eloUpdate.userElo),
6733
6737
  courseDB.updateCardElo(card_id, eloUpdate.cardElo)
6734
- ]).then((results) => {
6735
- const user = results[0];
6736
- const card = results[1];
6738
+ ]);
6739
+ const userEloStatus = results[0].status === "fulfilled";
6740
+ const cardEloStatus = results[1].status === "fulfilled";
6741
+ if (userEloStatus && cardEloStatus) {
6742
+ const user = results[0].value;
6743
+ const card = results[1].value;
6737
6744
  if (user.ok && card && card.ok) {
6738
6745
  console.log(
6739
6746
  `[StudySession] Updated ELOS:
@@ -6742,7 +6749,19 @@ const _sfc_main$f = defineComponent({
6742
6749
  `
6743
6750
  );
6744
6751
  }
6745
- });
6752
+ } else {
6753
+ console.log(
6754
+ `[StudySession] Partial ELO update:
6755
+ User ELO update: ${userEloStatus ? "SUCCESS" : "FAILED"}
6756
+ Card ELO update: ${cardEloStatus ? "SUCCESS" : "FAILED"}`
6757
+ );
6758
+ if (!userEloStatus && results[0].status === "rejected") {
6759
+ console.error("[StudySession] User ELO update error:", results[0].reason);
6760
+ }
6761
+ if (!cardEloStatus && results[1].status === "rejected") {
6762
+ console.error("[StudySession] Card ELO update error:", results[1].reason);
6763
+ }
6764
+ }
6746
6765
  }
6747
6766
  },
6748
6767
  clearFeedbackShadow() {
@@ -6844,22 +6863,22 @@ const _sfc_main$f = defineComponent({
6844
6863
  }
6845
6864
  }
6846
6865
  });
6847
- const _hoisted_1$7 = {
6866
+ const _hoisted_1$b = {
6848
6867
  key: 0,
6849
6868
  class: "StudySession"
6850
6869
  };
6851
- const _hoisted_2$3 = { class: "text-h3" };
6852
- const _hoisted_3$1 = {
6870
+ const _hoisted_2$6 = { class: "text-h3" };
6871
+ const _hoisted_3$4 = {
6853
6872
  key: 0,
6854
6873
  class: "text-h4"
6855
6874
  };
6856
- const _hoisted_4$1 = { key: 0 };
6857
- const _hoisted_5$1 = {
6875
+ const _hoisted_4$3 = { key: 0 };
6876
+ const _hoisted_5$3 = {
6858
6877
  key: 1,
6859
6878
  ref: "shadowWrapper"
6860
6879
  };
6861
6880
  const _hoisted_6$1 = { key: 2 };
6862
- function _sfc_render$a(_ctx, _cache, $props, $setup, $data, $options) {
6881
+ function _sfc_render$d(_ctx, _cache, $props, $setup, $data, $options) {
6863
6882
  const _component_v_progress_circular = resolveComponent("v-progress-circular");
6864
6883
  const _component_v_spacer = resolveComponent("v-spacer");
6865
6884
  const _component_v_col = resolveComponent("v-col");
@@ -6869,12 +6888,12 @@ function _sfc_render$a(_ctx, _cache, $props, $setup, $data, $options) {
6869
6888
  const _component_card_viewer = resolveComponent("card-viewer");
6870
6889
  const _component_StudySessionTimer = resolveComponent("StudySessionTimer");
6871
6890
  const _component_SkMouseTrap = resolveComponent("SkMouseTrap");
6872
- return _ctx.sessionPrepared ? (openBlock(), createElementBlock("div", _hoisted_1$7, [
6891
+ return _ctx.sessionPrepared ? (openBlock(), createElementBlock("div", _hoisted_1$b, [
6873
6892
  createVNode(_component_v_row, { align: "center" }, {
6874
6893
  default: withCtx(() => [
6875
6894
  createVNode(_component_v_col, null, {
6876
6895
  default: withCtx(() => [
6877
- createElementVNode("h1", _hoisted_2$3, [
6896
+ createElementVNode("h1", _hoisted_2$6, [
6878
6897
  createTextVNode(toDisplayString(_ctx.courseNames[_ctx.courseID]) + ": ", 1),
6879
6898
  _ctx.loading ? (openBlock(), createBlock(_component_v_progress_circular, {
6880
6899
  key: 0,
@@ -6892,9 +6911,9 @@ function _sfc_render$a(_ctx, _cache, $props, $setup, $data, $options) {
6892
6911
  _: 1
6893
6912
  }),
6894
6913
  _cache[7] || (_cache[7] = createElementVNode("br", null, null, -1)),
6895
- _ctx.sessionFinished ? (openBlock(), createElementBlock("div", _hoisted_3$1, [
6914
+ _ctx.sessionFinished ? (openBlock(), createElementBlock("div", _hoisted_3$4, [
6896
6915
  _cache[6] || (_cache[6] = createElementVNode("p", null, "Study session finished! Great job!", -1)),
6897
- _ctx.sessionController ? (openBlock(), createElementBlock("p", _hoisted_4$1, toDisplayString(_ctx.sessionController.report), 1)) : createCommentVNode("", true),
6916
+ _ctx.sessionController ? (openBlock(), createElementBlock("p", _hoisted_4$3, toDisplayString(_ctx.sessionController.report), 1)) : createCommentVNode("", true),
6898
6917
  createElementVNode("p", null, [
6899
6918
  _cache[3] || (_cache[3] = createTextVNode(" Start ")),
6900
6919
  createElementVNode("a", {
@@ -6914,7 +6933,7 @@ function _sfc_render$a(_ctx, _cache, $props, $setup, $data, $options) {
6914
6933
  createVNode(_component_heat_map, {
6915
6934
  "activity-records-getter": () => _ctx.user.getActivityRecords()
6916
6935
  }, null, 8, ["activity-records-getter"])
6917
- ])) : (openBlock(), createElementBlock("div", _hoisted_5$1, [
6936
+ ])) : (openBlock(), createElementBlock("div", _hoisted_5$3, [
6918
6937
  createVNode(_component_card_viewer, {
6919
6938
  ref: "cardViewer",
6920
6939
  class: normalizeClass(_ctx.loading ? "muted" : ""),
@@ -6970,7 +6989,7 @@ function _sfc_render$a(_ctx, _cache, $props, $setup, $data, $options) {
6970
6989
  })
6971
6990
  ])) : createCommentVNode("", true);
6972
6991
  }
6973
- const StudySession = /* @__PURE__ */ _export_sfc(_sfc_main$f, [["render", _sfc_render$a], ["__scopeId", "data-v-70e29902"]]);
6992
+ const StudySession = /* @__PURE__ */ _export_sfc(_sfc_main$j, [["render", _sfc_render$d], ["__scopeId", "data-v-37cad857"]]);
6974
6993
  let _pinia = null;
6975
6994
  const setPinia = (pinia) => {
6976
6995
  _pinia = pinia;
@@ -6979,7 +6998,7 @@ const getPinia = () => {
6979
6998
  return _pinia;
6980
6999
  };
6981
7000
  const piniaPlugin = {
6982
- install(app, options) {
7001
+ install(_app, options) {
6983
7002
  const pinia = options?.pinia;
6984
7003
  if (pinia) {
6985
7004
  setPinia(pinia);
@@ -7065,7 +7084,7 @@ Exceeded maximum ancestor lookup depth.`;
7065
7084
  }
7066
7085
  }
7067
7086
  });
7068
- const _sfc_main$e = defineComponent({
7087
+ const _sfc_main$i = defineComponent({
7069
7088
  name: "MultipleChoiceOption",
7070
7089
  components: {
7071
7090
  MarkdownRenderer: defineAsyncComponent(() => Promise.resolve().then(() => MarkdownRenderer$1))
@@ -7151,7 +7170,7 @@ const _sfc_main$e = defineComponent({
7151
7170
  }
7152
7171
  }
7153
7172
  });
7154
- function _sfc_render$9(_ctx, _cache, $props, $setup, $data, $options) {
7173
+ function _sfc_render$c(_ctx, _cache, $props, $setup, $data, $options) {
7155
7174
  const _component_markdown_renderer = resolveComponent("markdown-renderer");
7156
7175
  const _component_v_card = resolveComponent("v-card");
7157
7176
  return openBlock(), createBlock(_component_v_card, {
@@ -7165,8 +7184,8 @@ function _sfc_render$9(_ctx, _cache, $props, $setup, $data, $options) {
7165
7184
  _: 1
7166
7185
  }, 8, ["class", "onMouseover", "onClick"]);
7167
7186
  }
7168
- const MultipleChoiceOption = /* @__PURE__ */ _export_sfc(_sfc_main$e, [["render", _sfc_render$9], ["__scopeId", "data-v-96de7172"]]);
7169
- const _sfc_main$d = defineComponent({
7187
+ const MultipleChoiceOption = /* @__PURE__ */ _export_sfc(_sfc_main$i, [["render", _sfc_render$c], ["__scopeId", "data-v-96de7172"]]);
7188
+ const _sfc_main$h = defineComponent({
7170
7189
  name: "RadioMultipleChoice",
7171
7190
  components: {
7172
7191
  MultipleChoiceOption
@@ -7305,13 +7324,13 @@ const _sfc_main$d = defineComponent({
7305
7324
  // },
7306
7325
  }
7307
7326
  });
7308
- const _hoisted_1$6 = {
7327
+ const _hoisted_1$a = {
7309
7328
  ref: "containerRef",
7310
7329
  class: "multipleChoice"
7311
7330
  };
7312
- function _sfc_render$8(_ctx, _cache, $props, $setup, $data, $options) {
7331
+ function _sfc_render$b(_ctx, _cache, $props, $setup, $data, $options) {
7313
7332
  const _component_MultipleChoiceOption = resolveComponent("MultipleChoiceOption");
7314
- return openBlock(), createElementBlock("div", _hoisted_1$6, [
7333
+ return openBlock(), createElementBlock("div", _hoisted_1$a, [
7315
7334
  (openBlock(true), createElementBlock(Fragment, null, renderList(_ctx.choiceList, (choice, i) => {
7316
7335
  return openBlock(), createBlock(_component_MultipleChoiceOption, {
7317
7336
  key: i,
@@ -7325,8 +7344,8 @@ function _sfc_render$8(_ctx, _cache, $props, $setup, $data, $options) {
7325
7344
  }), 128))
7326
7345
  ], 512);
7327
7346
  }
7328
- const RadioMultipleChoice = /* @__PURE__ */ _export_sfc(_sfc_main$d, [["render", _sfc_render$8]]);
7329
- const _sfc_main$c = defineComponent({
7347
+ const RadioMultipleChoice = /* @__PURE__ */ _export_sfc(_sfc_main$h, [["render", _sfc_render$b]]);
7348
+ const _sfc_main$g = defineComponent({
7330
7349
  name: "TrueFalse",
7331
7350
  components: {
7332
7351
  RadioMultipleChoice
@@ -7342,10 +7361,10 @@ const _sfc_main$c = defineComponent({
7342
7361
  }
7343
7362
  }
7344
7363
  });
7345
- const _hoisted_1$5 = { "data-viewable": "TrueFalse" };
7346
- function _sfc_render$7(_ctx, _cache, $props, $setup, $data, $options) {
7364
+ const _hoisted_1$9 = { "data-viewable": "TrueFalse" };
7365
+ function _sfc_render$a(_ctx, _cache, $props, $setup, $data, $options) {
7347
7366
  const _component_RadioMultipleChoice = resolveComponent("RadioMultipleChoice");
7348
- return openBlock(), createElementBlock("div", _hoisted_1$5, [
7367
+ return openBlock(), createElementBlock("div", _hoisted_1$9, [
7349
7368
  createVNode(_component_RadioMultipleChoice, {
7350
7369
  "choice-list": ["True", "False"],
7351
7370
  MouseTrap: _ctx.MouseTrap,
@@ -7353,8 +7372,8 @@ function _sfc_render$7(_ctx, _cache, $props, $setup, $data, $options) {
7353
7372
  }, null, 8, ["MouseTrap", "submit"])
7354
7373
  ]);
7355
7374
  }
7356
- const TrueFalse = /* @__PURE__ */ _export_sfc(_sfc_main$c, [["render", _sfc_render$7]]);
7357
- const _sfc_main$b = defineComponent({
7375
+ const TrueFalse = /* @__PURE__ */ _export_sfc(_sfc_main$g, [["render", _sfc_render$a]]);
7376
+ const _sfc_main$f = defineComponent({
7358
7377
  name: "UserInputNumber",
7359
7378
  ref: {},
7360
7379
  extends: UserInput,
@@ -7374,7 +7393,7 @@ const _sfc_main$b = defineComponent({
7374
7393
  }
7375
7394
  }
7376
7395
  });
7377
- function _sfc_render$6(_ctx, _cache, $props, $setup, $data, $options) {
7396
+ function _sfc_render$9(_ctx, _cache, $props, $setup, $data, $options) {
7378
7397
  const _component_v_text_field = resolveComponent("v-text-field");
7379
7398
  const _component_v_container = resolveComponent("v-container");
7380
7399
  return openBlock(), createBlock(_component_v_container, { class: "pa-0" }, {
@@ -7395,8 +7414,8 @@ function _sfc_render$6(_ctx, _cache, $props, $setup, $data, $options) {
7395
7414
  _: 1
7396
7415
  });
7397
7416
  }
7398
- const UserInputNumber = /* @__PURE__ */ _export_sfc(_sfc_main$b, [["render", _sfc_render$6], ["__scopeId", "data-v-a56dcd1c"]]);
7399
- const _sfc_main$a = defineComponent({
7417
+ const UserInputNumber = /* @__PURE__ */ _export_sfc(_sfc_main$f, [["render", _sfc_render$9], ["__scopeId", "data-v-a56dcd1c"]]);
7418
+ const _sfc_main$e = defineComponent({
7400
7419
  name: "UserInputString",
7401
7420
  extends: UserInput,
7402
7421
  props: {
@@ -7433,10 +7452,10 @@ const _sfc_main$a = defineComponent({
7433
7452
  // },
7434
7453
  }
7435
7454
  });
7436
- const _hoisted_1$4 = { class: "user-input-container" };
7437
- const _hoisted_2$2 = ["autofocus"];
7438
- function _sfc_render$5(_ctx, _cache, $props, $setup, $data, $options) {
7439
- return openBlock(), createElementBlock("span", _hoisted_1$4, [
7455
+ const _hoisted_1$8 = { class: "user-input-container" };
7456
+ const _hoisted_2$5 = ["autofocus"];
7457
+ function _sfc_render$8(_ctx, _cache, $props, $setup, $data, $options) {
7458
+ return openBlock(), createElementBlock("span", _hoisted_1$8, [
7440
7459
  withDirectives(createElementVNode("input", {
7441
7460
  "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => _ctx.answer = $event),
7442
7461
  autofocus: _ctx.autofocus,
@@ -7444,13 +7463,13 @@ function _sfc_render$5(_ctx, _cache, $props, $setup, $data, $options) {
7444
7463
  class: "user-input-string",
7445
7464
  ref: "input",
7446
7465
  onKeyup: _cache[1] || (_cache[1] = withKeys(($event) => _ctx.submitAnswer(_ctx.answer), ["enter"]))
7447
- }, null, 40, _hoisted_2$2), [
7466
+ }, null, 40, _hoisted_2$5), [
7448
7467
  [vModelText, _ctx.answer]
7449
7468
  ])
7450
7469
  ]);
7451
7470
  }
7452
- const UserInputString = /* @__PURE__ */ _export_sfc(_sfc_main$a, [["render", _sfc_render$5], ["__scopeId", "data-v-aa14961f"]]);
7453
- const _sfc_main$9 = defineComponent({
7471
+ const UserInputString = /* @__PURE__ */ _export_sfc(_sfc_main$e, [["render", _sfc_render$8], ["__scopeId", "data-v-aa14961f"]]);
7472
+ const _sfc_main$d = defineComponent({
7454
7473
  name: "FillInInput",
7455
7474
  components: {
7456
7475
  UserInputString
@@ -7483,13 +7502,13 @@ const _sfc_main$9 = defineComponent({
7483
7502
  };
7484
7503
  }
7485
7504
  });
7486
- const _hoisted_1$3 = {
7505
+ const _hoisted_1$7 = {
7487
7506
  key: 0,
7488
7507
  class: "text-h5 underline"
7489
7508
  };
7490
- function _sfc_render$4(_ctx, _cache, $props, $setup, $data, $options) {
7509
+ function _sfc_render$7(_ctx, _cache, $props, $setup, $data, $options) {
7491
7510
  const _component_user_input_string = resolveComponent("user-input-string");
7492
- return _ctx.radioType ? (openBlock(), createElementBlock("span", _hoisted_1$3, "             ")) : (openBlock(), createBlock(_component_user_input_string, {
7511
+ return _ctx.radioType ? (openBlock(), createElementBlock("span", _hoisted_1$7, "             ")) : (openBlock(), createBlock(_component_user_input_string, {
7493
7512
  key: 1,
7494
7513
  id: "input",
7495
7514
  icon: false,
@@ -7497,8 +7516,8 @@ function _sfc_render$4(_ctx, _cache, $props, $setup, $data, $options) {
7497
7516
  value: _ctx.processedText
7498
7517
  }, null, 8, ["value"]));
7499
7518
  }
7500
- const FillInInput = /* @__PURE__ */ _export_sfc(_sfc_main$9, [["render", _sfc_render$4], ["__scopeId", "data-v-486ac035"]]);
7501
- const _sfc_main$8 = defineComponent({
7519
+ const FillInInput = /* @__PURE__ */ _export_sfc(_sfc_main$d, [["render", _sfc_render$7], ["__scopeId", "data-v-486ac035"]]);
7520
+ const _sfc_main$c = defineComponent({
7502
7521
  name: "CardLoader",
7503
7522
  components: {
7504
7523
  CardViewer
@@ -7572,7 +7591,7 @@ const _sfc_main$8 = defineComponent({
7572
7591
  }
7573
7592
  }
7574
7593
  });
7575
- function _sfc_render$3(_ctx, _cache, $props, $setup, $data, $options) {
7594
+ function _sfc_render$6(_ctx, _cache, $props, $setup, $data, $options) {
7576
7595
  const _component_card_viewer = resolveComponent("card-viewer");
7577
7596
  return !_ctx.loading ? (openBlock(), createBlock(_component_card_viewer, {
7578
7597
  key: 0,
@@ -7585,7 +7604,7 @@ function _sfc_render$3(_ctx, _cache, $props, $setup, $data, $options) {
7585
7604
  onEmitResponse: _cache[0] || (_cache[0] = ($event) => _ctx.processResponse($event))
7586
7605
  }, null, 8, ["class", "view", "data", "card_id", "course_id", "session-order"])) : createCommentVNode("", true);
7587
7606
  }
7588
- const CardLoader = /* @__PURE__ */ _export_sfc(_sfc_main$8, [["render", _sfc_render$3], ["__scopeId", "data-v-9ca53bc4"]]);
7607
+ const CardLoader = /* @__PURE__ */ _export_sfc(_sfc_main$c, [["render", _sfc_render$6], ["__scopeId", "data-v-9ca53bc4"]]);
7589
7608
  function _getDefaults() {
7590
7609
  return {
7591
7610
  async: false,
@@ -9732,7 +9751,7 @@ function splitTextToken(token2) {
9732
9751
  const textChunks = splitByDelimiters(token2.text, "{{", "}}");
9733
9752
  const rawChunks = splitByDelimiters(token2.raw, "{{", "}}");
9734
9753
  if (textChunks.length === rawChunks.length) {
9735
- return textChunks.map((c, i) => {
9754
+ return textChunks.map((_c, i) => {
9736
9755
  return {
9737
9756
  type: "text",
9738
9757
  text: textChunks[i],
@@ -9796,7 +9815,7 @@ function isComponent(token2) {
9796
9815
  return token2.type === "text" && token2.text.startsWith("{{") && token2.text.endsWith("}}");
9797
9816
  }
9798
9817
  const playbackGap = 500;
9799
- const _sfc_main$7 = /* @__PURE__ */ defineComponent({
9818
+ const _sfc_main$b = /* @__PURE__ */ defineComponent({
9800
9819
  __name: "AudioAutoPlayer",
9801
9820
  props: {
9802
9821
  src: {}
@@ -9907,7 +9926,7 @@ const _sfc_main$7 = /* @__PURE__ */ defineComponent({
9907
9926
  };
9908
9927
  }
9909
9928
  });
9910
- const AudioAutoPlayer = /* @__PURE__ */ _export_sfc(_sfc_main$7, [["__scopeId", "data-v-e1a0f62c"]]);
9929
+ const AudioAutoPlayer = /* @__PURE__ */ _export_sfc(_sfc_main$b, [["__scopeId", "data-v-e1a0f62c"]]);
9911
9930
  var core;
9912
9931
  var hasRequiredCore;
9913
9932
  function requireCore() {
@@ -15140,12 +15159,12 @@ function python(hljs) {
15140
15159
  ]
15141
15160
  };
15142
15161
  }
15143
- const _hoisted_1$2 = { class: "code-block-wrapper pa-2" };
15144
- const _hoisted_2$1 = {
15162
+ const _hoisted_1$6 = { class: "code-block-wrapper pa-2" };
15163
+ const _hoisted_2$4 = {
15145
15164
  key: 0,
15146
15165
  class: "language-indicator"
15147
15166
  };
15148
- const _sfc_main$6 = /* @__PURE__ */ defineComponent({
15167
+ const _sfc_main$a = /* @__PURE__ */ defineComponent({
15149
15168
  __name: "CodeBlockRenderer",
15150
15169
  props: {
15151
15170
  code: {
@@ -15176,8 +15195,8 @@ const _sfc_main$6 = /* @__PURE__ */ defineComponent({
15176
15195
  }
15177
15196
  return (_ctx, _cache) => {
15178
15197
  const _component_highlightjs = resolveComponent("highlightjs");
15179
- return openBlock(), createElementBlock("div", _hoisted_1$2, [
15180
- __props.language ? (openBlock(), createElementBlock("div", _hoisted_2$1, toDisplayString(__props.language), 1)) : createCommentVNode("", true),
15198
+ return openBlock(), createElementBlock("div", _hoisted_1$6, [
15199
+ __props.language ? (openBlock(), createElementBlock("div", _hoisted_2$4, toDisplayString(__props.language), 1)) : createCommentVNode("", true),
15181
15200
  createVNode(_component_highlightjs, {
15182
15201
  language: __props.language,
15183
15202
  code: __props.code
@@ -15186,11 +15205,11 @@ const _sfc_main$6 = /* @__PURE__ */ defineComponent({
15186
15205
  };
15187
15206
  }
15188
15207
  });
15189
- const _hoisted_1$1 = { key: 0 };
15190
- const _hoisted_2 = { key: 0 };
15191
- const _hoisted_3 = { key: 0 };
15192
- const _hoisted_4 = { key: 1 };
15193
- const _hoisted_5 = { key: 2 };
15208
+ const _hoisted_1$5 = { key: 0 };
15209
+ const _hoisted_2$3 = { key: 0 };
15210
+ const _hoisted_3$3 = { key: 0 };
15211
+ const _hoisted_4$2 = { key: 1 };
15212
+ const _hoisted_5$2 = { key: 2 };
15194
15213
  const _hoisted_6 = { key: 1 };
15195
15214
  const _hoisted_7 = { key: 1 };
15196
15215
  const _hoisted_8 = {
@@ -15228,7 +15247,7 @@ const _hoisted_27 = ["innerHTML"];
15228
15247
  const _hoisted_28 = { key: 16 };
15229
15248
  const _hoisted_29 = { key: 17 };
15230
15249
  const _hoisted_30 = { key: 18 };
15231
- const _sfc_main$5 = /* @__PURE__ */ defineComponent({
15250
+ const _sfc_main$9 = /* @__PURE__ */ defineComponent({
15232
15251
  __name: "MdTokenRenderer",
15233
15252
  props: {
15234
15253
  token: {
@@ -15295,21 +15314,21 @@ const _sfc_main$5 = /* @__PURE__ */ defineComponent({
15295
15314
  });
15296
15315
  return (_ctx, _cache) => {
15297
15316
  const _component_md_token_renderer = resolveComponent("md-token-renderer", true);
15298
- return isText(__props.token) ? (openBlock(), createElementBlock("span", _hoisted_1$1, [
15299
- !__props.token.tokens || __props.token.tokens.length === 0 ? (openBlock(), createElementBlock("span", _hoisted_2, [
15300
- isComponent$1(__props.token) ? (openBlock(), createElementBlock("span", _hoisted_3, [
15317
+ return isText(__props.token) ? (openBlock(), createElementBlock("span", _hoisted_1$5, [
15318
+ !__props.token.tokens || __props.token.tokens.length === 0 ? (openBlock(), createElementBlock("span", _hoisted_2$3, [
15319
+ isComponent$1(__props.token) ? (openBlock(), createElementBlock("span", _hoisted_3$3, [
15301
15320
  !__props.last ? (openBlock(), createBlock(resolveDynamicComponent(getComponent(parsedComponent(__props.token).is)), {
15302
15321
  key: 0,
15303
15322
  text: parsedComponent(__props.token).text
15304
15323
  }, null, 8, ["text"])) : createCommentVNode("", true)
15305
- ])) : containsComponent$1(__props.token) ? (openBlock(), createElementBlock("span", _hoisted_4, [
15324
+ ])) : containsComponent$1(__props.token) ? (openBlock(), createElementBlock("span", _hoisted_4$2, [
15306
15325
  (openBlock(true), createElementBlock(Fragment, null, renderList(splitTextToken$1(__props.token), (subTok, j) => {
15307
15326
  return openBlock(), createBlock(_component_md_token_renderer, {
15308
15327
  key: j,
15309
15328
  token: subTok
15310
15329
  }, null, 8, ["token"]);
15311
15330
  }), 128))
15312
- ])) : (openBlock(), createElementBlock("span", _hoisted_5, toDisplayString(decodeBasicEntities(__props.token.text)), 1))
15331
+ ])) : (openBlock(), createElementBlock("span", _hoisted_5$2, toDisplayString(decodeBasicEntities(__props.token.text)), 1))
15313
15332
  ])) : __props.token.tokens && __props.token.tokens.length !== 0 ? (openBlock(), createElementBlock("span", _hoisted_6, [
15314
15333
  (openBlock(true), createElementBlock(Fragment, null, renderList(__props.token.tokens, (subTok, j) => {
15315
15334
  return openBlock(), createBlock(_component_md_token_renderer, {
@@ -15442,7 +15461,7 @@ const _sfc_main$5 = /* @__PURE__ */ defineComponent({
15442
15461
  ], 8, _hoisted_25)) : __props.token.type === "html" ? (openBlock(), createElementBlock("span", {
15443
15462
  key: 13,
15444
15463
  innerHTML: __props.token.raw
15445
- }, null, 8, _hoisted_26)) : __props.token.type === "code" ? (openBlock(), createBlock(_sfc_main$6, {
15464
+ }, null, 8, _hoisted_26)) : __props.token.type === "code" ? (openBlock(), createBlock(_sfc_main$a, {
15446
15465
  key: 14,
15447
15466
  code: __props.token.text,
15448
15467
  language: __props.token.lang
@@ -15468,8 +15487,8 @@ const _sfc_main$5 = /* @__PURE__ */ defineComponent({
15468
15487
  };
15469
15488
  }
15470
15489
  });
15471
- const MdTokenRenderer = /* @__PURE__ */ _export_sfc(_sfc_main$5, [["__scopeId", "data-v-047d0fa4"]]);
15472
- const _sfc_main$4 = defineComponent({
15490
+ const MdTokenRenderer = /* @__PURE__ */ _export_sfc(_sfc_main$9, [["__scopeId", "data-v-047d0fa4"]]);
15491
+ const _sfc_main$8 = defineComponent({
15473
15492
  name: "MarkdownRenderer",
15474
15493
  components: {
15475
15494
  MdTokenRenderer,
@@ -15487,7 +15506,7 @@ const _sfc_main$4 = defineComponent({
15487
15506
  }
15488
15507
  }
15489
15508
  });
15490
- function _sfc_render$2(_ctx, _cache, $props, $setup, $data, $options) {
15509
+ function _sfc_render$5(_ctx, _cache, $props, $setup, $data, $options) {
15491
15510
  const _component_md_token_renderer = resolveComponent("md-token-renderer");
15492
15511
  const _component_audio_auto_player = resolveComponent("audio-auto-player");
15493
15512
  return openBlock(), createElementBlock("div", null, [
@@ -15505,7 +15524,7 @@ function _sfc_render$2(_ctx, _cache, $props, $setup, $data, $options) {
15505
15524
  }), 128))
15506
15525
  ]);
15507
15526
  }
15508
- const MarkdownRenderer = /* @__PURE__ */ _export_sfc(_sfc_main$4, [["render", _sfc_render$2]]);
15527
+ const MarkdownRenderer = /* @__PURE__ */ _export_sfc(_sfc_main$8, [["render", _sfc_render$5]]);
15509
15528
  const MarkdownRenderer$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
15510
15529
  __proto__: null,
15511
15530
  default: MarkdownRenderer
@@ -15557,6 +15576,22 @@ const useAuthStore = () => {
15557
15576
  },
15558
15577
  setRegDialog(open) {
15559
15578
  this.loginAndRegistration.regDialogOpen = open;
15579
+ },
15580
+ async resetUserData() {
15581
+ try {
15582
+ if (!this._user) {
15583
+ throw new Error("No user available for data reset");
15584
+ }
15585
+ const result = await this._user.resetUserData();
15586
+ if (result.status !== "ok") {
15587
+ throw new Error(result.error || "Reset failed");
15588
+ }
15589
+ console.log("User data reset successfully");
15590
+ return result;
15591
+ } catch (error) {
15592
+ console.error("Failed to reset user data:", error);
15593
+ throw error;
15594
+ }
15560
15595
  }
15561
15596
  },
15562
15597
  getters: {
@@ -15621,185 +15656,364 @@ const useConfigStore = () => {
15621
15656
  }
15622
15657
  })();
15623
15658
  };
15624
- const _sfc_main$3 = defineComponent({
15625
- name: "UserChip",
15626
- data() {
15627
- return {
15628
- username: "",
15629
- items: [],
15630
- checked: false,
15631
- authStore: useAuthStore(),
15632
- configStore: useConfigStore()
15633
- };
15634
- },
15635
- computed: {
15636
- hasNewItems() {
15637
- return this.items.length > 0;
15659
+ function useAuthUI() {
15660
+ const isLoading = ref(true);
15661
+ const syncStrategyDetected = ref(false);
15662
+ const isLocalOnlyMode = ref(false);
15663
+ const config = computed(() => {
15664
+ if (isLocalOnlyMode.value) {
15665
+ return {
15666
+ showLoginRegistration: false,
15667
+ showLogout: false,
15668
+ showResetData: true,
15669
+ logoutLabel: "",
15670
+ resetLabel: "Reset User Data"
15671
+ };
15672
+ } else {
15673
+ return {
15674
+ showLoginRegistration: true,
15675
+ showLogout: true,
15676
+ showResetData: false,
15677
+ logoutLabel: "Log out",
15678
+ resetLabel: ""
15679
+ };
15638
15680
  }
15639
- },
15640
- created() {
15641
- getCurrentUser().then((u) => {
15642
- this.username = u.getUsername();
15681
+ });
15682
+ const detectSyncStrategy = async () => {
15683
+ try {
15684
+ isLoading.value = true;
15685
+ const user = await getCurrentUser();
15686
+ const userInternal = user;
15687
+ const canCreateAccount = userInternal.syncStrategy?.canCreateAccount?.();
15688
+ isLocalOnlyMode.value = !canCreateAccount;
15689
+ syncStrategyDetected.value = true;
15690
+ } catch (error) {
15691
+ console.error("Failed to detect sync strategy:", error);
15692
+ isLocalOnlyMode.value = false;
15693
+ syncStrategyDetected.value = true;
15694
+ } finally {
15695
+ isLoading.value = false;
15696
+ }
15697
+ };
15698
+ return {
15699
+ config,
15700
+ isLoading,
15701
+ syncStrategyDetected,
15702
+ isLocalOnlyMode,
15703
+ detectSyncStrategy
15704
+ };
15705
+ }
15706
+ const _sfc_main$7 = /* @__PURE__ */ defineComponent({
15707
+ __name: "UserChip",
15708
+ setup(__props) {
15709
+ const router = useRouter();
15710
+ const authStore = useAuthStore();
15711
+ const configStore = useConfigStore();
15712
+ const authUI = useAuthUI();
15713
+ const username = ref("");
15714
+ const items = ref([]);
15715
+ const showResetDialog = ref(false);
15716
+ const confirmationText = ref("");
15717
+ const isConfirmationValid = computed(() => confirmationText.value === "reset");
15718
+ const resetDialogState = () => {
15719
+ confirmationText.value = "";
15720
+ showResetDialog.value = false;
15721
+ };
15722
+ const hasNewItems = computed(() => items.value.length > 0);
15723
+ const authUIConfig = computed(() => {
15724
+ const configValue = authUI.config.value;
15725
+ const fallback = {
15726
+ showLoginRegistration: true,
15727
+ showLogout: true,
15728
+ showResetData: false,
15729
+ logoutLabel: "Log out",
15730
+ resetLabel: ""
15731
+ };
15732
+ return configValue || fallback;
15643
15733
  });
15644
- },
15645
- methods: {
15646
- async gotoSettings() {
15647
- this.$router.push(`/u/${(await getCurrentUser()).getUsername()}`);
15648
- },
15649
- async gotoStats() {
15650
- this.$router.push(`/u/${(await getCurrentUser()).getUsername()}/stats`);
15651
- },
15652
- dismiss(item) {
15653
- const index = this.items.indexOf(item);
15654
- this.items.splice(index, 1);
15655
- },
15656
- async logout() {
15657
- const res = await this.authStore._user.logout();
15734
+ onMounted(async () => {
15735
+ const user = await getCurrentUser();
15736
+ username.value = user.getUsername();
15737
+ await authUI.detectSyncStrategy();
15738
+ });
15739
+ const gotoSettings = async () => {
15740
+ router.push(`/u/${(await getCurrentUser()).getUsername()}`);
15741
+ };
15742
+ const gotoStats = async () => {
15743
+ router.push(`/u/${(await getCurrentUser()).getUsername()}/stats`);
15744
+ };
15745
+ const dismiss = (item) => {
15746
+ const index = items.value.indexOf(item);
15747
+ items.value.splice(index, 1);
15748
+ };
15749
+ const logout = async () => {
15750
+ const res = await authStore._user.logout();
15658
15751
  if (res.ok) {
15659
- this.authStore.loginAndRegistration = {
15752
+ authStore.loginAndRegistration = {
15660
15753
  init: true,
15661
15754
  loggedIn: false,
15662
15755
  regDialogOpen: false,
15663
15756
  loginDialogOpen: false
15664
15757
  };
15665
- this.configStore.resetDefaults();
15666
- this.$router.push("/home");
15758
+ configStore.resetDefaults();
15759
+ router.push("/home");
15667
15760
  }
15668
- }
15669
- }
15670
- });
15671
- function _sfc_render$1(_ctx, _cache, $props, $setup, $data, $options) {
15672
- const _component_v_icon = resolveComponent("v-icon");
15673
- const _component_v_avatar = resolveComponent("v-avatar");
15674
- const _component_v_chip = resolveComponent("v-chip");
15675
- const _component_v_list_item_title = resolveComponent("v-list-item-title");
15676
- const _component_v_list_item = resolveComponent("v-list-item");
15677
- const _component_v_divider = resolveComponent("v-divider");
15678
- const _component_v_list = resolveComponent("v-list");
15679
- const _component_v_menu = resolveComponent("v-menu");
15680
- const _component_v_badge = resolveComponent("v-badge");
15681
- return openBlock(), createBlock(_component_v_badge, {
15682
- content: _ctx.items.length,
15683
- "model-value": _ctx.hasNewItems,
15684
- color: "accent",
15685
- location: "end top"
15686
- }, {
15687
- default: withCtx(() => [
15688
- createVNode(_component_v_menu, {
15689
- location: "bottom end",
15690
- transition: "scale-transition"
15691
- }, {
15692
- activator: withCtx(({ props }) => [
15693
- createVNode(_component_v_chip, mergeProps(props, { class: "ma-2" }), {
15694
- default: withCtx(() => [
15695
- createVNode(_component_v_avatar, {
15696
- start: "",
15697
- class: "bg-primary"
15698
- }, {
15699
- default: withCtx(() => [
15700
- createVNode(_component_v_icon, null, {
15701
- default: withCtx(() => _cache[0] || (_cache[0] = [
15702
- createTextVNode("mdi-school")
15703
- ])),
15704
- _: 1
15705
- })
15706
- ]),
15707
- _: 1
15708
- }),
15709
- createTextVNode(" " + toDisplayString(_ctx.username), 1)
15710
- ]),
15711
- _: 2
15712
- }, 1040)
15713
- ]),
15714
- default: withCtx(() => [
15715
- createVNode(_component_v_list, null, {
15716
- default: withCtx(() => [
15717
- (openBlock(true), createElementBlock(Fragment, null, renderList(_ctx.items, (item) => {
15718
- return openBlock(), createBlock(_component_v_list_item, {
15719
- key: item,
15720
- onClick: ($event) => _ctx.dismiss(item)
15721
- }, {
15761
+ };
15762
+ const executeReset = async () => {
15763
+ try {
15764
+ await authStore.resetUserData();
15765
+ configStore.resetDefaults();
15766
+ resetDialogState();
15767
+ router.push("/home");
15768
+ } catch (error) {
15769
+ console.error("Failed to reset user data:", error);
15770
+ }
15771
+ };
15772
+ return (_ctx, _cache) => {
15773
+ const _component_v_icon = resolveComponent("v-icon");
15774
+ const _component_v_avatar = resolveComponent("v-avatar");
15775
+ const _component_v_chip = resolveComponent("v-chip");
15776
+ const _component_v_list_item_title = resolveComponent("v-list-item-title");
15777
+ const _component_v_list_item = resolveComponent("v-list-item");
15778
+ const _component_v_divider = resolveComponent("v-divider");
15779
+ const _component_v_list = resolveComponent("v-list");
15780
+ const _component_v_menu = resolveComponent("v-menu");
15781
+ const _component_v_badge = resolveComponent("v-badge");
15782
+ const _component_v_card_title = resolveComponent("v-card-title");
15783
+ const _component_v_text_field = resolveComponent("v-text-field");
15784
+ const _component_v_card_text = resolveComponent("v-card-text");
15785
+ const _component_v_spacer = resolveComponent("v-spacer");
15786
+ const _component_v_btn = resolveComponent("v-btn");
15787
+ const _component_v_card_actions = resolveComponent("v-card-actions");
15788
+ const _component_v_card = resolveComponent("v-card");
15789
+ const _component_v_dialog = resolveComponent("v-dialog");
15790
+ return openBlock(), createElementBlock(Fragment, null, [
15791
+ createVNode(_component_v_badge, {
15792
+ content: items.value.length,
15793
+ "model-value": hasNewItems.value,
15794
+ color: "accent",
15795
+ location: "end top"
15796
+ }, {
15797
+ default: withCtx(() => [
15798
+ createVNode(_component_v_menu, {
15799
+ location: "bottom end",
15800
+ transition: "scale-transition"
15801
+ }, {
15802
+ activator: withCtx(({ props }) => [
15803
+ createVNode(_component_v_chip, mergeProps(props, { class: "ma-2" }), {
15722
15804
  default: withCtx(() => [
15723
- createVNode(_component_v_list_item_title, null, {
15805
+ createVNode(_component_v_avatar, {
15806
+ start: "",
15807
+ class: "bg-primary"
15808
+ }, {
15724
15809
  default: withCtx(() => [
15725
- createTextVNode(toDisplayString(item), 1)
15810
+ createVNode(_component_v_icon, null, {
15811
+ default: withCtx(() => _cache[4] || (_cache[4] = [
15812
+ createTextVNode("mdi-school")
15813
+ ])),
15814
+ _: 1
15815
+ })
15726
15816
  ]),
15727
- _: 2
15728
- }, 1024)
15817
+ _: 1
15818
+ }),
15819
+ createTextVNode(" " + toDisplayString(username.value), 1)
15729
15820
  ]),
15730
15821
  _: 2
15731
- }, 1032, ["onClick"]);
15732
- }), 128)),
15733
- _ctx.items.length ? (openBlock(), createBlock(_component_v_divider, { key: 0 })) : createCommentVNode("", true),
15734
- createVNode(_component_v_list_item, { onClick: _ctx.gotoStats }, {
15735
- prepend: withCtx(() => [
15736
- createVNode(_component_v_icon, null, {
15737
- default: withCtx(() => _cache[1] || (_cache[1] = [
15738
- createTextVNode("mdi-trending-up")
15739
- ])),
15740
- _: 1
15741
- })
15742
- ]),
15743
- default: withCtx(() => [
15744
- createVNode(_component_v_list_item_title, null, {
15745
- default: withCtx(() => _cache[2] || (_cache[2] = [
15746
- createTextVNode("Stats")
15747
- ])),
15748
- _: 1
15749
- })
15750
- ]),
15751
- _: 1
15752
- }, 8, ["onClick"]),
15753
- createVNode(_component_v_list_item, { onClick: _ctx.gotoSettings }, {
15754
- prepend: withCtx(() => [
15755
- createVNode(_component_v_icon, null, {
15756
- default: withCtx(() => _cache[3] || (_cache[3] = [
15757
- createTextVNode("mdi-cog")
15758
- ])),
15759
- _: 1
15760
- })
15761
- ]),
15762
- default: withCtx(() => [
15763
- createVNode(_component_v_list_item_title, null, {
15764
- default: withCtx(() => _cache[4] || (_cache[4] = [
15765
- createTextVNode("Settings")
15766
- ])),
15767
- _: 1
15768
- })
15769
- ]),
15770
- _: 1
15771
- }, 8, ["onClick"]),
15772
- createVNode(_component_v_list_item, { onClick: _ctx.logout }, {
15773
- prepend: withCtx(() => [
15774
- createVNode(_component_v_icon, null, {
15775
- default: withCtx(() => _cache[5] || (_cache[5] = [
15776
- createTextVNode("mdi-logout")
15777
- ])),
15778
- _: 1
15779
- })
15780
- ]),
15781
- default: withCtx(() => [
15782
- createVNode(_component_v_list_item_title, null, {
15783
- default: withCtx(() => _cache[6] || (_cache[6] = [
15784
- createTextVNode("Log out")
15785
- ])),
15786
- _: 1
15787
- })
15788
- ]),
15789
- _: 1
15790
- }, 8, ["onClick"])
15791
- ]),
15792
- _: 1
15793
- })
15794
- ]),
15795
- _: 1
15796
- })
15797
- ]),
15798
- _: 1
15799
- }, 8, ["content", "model-value"]);
15800
- }
15801
- const UserChip = /* @__PURE__ */ _export_sfc(_sfc_main$3, [["render", _sfc_render$1]]);
15802
- const _sfc_main$2 = /* @__PURE__ */ defineComponent({
15822
+ }, 1040)
15823
+ ]),
15824
+ default: withCtx(() => [
15825
+ createVNode(_component_v_list, null, {
15826
+ default: withCtx(() => [
15827
+ (openBlock(true), createElementBlock(Fragment, null, renderList(items.value, (item) => {
15828
+ return openBlock(), createBlock(_component_v_list_item, {
15829
+ key: item,
15830
+ onClick: ($event) => dismiss(item)
15831
+ }, {
15832
+ default: withCtx(() => [
15833
+ createVNode(_component_v_list_item_title, null, {
15834
+ default: withCtx(() => [
15835
+ createTextVNode(toDisplayString(item), 1)
15836
+ ]),
15837
+ _: 2
15838
+ }, 1024)
15839
+ ]),
15840
+ _: 2
15841
+ }, 1032, ["onClick"]);
15842
+ }), 128)),
15843
+ items.value.length ? (openBlock(), createBlock(_component_v_divider, { key: 0 })) : createCommentVNode("", true),
15844
+ createVNode(_component_v_list_item, { onClick: gotoStats }, {
15845
+ prepend: withCtx(() => [
15846
+ createVNode(_component_v_icon, null, {
15847
+ default: withCtx(() => _cache[5] || (_cache[5] = [
15848
+ createTextVNode("mdi-trending-up")
15849
+ ])),
15850
+ _: 1
15851
+ })
15852
+ ]),
15853
+ default: withCtx(() => [
15854
+ createVNode(_component_v_list_item_title, null, {
15855
+ default: withCtx(() => _cache[6] || (_cache[6] = [
15856
+ createTextVNode("Stats")
15857
+ ])),
15858
+ _: 1
15859
+ })
15860
+ ]),
15861
+ _: 1
15862
+ }),
15863
+ createVNode(_component_v_list_item, { onClick: gotoSettings }, {
15864
+ prepend: withCtx(() => [
15865
+ createVNode(_component_v_icon, null, {
15866
+ default: withCtx(() => _cache[7] || (_cache[7] = [
15867
+ createTextVNode("mdi-cog")
15868
+ ])),
15869
+ _: 1
15870
+ })
15871
+ ]),
15872
+ default: withCtx(() => [
15873
+ createVNode(_component_v_list_item_title, null, {
15874
+ default: withCtx(() => _cache[8] || (_cache[8] = [
15875
+ createTextVNode("Settings")
15876
+ ])),
15877
+ _: 1
15878
+ })
15879
+ ]),
15880
+ _: 1
15881
+ }),
15882
+ authUIConfig.value.showLogout ? (openBlock(), createBlock(_component_v_list_item, {
15883
+ key: 1,
15884
+ onClick: logout
15885
+ }, {
15886
+ prepend: withCtx(() => [
15887
+ createVNode(_component_v_icon, null, {
15888
+ default: withCtx(() => _cache[9] || (_cache[9] = [
15889
+ createTextVNode("mdi-logout")
15890
+ ])),
15891
+ _: 1
15892
+ })
15893
+ ]),
15894
+ default: withCtx(() => [
15895
+ createVNode(_component_v_list_item_title, null, {
15896
+ default: withCtx(() => [
15897
+ createTextVNode(toDisplayString(authUIConfig.value.logoutLabel), 1)
15898
+ ]),
15899
+ _: 1
15900
+ })
15901
+ ]),
15902
+ _: 1
15903
+ })) : createCommentVNode("", true),
15904
+ authUIConfig.value.showResetData ? (openBlock(), createBlock(_component_v_list_item, {
15905
+ key: 2,
15906
+ onClick: _cache[0] || (_cache[0] = ($event) => showResetDialog.value = true)
15907
+ }, {
15908
+ prepend: withCtx(() => [
15909
+ createVNode(_component_v_icon, null, {
15910
+ default: withCtx(() => _cache[10] || (_cache[10] = [
15911
+ createTextVNode("mdi-delete-sweep")
15912
+ ])),
15913
+ _: 1
15914
+ })
15915
+ ]),
15916
+ default: withCtx(() => [
15917
+ createVNode(_component_v_list_item_title, null, {
15918
+ default: withCtx(() => [
15919
+ createTextVNode(toDisplayString(authUIConfig.value.resetLabel), 1)
15920
+ ]),
15921
+ _: 1
15922
+ })
15923
+ ]),
15924
+ _: 1
15925
+ })) : createCommentVNode("", true)
15926
+ ]),
15927
+ _: 1
15928
+ })
15929
+ ]),
15930
+ _: 1
15931
+ })
15932
+ ]),
15933
+ _: 1
15934
+ }, 8, ["content", "model-value"]),
15935
+ createVNode(_component_v_dialog, {
15936
+ modelValue: showResetDialog.value,
15937
+ "onUpdate:modelValue": _cache[3] || (_cache[3] = ($event) => showResetDialog.value = $event),
15938
+ "max-width": "500px",
15939
+ persistent: ""
15940
+ }, {
15941
+ default: withCtx(() => [
15942
+ createVNode(_component_v_card, null, {
15943
+ default: withCtx(() => [
15944
+ createVNode(_component_v_card_title, { class: "text-h5 d-flex align-center" }, {
15945
+ default: withCtx(() => [
15946
+ createVNode(_component_v_icon, {
15947
+ color: "warning",
15948
+ class: "mr-3"
15949
+ }, {
15950
+ default: withCtx(() => _cache[11] || (_cache[11] = [
15951
+ createTextVNode("mdi-alert-circle")
15952
+ ])),
15953
+ _: 1
15954
+ }),
15955
+ _cache[12] || (_cache[12] = createTextVNode(" Reset All User Data "))
15956
+ ]),
15957
+ _: 1
15958
+ }),
15959
+ createVNode(_component_v_card_text, null, {
15960
+ default: withCtx(() => [
15961
+ _cache[13] || (_cache[13] = createElementVNode("p", { class: "mb-4" }, "This will permanently delete:", -1)),
15962
+ _cache[14] || (_cache[14] = createElementVNode("ul", { class: "mb-4" }, [
15963
+ createElementVNode("li", null, "All course progress and history"),
15964
+ createElementVNode("li", null, "Scheduled card reviews"),
15965
+ createElementVNode("li", null, "Course registrations"),
15966
+ createElementVNode("li", null, "User preferences")
15967
+ ], -1)),
15968
+ _cache[15] || (_cache[15] = createElementVNode("p", { class: "mb-4 text-error font-weight-bold" }, "This cannot be undone.", -1)),
15969
+ createVNode(_component_v_text_field, {
15970
+ modelValue: confirmationText.value,
15971
+ "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => confirmationText.value = $event),
15972
+ label: 'Type "reset" to confirm',
15973
+ outlined: "",
15974
+ dense: "",
15975
+ onKeyup: _cache[2] || (_cache[2] = withKeys(($event) => isConfirmationValid.value && executeReset(), ["enter"]))
15976
+ }, null, 8, ["modelValue"])
15977
+ ]),
15978
+ _: 1
15979
+ }),
15980
+ createVNode(_component_v_card_actions, null, {
15981
+ default: withCtx(() => [
15982
+ createVNode(_component_v_spacer),
15983
+ createVNode(_component_v_btn, {
15984
+ text: "",
15985
+ onClick: resetDialogState
15986
+ }, {
15987
+ default: withCtx(() => _cache[16] || (_cache[16] = [
15988
+ createTextVNode("Cancel")
15989
+ ])),
15990
+ _: 1
15991
+ }),
15992
+ createVNode(_component_v_btn, {
15993
+ color: "error",
15994
+ disabled: !isConfirmationValid.value,
15995
+ onClick: executeReset
15996
+ }, {
15997
+ default: withCtx(() => _cache[17] || (_cache[17] = [
15998
+ createTextVNode(" Reset All Data ")
15999
+ ])),
16000
+ _: 1
16001
+ }, 8, ["disabled"])
16002
+ ]),
16003
+ _: 1
16004
+ })
16005
+ ]),
16006
+ _: 1
16007
+ })
16008
+ ]),
16009
+ _: 1
16010
+ }, 8, ["modelValue"])
16011
+ ], 64);
16012
+ };
16013
+ }
16014
+ });
16015
+ const UserChip = /* @__PURE__ */ _export_sfc(_sfc_main$7, [["__scopeId", "data-v-53f0aa4d"]]);
16016
+ const _sfc_main$6 = /* @__PURE__ */ defineComponent({
15803
16017
  __name: "UserLogin",
15804
16018
  emits: ["toggle"],
15805
16019
  setup(__props, { emit: __emit }) {
@@ -15986,8 +16200,8 @@ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
15986
16200
  };
15987
16201
  }
15988
16202
  });
15989
- const UserLogin = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["__scopeId", "data-v-acfaa2d0"]]);
15990
- const _sfc_main$1 = defineComponent({
16203
+ const UserLogin = /* @__PURE__ */ _export_sfc(_sfc_main$6, [["__scopeId", "data-v-acfaa2d0"]]);
16204
+ const _sfc_main$5 = defineComponent({
15991
16205
  name: "UserRegistration",
15992
16206
  emits: ["toggle"],
15993
16207
  data() {
@@ -16086,7 +16300,7 @@ Author: ${this.author}
16086
16300
  }
16087
16301
  }
16088
16302
  });
16089
- function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
16303
+ function _sfc_render$4(_ctx, _cache, $props, $setup, $data, $options) {
16090
16304
  const _component_v_card_title = resolveComponent("v-card-title");
16091
16305
  const _component_v_text_field = resolveComponent("v-text-field");
16092
16306
  const _component_v_btn = resolveComponent("v-btn");
@@ -16221,13 +16435,17 @@ function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
16221
16435
  _: 1
16222
16436
  });
16223
16437
  }
16224
- const UserRegistration = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["render", _sfc_render]]);
16225
- const _hoisted_1 = { key: 0 };
16226
- const _sfc_main = /* @__PURE__ */ defineComponent({
16438
+ const UserRegistration = /* @__PURE__ */ _export_sfc(_sfc_main$5, [["render", _sfc_render$4]]);
16439
+ const _hoisted_1$4 = { key: 0 };
16440
+ const _sfc_main$4 = /* @__PURE__ */ defineComponent({
16227
16441
  __name: "UserLoginAndRegistrationContainer",
16228
16442
  setup(__props) {
16229
16443
  const route = useRoute();
16230
16444
  const authStore = useAuthStore();
16445
+ const authUI = useAuthUI();
16446
+ onMounted(async () => {
16447
+ await authUI.detectSyncStrategy();
16448
+ });
16231
16449
  const display = computed(() => {
16232
16450
  if (!route.name || typeof route.name !== "string") {
16233
16451
  return true;
@@ -16242,6 +16460,13 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
16242
16460
  }
16243
16461
  return !authStore.loginAndRegistration.loggedIn;
16244
16462
  });
16463
+ const authUIConfig = computed(() => authUI.config.value || {
16464
+ showLoginRegistration: true,
16465
+ showLogout: true,
16466
+ showResetData: false,
16467
+ logoutLabel: "Log out",
16468
+ resetLabel: ""
16469
+ });
16245
16470
  const regDialog = computed({
16246
16471
  get: () => authStore.loginAndRegistration.regDialogOpen,
16247
16472
  set: (value) => {
@@ -16273,7 +16498,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
16273
16498
  mode: "out-in"
16274
16499
  }, {
16275
16500
  default: withCtx(() => [
16276
- guestMode.value ? (openBlock(), createElementBlock("div", _hoisted_1, [
16501
+ guestMode.value && authUIConfig.value.showLoginRegistration ? (openBlock(), createElementBlock("div", _hoisted_1$4, [
16277
16502
  createVNode(_component_v_dialog, {
16278
16503
  modelValue: regDialog.value,
16279
16504
  "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => regDialog.value = $event),
@@ -16324,12 +16549,962 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
16324
16549
  };
16325
16550
  }
16326
16551
  });
16327
- const UserLoginAndRegistrationContainer = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-e53f877f"]]);
16552
+ const UserLoginAndRegistrationContainer = /* @__PURE__ */ _export_sfc(_sfc_main$4, [["__scopeId", "data-v-6091ae05"]]);
16553
+ const _sfc_main$3 = defineComponent({
16554
+ name: "SkTagsInput",
16555
+ components: {
16556
+ VueTagsInput
16557
+ },
16558
+ props: {
16559
+ courseID: {
16560
+ type: String,
16561
+ required: true,
16562
+ default: ""
16563
+ },
16564
+ cardID: {
16565
+ type: String,
16566
+ required: false,
16567
+ default: ""
16568
+ },
16569
+ hideSubmit: {
16570
+ type: Boolean,
16571
+ required: false,
16572
+ default: false
16573
+ }
16574
+ },
16575
+ data() {
16576
+ return {
16577
+ loading: true,
16578
+ tag: "",
16579
+ tags: [],
16580
+ initialTags: [],
16581
+ availableCourseTags: [],
16582
+ separators: [";", ",", " "],
16583
+ courseDB: null
16584
+ };
16585
+ },
16586
+ computed: {
16587
+ autoCompleteSuggestions() {
16588
+ return this.availableCourseTags.filter((availableTag) => {
16589
+ return availableTag.name.toLowerCase().indexOf(this.tag.toLowerCase()) !== -1;
16590
+ }).map((availableTag) => {
16591
+ return {
16592
+ text: availableTag.name,
16593
+ data: {
16594
+ snippet: availableTag.snippet
16595
+ }
16596
+ };
16597
+ });
16598
+ }
16599
+ },
16600
+ watch: {
16601
+ async cardID() {
16602
+ await this.getAppliedTags();
16603
+ },
16604
+ async courseID() {
16605
+ this.courseDB = getDataLayer().getCourseDB(this.courseID);
16606
+ await this.updateAvailableCourseTags();
16607
+ }
16608
+ },
16609
+ async created() {
16610
+ this.courseDB = getDataLayer().getCourseDB(this.courseID);
16611
+ await this.updateAvailableCourseTags();
16612
+ await this.getAppliedTags();
16613
+ },
16614
+ methods: {
16615
+ tagsChanged(newTags) {
16616
+ console.log(`[TagsInput] Tags changing: ${JSON.stringify(newTags)}`);
16617
+ this.tags = newTags;
16618
+ },
16619
+ async getAppliedTags() {
16620
+ this.initialTags = [];
16621
+ this.tags = [];
16622
+ try {
16623
+ const appliedDocsFindResult = await this.courseDB.getAppliedTags(this.cardID);
16624
+ appliedDocsFindResult.rows.forEach((row) => {
16625
+ console.log(`[TagsInput] The following tag is applied:
16626
+ ${JSON.stringify(row)}`);
16627
+ this.tags.push({
16628
+ text: row.value.name,
16629
+ style: "",
16630
+ classes: ""
16631
+ });
16632
+ });
16633
+ this.initialTags = this.tags.map((tag2) => tag2.text);
16634
+ } catch (e) {
16635
+ console.error(`Error in init-getAppliedTags: ${JSON.stringify(e)}, ${e}`);
16636
+ } finally {
16637
+ this.loading = false;
16638
+ }
16639
+ },
16640
+ async updateAvailableCourseTags() {
16641
+ try {
16642
+ this.availableCourseTags = (await this.courseDB.getCourseTagStubs()).rows.map((row) => {
16643
+ console.log(`[TagsInput] available tag: ${JSON.stringify(row)}`);
16644
+ return row.doc;
16645
+ });
16646
+ } catch (e) {
16647
+ console.error(`Error in init-availableCourseTags: ${JSON.stringify(e)}`);
16648
+ }
16649
+ },
16650
+ async submit() {
16651
+ console.log(`[TagsInput] tagsInput is submitting...`);
16652
+ this.loading = true;
16653
+ try {
16654
+ await Promise.all(
16655
+ this.tags.map(async (currentTag) => {
16656
+ if (!this.initialTags.includes(currentTag.text)) {
16657
+ try {
16658
+ await this.courseDB.addTagToCard(this.cardID, currentTag.text);
16659
+ console.log(`[TagsInput] Successfully added tag: ${currentTag.text}`);
16660
+ } catch (error) {
16661
+ console.error(`Failed to add tag ${currentTag.text}:`, error);
16662
+ }
16663
+ }
16664
+ })
16665
+ );
16666
+ } catch (e) {
16667
+ console.error(`Exception adding tags: ${JSON.stringify(e)}`);
16668
+ }
16669
+ try {
16670
+ await Promise.all(
16671
+ this.initialTags.map(async (initialTag) => {
16672
+ if (this.tags.filter((tag2) => {
16673
+ return tag2.text === initialTag;
16674
+ }).length === 0) {
16675
+ try {
16676
+ await this.courseDB.removeTagFromCard(this.cardID, initialTag);
16677
+ console.log(`[TagsInput] Successfully removed tag: ${initialTag}`);
16678
+ } catch (error) {
16679
+ console.error(`Failed to remove tag ${initialTag}:`, error);
16680
+ }
16681
+ }
16682
+ })
16683
+ );
16684
+ } catch (e) {
16685
+ console.error(`Exception removing tags: ${JSON.stringify(e)}`);
16686
+ }
16687
+ this.loading = false;
16688
+ }
16689
+ }
16690
+ });
16691
+ const _hoisted_1$3 = { "data-cy": "tags-input" };
16692
+ const _hoisted_2$2 = { class: "tag-name" };
16693
+ const _hoisted_3$2 = {
16694
+ key: 0,
16695
+ class: "tag-snippet"
16696
+ };
16697
+ function _sfc_render$3(_ctx, _cache, $props, $setup, $data, $options) {
16698
+ const _component_vue_tags_input = resolveComponent("vue-tags-input");
16699
+ const _component_v_btn = resolveComponent("v-btn");
16700
+ return openBlock(), createElementBlock("div", _hoisted_1$3, [
16701
+ createVNode(_component_vue_tags_input, {
16702
+ modelValue: _ctx.tag,
16703
+ "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => _ctx.tag = $event),
16704
+ tags: _ctx.tags,
16705
+ "autocomplete-items": _ctx.autoCompleteSuggestions,
16706
+ separators: _ctx.separators,
16707
+ "add-on-key": _ctx.separators,
16708
+ onTagsChanged: _ctx.tagsChanged
16709
+ }, {
16710
+ "autocomplete-item": withCtx((props) => [
16711
+ createElementVNode("div", {
16712
+ class: normalizeClass(["autocomplete-item", { "is-active": props.selected }])
16713
+ }, [
16714
+ createElementVNode("span", _hoisted_2$2, toDisplayString(props.item.text), 1),
16715
+ props.item.data && props.item.data.snippet ? (openBlock(), createElementBlock("span", _hoisted_3$2, " - " + toDisplayString(props.item.data.snippet), 1)) : createCommentVNode("", true)
16716
+ ], 2)
16717
+ ]),
16718
+ _: 1
16719
+ }, 8, ["modelValue", "tags", "autocomplete-items", "separators", "add-on-key", "onTagsChanged"]),
16720
+ !_ctx.hideSubmit ? (openBlock(), createBlock(_component_v_btn, {
16721
+ key: 0,
16722
+ color: "success",
16723
+ loading: _ctx.loading,
16724
+ onClick: _ctx.submit
16725
+ }, {
16726
+ default: withCtx(() => _cache[1] || (_cache[1] = [
16727
+ createTextVNode("Save Changes")
16728
+ ])),
16729
+ _: 1
16730
+ }, 8, ["loading", "onClick"])) : createCommentVNode("", true)
16731
+ ]);
16732
+ }
16733
+ const TagsInput = /* @__PURE__ */ _export_sfc(_sfc_main$3, [["render", _sfc_render$3], ["__scopeId", "data-v-dec3b5c8"]]);
16734
+ function isConstructor(obj) {
16735
+ try {
16736
+ new obj();
16737
+ return true;
16738
+ } catch (e) {
16739
+ console.warn(`not a constructor: ${obj}, err: ${e}`);
16740
+ return false;
16741
+ }
16742
+ }
16743
+ const _sfc_main$2 = defineComponent({
16744
+ name: "CourseCardBrowser",
16745
+ components: {
16746
+ CardLoader,
16747
+ TagsInput,
16748
+ PaginatingToolbar
16749
+ },
16750
+ props: {
16751
+ courseId: {
16752
+ type: String,
16753
+ required: true
16754
+ },
16755
+ tagId: {
16756
+ type: String,
16757
+ required: false,
16758
+ default: ""
16759
+ },
16760
+ viewLookupFunction: {
16761
+ type: Function,
16762
+ required: true,
16763
+ default: (x) => {
16764
+ console.warn("No viewLookupFunction provided to CourseCardBrowser");
16765
+ return null;
16766
+ }
16767
+ },
16768
+ editMode: {
16769
+ type: String,
16770
+ required: false,
16771
+ default: "full"
16772
+ }
16773
+ },
16774
+ data() {
16775
+ return {
16776
+ courseDB: null,
16777
+ page: 1,
16778
+ pages: [],
16779
+ cards: [],
16780
+ cardData: {},
16781
+ cardPreview: {},
16782
+ internalEditMode: "none",
16783
+ delBtn: false,
16784
+ updatePending: true,
16785
+ userIsRegistered: false,
16786
+ questionCount: 0,
16787
+ tags: [],
16788
+ viewLookup: this.viewLookupFunction
16789
+ };
16790
+ },
16791
+ async created() {
16792
+ try {
16793
+ this.courseDB = getDataLayer().getCourseDB(this.courseId);
16794
+ if (this.tagId) {
16795
+ this.questionCount = (await this.courseDB.getTag(this.tagId)).taggedCards.length;
16796
+ } else {
16797
+ this.questionCount = (await this.courseDB.getCourseInfo()).cardCount;
16798
+ }
16799
+ for (let i = 1; (i - 1) * 25 < this.questionCount; i++) {
16800
+ this.pages.push(i);
16801
+ }
16802
+ await this.populateTableData();
16803
+ } catch (error) {
16804
+ console.error("Error initializing CourseCardBrowser:", error);
16805
+ } finally {
16806
+ this.updatePending = false;
16807
+ }
16808
+ },
16809
+ methods: {
16810
+ first() {
16811
+ this.page = 1;
16812
+ this.populateTableData();
16813
+ },
16814
+ prev() {
16815
+ this.page--;
16816
+ this.populateTableData();
16817
+ },
16818
+ next() {
16819
+ this.page++;
16820
+ this.populateTableData();
16821
+ },
16822
+ last() {
16823
+ this.page = this.pages.length;
16824
+ this.populateTableData();
16825
+ },
16826
+ setPage(n) {
16827
+ this.page = n;
16828
+ this.populateTableData();
16829
+ },
16830
+ clearSelections(exception = "") {
16831
+ this.cards.forEach((card) => {
16832
+ if (card.id !== exception) {
16833
+ card.isOpen = false;
16834
+ }
16835
+ });
16836
+ this.internalEditMode = "none";
16837
+ this.delBtn = false;
16838
+ },
16839
+ async deleteCard(c) {
16840
+ console.log(`Deleting card ${c}`);
16841
+ const res = await this.courseDB.removeCard(c.split("-")[1]);
16842
+ if (res.ok) {
16843
+ this.cards = this.cards.filter((card) => card.id != c);
16844
+ this.clearSelections();
16845
+ } else {
16846
+ console.error(`Failed to delete card:
16847
+
16848
+ ${JSON.stringify(res)}`);
16849
+ alertUser({
16850
+ text: "Failed to delete card",
16851
+ status: Status.error
16852
+ });
16853
+ }
16854
+ },
16855
+ async populateTableData() {
16856
+ this.updatePending = true;
16857
+ if (this.tagId) {
16858
+ const tag2 = await this.courseDB.getTag(this.tagId);
16859
+ this.cards = tag2.taggedCards.map((c) => {
16860
+ return { id: `${this.courseId}-${c}`, isOpen: false, delBtn: false };
16861
+ });
16862
+ } else {
16863
+ this.cards = (await this.courseDB.getCardsByELO(0, 25)).map((c) => {
16864
+ return {
16865
+ id: c,
16866
+ isOpen: false,
16867
+ delBtn: false
16868
+ };
16869
+ });
16870
+ }
16871
+ const toRemove = [];
16872
+ const hydratedCardData = (await this.courseDB.getCourseDocs(
16873
+ this.cards.map((c) => c.id.split("-")[1]),
16874
+ {
16875
+ include_docs: true
16876
+ }
16877
+ )).rows.filter((r2) => {
16878
+ if (r2.doc) {
16879
+ return true;
16880
+ } else {
16881
+ console.error(`Card ${r2.id}.doc not found.
16882
+ card: ${JSON.stringify(r2)}`);
16883
+ return false;
16884
+ }
16885
+ }).map((r2) => r2.doc);
16886
+ this.cards = this.cards.filter((c) => !toRemove.includes(c.id.split("-")[1]));
16887
+ hydratedCardData.forEach((c) => {
16888
+ if (c && c.id_displayable_data) {
16889
+ this.cardData[c._id] = c.id_displayable_data;
16890
+ }
16891
+ });
16892
+ try {
16893
+ await Promise.all(
16894
+ this.cards.map(async (c) => {
16895
+ const _cardID = c.id.split("-")[1];
16896
+ const tmpCardData = hydratedCardData.find((c2) => c2._id == _cardID);
16897
+ if (!tmpCardData || !tmpCardData.id_displayable_data) {
16898
+ console.error(`No valid data found for card ${_cardID}`);
16899
+ return;
16900
+ }
16901
+ const tmpView = this.viewLookupFunction(
16902
+ tmpCardData.id_view || "default.question.BlanksCard.FillInView"
16903
+ );
16904
+ const tmpDataDocs = tmpCardData.id_displayable_data.map((id) => {
16905
+ return this.courseDB.getCourseDoc(id, {
16906
+ attachments: false,
16907
+ binary: true
16908
+ });
16909
+ });
16910
+ const allDocs = await Promise.all(tmpDataDocs);
16911
+ await Promise.all(
16912
+ allDocs.map((doc) => {
16913
+ const tmpData = [];
16914
+ tmpData.unshift(displayableDataToViewData(doc));
16915
+ if (isConstructor(tmpView)) {
16916
+ const view = new tmpView();
16917
+ view.data = tmpData;
16918
+ this.cardPreview[c.id] = view.toString();
16919
+ } else {
16920
+ this.cardPreview[c.id] = tmpView.name ? tmpView.name : "Unknown";
16921
+ }
16922
+ })
16923
+ );
16924
+ })
16925
+ );
16926
+ } catch (error) {
16927
+ console.error("Error populating table data:", error);
16928
+ } finally {
16929
+ this.updatePending = false;
16930
+ this.$forceUpdate();
16931
+ }
16932
+ }
16933
+ }
16934
+ });
16935
+ const _hoisted_1$2 = {
16936
+ key: 0,
16937
+ class: "d-flex justify-center align-center pa-6"
16938
+ };
16939
+ const _hoisted_2$1 = { key: 1 };
16940
+ const _hoisted_3$1 = {
16941
+ key: 0,
16942
+ class: "px-4 py-2 bg-blue-grey-lighten-5"
16943
+ };
16944
+ const _hoisted_4$1 = { class: "mt-4" };
16945
+ const _hoisted_5$1 = {
16946
+ key: 0,
16947
+ class: "ml-4"
16948
+ };
16949
+ function _sfc_render$2(_ctx, _cache, $props, $setup, $data, $options) {
16950
+ const _component_v_progress_circular = resolveComponent("v-progress-circular");
16951
+ const _component_paginating_toolbar = resolveComponent("paginating-toolbar");
16952
+ const _component_v_list_item_title = resolveComponent("v-list-item-title");
16953
+ const _component_v_list_item_subtitle = resolveComponent("v-list-item-subtitle");
16954
+ const _component_v_btn = resolveComponent("v-btn");
16955
+ const _component_v_icon = resolveComponent("v-icon");
16956
+ const _component_v_speed_dial = resolveComponent("v-speed-dial");
16957
+ const _component_v_list_item = resolveComponent("v-list-item");
16958
+ const _component_card_loader = resolveComponent("card-loader");
16959
+ const _component_tags_input = resolveComponent("tags-input");
16960
+ const _component_v_list = resolveComponent("v-list");
16961
+ const _component_v_card = resolveComponent("v-card");
16962
+ return openBlock(), createBlock(_component_v_card, null, {
16963
+ default: withCtx(() => [
16964
+ _ctx.updatePending ? (openBlock(), createElementBlock("div", _hoisted_1$2, [
16965
+ createVNode(_component_v_progress_circular, {
16966
+ indeterminate: "",
16967
+ color: "primary"
16968
+ })
16969
+ ])) : (openBlock(), createElementBlock("div", _hoisted_2$1, [
16970
+ createVNode(_component_paginating_toolbar, {
16971
+ title: "Exercises",
16972
+ page: _ctx.page,
16973
+ pages: _ctx.pages,
16974
+ subtitle: `(${_ctx.questionCount})`,
16975
+ onFirst: _ctx.first,
16976
+ onPrev: _ctx.prev,
16977
+ onNext: _ctx.next,
16978
+ onLast: _ctx.last,
16979
+ onSetPage: _cache[0] || (_cache[0] = (n) => _ctx.setPage(n))
16980
+ }, null, 8, ["page", "pages", "subtitle", "onFirst", "onPrev", "onNext", "onLast"]),
16981
+ createVNode(_component_v_list, null, {
16982
+ default: withCtx(() => [
16983
+ (openBlock(true), createElementBlock(Fragment, null, renderList(_ctx.cards, (c) => {
16984
+ return openBlock(), createElementBlock(Fragment, {
16985
+ key: c.id
16986
+ }, [
16987
+ createVNode(_component_v_list_item, {
16988
+ class: normalizeClass({
16989
+ "bg-blue-grey-lighten-5": c.isOpen,
16990
+ "elevation-4": c.isOpen
16991
+ }),
16992
+ density: "compact",
16993
+ "data-cy": "course-card"
16994
+ }, {
16995
+ prepend: withCtx(() => [
16996
+ createElementVNode("div", null, [
16997
+ createVNode(_component_v_list_item_title, {
16998
+ class: normalizeClass([{ "text-blue-grey-darken-1": c.isOpen }, "font-weight-medium"])
16999
+ }, {
17000
+ default: withCtx(() => [
17001
+ createTextVNode(toDisplayString(_ctx.cardPreview[c.id]), 1)
17002
+ ]),
17003
+ _: 2
17004
+ }, 1032, ["class"]),
17005
+ createVNode(_component_v_list_item_subtitle, null, {
17006
+ default: withCtx(() => [
17007
+ createTextVNode(toDisplayString(c.id.split("-").length === 3 ? c.id.split("-")[2] : ""), 1)
17008
+ ]),
17009
+ _: 2
17010
+ }, 1024)
17011
+ ])
17012
+ ]),
17013
+ append: withCtx(() => [
17014
+ _ctx.editMode === "full" ? (openBlock(), createBlock(_component_v_speed_dial, {
17015
+ key: 0,
17016
+ modelValue: c.isOpen,
17017
+ "onUpdate:modelValue": ($event) => c.isOpen = $event,
17018
+ location: "left center",
17019
+ transition: "slide-x-transition",
17020
+ style: { "display": "flex", "flex-direction": "row-reverse" },
17021
+ persistent: ""
17022
+ }, {
17023
+ activator: withCtx(({ props }) => [
17024
+ createVNode(_component_v_btn, mergeProps({ ref_for: true }, props, {
17025
+ icon: c.isOpen ? "mdi-close" : "mdi-plus",
17026
+ size: "small",
17027
+ variant: "text",
17028
+ onClick: ($event) => _ctx.clearSelections(c.id)
17029
+ }), null, 16, ["icon", "onClick"])
17030
+ ]),
17031
+ default: withCtx(() => [
17032
+ createVNode(_component_v_btn, {
17033
+ key: "tags",
17034
+ icon: "",
17035
+ size: "small",
17036
+ variant: _ctx.internalEditMode !== "tags" ? "outlined" : "elevated",
17037
+ color: _ctx.internalEditMode === "tags" ? "teal" : "teal-darken-3",
17038
+ onClick: _cache[1] || (_cache[1] = withModifiers(($event) => _ctx.internalEditMode = "tags", ["stop"]))
17039
+ }, {
17040
+ default: withCtx(() => [
17041
+ createVNode(_component_v_icon, null, {
17042
+ default: withCtx(() => _cache[4] || (_cache[4] = [
17043
+ createTextVNode("mdi-bookmark")
17044
+ ])),
17045
+ _: 1
17046
+ })
17047
+ ]),
17048
+ _: 1
17049
+ }, 8, ["variant", "color"]),
17050
+ createVNode(_component_v_btn, {
17051
+ key: "flag",
17052
+ icon: "",
17053
+ size: "small",
17054
+ variant: _ctx.internalEditMode !== "flag" ? "outlined" : "elevated",
17055
+ color: _ctx.internalEditMode === "flag" ? "error" : "error-darken-3",
17056
+ onClick: _cache[2] || (_cache[2] = withModifiers(($event) => _ctx.internalEditMode = "flag", ["stop"]))
17057
+ }, {
17058
+ default: withCtx(() => [
17059
+ createVNode(_component_v_icon, null, {
17060
+ default: withCtx(() => _cache[5] || (_cache[5] = [
17061
+ createTextVNode("mdi-flag")
17062
+ ])),
17063
+ _: 1
17064
+ })
17065
+ ]),
17066
+ _: 1
17067
+ }, 8, ["variant", "color"])
17068
+ ]),
17069
+ _: 2
17070
+ }, 1032, ["modelValue", "onUpdate:modelValue"])) : createCommentVNode("", true)
17071
+ ]),
17072
+ _: 2
17073
+ }, 1032, ["class"]),
17074
+ c.isOpen ? (openBlock(), createElementBlock("div", _hoisted_3$1, [
17075
+ createVNode(_component_card_loader, {
17076
+ qualified_id: c.id,
17077
+ "view-lookup": _ctx.viewLookup,
17078
+ class: "elevation-1"
17079
+ }, null, 8, ["qualified_id", "view-lookup"]),
17080
+ withDirectives(createVNode(_component_tags_input, {
17081
+ "course-i-d": _ctx.courseId,
17082
+ "card-i-d": c.id.split("-")[1],
17083
+ class: "mt-4"
17084
+ }, null, 8, ["course-i-d", "card-i-d"]), [
17085
+ [vShow, _ctx.internalEditMode === "tags"]
17086
+ ]),
17087
+ withDirectives(createElementVNode("div", _hoisted_4$1, [
17088
+ createVNode(_component_v_btn, {
17089
+ color: "error",
17090
+ variant: "outlined",
17091
+ onClick: ($event) => c.delBtn = true
17092
+ }, {
17093
+ default: withCtx(() => _cache[6] || (_cache[6] = [
17094
+ createTextVNode(" Delete this card ")
17095
+ ])),
17096
+ _: 2
17097
+ }, 1032, ["onClick"]),
17098
+ c.delBtn ? (openBlock(), createElementBlock("span", _hoisted_5$1, [
17099
+ _cache[8] || (_cache[8] = createElementVNode("span", { class: "mr-2" }, "Are you sure?", -1)),
17100
+ createVNode(_component_v_btn, {
17101
+ color: "error",
17102
+ variant: "elevated",
17103
+ onClick: ($event) => _ctx.deleteCard(c.id)
17104
+ }, {
17105
+ default: withCtx(() => _cache[7] || (_cache[7] = [
17106
+ createTextVNode(" Confirm ")
17107
+ ])),
17108
+ _: 2
17109
+ }, 1032, ["onClick"])
17110
+ ])) : createCommentVNode("", true)
17111
+ ], 512), [
17112
+ [vShow, _ctx.internalEditMode === "flag"]
17113
+ ])
17114
+ ])) : createCommentVNode("", true)
17115
+ ], 64);
17116
+ }), 128))
17117
+ ]),
17118
+ _: 1
17119
+ }),
17120
+ createVNode(_component_paginating_toolbar, {
17121
+ class: "elevation-0",
17122
+ page: _ctx.page,
17123
+ pages: _ctx.pages,
17124
+ onFirst: _ctx.first,
17125
+ onPrev: _ctx.prev,
17126
+ onNext: _ctx.next,
17127
+ onLast: _ctx.last,
17128
+ onSetPage: _cache[3] || (_cache[3] = (n) => _ctx.setPage(n))
17129
+ }, null, 8, ["page", "pages", "onFirst", "onPrev", "onNext", "onLast"])
17130
+ ]))
17131
+ ]),
17132
+ _: 1
17133
+ });
17134
+ }
17135
+ const CourseCardBrowser = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["render", _sfc_render$2], ["__scopeId", "data-v-0beef6a4"]]);
17136
+ const _sfc_main$1 = defineComponent({
17137
+ name: "CourseInformation",
17138
+ components: {
17139
+ // MidiConfig, // Removed to break circular dependency
17140
+ CourseCardBrowser
17141
+ },
17142
+ props: {
17143
+ courseId: {
17144
+ type: String,
17145
+ required: true
17146
+ },
17147
+ viewLookupFunction: {
17148
+ type: Function,
17149
+ required: false,
17150
+ default: (x) => {
17151
+ console.warn("No viewLookupFunction provided to CourseInformation");
17152
+ return null;
17153
+ }
17154
+ },
17155
+ editMode: {
17156
+ type: String,
17157
+ required: false,
17158
+ default: "full"
17159
+ }
17160
+ },
17161
+ data() {
17162
+ return {
17163
+ courseDB: null,
17164
+ nameRules: [
17165
+ (value) => {
17166
+ const max2 = 30;
17167
+ return value.length > max2 ? `Course name must be ${max2} characters or less` : true;
17168
+ }
17169
+ ],
17170
+ updatePending: true,
17171
+ courseConfig: {},
17172
+ userIsRegistered: false,
17173
+ tags: [],
17174
+ user: null
17175
+ };
17176
+ },
17177
+ computed: {
17178
+ // isPianoCourse removed - piano-specific logic should be in wrapper component
17179
+ },
17180
+ async created() {
17181
+ this.courseDB = getDataLayer().getCourseDB(this.courseId);
17182
+ this.user = await getCurrentUser();
17183
+ const userCourses = await this.user.getCourseRegistrationsDoc();
17184
+ this.userIsRegistered = userCourses.courses.filter((c) => {
17185
+ return c.courseID === this.courseId && (c.status === "active" || c.status === void 0);
17186
+ }).length === 1;
17187
+ this.courseConfig = await this.courseDB.getCourseConfig();
17188
+ this.tags = (await this.courseDB.getCourseTagStubs()).rows.map((r2) => r2.doc);
17189
+ this.updatePending = false;
17190
+ },
17191
+ methods: {
17192
+ async register() {
17193
+ log(`Registering for ${this.courseId}`);
17194
+ const res = await this.user.registerForCourse(this.courseId);
17195
+ if (res.ok) {
17196
+ this.userIsRegistered = true;
17197
+ }
17198
+ },
17199
+ async drop() {
17200
+ log(`Dropping course ${this.courseId}`);
17201
+ const res = await this.user.dropCourse(this.courseId);
17202
+ if (res.ok) {
17203
+ this.userIsRegistered = false;
17204
+ }
17205
+ }
17206
+ }
17207
+ });
17208
+ const _hoisted_1$1 = { key: 0 };
17209
+ const _hoisted_2 = { class: "text-h4 mb-2" };
17210
+ const _hoisted_3 = { class: "text-body-2" };
17211
+ const _hoisted_4 = { key: 0 };
17212
+ const _hoisted_5 = { key: 1 };
17213
+ function _sfc_render$1(_ctx, _cache, $props, $setup, $data, $options) {
17214
+ const _component_v_btn = resolveComponent("v-btn");
17215
+ const _component_v_icon = resolveComponent("v-icon");
17216
+ const _component_v_toolbar_title = resolveComponent("v-toolbar-title");
17217
+ const _component_v_toolbar_items = resolveComponent("v-toolbar-items");
17218
+ const _component_v_toolbar = resolveComponent("v-toolbar");
17219
+ const _component_v_chip = resolveComponent("v-chip");
17220
+ const _component_v_card_text = resolveComponent("v-card-text");
17221
+ const _component_v_card = resolveComponent("v-card");
17222
+ const _component_course_card_browser = resolveComponent("course-card-browser");
17223
+ return !_ctx.updatePending ? (openBlock(), createElementBlock("div", _hoisted_1$1, [
17224
+ renderSlot(_ctx.$slots, "header", {
17225
+ courseConfig: _ctx.courseConfig,
17226
+ courseId: _ctx.courseId
17227
+ }, () => [
17228
+ createElementVNode("h1", _hoisted_2, toDisplayString(_ctx.courseConfig.name), 1)
17229
+ ], true),
17230
+ createElementVNode("p", _hoisted_3, toDisplayString(_ctx.courseConfig.description), 1),
17231
+ renderSlot(_ctx.$slots, "actions", {
17232
+ userIsRegistered: _ctx.userIsRegistered,
17233
+ courseId: _ctx.courseId,
17234
+ editMode: _ctx.editMode,
17235
+ register: _ctx.register,
17236
+ drop: _ctx.drop
17237
+ }, () => [
17238
+ createVNode(Transition, {
17239
+ name: "component-fade",
17240
+ mode: "out-in"
17241
+ }, {
17242
+ default: withCtx(() => [
17243
+ _ctx.userIsRegistered ? (openBlock(), createElementBlock("div", _hoisted_4, [
17244
+ createVNode(_component_v_btn, {
17245
+ color: "success",
17246
+ class: "me-2"
17247
+ }, {
17248
+ default: withCtx(() => _cache[0] || (_cache[0] = [
17249
+ createTextVNode("Start a study session")
17250
+ ])),
17251
+ _: 1
17252
+ }),
17253
+ _ctx.editMode === "full" ? (openBlock(), createBlock(_component_v_btn, {
17254
+ key: 0,
17255
+ "data-cy": "add-content-btn",
17256
+ color: "indigo-lighten-1",
17257
+ class: "me-2"
17258
+ }, {
17259
+ default: withCtx(() => [
17260
+ createVNode(_component_v_icon, { start: "" }, {
17261
+ default: withCtx(() => _cache[1] || (_cache[1] = [
17262
+ createTextVNode("mdi-plus")
17263
+ ])),
17264
+ _: 1
17265
+ }),
17266
+ _cache[2] || (_cache[2] = createTextVNode(" Add content "))
17267
+ ]),
17268
+ _: 1
17269
+ })) : createCommentVNode("", true),
17270
+ _ctx.editMode === "full" ? (openBlock(), createBlock(_component_v_btn, {
17271
+ key: 1,
17272
+ color: "green-darken-2",
17273
+ title: "Rank course content for difficulty",
17274
+ class: "me-2"
17275
+ }, {
17276
+ default: withCtx(() => [
17277
+ createVNode(_component_v_icon, { start: "" }, {
17278
+ default: withCtx(() => _cache[3] || (_cache[3] = [
17279
+ createTextVNode("mdi-format-list-numbered")
17280
+ ])),
17281
+ _: 1
17282
+ }),
17283
+ _cache[4] || (_cache[4] = createTextVNode(" Arrange "))
17284
+ ]),
17285
+ _: 1
17286
+ })) : createCommentVNode("", true),
17287
+ _ctx.editMode === "full" ? (openBlock(), createBlock(_component_v_btn, {
17288
+ key: 2,
17289
+ color: "error",
17290
+ size: "small",
17291
+ variant: "outlined",
17292
+ onClick: _ctx.drop
17293
+ }, {
17294
+ default: withCtx(() => _cache[5] || (_cache[5] = [
17295
+ createTextVNode(" Drop this course ")
17296
+ ])),
17297
+ _: 1
17298
+ }, 8, ["onClick"])) : createCommentVNode("", true)
17299
+ ])) : (openBlock(), createElementBlock("div", _hoisted_5, [
17300
+ createVNode(_component_v_btn, {
17301
+ "data-cy": "register-btn",
17302
+ color: "primary",
17303
+ class: "me-2",
17304
+ onClick: _ctx.register
17305
+ }, {
17306
+ default: withCtx(() => _cache[6] || (_cache[6] = [
17307
+ createTextVNode("Register")
17308
+ ])),
17309
+ _: 1
17310
+ }, 8, ["onClick"]),
17311
+ createVNode(_component_v_btn, {
17312
+ variant: "outlined",
17313
+ color: "primary",
17314
+ class: "me-2"
17315
+ }, {
17316
+ default: withCtx(() => _cache[7] || (_cache[7] = [
17317
+ createTextVNode("Start a trial study session")
17318
+ ])),
17319
+ _: 1
17320
+ })
17321
+ ]))
17322
+ ]),
17323
+ _: 1
17324
+ })
17325
+ ], true),
17326
+ renderSlot(_ctx.$slots, "additional-content", {}, void 0, true),
17327
+ createVNode(_component_v_card, { class: "my-2" }, {
17328
+ default: withCtx(() => [
17329
+ createVNode(_component_v_toolbar, { density: "compact" }, {
17330
+ default: withCtx(() => [
17331
+ createVNode(_component_v_toolbar_title, null, {
17332
+ default: withCtx(() => _cache[8] || (_cache[8] = [
17333
+ createTextVNode("Tags")
17334
+ ])),
17335
+ _: 1
17336
+ }),
17337
+ createVNode(_component_v_toolbar_items, null, {
17338
+ default: withCtx(() => [
17339
+ createVNode(_component_v_btn, { variant: "text" }, {
17340
+ default: withCtx(() => [
17341
+ createTextVNode("(" + toDisplayString(_ctx.tags.length) + ")", 1)
17342
+ ]),
17343
+ _: 1
17344
+ })
17345
+ ]),
17346
+ _: 1
17347
+ })
17348
+ ]),
17349
+ _: 1
17350
+ }),
17351
+ createVNode(_component_v_card_text, null, {
17352
+ default: withCtx(() => [
17353
+ (openBlock(true), createElementBlock(Fragment, null, renderList(_ctx.tags, (tag2, i) => {
17354
+ return openBlock(), createElementBlock("span", { key: i }, [
17355
+ renderSlot(_ctx.$slots, "tag-link", {
17356
+ tag: tag2,
17357
+ courseId: _ctx.courseId
17358
+ }, () => [
17359
+ createVNode(_component_v_chip, {
17360
+ variant: "tonal",
17361
+ class: "me-2 mb-2"
17362
+ }, {
17363
+ default: withCtx(() => [
17364
+ createTextVNode(toDisplayString(tag2.name), 1)
17365
+ ]),
17366
+ _: 2
17367
+ }, 1024)
17368
+ ], true)
17369
+ ]);
17370
+ }), 128))
17371
+ ]),
17372
+ _: 3
17373
+ })
17374
+ ]),
17375
+ _: 3
17376
+ }),
17377
+ createVNode(_component_course_card_browser, {
17378
+ class: "my-3",
17379
+ "course-id": _ctx.courseId,
17380
+ "view-lookup-function": _ctx.viewLookupFunction,
17381
+ "edit-mode": _ctx.editMode
17382
+ }, null, 8, ["course-id", "view-lookup-function", "edit-mode"])
17383
+ ])) : createCommentVNode("", true);
17384
+ }
17385
+ const CourseInformation = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["render", _sfc_render$1], ["__scopeId", "data-v-799f691d"]]);
17386
+ const _sfc_main = defineComponent({
17387
+ name: "CardBrowser",
17388
+ components: {
17389
+ CardViewer
17390
+ },
17391
+ props: {
17392
+ views: {
17393
+ type: Array,
17394
+ required: true
17395
+ },
17396
+ data: {
17397
+ type: Array,
17398
+ required: true
17399
+ },
17400
+ suppressSpinner: {
17401
+ type: Boolean,
17402
+ default: false
17403
+ }
17404
+ },
17405
+ data() {
17406
+ return {
17407
+ viewIndex: 0,
17408
+ previewMode: useCardPreviewModeStore()
17409
+ };
17410
+ },
17411
+ computed: {
17412
+ spinner() {
17413
+ return this.views.length > 1;
17414
+ }
17415
+ },
17416
+ created() {
17417
+ console.log(`[CardBrowser] Card browser created. Cards now in 'prewviewMode'`);
17418
+ this.previewMode.setPreviewMode(true);
17419
+ },
17420
+ unmounted() {
17421
+ console.log(`[CardBrowser] Card browser unmounted. Cards no longer in 'prewviewMode'`);
17422
+ this.previewMode.setPreviewMode(false);
17423
+ },
17424
+ methods: {
17425
+ incrementView() {
17426
+ this.viewIndex++;
17427
+ this.viewIndex = (this.viewIndex + this.views.length) % this.views.length;
17428
+ },
17429
+ decrementView() {
17430
+ this.viewIndex--;
17431
+ this.viewIndex = (this.viewIndex + this.views.length) % this.views.length;
17432
+ }
17433
+ }
17434
+ });
17435
+ const _hoisted_1 = {
17436
+ key: 0,
17437
+ class: "text-subtitle-1 pa-2"
17438
+ };
17439
+ function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
17440
+ const _component_CardViewer = resolveComponent("CardViewer");
17441
+ const _component_v_icon = resolveComponent("v-icon");
17442
+ const _component_v_btn = resolveComponent("v-btn");
17443
+ const _component_v_row = resolveComponent("v-row");
17444
+ return openBlock(), createBlock(_component_v_row, {
17445
+ column: "",
17446
+ align: "center",
17447
+ justify: "center"
17448
+ }, {
17449
+ default: withCtx(() => [
17450
+ createVNode(_component_CardViewer, {
17451
+ view: _ctx.views[_ctx.viewIndex],
17452
+ data: _ctx.data,
17453
+ course_id: "[browsing]",
17454
+ card_id: "[browsing]"
17455
+ }, null, 8, ["view", "data"]),
17456
+ _cache[2] || (_cache[2] = createElementVNode("br", null, null, -1)),
17457
+ _cache[3] || (_cache[3] = createElementVNode("br", null, null, -1)),
17458
+ !_ctx.suppressSpinner ? (openBlock(), createElementBlock("div", _hoisted_1, [
17459
+ _ctx.spinner ? (openBlock(), createBlock(_component_v_btn, {
17460
+ key: 0,
17461
+ icon: "",
17462
+ variant: "outlined",
17463
+ color: "primary",
17464
+ onClick: _ctx.decrementView
17465
+ }, {
17466
+ default: withCtx(() => [
17467
+ createVNode(_component_v_icon, null, {
17468
+ default: withCtx(() => _cache[0] || (_cache[0] = [
17469
+ createTextVNode("mdi-chevron-left")
17470
+ ])),
17471
+ _: 1
17472
+ })
17473
+ ]),
17474
+ _: 1
17475
+ }, 8, ["onClick"])) : createCommentVNode("", true),
17476
+ createTextVNode(" " + toDisplayString(_ctx.views[_ctx.viewIndex].name) + " ", 1),
17477
+ _ctx.spinner ? (openBlock(), createBlock(_component_v_btn, {
17478
+ key: 1,
17479
+ icon: "",
17480
+ variant: "outlined",
17481
+ color: "primary",
17482
+ onClick: _ctx.incrementView
17483
+ }, {
17484
+ default: withCtx(() => [
17485
+ createVNode(_component_v_icon, null, {
17486
+ default: withCtx(() => _cache[1] || (_cache[1] = [
17487
+ createTextVNode("mdi-chevron-right")
17488
+ ])),
17489
+ _: 1
17490
+ })
17491
+ ]),
17492
+ _: 1
17493
+ }, 8, ["onClick"])) : createCommentVNode("", true)
17494
+ ])) : createCommentVNode("", true)
17495
+ ]),
17496
+ _: 1
17497
+ });
17498
+ }
17499
+ const CardBrowser = /* @__PURE__ */ _export_sfc(_sfc_main, [["render", _sfc_render]]);
16328
17500
  export {
16329
17501
  AudioAutoPlayer,
17502
+ CardBrowser,
16330
17503
  CardLoader,
16331
17504
  CardViewer,
16332
- _sfc_main$6 as CodeBlockRenderer,
17505
+ _sfc_main$a as CodeBlockRenderer,
17506
+ CourseCardBrowser,
17507
+ CourseInformation,
16333
17508
  Displayable,
16334
17509
  FillInInput,
16335
17510
  HeatMap,
@@ -16346,6 +17521,7 @@ export {
16346
17521
  StudySession,
16347
17522
  StudySessionTimer,
16348
17523
  TrueFalse as TFSelect,
17524
+ TagsInput,
16349
17525
  UserChip,
16350
17526
  UserInputNumber,
16351
17527
  UserInputString,