@vue-skuilder/common-ui 0.1.1

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 (68) hide show
  1. package/dist/assets/index.css +10 -0
  2. package/dist/common-ui.es.js +16404 -0
  3. package/dist/common-ui.es.js.map +1 -0
  4. package/dist/common-ui.umd.js +9 -0
  5. package/dist/common-ui.umd.js.map +1 -0
  6. package/dist/components/HeatMap.types.d.ts +13 -0
  7. package/dist/components/PaginatingToolbar.types.d.ts +40 -0
  8. package/dist/components/SkMouseTrap.types.d.ts +3 -0
  9. package/dist/components/SkMouseTrapToolTip.types.d.ts +35 -0
  10. package/dist/components/SnackbarService.d.ts +11 -0
  11. package/dist/components/StudySession.types.d.ts +6 -0
  12. package/dist/components/auth/index.d.ts +4 -0
  13. package/dist/components/cardRendering/MarkdownRendererHelpers.d.ts +22 -0
  14. package/dist/components/studentInputs/BaseUserInput.d.ts +16 -0
  15. package/dist/components/studentInputs/RadioMultipleChoice.types.d.ts +5 -0
  16. package/dist/composables/CompositionViewable.d.ts +33 -0
  17. package/dist/composables/Displayable.d.ts +47 -0
  18. package/dist/composables/index.d.ts +2 -0
  19. package/dist/index.d.ts +36 -0
  20. package/dist/plugins/pinia.d.ts +5 -0
  21. package/dist/stores/useAuthStore.d.ts +225 -0
  22. package/dist/stores/useCardPreviewModeStore.d.ts +8 -0
  23. package/dist/stores/useConfigStore.d.ts +11 -0
  24. package/dist/utils/SkldrMouseTrap.d.ts +32 -0
  25. package/package.json +67 -0
  26. package/src/components/HeatMap.types.ts +15 -0
  27. package/src/components/HeatMap.vue +354 -0
  28. package/src/components/PaginatingToolbar.types.ts +48 -0
  29. package/src/components/PaginatingToolbar.vue +75 -0
  30. package/src/components/SkMouseTrap.types.ts +3 -0
  31. package/src/components/SkMouseTrap.vue +70 -0
  32. package/src/components/SkMouseTrapToolTip.types.ts +41 -0
  33. package/src/components/SkMouseTrapToolTip.vue +316 -0
  34. package/src/components/SnackbarService.ts +39 -0
  35. package/src/components/SnackbarService.vue +71 -0
  36. package/src/components/StudySession.types.ts +6 -0
  37. package/src/components/StudySession.vue +670 -0
  38. package/src/components/StudySessionTimer.vue +121 -0
  39. package/src/components/auth/UserChip.vue +106 -0
  40. package/src/components/auth/UserLogin.vue +141 -0
  41. package/src/components/auth/UserLoginAndRegistrationContainer.vue +85 -0
  42. package/src/components/auth/UserRegistration.vue +181 -0
  43. package/src/components/auth/index.ts +4 -0
  44. package/src/components/cardRendering/AudioAutoPlayer.vue +131 -0
  45. package/src/components/cardRendering/CardLoader.vue +123 -0
  46. package/src/components/cardRendering/CardViewer.vue +101 -0
  47. package/src/components/cardRendering/CodeBlockRenderer.vue +81 -0
  48. package/src/components/cardRendering/MarkdownRenderer.vue +46 -0
  49. package/src/components/cardRendering/MarkdownRendererHelpers.ts +114 -0
  50. package/src/components/cardRendering/MdTokenRenderer.vue +244 -0
  51. package/src/components/studentInputs/BaseUserInput.ts +71 -0
  52. package/src/components/studentInputs/MultipleChoiceOption.vue +127 -0
  53. package/src/components/studentInputs/RadioMultipleChoice.types.ts +6 -0
  54. package/src/components/studentInputs/RadioMultipleChoice.vue +168 -0
  55. package/src/components/studentInputs/TrueFalse.vue +27 -0
  56. package/src/components/studentInputs/UserInputNumber.vue +63 -0
  57. package/src/components/studentInputs/UserInputString.vue +89 -0
  58. package/src/components/studentInputs/fillInInput.vue +71 -0
  59. package/src/composables/CompositionViewable.ts +180 -0
  60. package/src/composables/Displayable.ts +133 -0
  61. package/src/composables/index.ts +2 -0
  62. package/src/index.ts +79 -0
  63. package/src/plugins/pinia.ts +24 -0
  64. package/src/stores/useAuthStore.ts +92 -0
  65. package/src/stores/useCardPreviewModeStore.ts +32 -0
  66. package/src/stores/useConfigStore.ts +60 -0
  67. package/src/utils/SkldrMouseTrap.ts +141 -0
  68. package/src/vue-shims.d.ts +5 -0
