@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
@@ -0,0 +1,103 @@
1
+ import { describe, it, expect, beforeEach, vi } from 'vitest';
2
+ import { useAuthUI } from '../useAuthUI';
3
+ import type { UserDBInterface } from '@vue-skuilder/db';
4
+
5
+ // Mock getCurrentUser
6
+ vi.mock('../../stores/useAuthStore', () => ({
7
+ getCurrentUser: vi.fn(),
8
+ }));
9
+
10
+ // Get reference to the mocked function
11
+ import { getCurrentUser } from '../../stores/useAuthStore';
12
+ const mockGetCurrentUser = vi.mocked(getCurrentUser);
13
+
14
+ describe('useAuthUI', () => {
15
+ beforeEach(() => {
16
+ vi.clearAllMocks();
17
+ });
18
+
19
+ it('should detect local-only mode when user cannot create account', async () => {
20
+ // Mock user that cannot create account (NoOpSyncStrategy)
21
+ const mockUser = {
22
+ syncStrategy: {
23
+ canCreateAccount: vi.fn().mockReturnValue(false),
24
+ },
25
+ getUsername: vi.fn().mockReturnValue('TestUser'),
26
+ } as unknown as UserDBInterface;
27
+ mockGetCurrentUser.mockResolvedValue(mockUser);
28
+
29
+ const { config, detectSyncStrategy, isLocalOnlyMode } = useAuthUI();
30
+
31
+ await detectSyncStrategy();
32
+
33
+ expect(isLocalOnlyMode.value).toBe(true);
34
+ expect(config.value).toEqual({
35
+ showLoginRegistration: false,
36
+ showLogout: false,
37
+ showResetData: true,
38
+ logoutLabel: '',
39
+ resetLabel: 'Reset User Data',
40
+ });
41
+ });
42
+
43
+ it('should detect remote sync mode when user can create account', async () => {
44
+ // Mock user that can create account (CouchDBSyncStrategy)
45
+ const mockUser = {
46
+ syncStrategy: {
47
+ canCreateAccount: vi.fn().mockReturnValue(true),
48
+ },
49
+ getUsername: vi.fn().mockReturnValue('TestUser'),
50
+ } as unknown as UserDBInterface;
51
+ mockGetCurrentUser.mockResolvedValue(mockUser);
52
+
53
+ const { config, detectSyncStrategy, isLocalOnlyMode } = useAuthUI();
54
+
55
+ await detectSyncStrategy();
56
+
57
+ expect(isLocalOnlyMode.value).toBe(false);
58
+ expect(config.value).toEqual({
59
+ showLoginRegistration: true,
60
+ showLogout: true,
61
+ showResetData: false,
62
+ logoutLabel: 'Log out',
63
+ resetLabel: '',
64
+ });
65
+ });
66
+
67
+ it('should handle errors gracefully and default to remote sync mode', async () => {
68
+ mockGetCurrentUser.mockRejectedValue(new Error('User not available'));
69
+
70
+ const { config, detectSyncStrategy, isLocalOnlyMode } = useAuthUI();
71
+
72
+ await detectSyncStrategy();
73
+
74
+ expect(isLocalOnlyMode.value).toBe(false);
75
+ expect(config.value).toEqual({
76
+ showLoginRegistration: true,
77
+ showLogout: true,
78
+ showResetData: false,
79
+ logoutLabel: 'Log out',
80
+ resetLabel: '',
81
+ });
82
+ });
83
+
84
+ it('should manage loading state correctly', async () => {
85
+ const mockUser = {
86
+ syncStrategy: {
87
+ canCreateAccount: vi.fn().mockReturnValue(true),
88
+ },
89
+ getUsername: vi.fn().mockReturnValue('TestUser'),
90
+ } as unknown as UserDBInterface;
91
+ mockGetCurrentUser.mockResolvedValue(mockUser);
92
+
93
+ const { isLoading, detectSyncStrategy } = useAuthUI();
94
+
95
+ expect(isLoading.value).toBe(true);
96
+
97
+ const promise = detectSyncStrategy();
98
+ expect(isLoading.value).toBe(true);
99
+
100
+ await promise;
101
+ expect(isLoading.value).toBe(false);
102
+ });
103
+ });
@@ -1,2 +1,3 @@
1
1
  export * from './CompositionViewable';
