@vue-skuilder/common-ui 0.1.5 → 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.
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=useAuthUI.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAuthUI.test.d.ts","sourceRoot":"","sources":["../../../src/composables/__tests__/useAuthUI.test.ts"],"names":[],"mappings":""}
@@ -1,3 +1,4 @@
1
1
  export * from './CompositionViewable';
2
2
  export * from './Displayable';
3
+ export * from './useAuthUI';
3
4
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/composables/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC;AACtC,cAAc,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/composables/index.ts"],"names":[],"mappings":"AAAA,cAAc,uBAAuB,CAAC;AACtC,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC"}
@@ -0,0 +1,15 @@
1
+ export interface AuthUIConfig {
2
+ showLoginRegistration: boolean;
3
+ showLogout: boolean;
4
+ showResetData: boolean;
5
+ logoutLabel: string;
6
+ resetLabel: string;
7
+ }
8
+ export declare function useAuthUI(): {
9
+ config: import("vue").ComputedRef<AuthUIConfig>;
10
+ isLoading: import("vue").Ref<boolean, boolean>;
11
+ syncStrategyDetected: import("vue").Ref<boolean, boolean>;
12
+ isLocalOnlyMode: import("vue").Ref<boolean, boolean>;
13
+ detectSyncStrategy: () => Promise<void>;
14
+ };
15
+ //# sourceMappingURL=useAuthUI.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAuthUI.d.ts","sourceRoot":"","sources":["../../src/composables/useAuthUI.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,YAAY;IAC3B,qBAAqB,EAAE,OAAO,CAAC;IAC/B,UAAU,EAAE,OAAO,CAAC;IACpB,aAAa,EAAE,OAAO,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,SAAS;;;;;;EAuDxB"}
package/dist/index.d.ts CHANGED
@@ -34,4 +34,8 @@ export * from './stores/useCardPreviewModeStore';
34
34
  export * from './stores/useAuthStore';
35
35
  export * from './stores/useConfigStore';
36
36
  export { piniaPlugin } from './plugins/pinia';
37
+ export { default as CourseInformation } from './components/CourseInformation.vue';
38
+ export { default as CardBrowser } from './components/CardBrowser.vue';
39
+ export { default as CourseCardBrowser } from './components/CourseCardBrowser.vue';
40
+ export { default as TagsInput } from './components/TagsInput.vue';
37
41
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,0BAA0B,CAAC;AAC9D,YAAY,EAAE,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAGjF,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,8BAA8B,CAAC;AACtE,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AACpF,cAAc,gCAAgC,CAAC;AAC/C,cAAc,uCAAuC,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,YAAY,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAGrE,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAC9E,OAAO,EAAE,SAAS,EAAE,KAAK,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAE/E,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AAClF,cAAc,sCAAsC,CAAC;AAGrD,cAAc,mCAAmC,CAAC;AAClD,cAAc,2BAA2B,CAAC;AAM1C,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,+BAA+B,CAAC;AACxE,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AAClF,YAAY,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAU1E,cAAc,0CAA0C,CAAC;AACzD,OAAO,EAAE,OAAO,IAAI,mBAAmB,EAAE,MAAM,oDAAoD,CAAC;AACpG,OAAO,EAAE,OAAO,IAAI,oBAAoB,EAAE,MAAM,qDAAqD,CAAC;AACtG,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,0CAA0C,CAAC;AAC/E,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,gDAAgD,CAAC;AAC5F,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,gDAAgD,CAAC;AAC5F,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,4CAA4C,CAAC;AAOpF,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,2CAA2C,CAAC;AAClF,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,2CAA2C,CAAC;AAElF,cAAc,oDAAoD,CAAC;AACnE,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,gDAAgD,CAAC;AAC5F,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,kDAAkD,CAAC;AAChG,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,gDAAgD,CAAC;AAC5F,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,iDAAiD,CAAC;AAK9F,cAAc,mBAAmB,CAAC;AAKlC,cAAc,kCAAkC,CAAC;AACjD,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AAKxC,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,OAAO,IAAI,OAAO,EAAE,MAAM,0BAA0B,CAAC;AAC9D,YAAY,EAAE,OAAO,EAAE,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAGjF,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,8BAA8B,CAAC;AACtE,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AACpF,cAAc,gCAAgC,CAAC;AAC/C,cAAc,uCAAuC,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,YAAY,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAGrE,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,kCAAkC,CAAC;AAC9E,OAAO,EAAE,SAAS,EAAE,KAAK,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAE/E,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AAClF,cAAc,sCAAsC,CAAC;AAGrD,cAAc,mCAAmC,CAAC;AAClD,cAAc,2BAA2B,CAAC;AAM1C,OAAO,EAAE,OAAO,IAAI,YAAY,EAAE,MAAM,+BAA+B,CAAC;AACxE,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AAClF,YAAY,EAAE,kBAAkB,EAAE,MAAM,iCAAiC,CAAC;AAU1E,cAAc,0CAA0C,CAAC;AACzD,OAAO,EAAE,OAAO,IAAI,mBAAmB,EAAE,MAAM,oDAAoD,CAAC;AACpG,OAAO,EAAE,OAAO,IAAI,oBAAoB,EAAE,MAAM,qDAAqD,CAAC;AACtG,OAAO,EAAE,OAAO,IAAI,QAAQ,EAAE,MAAM,0CAA0C,CAAC;AAC/E,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,gDAAgD,CAAC;AAC5F,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,gDAAgD,CAAC;AAC5F,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,4CAA4C,CAAC;AAOpF,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,2CAA2C,CAAC;AAClF,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE,MAAM,2CAA2C,CAAC;AAElF,cAAc,oDAAoD,CAAC;AACnE,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,gDAAgD,CAAC;AAC5F,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,kDAAkD,CAAC;AAChG,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,gDAAgD,CAAC;AAC5F,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,iDAAiD,CAAC;AAK9F,cAAc,mBAAmB,CAAC;AAKlC,cAAc,kCAAkC,CAAC;AACjD,cAAc,uBAAuB,CAAC;AACtC,cAAc,yBAAyB,CAAC;AAKxC,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AAK9C,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AAClF,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,8BAA8B,CAAC;AACtE,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AAClF,OAAO,EAAE,OAAO,IAAI,SAAS,EAAE,MAAM,4BAA4B,CAAC"}
@@ -54,6 +54,10 @@ export declare const useAuthStore: () => import("pinia").Store<"auth", AuthState
54
54
  getUserClassrooms: () => Promise<import("@vue-skuilder/db").ClassroomRegistrationDoc>;