@@ -0,0 +1,121 @@
1
+ <!-- @vue-skuilder/common-ui/src/components/StudySessionTimer.vue -->
2
+ <template>
3
+ <v-tooltip location="right" :open-delay="0" :close-delay="200" color="secondary" class="text-subtitle-1">
4
+ <template #activator="{ props }">
5
+ <div class="timer-container" v-bind="props" @mouseenter="hovered = true" @mouseleave="hovered = false">
6
+ <v-progress-circular
7
+ alt="Time remaining in study session"
8
+ size="64"
9
+ width="8"
10
+ rotate="0"
11
+ :color="timerColor"
12
+ :model-value="percentageRemaining"
13
+ >
14
+ <v-btn
15
+ v-if="timeRemaining > 0 && hovered"
16
+ icon
17
+ color="transparent"
18
+ location="bottom left"
19
+ @click="addSessionTime"
20
+ >
21
+ <v-icon size="large">mdi-plus</v-icon>
22
+ </v-btn>
23
+ </v-progress-circular>
24
+ </div>
25
+ </template>
26
+ {{ formattedTimeRemaining }}
27
+ </v-tooltip>
28
+ </template>
29
+
30
+ <script lang="ts">
31
+ import { defineComponent, computed, ref } from 'vue';
32
+
33
+ export default defineComponent({
34
+ name: 'StudySessionTimer',
35
+
36
+ props: {
37
+ /**
38
+ * Time remaining in seconds
39
+ */
40
+ timeRemaining: {
41
+ type: Number,
42
+ required: true,
43
+ },
44
+ /**
45
+ * Total session time limit in minutes
46
+ */
47
+ sessionTimeLimit: {
48
+ type: Number,
49
+ required: true,
50
+ default: 5,
51
+ },
52
+ },
53
+
54
+ emits: ['add-time'],
55
+
56
+ setup(props, { emit }) {
57
+ const hovered = ref(false);
58
+
59
+ /**
60
+ * Formats the time remaining into a readable string
61
+ */
62
+ const formattedTimeRemaining = computed(() => {
63
+ let timeString = '';
64
+ const seconds = props.timeRemaining;
65
+
66
+ if (seconds > 60) {
67
+ timeString = Math.floor(seconds / 60).toString() + ':';
68
+ }
69
+
70
+ const secondsRemaining = seconds % 60;
71
+ timeString += secondsRemaining >= 10 ? secondsRemaining : '0' + secondsRemaining;
72
+
73
+ if (seconds <= 60) {
74
+ timeString += ' seconds';
75
+ }
76
+
77
+ timeString += ' left!';
78
+
79
+ return timeString;
80
+ });
81
+
82
+ /**
83
+ * Calculates the percentage of time remaining for the progress indicator
84
+ */
85
+ const percentageRemaining = computed(() => {
86
+ return props.timeRemaining > 60
87
+ ? 100 * (props.timeRemaining / (60 * props.sessionTimeLimit))
88
+ : 100 * (props.timeRemaining / 60);
89
+ });
90
+
91
+ /**
92
+ * Determines the color of the timer based on time remaining
93
+ */
94
+ const timerColor = computed(() => {
95
+ return props.timeRemaining > 60 ? 'primary' : 'orange darken-3';
96
+ });
97
+
98
+ /**
99
+ * Handles adding time to the session
100
+ */
101
+ const addSessionTime = () => {
102
+ emit('add-time');
103
+ };
104
+
105
+ return {
106
+ hovered,
107
+ formattedTimeRemaining,
108
+ percentageRemaining,
109
+ timerColor,
110
+ addSessionTime,
111
+ };
112
+ },
113
+ });
114
+ </script>
115
+
116
+ <style scoped>
117
+ .timer-container {
118
+ display: inline-flex;
119
+ cursor: pointer;
120
+ }
121
+ </style>
@@ -0,0 +1,106 @@
1
+ <template>
2
+ <v-badge :content="items.length" :model-value="hasNewItems" color="accent" location="end top">
3
+ <v-menu location="bottom end" transition="scale-transition">
4
+ <template #activator="{ props }">
5
+ <v-chip v-bind="props" class="ma-2">
6
+ <v-avatar start class="bg-primary">
7
+ <v-icon>mdi-school</v-icon>
8
+ </v-avatar>
9
+ {{ username }}
10
+ </v-chip>
11
+ </template>
12
+
13
+ <v-list>
14
+ <v-list-item v-for="item in items" :key="item" @click="dismiss(item)">
15
+ <v-list-item-title>{{ item }}</v-list-item-title>
16
+ </v-list-item>
17
+
18
+ <v-divider v-if="items.length" />
19
+
20
+ <v-list-item @click="gotoStats">
21
+ <template #prepend>
22
+ <v-icon>mdi-trending-up</v-icon>
23
+ </template>
24
+ <v-list-item-title>Stats</v-list-item-title>
25
+ </v-list-item>
26
+
27
+ <v-list-item @click="gotoSettings">
28
+ <template #prepend>
29
+ <v-icon>mdi-cog</v-icon>
30
+ </template>
31
+ <v-list-item-title>Settings</v-list-item-title>
32
+ </v-list-item>
33
+
34
+ <v-list-item @click="logout">
35
+ <template #prepend>
36
+ <v-icon>mdi-logout</v-icon>
37
+ </template>
38
+ <v-list-item-title>Log out</v-list-item-title>
39
+ </v-list-item>
40
+ </v-list>
41
+ </v-menu>
42
+ </v-badge>
43
+ </template>
44
+
45
+ <script lang="ts">
46
+ import { defineComponent } from 'vue';
47
+ import { getCurrentUser, useAuthStore } from '../../stores/useAuthStore';
48
+ import { useConfigStore } from '../../stores/useConfigStore';
49
+
50
+ export default defineComponent({
51
+ name: 'UserChip',
52
+
53
+ data() {
54
+ return {
55
+ username: '',
56
+ items: [] as string[],
57
+ checked: false,
58
+ authStore: useAuthStore(),
59
+ configStore: useConfigStore(),
60
+ };
61
+ },
62
+
63
+ computed: {
64
+ hasNewItems(): boolean {
65
+ return this.items.length > 0;
66
+ },
67
+ },
68
+
69
+ created() {
70
+ getCurrentUser().then((u) => {
71
+ this.username = u.getUsername();
72
+ });
73
+ },
74
+
75
+ methods: {
76
+ async gotoSettings() {
77
+ this.$router.push(`/u/${(await getCurrentUser()).getUsername()}`);
78
+ },
79
+
80
+ async gotoStats() {
81
+ this.$router.push(`/u/${(await getCurrentUser()).getUsername()}/stats`);
82
+ },
83
+
84
+ dismiss(item: string) {
85
+ const index = this.items.indexOf(item);
86
+ this.items.splice(index, 1);
87
+ },
88
+
89
+ async logout() {
90
+ const res = await this.authStore._user!.logout();
91
+ if (res.ok) {
92
+ this.authStore.loginAndRegistration = {
93
+ init: true,
94
+ loggedIn: false,
95
+ regDialogOpen: false,
96
+ loginDialogOpen: false,
97
+ };
98
+
99
+ this.configStore.resetDefaults();
100
+
101
+ this.$router.push('/home');
102
+ }
103
+ },
104
+ },
105
+ });
106
+ </script>
@@ -0,0 +1,141 @@
1
+ <template>
2
+ <v-card>
3
+ <v-card-title v-if="!loginRoute" class="text-h5 bg-grey-lighten-2">Log In</v-card-title>
4
+
5
+ <v-card-text>
6
+ <v-form onsubmit="return false;" @submit.prevent="login">
7
+ <v-text-field
8
+ id=""
9
+ v-model="username"
10
+ autofocus
11
+ name="username"
12
+ label="Username"
13
+ prepend-icon="mdi-account-circle"
14
+ ></v-text-field>
15
+ <v-text-field
16
+ v-model="password"
17
+ prepend-icon="mdi-lock"
18
+ name="password"
19
+ hover="Show password input"
20
+ label="Enter your password"
21
+ hint=""
22
+ min="0"
23
+ :append-icon="passwordVisible ? 'mdi-eye-off' : 'mdi-eye'"
24
+ :type="passwordVisible ? 'text' : 'password'"
25
+ @click:append="() => (passwordVisible = !passwordVisible)"
26
+ ></v-text-field>
27
+
28
+ <v-snackbar v-model="badLoginAttempt" location="bottom right" :timeout="errorTimeout">
29
+ Username or password was incorrect.
30
+ <v-btn color="pink" variant="text" @click="badLoginAttempt = false">Close</v-btn>
31
+ </v-snackbar>
32
+
33
+ <v-btn class="mr-2" type="submit" :loading="awaitingResponse" :color="buttonStatus.color">
34
+ <v-icon start>mdi-lock-open</v-icon>
35
+ Log In
36
+ </v-btn>
37
+ <router-link v-if="loginRoute" to="signup">
38
+ <v-btn variant="text">Create New Account</v-btn>
39
+ </router-link>
40
+ <v-btn v-else variant="text" @click="toggle">Create New Account</v-btn>
41
+ </v-form>
42
+ </v-card-text>
43
+ </v-card>
44
+ </template>
45
+
46
+ <script lang="ts" setup>
47
+ import { ref, computed } from 'vue';
48
+ import { useRouter, useRoute } from 'vue-router';
49
+ import { alertUser } from '../SnackbarService';
50
+ import { log } from '@vue-skuilder/common';
51
+ import { Status } from '@vue-skuilder/common';
52
+ import { User } from '@vue-skuilder/db';
53
+ import { getCurrentUser, useAuthStore } from '../../stores/useAuthStore';
54
+ import { useConfigStore } from '../../stores/useConfigStore';
55
+
56
+ const router = useRouter();
57
+ const route = useRoute();
58
+ const authStore = useAuthStore();
59
+ const configStore = useConfigStore();
60
+
61
+ const username = ref('');
62
+ const password = ref('');
63
+ const passwordVisible = ref(false);
64
+ const awaitingResponse = ref(false);
65
+ const badLoginAttempt = ref(false);
66
+ const errorTimeout = ref(7000);
67
+ const user = ref<User | undefined>(undefined);
68
+
69
+ const loginRoute = computed(() => route.name === 'login');
70
+
71
+ const buttonStatus = computed(() => ({
72
+ color: badLoginAttempt.value ? 'error' : 'success',
73
+ text: badLoginAttempt.value ? 'Try again' : 'Log In',
74
+ }));
75
+
76
+ const initBadLogin = () => {
77
+ badLoginAttempt.value = true;
78
+
79
+ alertUser({
80
+ text: 'Username or password was incorrect.',
81
+ status: Status.error,
82
+ timeout: errorTimeout.value,
83
+ });
84
+ setTimeout(() => {
85
+ badLoginAttempt.value = false;
86
+ }, errorTimeout.value);
87
+ };
88
+
89
+ const login = async () => {
90
+ awaitingResponse.value = true;
91
+ log('Starting login attempt');
92
+ log(`Login attempt for username: ${username.value}`);
93
+
94
+ try {
95
+ log('Attempting to get User instance');
96
+ // #172 starting point - why is the pre-existing _user being referenced here?
97
+ user.value = await getCurrentUser();
98
+ log('Got User instance, attempting login');
99
+
100
+ await user.value.login(username.value, password.value);
101
+ log('Login successful');
102
+
103
+ // load user config
104
+ log('Initializing user config');
105
+ configStore.init();
106
+ log('User config initialized');
107
+
108
+ // set login state
109
+ log('Setting authentication state');
110
+ authStore.loginAndRegistration.loggedIn = true;
111
+ log('Authentication state set, redirecting to study page');
112
+ router.push('/study');
113
+ log('Login and redirect complete');
114
+ } catch (e) {
115
+ // entry #186
116
+ log('Login attempt failed');
117
+ log(`Login error details: ${JSON.stringify(e)}`);
118
+ console.log(`login error: ${JSON.stringify(e)}`);
119
+ // - differentiate response
120
+ // - return better message to UI
121
+ log('Initiating bad login feedback');
122
+ initBadLogin();
123
+ }
124
+
125
+ log('Resetting awaiting response state');
126
+ awaitingResponse.value = false;
127
+ };
128
+
129
+ const emit = defineEmits(['toggle']);
130
+
131
+ const toggle = () => {
132
+ log('Toggling registration / login forms.');
133
+ emit('toggle');
134
+ };
135
+ </script>
136
+
137
+ <style lang="css" scoped>
138
+ .login {
139
+ max-width: 650px;
140
+ }
141
+ </style>
@@ -0,0 +1,85 @@
1
+ <template>
2
+ <transition v-if="userReady && display" name="component-fade" mode="out-in">
3
+ <div v-if="guestMode">
4
+ <v-dialog v-model="regDialog" width="500px">
5
+ <template #activator="{ props }">
6
+ <v-btn class="mr-2" size="small" color="success" v-bind="props">Sign Up</v-btn>
7
+ </template>
8
+ <UserRegistration @toggle="toggle" />
9
+ </v-dialog>
10
+ <v-dialog v-model="loginDialog" width="500px">
11
+ <template #activator="{ props }">
12
+ <v-btn size="small" color="success" v-bind="props">Log In</v-btn>
13
+ </template>
14
+ <UserLogin @toggle="toggle" />
15
+ </v-dialog>
16
+ </div>
17
+ <user-chip v-else />
18
+ </transition>
19
+ </template>
20
+
21
+ <script lang="ts" setup>
22
+ import { computed } from 'vue';
23
+ import { useRoute } from 'vue-router';
24
+ import UserLogin from './UserLogin.vue';
25
+ import UserRegistration from './UserRegistration.vue';
26
+ import UserChip from './UserChip.vue';
27
+ import { useAuthStore } from '../../stores/useAuthStore';
28
+ import { GuestUsername } from '@vue-skuilder/db';
29
+
30
+ const route = useRoute();
31
+ const authStore = useAuthStore();
32
+
33
+ const display = computed(() => {
34
+ if (!route.name || typeof route.name !== 'string') {
35
+ return true;
36
+ }
37
+ const routeName = route.name.toLowerCase();
38
+ return !(routeName === 'login' || routeName === 'signup');
39
+ });
40
+
41
+ const userReady = computed(() => authStore.onLoadComplete);
42
+
43
+ const guestMode = computed(() => {
44
+ if (authStore._user) {
45
+ return authStore._user.getUsername().startsWith(GuestUsername);
46
+ }
47
+ return !authStore.loginAndRegistration.loggedIn;
48
+ });
49
+
50
+ const regDialog = computed({
51
+ get: () => authStore.loginAndRegistration.regDialogOpen,
52
+ set: (value: boolean) => {
53
+ authStore.loginAndRegistration.regDialogOpen = value;
54
+ },
55
+ });
56
+
57
+ const loginDialog = computed({
58
+ get: () => authStore.loginAndRegistration.loginDialogOpen,
59
+ set: (value: boolean) => {
60
+ authStore.loginAndRegistration.loginDialogOpen = value;
61
+ },
62
+ });
63
+
64
+ const toggle = () => {
65
+ if (regDialog.value && loginDialog.value) {
66
+ throw new Error('Registration / Login dialogs both activated.');
67
+ } else if (regDialog.value === loginDialog.value) {
68
+ throw new Error('Registration / Login dialogs toggled while both were dormant.');
69
+ } else {
70
+ regDialog.value = !regDialog.value;
71
+ loginDialog.value = !loginDialog.value;
72
+ }
73
+ };
74
+ </script>
75
+
76
+ <style scoped>
77
+ .component-fade-enter-active,
78
+ .component-fade-leave-active {
79
+ transition: opacity 0.5s ease;
80
+ }
81
+ .component-fade-enter,
82
+ .component-fade-leave-to {
83
+ opacity: 0;
84
+ }
85
+ </style>
@@ -0,0 +1,181 @@
1
+ <template>
2
+ <v-card>
3
+ <v-card-title v-if="!registrationRoute" class="text-h5 bg-grey-lighten-2"> Create an Account </v-card-title>
4
+
5
+ <v-card-text>
6
+ <v-form @submit.prevent="createUser">
7
+ <v-text-field
8
+ id=""
9
+ ref="userNameTextField"
10
+ v-model="username"
11
+ autofocus
12
+ name="username"
13
+ label="Choose a Username"
14
+ prepend-icon="mdi-account-circle"
15
+ :error="usernameError"
16
+ :hint="usernameHint"
17
+ @blur="validateUsername"
18
+ ></v-text-field>
19
+ <v-text-field
20
+ v-model="password"
21
+ prepend-icon="mdi-lock"
22
+ name="password"
23
+ hover="Show password"
24
+ label="Create a password"
25
+ hint=""
26
+ min="4"
27
+ :append-icon="passwordVisible ? 'mdi-eye-off' : 'mdi-eye'"
28
+ :type="passwordVisible ? 'text' : 'password'"
29
+ @click:append="() => (passwordVisible = !passwordVisible)"
30
+ ></v-text-field>
31
+ <v-text-field
32
+ v-model="retypedPassword"
33
+ prepend-icon="mdi-lock"
34
+ name="retypedPassword"
35
+ hover="Show password"
36
+ label="Retype your password"
37
+ hint=""
38
+ min="4"
39
+ :type="passwordVisible ? 'text' : 'password'"
40
+ ></v-text-field>
41
+
42
+ <!-- <v-checkbox label="Student" v-model="student" ></v-checkbox>
43
+ <v-checkbox label="Teacher" v-model="teacher" ></v-checkbox>
44
+ <v-checkbox label="Author" v-model="author" ></v-checkbox> -->
45
+
46
+ <v-snackbar v-model="badLoginAttempt" location="bottom right" :timeout="5000">
47
+ Username or password was incorrect.
48
+ <v-btn color="pink" variant="text" @click="badLoginAttempt = false"> Close </v-btn>
49
+ </v-snackbar>
50
+ <v-btn class="mr-2" type="submit" :loading="awaitingResponse" :color="buttonStatus.color">
51
+ <v-icon start>mdi-lock-open</v-icon>
52
+ Create Account
53
+ </v-btn>
54
+ <router-link v-if="registrationRoute" to="login">
55
+ <v-btn variant="text">Log In</v-btn>
56
+ </router-link>
57
+ <v-btn v-else variant="text" @click="toggle"> Log In </v-btn>
58
+ </v-form>
59
+ </v-card-text>
60
+ </v-card>
61
+ </template>
62
+
63
+ <script lang="ts">
64
+ import { defineComponent } from 'vue';
65
+ import { UserDBInterface } from '@vue-skuilder/db';
66
+ import { alertUser } from '../SnackbarService';
67
+ import { Status, log } from '@vue-skuilder/common';
68
+ import { getCurrentUser, useAuthStore } from '../../stores/useAuthStore';
69
+
70
+ export default defineComponent({
71
+ name: 'UserRegistration',
72
+
73
+ emits: ['toggle'],
74
+
75
+ data() {
76
+ return {
77
+ username: '',
78
+ password: '',
79
+ retypedPassword: '',
80
+ passwordVisible: false,
81
+ usernameValidationInProgress: false,
82
+ usernameError: false,
83
+ usernameHint: '',
84
+ awaitingResponse: false,
85
+ badLoginAttempt: false,
86
+ userSecret: '',
87
+ secret: 'goons',
88
+ user: null as UserDBInterface | null,
89
+ roles: ['Student', 'Teacher', 'Author'] as string[],
90
+ student: true,
91
+ teacher: false,
92
+ author: false,
93
+ authStore: useAuthStore(),
94
+ };
95
+ },
96
+
97
+ computed: {
98
+ registrationRoute(): boolean {
99
+ return typeof this.$route.name === 'string' && this.$route.name.toLowerCase() === 'signup';
100
+ },
101
+ buttonStatus() {
102
+ return {
103
+ color: this.badLoginAttempt ? 'error' : 'success',
104
+ text: this.badLoginAttempt ? 'Try again' : 'Log In',
105
+ };
106
+ },
107
+ },
108
+
109
+ async created() {
110
+ this.user = await getCurrentUser();
111
+ },
112
+
113
+ methods: {
114
+ toggle() {
115
+ log('Toggling registration / login forms.');
116
+ this.$emit('toggle');
117
+ },
118
+
119
+ validateUsername() {
120
+ this.usernameError = false;
121
+ },
122
+
123
+ async createUser() {
124
+ this.awaitingResponse = true;
125
+ log(`
126
+ User creation
127
+ -------------
128
+
129
+ Name: ${this.username}
130
+ Student: ${this.student}
131
+ Teacher: ${this.teacher}
132
+ Author: ${this.author}
133
+ `);
134
+ if (this.password === this.retypedPassword) {
135
+ if (!this.user) return;
136
+
137
+ this.user
138
+ .createAccount(this.username, this.password)
139
+ .then(async (resp) => {
140
+ if (resp.status === Status.ok) {
141
+ this.authStore.loginAndRegistration.loggedIn = true;
142
+ this.authStore.loginAndRegistration.init = false;
143
+ this.authStore.loginAndRegistration.init = true;
144
+
145
+ this.$router.push(`/u/${(await getCurrentUser()).getUsername()}/new`);
146
+ } else {
147
+ if (resp.error === 'This username is taken!') {
148
+ this.usernameError = true;
149
+ this.usernameHint = 'Try a different name.';
150
+ (this.$refs.userNameTextField as HTMLInputElement).focus();
151
+ alertUser({
152
+ text: `The name ${this.username} is taken!`,
153
+ status: resp.status,
154
+ });
155
+ } else {
156
+ alertUser({
157
+ text: resp.error,
158
+ status: resp.status,
159
+ });
160
+ }
161
+ }
162
+ })
163
+ .catch((e) => {
164
+ if (e)
165
+ alertUser({
166
+ text: JSON.stringify(e),
167
+ status: Status.error,
168
+ });
169
+ });
170
+ this.awaitingResponse = false;
171
+ } else {
172
+ alertUser({
173
+ text: 'Passwords do not match.',
174
+ status: Status.error,
175
+ });
176
+ this.awaitingResponse = false;
177
+ }
178
+ },
179
+ },
180
+ });
181
+ </script>
@@ -0,0 +1,4 @@
1
+ export { default as UserChip } from './UserChip.vue';
2
+ export { default as UserLogin } from './UserLogin.vue';
3
+ export { default as UserRegistration } from './UserRegistration.vue';
4
+ export { default as UserLoginAndRegistrationContainer } from './UserLoginAndRegistrationContainer.vue';