2
2
  export * from './Displayable';
3
+ export * from './useAuthUI';
@@ -0,0 +1,67 @@
1
+ import { ref, computed } from 'vue';
2
+ import { getCurrentUser } from '../stores/useAuthStore';
3
+
4
+ export interface AuthUIConfig {
5
+ showLoginRegistration: boolean;
6
+ showLogout: boolean;
7
+ showResetData: boolean;
8
+ logoutLabel: string;
9
+ resetLabel: string;
10
+ }
11
+
12
+ export function useAuthUI() {
13
+ const isLoading = ref(true);
14
+ const syncStrategyDetected = ref(false);
15
+ const isLocalOnlyMode = ref(false);
16
+
17
+ const config = computed<AuthUIConfig>(() => {
18
+ if (isLocalOnlyMode.value) {
19
+ return {
20
+ showLoginRegistration: false,
21
+ showLogout: false,
22
+ showResetData: true,
23
+ logoutLabel: '',
24
+ resetLabel: 'Reset User Data',
25
+ };
26
+ } else {
27
+ return {
28
+ showLoginRegistration: true,
29
+ showLogout: true,
30
+ showResetData: false,
31
+ logoutLabel: 'Log out',
32
+ resetLabel: '',
33
+ };
34
+ }
35
+ });
36
+
37
+ const detectSyncStrategy = async () => {
38
+ try {
39
+ isLoading.value = true;
40
+ const user = await getCurrentUser();
41
+
42
+ // Access the sync strategy through the user's private syncStrategy property
43
+ // NoOpSyncStrategy (local-only) returns false for canCreateAccount
44
+ // CouchDBSyncStrategy (remote sync) returns true for canCreateAccount
45
+ const userInternal = user as any; // Type assertion to access private members
46
+ const canCreateAccount = userInternal.syncStrategy?.canCreateAccount?.();
47
+
48
+ isLocalOnlyMode.value = !canCreateAccount;
49
+ syncStrategyDetected.value = true;
50
+ } catch (error) {
51
+ console.error('Failed to detect sync strategy:', error);
52
+ // Default to remote sync mode on error
53
+ isLocalOnlyMode.value = false;
54
+ syncStrategyDetected.value = true;
55
+ } finally {
56
+ isLoading.value = false;
57
+ }
58
+ };
59
+
60
+ return {
61
+ config,
62
+ isLoading,
63
+ syncStrategyDetected,
64
+ isLocalOnlyMode,
65
+ detectSyncStrategy,
66
+ };
67
+ }
package/src/index.ts CHANGED
@@ -77,3 +77,11 @@ export * from './stores/useConfigStore';
77
77
  plugins
78
78
  */
79
79
  export { piniaPlugin } from './plugins/pinia';
80
+
81
+ /*
82
+ Course browsing components
83
+ */
84
+ export { default as CourseInformation } from './components/CourseInformation.vue';
85
+ export { default as CardBrowser } from './components/CardBrowser.vue';
86
+ export { default as CourseCardBrowser } from './components/CourseCardBrowser.vue';
87
+ export { default as TagsInput } from './components/TagsInput.vue';
@@ -15,7 +15,7 @@ export const getPinia = (): Pinia | null => {
15
15
 
16
16
  // Create a plugin that the main app can use
17
17
  export const piniaPlugin: Plugin = {
18
- install(app, options) {
18
+ install(_app, options) {
19
19
  const pinia = options?.pinia;
20
20
  if (pinia) {
21
21
  setPinia(pinia);
@@ -74,6 +74,25 @@ export const useAuthStore = () => {
74
74
  setRegDialog(open: boolean) {
75
75
  this.loginAndRegistration.regDialogOpen = open;
76
76
  },
77
+
78
+ async resetUserData() {
79
+ try {
80
+ if (!this._user) {
81
+ throw new Error('No user available for data reset');
82
+ }
83
+
84
+ const result = await this._user.resetUserData();
85
+ if (result.status !== 'ok') {
86
+ throw new Error(result.error || 'Reset failed');
87
+ }
88
+
89
+ console.log('User data reset successfully');
90
+ return result;
91
+ } catch (error) {
92
+ console.error('Failed to reset user data:', error);
93
+ throw error;
94
+ }
95
+ },
77
96
  },
78
97
 
79
98
  getters: {