55
55
  getActiveClasses: () => Promise<string[]>;
56
56
  updateUserElo: (courseId: string, elo: import("@vue-skuilder/common").CourseElo) => Promise<PouchDB.Core.Response>;
57
+ resetUserData: () => Promise<{
58
+ status: import("@vue-skuilder/common").Status;
59
+ error?: string;
60
+ }>;
57
61
  getCourseInterface: (courseId: string) => Promise<import("@vue-skuilder/db").UsrCrsDataInterface>;
58
62
  update: <T extends PouchDB.Core.Document<object>>(id: string, update: Partial<T> | ((x: T) => T)) => Promise<T>;
59
63
  } | undefined;
@@ -107,6 +111,10 @@ export declare const useAuthStore: () => import("pinia").Store<"auth", AuthState
107
111
  getUserClassrooms: () => Promise<import("@vue-skuilder/db").ClassroomRegistrationDoc>;
108
112
  getActiveClasses: () => Promise<string[]>;
109
113
  updateUserElo: (courseId: string, elo: import("@vue-skuilder/common").CourseElo) => Promise<PouchDB.Core.Response>;
114
+ resetUserData: () => Promise<{
115
+ status: import("@vue-skuilder/common").Status;
116
+ error?: string;
117
+ }>;
110
118
  getCourseInterface: (courseId: string) => Promise<import("@vue-skuilder/db").UsrCrsDataInterface>;
111
119
  update: <T extends PouchDB.Core.Document<object>>(id: string, update: Partial<T> | ((x: T) => T)) => Promise<T>;
112
120
  } | undefined;
@@ -160,6 +168,10 @@ export declare const useAuthStore: () => import("pinia").Store<"auth", AuthState
160
168
  getUserClassrooms: () => Promise<import("@vue-skuilder/db").ClassroomRegistrationDoc>;
161
169
  getActiveClasses: () => Promise<string[]>;
162
170
  updateUserElo: (courseId: string, elo: import("@vue-skuilder/common").CourseElo) => Promise<PouchDB.Core.Response>;
171
+ resetUserData: () => Promise<{
172
+ status: import("@vue-skuilder/common").Status;
173
+ error?: string;
174
+ }>;
163
175
  getCourseInterface: (courseId: string) => Promise<import("@vue-skuilder/db").UsrCrsDataInterface>;
164
176
  update: <T extends PouchDB.Core.Document<object>>(id: string, update: Partial<T> | ((x: T) => T)) => Promise<T>;
165
177
  } | undefined;
@@ -214,6 +226,10 @@ export declare const useAuthStore: () => import("pinia").Store<"auth", AuthState
214
226
  getUserClassrooms: () => Promise<import("@vue-skuilder/db").ClassroomRegistrationDoc>;
215
227
  getActiveClasses: () => Promise<string[]>;
216
228
  updateUserElo: (courseId: string, elo: import("@vue-skuilder/common").CourseElo) => Promise<PouchDB.Core.Response>;
229
+ resetUserData: () => Promise<{
230
+ status: import("@vue-skuilder/common").Status;
231
+ error?: string;
232
+ }>;
217
233
  getCourseInterface: (courseId: string) => Promise<import("@vue-skuilder/db").UsrCrsDataInterface>;
218
234
  update: <T extends PouchDB.Core.Document<object>>(id: string, update: Partial<T> | ((x: T) => T)) => Promise<T>;
219
235
  } | undefined;
@@ -222,5 +238,9 @@ export declare const useAuthStore: () => import("pinia").Store<"auth", AuthState
222
238
  init(): Promise<void>;
223
239
  setLoginDialog(open: boolean): void;
224
240
  setRegDialog(open: boolean): void;
241
+ resetUserData(): Promise<{
242
+ status: import("@vue-skuilder/common").Status;
243
+ error?: string;
244
+ }>;
225
245
  }>;
226
246
  //# sourceMappingURL=useAuthStore.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useAuthStore.d.ts","sourceRoot":"","sources":["../../src/stores/useAuthStore.ts"],"names":[],"mappings":"AAEA,OAAO,EAAgB,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAGjE,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,eAAe,GAAG,SAAS,CAAC;IACnC,oBAAoB,EAAE;QACpB,IAAI,EAAE,OAAO,CAAC;QACd,QAAQ,EAAE,OAAO,CAAC;QAClB,aAAa,EAAE,OAAO,CAAC;QACvB,eAAe,EAAE,OAAO,CAAC;KAC1B,CAAC;IACF,cAAc,EAAE,OAAO,CAAC;CACzB;AAED,wBAAsB,cAAc,IAAI,OAAO,CAAC,eAAe,CAAC,CAiB/D;AAED,eAAO,MAAM,YAAY;;;;;;;;;;oBAyDq3Q,CAAC;qBAAuB,CAAC;;;;;;;;;;;;oFAA+1B,QAAQ,IAAI,CAAC,QAAQ;2EAAoH,QAAQ,IAAI,CAAC,QAAQ;;;;;;;;;;;;;;;qHAA+lC,QAAQ,IAAI,CAAC,QAAQ;4DAAmG,QAAQ,IAAI,CAAC,QAAQ;;;wGAA+U,QAAQ,IAAI,CAAC,QAAQ;;+BAAh2G,QAAS,IAAI,CAAC,QAAQ;;;kBApFhqP,OAAO;sBACH,OAAO;2BACF,OAAO;6BACL,OAAO;;wBAEV,OAAO;;;;;;;;;;oBA+Eq3Q,CAAC;qBAAuB,CAAC;;;;;;;;;;;;oFAA+1B,QAAQ,IAAI,CAAC,QAAQ;2EAAoH,QAAQ,IAAI,CAAC,QAAQ;;;;;;;;;;;;;;;qHAA+lC,QAAQ,IAAI,CAAC,QAAQ;4DAAmG,QAAQ,IAAI,CAAC,QAAQ;;;wGAA+U,QAAQ,IAAI,CAAC,QAAQ;;+BAAh2G,QAAS,IAAI,CAAC,QAAQ;;;kBApFhqP,OAAO;sBACH,OAAO;2BACF,OAAO;6BACL,OAAO;;wBAEV,OAAO;;;;;;;;;;oBA+Eq3Q,CAAC;qBAAuB,CAAC;;;;;;;;;;;;oFAA+1B,QAAQ,IAAI,CAAC,QAAQ;2EAAoH,QAAQ,IAAI,CAAC,QAAQ;;;;;;;;;;;;;;;qHAA+lC,QAAQ,IAAI,CAAC,QAAQ;4DAAmG,QAAQ,IAAI,CAAC,QAAQ;;;wGAA+U,QAAQ,IAAI,CAAC,QAAQ;;+BAAh2G,QAAS,IAAI,CAAC,QAAQ;;;kBApFhqP,OAAO;sBACH,OAAO;2BACF,OAAO;6BACL,OAAO;;wBAEV,OAAO;;;;;;;;;;;oBA+Eq3Q,CAAC;qBAAuB,CAAC;;;;;;;;;;;;oFAA+1B,QAAQ,IAAI,CAAC,QAAQ;2EAAoH,QAAQ,IAAI,CAAC,QAAQ;;;;;;;;;;;;;;;qHAA+lC,QAAQ,IAAI,CAAC,QAAQ;4DAAmG,QAAQ,IAAI,CAAC,QAAQ;;;wGAA+U,QAAQ,IAAI,CAAC,QAAQ;;+BAAh2G,QAAS,IAAI,CAAC,QAAQ;;;;;yBAvB/oP,OAAO;uBAIT,OAAO;EAkB/B,CAAC"}
1
+ {"version":3,"file":"useAuthStore.d.ts","sourceRoot":"","sources":["../../src/stores/useAuthStore.ts"],"names":[],"mappings":"AAEA,OAAO,EAAgB,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAGjE,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,eAAe,GAAG,SAAS,CAAC;IACnC,oBAAoB,EAAE;QACpB,IAAI,EAAE,OAAO,CAAC;QACd,QAAQ,EAAE,OAAO,CAAC;QAClB,aAAa,EAAE,OAAO,CAAC;QACvB,eAAe,EAAE,OAAO,CAAC;KAC1B,CAAC;IACF,cAAc,EAAE,OAAO,CAAC;CACzB;AAED,wBAAsB,cAAc,IAAI,OAAO,CAAC,eAAe,CAAC,CAiB/D;AAED,eAAO,MAAM,YAAY;;;;;;;;;;oBA4E01P,CAAC;qBAAuB,CAAC;;;;;;;;;;;;oFAA+1B,QAAQ,IAAI,CAAC,QAAQ;2EAAoH,QAAQ,IAAI,CAAC,QAAQ;;;;;;;;;;;;;;;qHAA+lC,QAAQ,IAAI,CAAC,QAAQ;4DAAmG,QAAQ,IAAI,CAAC,QAAQ;;;wGAA+U,QAAQ,IAAI,CAAC,QAAQ;;;qBAAkL,CAAC;;;+BAAnhH,QAAS,IAAI,CAAC,QAAQ;;;kBAvGroO,OAAO;sBACH,OAAO;2BACF,OAAO;6BACL,OAAO;;wBAEV,OAAO;;;;;;;;;;oBAkG01P,CAAC;qBAAuB,CAAC;;;;;;;;;;;;oFAA+1B,QAAQ,IAAI,CAAC,QAAQ;2EAAoH,QAAQ,IAAI,CAAC,QAAQ;;;;;;;;;;;;;;;qHAA+lC,QAAQ,IAAI,CAAC,QAAQ;4DAAmG,QAAQ,IAAI,CAAC,QAAQ;;;wGAA+U,QAAQ,IAAI,CAAC,QAAQ;;;qBAAkL,CAAC;;;+BAAnhH,QAAS,IAAI,CAAC,QAAQ;;;kBAvGroO,OAAO;sBACH,OAAO;2BACF,OAAO;6BACL,OAAO;;wBAEV,OAAO;;;;;;;;;;oBAkG01P,CAAC;qBAAuB,CAAC;;;;;;;;;;;;oFAA+1B,QAAQ,IAAI,CAAC,QAAQ;2EAAoH,QAAQ,IAAI,CAAC,QAAQ;;;;;;;;;;;;;;;qHAA+lC,QAAQ,IAAI,CAAC,QAAQ;4DAAmG,QAAQ,IAAI,CAAC,QAAQ;;;wGAA+U,QAAQ,IAAI,CAAC,QAAQ;;;qBAAkL,CAAC;;;+BAAnhH,QAAS,IAAI,CAAC,QAAQ;;;kBAvGroO,OAAO;sBACH,OAAO;2BACF,OAAO;6BACL,OAAO;;wBAEV,OAAO;;;;;;;;;;;oBAkG01P,CAAC;qBAAuB,CAAC;;;;;;;;;;;;oFAA+1B,QAAQ,IAAI,CAAC,QAAQ;2EAAoH,QAAQ,IAAI,CAAC,QAAQ;;;;;;;;;;;;;;;qHAA+lC,QAAQ,IAAI,CAAC,QAAQ;4DAAmG,QAAQ,IAAI,CAAC,QAAQ;;;wGAA+U,QAAQ,IAAI,CAAC,QAAQ;;;qBAAkL,CAAC;;;+BAAnhH,QAAS,IAAI,CAAC,QAAQ;;;;;yBA1CpnO,OAAO;uBAIT,OAAO;;;;;EAqC/B,CAAC"}
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "version": "0.1.5",
6
+ "version": "0.1.6",
7
7
  "main": "./dist/common-ui.umd.js",
8
8
  "module": "./dist/common-ui.es.js",
9
9
  "types": "./dist/index.d.ts",
@@ -33,8 +33,9 @@
33
33
  "dependencies": {
34
34
  "@highlightjs/vue-plugin": "^2.1.2",
35
35
  "@mdi/font": "^7.3.67",
36
- "@vue-skuilder/common": "0.1.5",
37
- "@vue-skuilder/db": "0.1.5",
36
+ "@vojtechlanka/vue-tags-input": "^3",
37
+ "@vue-skuilder/common": "0.1.6",
38
+ "@vue-skuilder/db": "0.1.6",
38
39
  "highlight.js": "^11.0.1",
39
40
  "marked": "^15.0.6",
40
41
  "moment": "^2.29.4",
@@ -50,16 +51,20 @@
50
51
  },
51
52
  "devDependencies": {
52
53
  "@cypress/vite-dev-server": "^6.0.3",
54
+ "@testing-library/jest-dom": "^6.6.3",
55
+ "@testing-library/vue": "^8.1.0",
53
56
  "@types/pouchdb": "^6.4.2",
54
57
  "@typescript-eslint/eslint-plugin": "^8.25.0",
55
58
  "@typescript-eslint/parser": "^8.25.0",
56
59
  "@vitejs/plugin-vue": "^5.2.1",
57
60
  "@vue/eslint-config-typescript": "^14.4.0",
61
+ "@vue/test-utils": "^2.4.6",
58
62
  "cypress": "^14.2.1",
59
63
  "cypress-vite": "^1.6.0",
60
64
  "eslint": "^9.21.0",
61
65
  "eslint-config-prettier": "^10.0.2",
62
66
  "eslint-plugin-vue": "^9.32.0",
67
+ "jsdom": "^25.0.1",
63
68
  "sass": "^1.83.0",
64
69
  "typescript": "~5.7.2",
65
70
  "vite": "^6.0.9",
@@ -0,0 +1,81 @@
1
+ <template>
2
+ <v-row column align="center" justify="center">
3
+ <CardViewer :view="views[viewIndex]" :data="data" :course_id="'[browsing]'" :card_id="'[browsing]'" />
4
+ <br /><br />
5
+ <div v-if="!suppressSpinner" class="text-subtitle-1 pa-2">
6
+ <v-btn v-if="spinner" icon variant="outlined" color="primary" @click="decrementView">
7
+ <v-icon>mdi-chevron-left</v-icon>
8
+ </v-btn>
9
+ {{ views[viewIndex].name }}
10
+ <v-btn v-if="spinner" icon variant="outlined" color="primary" @click="incrementView">
11
+ <v-icon>mdi-chevron-right</v-icon>
12
+ </v-btn>
13
+ </div>
14
+ </v-row>
15
+ </template>
16
+
17
+ <script lang="ts">
18
+ import { ViewData } from '@vue-skuilder/common';
19
+ import { defineComponent, PropType } from 'vue';
20
+ import { ViewComponent } from '../composables/Displayable';
21
+ import CardViewer from './cardRendering/CardViewer.vue';
22
+ import { useCardPreviewModeStore } from '../stores/useCardPreviewModeStore';
23
+
24
+ export default defineComponent({
25
+ name: 'CardBrowser',
26
+
27
+ components: {
28
+ CardViewer,
29
+ },
30
+
31
+ props: {
32
+ views: {
33
+ type: Array as PropType<Array<ViewComponent>>,
34
+ required: true,
35
+ },
36
+ data: {
37
+ type: Array as PropType<ViewData[]>,
38
+ required: true,
39
+ },
40
+ suppressSpinner: {
41
+ type: Boolean,
42
+ default: false,
43
+ },
44
+ },
45
+
46
+ data() {
47
+ return {
48
+ viewIndex: 0,
49
+ previewMode: useCardPreviewModeStore(),
50
+ };
51
+ },
52
+
53
+ computed: {
54
+ spinner(): boolean {
55
+ return this.views.length > 1;
56
+ },
57
+ },
58
+
59
+ created() {
60
+ console.log(`[CardBrowser] Card browser created. Cards now in 'prewviewMode'`);
61
+ this.previewMode.setPreviewMode(true);
62
+ },
63
+
64
+ unmounted() {
65
+ console.log(`[CardBrowser] Card browser unmounted. Cards no longer in 'prewviewMode'`);
66
+ this.previewMode.setPreviewMode(false);
67
+ },
68
+
69
+ methods: {
70
+ incrementView() {
71
+ this.viewIndex++;
72
+ this.viewIndex = (this.viewIndex + this.views.length) % this.views.length;
73
+ },
74
+
75
+ decrementView() {
76
+ this.viewIndex--;
77
+ this.viewIndex = (this.viewIndex + this.views.length) % this.views.length;
78
+ },
79
+ },
80
+ });
81
+ </script>
@@ -0,0 +1,384 @@
1
+ <template>
2
+ <v-card>
3
+ <div v-if="updatePending" class="d-flex justify-center align-center pa-6">
4
+ <v-progress-circular indeterminate color="primary" />
5
+ </div>
6
+ <div v-else>
7
+ <paginating-toolbar
8
+ title="Exercises"
9
+ :page="page"
10
+ :pages="pages"
11
+ :subtitle="`(${questionCount})`"
12
+ @first="first"
13
+ @prev="prev"
14
+ @next="next"
15
+ @last="last"
16
+ @set-page="(n) => setPage(n)"
17
+ />
18
+
19
+ <v-list>
20
+ <template v-for="c in cards" :key="c.id">
21
+ <v-list-item
22
+ :class="{
23
+ 'bg-blue-grey-lighten-5': c.isOpen,
24
+ 'elevation-4': c.isOpen,
25
+ }"
26
+ density="compact"
27
+ data-cy="course-card"
28
+ >
29
+ <template #prepend>
30
+ <div>
31
+ <v-list-item-title :class="{ 'text-blue-grey-darken-1': c.isOpen }" class="font-weight-medium">
32
+ {{ cardPreview[c.id] }}
33
+ </v-list-item-title>
34
+ <v-list-item-subtitle>
35
+ {{ c.id.split('-').length === 3 ? c.id.split('-')[2] : '' }}
36
+ </v-list-item-subtitle>
37
+ </div>
38
+ </template>
39
+
40
+ <template #append>
41
+ <v-speed-dial
42
+ v-if="editMode === 'full'"
43
+ v-model="c.isOpen"
44
+ location="left center"
45
+ transition="slide-x-transition"
46
+ style="display: flex; flex-direction: row-reverse"
47
+ persistent
48
+ >
49
+ <template #activator="{ props }">
50
+ <v-btn
51
+ v-bind="props"
52
+ :icon="c.isOpen ? 'mdi-close' : 'mdi-plus'"
53
+ size="small"
54
+ variant="text"
55
+ @click="clearSelections(c.id)"
56
+ />
57
+ </template>
58
+
59
+ <v-btn
60
+ key="tags"
61
+ icon
62
+ size="small"
63
+ :variant="internalEditMode !== 'tags' ? 'outlined' : 'elevated'"
64
+ :color="internalEditMode === 'tags' ? 'teal' : 'teal-darken-3'"
65
+ @click.stop="internalEditMode = 'tags'"
66
+ >
67
+ <v-icon>mdi-bookmark</v-icon>
68
+ </v-btn>
69
+
70
+ <v-btn
71
+ key="flag"
72
+ icon
73
+ size="small"
74
+ :variant="internalEditMode !== 'flag' ? 'outlined' : 'elevated'"
75
+ :color="internalEditMode === 'flag' ? 'error' : 'error-darken-3'"
76
+ @click.stop="internalEditMode = 'flag'"
77
+ >
78
+ <v-icon>mdi-flag</v-icon>
79
+ </v-btn>
80
+ </v-speed-dial>
81
+ </template>
82
+ </v-list-item>
83
+
84
+ <div v-if="c.isOpen" class="px-4 py-2 bg-blue-grey-lighten-5">
85
+ <card-loader :qualified_id="c.id" :view-lookup="viewLookup" class="elevation-1" />
86
+
87
+ <tags-input
88
+ v-show="internalEditMode === 'tags'"
89
+ :course-i-d="courseId"
90
+ :card-i-d="c.id.split('-')[1]"
91
+ class="mt-4"
92
+ />
93
+
94
+ <div v-show="internalEditMode === 'flag'" class="mt-4">
95
+ <v-btn color="error" variant="outlined" @click="c.delBtn = true"> Delete this card </v-btn>
96
+ <span v-if="c.delBtn" class="ml-4">
97
+ <span class="mr-2">Are you sure?</span>
98
+ <v-btn color="error" variant="elevated" @click="deleteCard(c.id)"> Confirm </v-btn>
99
+ </span>
100
+ </div>
101
+ </div>
102
+ </template>
103
+ </v-list>
104
+
105
+ <paginating-toolbar
106
+ class="elevation-0"
107
+ :page="page"
108
+ :pages="pages"
109
+ @first="first"
110
+ @prev="prev"
111
+ @next="next"
112
+ @last="last"
113
+ @set-page="(n) => setPage(n)"
114
+ />
115
+ </div>
116
+ </v-card>
117
+ </template>
118
+
119
+ <script lang="ts">
120
+ import { defineComponent, PropType } from 'vue';
121
+ import { displayableDataToViewData, Status } from '@vue-skuilder/common';
122
+ import { getDataLayer, CourseDBInterface, CardData, DisplayableData, Tag } from '@vue-skuilder/db';
123
+ // local imports
124
+ import TagsInput from './TagsInput.vue';
125
+ import PaginatingToolbar from './PaginatingToolbar.vue';
126
+ import { ViewComponent } from '../composables/Displayable';
127
+ import CardLoader from './cardRendering/CardLoader.vue';
128
+ import { alertUser } from './SnackbarService';
129
+
130
+ function isConstructor(obj: unknown) {
131
+ try {
132
+ // @ts-expect-error - we are specifically probing an unknown object
133
+ new obj();
134
+ return true;
135
+ } catch (e) {
136
+ console.warn(`not a constructor: ${obj}, err: ${e}`);
137
+ return false;
138
+ }
139
+ }
140
+
141
+ export default defineComponent({
142
+ name: 'CourseCardBrowser',
143
+
144
+ components: {
145
+ CardLoader,
146
+ TagsInput,
147
+ PaginatingToolbar,
148
+ },
149
+
150
+ props: {
151
+ courseId: {
152
+ type: String,
153
+ required: true,
154
+ },
155
+ tagId: {
156
+ type: String,
157
+ required: false,
158
+ default: '',
159
+ },
160
+ viewLookupFunction: {
161
+ type: Function,
162
+ required: true,
163
+ default: (x: unknown) => {
164
+ console.warn('No viewLookupFunction provided to CourseCardBrowser');
165
+ return null;
166
+ },
167
+ },
168
+ editMode: {
169
+ type: String as PropType<'none' | 'readonly' | 'full'>,
170
+ required: false,
171
+ default: 'full',
172
+ },
173
+ },
174
+
175
+ data() {
176
+ return {
177
+ courseDB: null as CourseDBInterface | null,
178
+ page: 1,
179
+ pages: [] as number[],
180
+ cards: [] as { id: string; isOpen: boolean; delBtn: boolean }[],
181
+ cardData: {} as { [card: string]: string[] },
182
+ cardPreview: {} as { [card: string]: string },
183
+ internalEditMode: 'none' as 'tags' | 'flag' | 'none',
184
+ delBtn: false,
185
+ updatePending: true,
186
+ userIsRegistered: false,
187
+ questionCount: 0,
188
+ tags: [] as Tag[],
189
+ viewLookup: this.viewLookupFunction,
190
+ };
191
+ },
192
+
193
+ async created() {
194
+ try {
195
+ this.courseDB = getDataLayer().getCourseDB(this.courseId);
196
+
197
+ if (this.tagId) {
198
+ this.questionCount = (await this.courseDB.getTag(this.tagId)).taggedCards.length;
199
+ } else {
200
+ this.questionCount = (await this.courseDB!.getCourseInfo()).cardCount;
201
+ }
202
+
203
+ for (let i = 1; (i - 1) * 25 < this.questionCount; i++) {
204
+ this.pages.push(i);
205
+ }
206
+
207
+ await this.populateTableData();
208
+ } catch (error) {
209
+ console.error('Error initializing CourseCardBrowser:', error);
210
+ } finally {
211
+ this.updatePending = false;
212
+ }
213
+ },
214
+
215
+ methods: {
216
+ first() {
217
+ this.page = 1;
218
+ this.populateTableData();
219
+ },
220
+ prev() {
221
+ this.page--;
222
+ this.populateTableData();
223
+ },
224
+ next() {
225
+ this.page++;
226
+ this.populateTableData();
227
+ },
228
+ last() {
229
+ this.page = this.pages.length;
230
+ this.populateTableData();
231
+ },
232
+ setPage(n: number) {
233
+ this.page = n;
234
+ this.populateTableData();
235
+ },
236
+ clearSelections(exception: string = '') {
237
+ this.cards.forEach((card) => {
238
+ if (card.id !== exception) {
239
+ card.isOpen = false;
240
+ }
241
+ });
242
+ this.internalEditMode = 'none';
243
+ this.delBtn = false;
244
+ },
245
+ async deleteCard(c: string) {
246
+ console.log(`Deleting card ${c}`);
247
+ const res = await this.courseDB!.removeCard(c.split('-')[1]);
248
+ if (res.ok) {
249
+ this.cards = this.cards.filter((card) => card.id != c);
250
+ this.clearSelections();
251
+ } else {
252
+ console.error(`Failed to delete card:\n\n${JSON.stringify(res)}`);
253
+ alertUser({
254
+ text: 'Failed to delete card',
255
+ status: Status.error,
256
+ });
257
+ }
258
+ },
259
+ async populateTableData() {
260
+ this.updatePending = true;
261
+ if (this.tagId) {
262
+ const tag = await this.courseDB!.getTag(this.tagId);
263
+ this.cards = tag.taggedCards.map((c) => {
264
+ return { id: `${this.courseId}-${c}`, isOpen: false, delBtn: false };
265
+ });
266
+ } else {
267
+ this.cards = (await this.courseDB!.getCardsByELO(0, 25)).map((c) => {
268
+ return {
269
+ id: c,
270
+ isOpen: false,
271
+ delBtn: false,
272
+ };
273
+ });
274
+ }
275
+
276
+ const toRemove: string[] = [];
277
+ const hydratedCardData = (
278
+ await this.courseDB!.getCourseDocs<CardData>(
279
+ this.cards.map((c) => c.id.split('-')[1]),
280
+ {
281
+ include_docs: true,
282
+ }
283
+ )
284
+ ).rows
285
+ .filter((r) => {
286
+ if (r.doc) {
287
+ return true;
288
+ } else {
289
+ console.error(`Card ${r.id}.doc not found.\ncard: ${JSON.stringify(r)}`);
290
+ // toRemove.push(r.id);
291
+ // if (this.tagId) {
292
+ // this.courseDB!.removeTagFromCard(r.id, this.tagId);
293
+ // }
294
+ return false;
295
+ }
296
+ })
297
+ .map((r) => r.doc!);
298
+
299
+ this.cards = this.cards.filter((c) => !toRemove.includes(c.id.split('-')[1]));
300
+
301
+ hydratedCardData.forEach((c) => {
302
+ if (c && c.id_displayable_data) {
303
+ this.cardData[c._id] = c.id_displayable_data;
304
+ }
305
+ });
306
+
307
+ try {
308
+ await Promise.all(
309
+ this.cards.map(async (c) => {
310
+ const _cardID: string = c.id.split('-')[1];
311
+
312
+ const tmpCardData = hydratedCardData.find((c) => c._id == _cardID);
313
+ if (!tmpCardData || !tmpCardData.id_displayable_data) {
314
+ console.error(`No valid data found for card ${_cardID}`);
315
+ return;
316
+ }
317
+ const tmpView: ViewComponent = this.viewLookupFunction(
318
+ tmpCardData.id_view || 'default.question.BlanksCard.FillInView'
319
+ );
320
+
321
+ const tmpDataDocs = tmpCardData.id_displayable_data.map((id) => {
322
+ return this.courseDB!.getCourseDoc<DisplayableData>(id, {
323
+ attachments: false,
324
+ binary: true,
325
+ });
326
+ });
327
+
328
+ const allDocs = await Promise.all(tmpDataDocs);
329
+ await Promise.all(
330
+ allDocs.map((doc) => {
331
+ const tmpData = [];
332
+ tmpData.unshift(displayableDataToViewData(doc));
333
+
334
+ // [ ] remove/replace this after the vue 3 migration is complete
335
+ // see PR #510
336
+ if (isConstructor(tmpView)) {
337
+ const view = new tmpView();
338
+ view.data = tmpData;
339
+
340
+ this.cardPreview[c.id] = view.toString();
341
+ } else {
342
+ this.cardPreview[c.id] = tmpView.name ? tmpView.name : 'Unknown';
343
+ }
344
+ })
345
+ );
346
+ })
347
+ );
348
+ } catch (error) {
349
+ console.error('Error populating table data:', error);
350
+ } finally {
351
+ this.updatePending = false;
352
+ this.$forceUpdate();
353
+ }
354
+ },
355
+ },
356
+ });
357
+ </script>
358
+
359
+ <style scoped>
360
+ .component-fade-enter-active,
361
+ .component-fade-leave-active {
362
+ transition: opacity 0.5s ease;
363
+ }
364
+ .component-fade-enter, .component-fade-leave-to
365
+ /* .component-fade-leave-active below version 2.1.8 */ {
366
+ opacity: 0;
367
+ }
368
+
369
+ .component-scale-enter-active,
370
+ .component-scale-leave-active {
371
+ max-height: auto;
372
+ transform: scale(1, 1);
373
+ transform-origin: top;
374
+ transition:
375
+ transform 0.3s ease,
376
+ max-height 0.3s ease;
377
+ }
378
+ .component-scale-enter,
379
+ .component-fade-leave-to {
380
+ max-height: 0px;
381
+ transform: scale(1, 0);
382
+ overflow: hidden;
383
+ }
384
+ </style>