Exercises selection system
This commit is contained in:
@@ -9,6 +9,7 @@
|
||||
"descriptionEn": "Before an attack, it's important to understand how the website is structured. How are the URLs structured? Where are input fields located that execute SQL queries in the backend?",
|
||||
"exercises": [
|
||||
{
|
||||
"uuid": "getting-known-register",
|
||||
"nameDe": "Registrieren",
|
||||
"nameEn": "Register",
|
||||
"exerciseNr": 1,
|
||||
@@ -16,6 +17,7 @@
|
||||
"descriptionEn": "We'll set up a regular account on the platform. To do this, navigate to the account page and register."
|
||||
},
|
||||
{
|
||||
"uuid": "getting-known-profile",
|
||||
"nameDe": "Profil vervollständigen",
|
||||
"nameEn": "Complete profile",
|
||||
"exerciseNr": 2,
|
||||
@@ -23,6 +25,7 @@
|
||||
"descriptionEn": "Orders are only possible once your account profile is complete. Log in, navigate to your account settings, fill in your name, and add an address and payment method. Finally, save everything."
|
||||
},
|
||||
{
|
||||
"uuid": "getting-known-buy-ticket",
|
||||
"nameDe": "Ein Ticket kaufen",
|
||||
"nameEn": "Buy a ticket",
|
||||
"exerciseNr": 3,
|
||||
@@ -40,6 +43,7 @@
|
||||
"descriptionEn": "A website contains publicly visible pages and some protected pages. The latter can only be accessed with appropriate permissions. Examples include an admin panel or the personal shopping cart. Access is often controlled via cookies or authentication on a backend server. With broken access control, this security mechanism is either not implemented or is incorrectly implemented. This allows pages to be accessed without authorization via the URL.",
|
||||
"exercises": [
|
||||
{
|
||||
"uuid": "broken-access-control-exercise-page",
|
||||
"nameDe": "Hilfe-Seite aufrufen",
|
||||
"nameEn": "Access Help Page",
|
||||
"exerciseNr": 1,
|
||||
@@ -47,6 +51,7 @@
|
||||
"descriptionEn": "The help page provides insight into the processing status of tasks. It's therefore not secure, but it's also not (yet) accessible as a button in the title bar. Expand the URL in the address bar to access the help page."
|
||||
},
|
||||
{
|
||||
"uuid": "broken-access-control-hidden-concert",
|
||||
"nameDe": "Das versteckte Konzert buchen",
|
||||
"nameEn": "Book the hidden concert",
|
||||
"exerciseNr": 2,
|
||||
@@ -64,6 +69,7 @@
|
||||
"descriptionEn": "A database uses SQL commands to create, retrieve, modify, and delete records. A server is accessed via API interfaces, executes the commands in the database, and returns the results. The client must not have direct access to the database. SQL injection attempts to circumvent this security mechanism and execute SQL commands directly via the API interfaces.",
|
||||
"exercises": [
|
||||
{
|
||||
"uuid": "sql-injection-database-scheme",
|
||||
"nameDe": "Wie sieht die Datenbank aus?",
|
||||
"nameEn": "How does the database look like?",
|
||||
"exerciseNr": 1,
|
||||
@@ -71,6 +77,7 @@
|
||||
"descriptionEn": "We'll now attempt to attack the database in the background. Currently, we don't yet know what the database looks like, or which tables it contains. However, we can use an SQL command to inject it. Go to the global search page. Open the Developer Tools using the keyboard shortcut Ctrl + D. Click on the Network tab. Here you can see how the frontend communicates with the server. Now write an SQL injection that ignores the search term and instead returns all records in the sqlite_master table, provided the type='table' condition is met. If the backend responds successfully, copy the table names into a text file so that we can specify the correct table names for future tasks."
|
||||
},
|
||||
{
|
||||
"uuid": "sql-injection-all-accounts",
|
||||
"nameDe": "Alle Accounts ausspähen",
|
||||
"nameEn": "Get all accounts",
|
||||
"exerciseNr": 2,
|
||||
@@ -78,6 +85,7 @@
|
||||
"descriptionEn": "Now write an SQL injection that ignores the search term and instead returns all records in the account table. Execute the attack using the search field. Watch the server's response."
|
||||
},
|
||||
{
|
||||
"uuid": "sql-injection-account-roles",
|
||||
"nameDe": "Alle Berechtigungsgruppen ausspähen",
|
||||
"nameEn": "Get all account roles",
|
||||
"exerciseNr": 3,
|
||||
@@ -85,6 +93,7 @@
|
||||
"descriptionEn": "We now see all the accounts. Each has an authorization ID (accountRoleId) that controls permissions such as access to the admin panel. However, we don't know what the IDs mean. Therefore, write an SQL injection that ignores the search term and instead returns all records in the >>AccountRoles<< table. Execute the attack via the search field. Observe the server's response via the >>Network<< tab."
|
||||
},
|
||||
{
|
||||
"uuid": "sql-injection-upgrade-privileges",
|
||||
"nameDe": "Eigene Berechtigungen erhöhen",
|
||||
"nameEn": "Upgrade your privileges",
|
||||
"exerciseNr": 4,
|
||||
@@ -92,6 +101,7 @@
|
||||
"descriptionEn": "Change the privileges of your account"
|
||||
},
|
||||
{
|
||||
"uuid": "sql-injection-capture-account",
|
||||
"nameDe": "Einen fremden Account übernehmen",
|
||||
"nameEn": "Capture another account",
|
||||
"exerciseNr": 5,
|
||||
@@ -99,6 +109,7 @@
|
||||
"descriptionEn": "Instead of increasing our own permissions, we can also take over an account that is already a >>super admin<<. To do this, select one from the list of accounts obtained in Task 2.1 that has the >>super admin<< role. Only then can we open the file manager, which we'll need later. Once you've found the account name, go to the login menu (log out if you're still logged in). Now perform an SQL injection to take over this account."
|
||||
},
|
||||
{
|
||||
"uuid": "sql-injection-delete-rating",
|
||||
"nameDe": "Bewertungen löschen",
|
||||
"nameEn": "Delete ratings",
|
||||
"exerciseNr": 6,
|
||||
@@ -116,6 +127,7 @@
|
||||
"descriptionEn": "Next, we want to inject malicious code into the web application. First, we'll test whether the website is vulnerable to this. Manipulate the URL of the band's page so that you see a >>Hello World!<< message as an >>alert<<. Note: Use an image tag! Set the number >>1<< as the >>src<<. You can execute the command in the >>onerror<< tag.",
|
||||
"exercises": [
|
||||
{
|
||||
"uuid": "cross-site-scripting-hello-world",
|
||||
"nameDe": "Hallo Welt!",
|
||||
"nameEn": "Hello World!",
|
||||
"exerciseNr": 1,
|
||||
@@ -123,6 +135,7 @@
|
||||
"descriptionEn": "Next, we want to inject malicious code into the web application. First, we'll test whether the website is vulnerable to malicious code. To do this, go to the >>All Bands<< page and filter the entries by any genre of your choice. In the URL bar, you'll now see that a parameter is specified after the URL and the resource target (the part after the question mark). We'll replace this parameter with an HTML tag. The trick here: We specify the numeric value >>1<< as the source, which automatically executes what's contained in the >>onerror<< tag. This is exactly where we want to insert an alert message with >>Hello World!<< as JavaScript code. Change the URL so that it displays the message. If you're not familiar with JavaScript, check out the last page for useful commands."
|
||||
},
|
||||
{
|
||||
"uuid": "cross-site-scripting-external-script",
|
||||
"nameDe": "Ein externes Script aufrufen",
|
||||
"nameEn": "Run an external script",
|
||||
"exerciseNr": 2,
|
||||
|
||||
@@ -3,6 +3,9 @@ import { ExerciseGroup } from "./exerciseGroup.model";
|
||||
|
||||
@Table({ timestamps: false })
|
||||
export class Exercise extends Model {
|
||||
@Column
|
||||
uuid: string
|
||||
|
||||
@Column
|
||||
nameDe: string
|
||||
|
||||
|
||||
@@ -40,9 +40,9 @@ exerciseStore.getAllExercises()
|
||||
/>
|
||||
|
||||
<v-btn
|
||||
v-if="exerciseStore.helpPageVisible"
|
||||
v-if="exerciseStore.exercisePageVisible"
|
||||
variant="plain"
|
||||
icon="mdi-help"
|
||||
icon="mdi-book-open-blank-variant"
|
||||
to="/help"
|
||||
/>
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { ExerciseGroupModel } from "./exerciseGroupModel"
|
||||
|
||||
export class ExerciseModel {
|
||||
id = -1
|
||||
id: number = -1
|
||||
uuid: string = ""
|
||||
nameDe: string = ""
|
||||
nameEn: string = ""
|
||||
exerciseNr: number = 0
|
||||
@@ -9,4 +10,5 @@ export class ExerciseModel {
|
||||
descriptionEn: string = ""
|
||||
solved: boolean = false
|
||||
exerciseGroup: ExerciseGroupModel
|
||||
available: boolean = true
|
||||
}
|
||||
@@ -168,6 +168,17 @@
|
||||
"title": "Auf Werkseinstellungen zurücksetzen?",
|
||||
"description": "Sollen alle Einstellungen und Daten auf Werkseinstellungen zurückgesetzt werden? Alle Änderungen und Fortschritte gehen verloren!"
|
||||
}
|
||||
},
|
||||
"exercises": {
|
||||
"available": "{0} von {1} Aufgaben verfügbar",
|
||||
"edit": "Verfügbare Aufgaben bearbeiten",
|
||||
"settings": "Aufgaben-Konfiguration"
|
||||
},
|
||||
"importExport": {
|
||||
"title": "Import/Export Konfiguration",
|
||||
"selectConfigFile": "Konfigurations-Datei auswählen",
|
||||
"download": "Konfiguration exportieren",
|
||||
"upload": "Datei hochladen"
|
||||
}
|
||||
},
|
||||
"help": {
|
||||
|
||||
@@ -168,6 +168,19 @@
|
||||
"title": "Factory reset?",
|
||||
"description": "Do you really want to reset everything? Every change will be lost!"
|
||||
}
|
||||
},
|
||||
"exercises": {
|
||||
"settings": "Exercise Configuration",
|
||||
"available": "{0} of {1} exercises are available",
|
||||
"uploadExerciseConfig": "Upload exercise config",
|
||||
"edit": "Edit available exercises",
|
||||
"upload": "Upload exercises config"
|
||||
},
|
||||
"importExport": {
|
||||
"title": "Import/Export config",
|
||||
"selectConfigFile": "Select config file",
|
||||
"upload": "Upload file",
|
||||
"download": "Export config"
|
||||
}
|
||||
},
|
||||
"help": {
|
||||
|
||||
@@ -1,54 +1,34 @@
|
||||
<script setup lang="ts">
|
||||
import { useExerciseStore } from '@/stores/exercise.store';
|
||||
import outlinedButton from '@/components/basics/outlinedButton.vue';
|
||||
import { generateResultsPdf } from '@/scripts/pdfScripts';
|
||||
import { usePreferencesStore } from '@/stores/preferences.store';
|
||||
import cardView from '@/components/basics/cardView.vue';
|
||||
import { LanguageEnum } from '@/data/enums/languageEnum';
|
||||
import { ExerciseModel } from '@/data/models/exercises/exerciseModel';
|
||||
import { useExerciseStore } from "@/stores/exercise.store";
|
||||
import outlinedButton from "@/components/basics/outlinedButton.vue";
|
||||
import { generateResultsPdf } from "@/scripts/pdfScripts";
|
||||
import { usePreferencesStore } from "@/stores/preferences.store";
|
||||
import cardView from "@/components/basics/cardView.vue";
|
||||
import { LanguageEnum } from "@/data/enums/languageEnum";
|
||||
import { ExerciseModel } from "@/data/models/exercises/exerciseModel";
|
||||
import { getExerciseDotColor } from "@/scripts/colorScripts";
|
||||
import { getExerciseDescriptionLanguage, getExerciseNameLanguage } from "@/scripts/languageScripts";
|
||||
|
||||
const exerciseStore = useExerciseStore()
|
||||
const preferencesStore = usePreferencesStore()
|
||||
const exerciseStore = useExerciseStore();
|
||||
const preferencesStore = usePreferencesStore();
|
||||
|
||||
exerciseStore.solveExercise(1, 1)
|
||||
|
||||
function getDotColor(exerciseGroupNr: number) {
|
||||
switch(exerciseGroupNr) {
|
||||
case 0: return "purple"
|
||||
case 1: return "orange"
|
||||
case 2: return "blue"
|
||||
case 3: return "pink"
|
||||
}
|
||||
}
|
||||
// Mark this exercise as solved if page was opened
|
||||
exerciseStore.solveExercise(1, 1);
|
||||
|
||||
function generateExerciseKey() {
|
||||
try {
|
||||
let code = ""
|
||||
let code = "";
|
||||
|
||||
for (let i = 0; i < 13; i++) {
|
||||
if (exerciseStore.exercises[i].solved) {
|
||||
code += "3"
|
||||
code += "3";
|
||||
} else {
|
||||
code += "0"
|
||||
code += "0";
|
||||
}
|
||||
}
|
||||
|
||||
return (Number(code) + Number(preferencesStore.registrationNumber)) * 237
|
||||
} catch(e) {}
|
||||
}
|
||||
|
||||
function getNameLanguage(exercise: ExerciseModel) {
|
||||
switch(preferencesStore.language) {
|
||||
case LanguageEnum.GERMAN: return exercise.nameDe
|
||||
case LanguageEnum.ENGLISH: return exercise.nameEn
|
||||
}
|
||||
}
|
||||
|
||||
function getDescriptionLanguage(exercise: ExerciseModel) {
|
||||
switch(preferencesStore.language) {
|
||||
case LanguageEnum.GERMAN: return exercise.descriptionDe
|
||||
case LanguageEnum.ENGLISH: return exercise.descriptionEn
|
||||
}
|
||||
return (Number(code) + Number(preferencesStore.registrationNumber)) * 237;
|
||||
} catch (e) {}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -61,9 +41,12 @@ function getDescriptionLanguage(exercise: ExerciseModel) {
|
||||
<outlined-button
|
||||
prepend-icon="mdi-file-pdf-box"
|
||||
@click="generateResultsPdf()"
|
||||
:disabled="preferencesStore.studentName.length < 3 || preferencesStore.registrationNumber.length < 7"
|
||||
:disabled="
|
||||
preferencesStore.studentName.length < 3 ||
|
||||
preferencesStore.registrationNumber.length < 7
|
||||
"
|
||||
>
|
||||
{{ $t('help.scoreBoard.generatePdf') }}
|
||||
{{ $t("help.scoreBoard.generatePdf") }}
|
||||
</outlined-button>
|
||||
</v-col>
|
||||
</v-row>
|
||||
@@ -71,7 +54,7 @@ function getDescriptionLanguage(exercise: ExerciseModel) {
|
||||
<v-row>
|
||||
<v-col class="text-h5 text-center">
|
||||
<div>
|
||||
{{ $t('help.scoreBoard.personalSolutionKey') + ':' }}
|
||||
{{ $t("help.scoreBoard.personalSolutionKey") + ":" }}
|
||||
</div>
|
||||
<div>
|
||||
{{ generateExerciseKey() }}
|
||||
@@ -86,49 +69,55 @@ function getDescriptionLanguage(exercise: ExerciseModel) {
|
||||
icon="mdi-checkbox-marked-circle-auto-outline"
|
||||
>
|
||||
<template #borderless>
|
||||
<v-timeline
|
||||
side="end"
|
||||
class="px-5"
|
||||
align="start"
|
||||
>
|
||||
<v-timeline side="end" class="px-5" align="start">
|
||||
<template v-for="exercise of exerciseStore.exercises">
|
||||
<!-- Add exercise group description item -->
|
||||
<v-timeline-item v-if="exercise.exerciseNr == 1"
|
||||
:dot-color="getDotColor(exercise.exerciseGroup.groupNr)"
|
||||
<v-timeline-item
|
||||
v-if="exercise.exerciseNr == 1"
|
||||
:dot-color="
|
||||
getExerciseDotColor(exercise.exerciseGroup.groupNr)
|
||||
"
|
||||
:icon="exercise.exerciseGroup.icon"
|
||||
fill-dot
|
||||
>
|
||||
<div
|
||||
:class="`pt-1 text-h5 font-weight-bold text-${getDotColor(exercise.exerciseGroup.groupNr)}`"
|
||||
:class="`pt-1 text-h5 font-weight-bold text-${getExerciseDotColor(
|
||||
exercise.exerciseGroup.groupNr
|
||||
)}`"
|
||||
>
|
||||
{{
|
||||
(preferencesStore.language == LanguageEnum.GERMAN
|
||||
? exercise.exerciseGroup.nameDe
|
||||
: exercise.exerciseGroup.nameEn)
|
||||
preferencesStore.language == LanguageEnum.GERMAN
|
||||
? exercise.exerciseGroup.nameDe
|
||||
: exercise.exerciseGroup.nameEn
|
||||
}}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{{
|
||||
(preferencesStore.language == LanguageEnum.GERMAN
|
||||
? exercise.exerciseGroup.descriptionDe
|
||||
: exercise.exerciseGroup.descriptionEn)
|
||||
preferencesStore.language == LanguageEnum.GERMAN
|
||||
? exercise.exerciseGroup.descriptionDe
|
||||
: exercise.exerciseGroup.descriptionEn
|
||||
}}
|
||||
</div>
|
||||
</v-timeline-item>
|
||||
|
||||
<!-- Exercise item -->
|
||||
<v-timeline-item
|
||||
v-if="exercise.available"
|
||||
:dot-color="exercise.solved ? 'green' : 'primary'"
|
||||
:icon="exercise.solved ? 'mdi-check' : 'mdi-pencil'"
|
||||
>
|
||||
<!-- Right side -->
|
||||
<!-- Right side -->
|
||||
<card-view
|
||||
:title="$t('help.scoreBoard.exerciseNr', [exercise.exerciseGroup.groupNr, exercise.exerciseNr]) +
|
||||
getNameLanguage(exercise)"
|
||||
:title="
|
||||
$t('help.scoreBoard.exerciseNr', [
|
||||
exercise.exerciseGroup.groupNr,
|
||||
exercise.exerciseNr,
|
||||
]) + getExerciseNameLanguage(exercise)
|
||||
"
|
||||
:color="exercise.solved ? 'green' : 'primary'"
|
||||
>
|
||||
{{ getDescriptionLanguage(exercise) }}
|
||||
{{ getExerciseDescriptionLanguage(exercise) }}
|
||||
</card-view>
|
||||
</v-timeline-item>
|
||||
</template>
|
||||
@@ -138,4 +127,4 @@ function getDescriptionLanguage(exercise: ExerciseModel) {
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-container>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
<script setup lang="ts">
|
||||
import actionDialog from "@/components/basics/actionDialog.vue";
|
||||
import OutlinedButton from "@/components/basics/outlinedButton.vue";
|
||||
import {
|
||||
getExerciseGroupNameLanguage,
|
||||
getExerciseNameLanguage,
|
||||
} from "@/scripts/languageScripts";
|
||||
import { useExerciseStore } from "@/stores/exercise.store";
|
||||
import { usePreferencesStore } from "@/stores/preferences.store";
|
||||
import { ModelRef } from "vue";
|
||||
|
||||
const showDialog: ModelRef<boolean> = defineModel();
|
||||
const exerciseStore = useExerciseStore();
|
||||
const preferencesStore = usePreferencesStore();
|
||||
|
||||
function saveConfig() {
|
||||
preferencesStore.notAvailableExercises = []
|
||||
|
||||
for (let exercise of exerciseStore.exercises) {
|
||||
if (!exercise.available) {
|
||||
preferencesStore.notAvailableExercises.push(exercise.uuid)
|
||||
}
|
||||
}
|
||||
|
||||
showDialog.value = false
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<action-dialog
|
||||
v-model="showDialog"
|
||||
:title="$t('preferences.exercises.edit')"
|
||||
icon="mdi-pencil"
|
||||
width="800"
|
||||
persistent
|
||||
>
|
||||
<v-container>
|
||||
<v-list>
|
||||
<div v-for="exercise in exerciseStore.exercises">
|
||||
<div
|
||||
v-if="exercise.exerciseNr == 1"
|
||||
>
|
||||
<v-divider v-if="exercise.exerciseGroup.groupNr != 0"></v-divider>
|
||||
<v-list-item
|
||||
type="subheader"
|
||||
:title="getExerciseGroupNameLanguage(exercise.exerciseGroup)"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<v-list-item>
|
||||
<v-checkbox
|
||||
:label="getExerciseNameLanguage(exercise)"
|
||||
v-model="exercise.available"
|
||||
hide-details
|
||||
density="compact"
|
||||
/>
|
||||
</v-list-item>
|
||||
</div>
|
||||
</v-list>
|
||||
</v-container>
|
||||
|
||||
<template #actions>
|
||||
<outlined-button color="warning" prepend-icon="mdi-close" @click="showDialog = false">
|
||||
{{ $t('misc.actions.cancel') }}
|
||||
</outlined-button>
|
||||
<outlined-button color="success" prepend-icon="mdi-content-save" @click="saveConfig()">
|
||||
{{ $t("misc.actions.save") }}
|
||||
</outlined-button>
|
||||
</template>
|
||||
</action-dialog>
|
||||
</template>
|
||||
@@ -0,0 +1,75 @@
|
||||
<script setup lang="ts">
|
||||
import cardView from "@/components/basics/cardView.vue";
|
||||
import OutlinedButton from "@/components/basics/outlinedButton.vue";
|
||||
import { ExerciseGroupModel } from "@/data/models/exercises/exerciseGroupModel";
|
||||
import { ExerciseModel } from "@/data/models/exercises/exerciseModel";
|
||||
import { getExerciseGroupNameLanguage } from "@/scripts/languageScripts";
|
||||
import { useExerciseStore } from "@/stores/exercise.store";
|
||||
import { ref, watch } from "vue";
|
||||
import exerciseDialog from "./exerciseDialog.vue";
|
||||
|
||||
const exerciseStore = useExerciseStore();
|
||||
const exerciseGroups = ref<Array<ExerciseGroupModel>>([]);
|
||||
const showExerciseDialog = ref(false);
|
||||
|
||||
exerciseStore.getAllExercises();
|
||||
|
||||
/**
|
||||
* Extract exercise groups from all exercises
|
||||
*/
|
||||
function groupExercises() {
|
||||
exerciseStore.exercises.forEach((exercise) => {
|
||||
if (
|
||||
!exerciseGroups.value.find(
|
||||
(exerciseGroup) => exerciseGroup.id == exercise.exerciseGroup.id
|
||||
)
|
||||
) {
|
||||
exerciseGroups.value.push(exercise.exerciseGroup);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function filterByExerciseGroup(
|
||||
exercises: Array<ExerciseModel>,
|
||||
group: ExerciseGroupModel
|
||||
) {
|
||||
return exercises.filter((exercise) => exercise.exerciseGroup.id == group.id);
|
||||
}
|
||||
|
||||
watch(
|
||||
() => exerciseStore.exercises,
|
||||
() => groupExercises()
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<card-view
|
||||
:title="$t('preferences.exercises.settings')"
|
||||
icon="mdi-book-open-blank-variant"
|
||||
>
|
||||
<template #borderless>
|
||||
<v-list>
|
||||
<v-list-item
|
||||
v-for="group in exerciseGroups"
|
||||
:title="getExerciseGroupNameLanguage(group)"
|
||||
:subtitle="
|
||||
$t('preferences.exercises.available', [
|
||||
filterByExerciseGroup(exerciseStore.exercises, group).filter((exercise) => exercise.available)
|
||||
.length,
|
||||
filterByExerciseGroup(exerciseStore.exercises, group).length,
|
||||
])
|
||||
"
|
||||
:prepend-icon="group.icon"
|
||||
/>
|
||||
</v-list>
|
||||
</template>
|
||||
|
||||
<template #actions>
|
||||
<outlined-button prepend-icon="mdi-pencil" @click="showExerciseDialog = true">
|
||||
{{ $t("preferences.exercises.edit") }}
|
||||
</outlined-button>
|
||||
</template>
|
||||
</card-view>
|
||||
|
||||
<exercise-dialog v-model="showExerciseDialog" />
|
||||
</template>
|
||||
34
src/pages/misc/preferencesPage/importExportSection.vue
Normal file
34
src/pages/misc/preferencesPage/importExportSection.vue
Normal file
@@ -0,0 +1,34 @@
|
||||
<script setup lang="ts">
|
||||
import cardView from '@/components/basics/cardView.vue';
|
||||
import OutlinedButton from "@/components/basics/outlinedButton.vue";
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<card-view
|
||||
:title="$t('preferences.importExport.title')"
|
||||
icon="mdi-swap-horizontal-bold"
|
||||
>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<v-file-input
|
||||
:label="$t('preferences.importExport.selectConfigFile')"
|
||||
variant="outlined"
|
||||
accept=".json"
|
||||
hide-details
|
||||
/>
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<template #actions>
|
||||
<outlined-button prepend-icon="mdi-export">
|
||||
{{ $t("preferences.importExport.download") }}
|
||||
</outlined-button>
|
||||
|
||||
<outlined-button prepend-icon="mdi-upload" color="green">
|
||||
{{ $t("preferences.importExport.upload") }}
|
||||
</outlined-button>
|
||||
</template>
|
||||
|
||||
</card-view>
|
||||
</template>
|
||||
@@ -2,16 +2,29 @@
|
||||
import pageSetup from './pageSetupSection.vue';
|
||||
import systemSetup from './systemSetupSection.vue';
|
||||
import aboutSection from './aboutSection.vue';
|
||||
import exerciseSection from './exerciseConfig/exerciseSection.vue';
|
||||
import importExportSection from './importExportSection.vue';
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-container max-width="800">
|
||||
<v-row>
|
||||
<v-col>
|
||||
<import-export-section />
|
||||
</v-col>
|
||||
</v-row>
|
||||
<v-row>
|
||||
<v-col>
|
||||
<page-setup />
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col>
|
||||
<exercise-section />
|
||||
</v-col>
|
||||
</v-row>
|
||||
|
||||
<v-row>
|
||||
<v-col>
|
||||
<system-setup />
|
||||
|
||||
@@ -1,27 +1,24 @@
|
||||
<script setup lang="ts">
|
||||
import cardView from '@/components/basics/cardView.vue';
|
||||
import outlinedButton from '@/components/basics/outlinedButton.vue';
|
||||
import confirmDialog from '@/components/basics/confirmDialog.vue';
|
||||
import { ServerStateEnum } from '@/data/enums/serverStateEnum';
|
||||
import { usePreferencesStore } from '@/stores/preferences.store';
|
||||
import ServerStateText from '@/components/pageParts/serverStateText.vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import cardView from "@/components/basics/cardView.vue";
|
||||
import outlinedButton from "@/components/basics/outlinedButton.vue";
|
||||
import confirmDialog from "@/components/basics/confirmDialog.vue";
|
||||
import { ServerStateEnum } from "@/data/enums/serverStateEnum";
|
||||
import { usePreferencesStore } from "@/stores/preferences.store";
|
||||
import ServerStateText from "@/components/pageParts/serverStateText.vue";
|
||||
import { useRouter } from "vue-router";
|
||||
|
||||
const preferenceStore = usePreferencesStore()
|
||||
const router = useRouter()
|
||||
const preferenceStore = usePreferencesStore();
|
||||
const router = useRouter();
|
||||
|
||||
preferenceStore.getServerState()
|
||||
preferenceStore.getServerState();
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<card-view
|
||||
:title="$t('preferences.systemSetup')"
|
||||
icon="mdi-engine"
|
||||
>
|
||||
<card-view :title="$t('preferences.systemSetup')" icon="mdi-engine">
|
||||
<template #borderless>
|
||||
<v-list>
|
||||
<v-list-item class="text-h6 text-center">
|
||||
{{ $t('preferences.serverState') + ':' }} <server-state-text />
|
||||
{{ $t("preferences.serverState") + ":" }} <server-state-text />
|
||||
</v-list-item>
|
||||
|
||||
<v-list-item class="text-center">
|
||||
@@ -29,9 +26,12 @@ preferenceStore.getServerState()
|
||||
@click="preferenceStore.showDeleteDbDialog = true"
|
||||
prepend-icon="mdi-database-refresh"
|
||||
color="warning"
|
||||
:disabled="preferenceStore.serverState != ServerStateEnum.ONLINE || preferenceStore.fetchInProgress"
|
||||
:disabled="
|
||||
preferenceStore.serverState != ServerStateEnum.ONLINE ||
|
||||
preferenceStore.fetchInProgress
|
||||
"
|
||||
>
|
||||
{{ $t('preferences.resetDatabase.resetDatabase') }}
|
||||
{{ $t("preferences.resetDatabase.resetDatabase") }}
|
||||
</outlined-button>
|
||||
</v-list-item>
|
||||
|
||||
@@ -40,9 +40,12 @@ preferenceStore.getServerState()
|
||||
@click="preferenceStore.showDeleteExerciseDialog = true"
|
||||
prepend-icon="mdi-progress-close"
|
||||
color="warning"
|
||||
:disabled="preferenceStore.serverState != ServerStateEnum.ONLINE || preferenceStore.fetchInProgress"
|
||||
:disabled="
|
||||
preferenceStore.serverState != ServerStateEnum.ONLINE ||
|
||||
preferenceStore.fetchInProgress
|
||||
"
|
||||
>
|
||||
{{ $t('preferences.resetExerciseProgress.resetExerciseProgress') }}
|
||||
{{ $t("preferences.resetExerciseProgress.resetExerciseProgress") }}
|
||||
</outlined-button>
|
||||
</v-list-item>
|
||||
|
||||
@@ -51,9 +54,12 @@ preferenceStore.getServerState()
|
||||
@click="preferenceStore.showFactoryResetDialog = true"
|
||||
prepend-icon="mdi-factory"
|
||||
color="warning"
|
||||
:disabled="preferenceStore.serverState != ServerStateEnum.ONLINE || preferenceStore.fetchInProgress"
|
||||
:disabled="
|
||||
preferenceStore.serverState != ServerStateEnum.ONLINE ||
|
||||
preferenceStore.fetchInProgress
|
||||
"
|
||||
>
|
||||
{{ $t('preferences.factoryReset.factoryReset') }}
|
||||
{{ $t("preferences.factoryReset.factoryReset") }}
|
||||
</outlined-button>
|
||||
</v-list-item>
|
||||
</v-list>
|
||||
@@ -85,10 +91,12 @@ preferenceStore.getServerState()
|
||||
:description="$t('preferences.factoryReset.dialog.description')"
|
||||
v-model="preferenceStore.showFactoryResetDialog"
|
||||
icon="mdi-factory"
|
||||
:onConfirm="() => {
|
||||
preferenceStore.resetToFactorySettings()
|
||||
router.push('/')
|
||||
}"
|
||||
:onConfirm="
|
||||
() => {
|
||||
preferenceStore.resetToFactorySettings();
|
||||
router.push('/');
|
||||
}
|
||||
"
|
||||
:loading="preferenceStore.fetchInProgress"
|
||||
/>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
@@ -19,4 +19,20 @@ export function getSeatColor(surcharge: number, state: number): string {
|
||||
case 1: return "grey"
|
||||
case 2: return "orange"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get color of exercise group
|
||||
*
|
||||
* @param exerciseGroupNr Number of exercise group
|
||||
*
|
||||
* @returns Color as string
|
||||
*/
|
||||
export function getExerciseDotColor(exerciseGroupNr: number) {
|
||||
switch(exerciseGroupNr) {
|
||||
case 0: return "purple"
|
||||
case 1: return "orange"
|
||||
case 2: return "blue"
|
||||
case 3: return "pink"
|
||||
}
|
||||
}
|
||||
37
src/scripts/languageScripts.ts
Normal file
37
src/scripts/languageScripts.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { LanguageEnum } from "@/data/enums/languageEnum";
|
||||
import { ExerciseGroupModel } from "@/data/models/exercises/exerciseGroupModel";
|
||||
import { ExerciseModel } from "@/data/models/exercises/exerciseModel";
|
||||
import { usePreferencesStore } from "@/stores/preferences.store";
|
||||
|
||||
export function getExerciseNameLanguage(exercise: ExerciseModel) {
|
||||
let preferencesStore = usePreferencesStore()
|
||||
|
||||
switch (preferencesStore.language) {
|
||||
case LanguageEnum.GERMAN:
|
||||
return exercise.nameDe;
|
||||
case LanguageEnum.ENGLISH:
|
||||
return exercise.nameEn;
|
||||
}
|
||||
}
|
||||
|
||||
export function getExerciseDescriptionLanguage(exercise: ExerciseModel) {
|
||||
let preferencesStore = usePreferencesStore()
|
||||
|
||||
switch (preferencesStore.language) {
|
||||
case LanguageEnum.GERMAN:
|
||||
return exercise.descriptionDe;
|
||||
case LanguageEnum.ENGLISH:
|
||||
return exercise.descriptionEn;
|
||||
}
|
||||
}
|
||||
|
||||
export function getExerciseGroupNameLanguage(exerciseGroup: ExerciseGroupModel) {
|
||||
let preferencesStore = usePreferencesStore()
|
||||
|
||||
switch (preferencesStore.language) {
|
||||
case LanguageEnum.GERMAN:
|
||||
return exerciseGroup.nameDe;
|
||||
case LanguageEnum.ENGLISH:
|
||||
return exerciseGroup.nameEn;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import { ref } from "vue";
|
||||
import { useFeedbackStore } from "./feedback.store";
|
||||
import { BannerStateEnum } from "@/data/enums/bannerStateEnum";
|
||||
import { ExerciseModel } from "@/data/models/exercises/exerciseModel";
|
||||
import { usePreferencesStore } from "./preferences.store";
|
||||
|
||||
export const useExerciseStore = defineStore("exerciseStore", {
|
||||
state: () => ({
|
||||
@@ -13,106 +14,155 @@ export const useExerciseStore = defineStore("exerciseStore", {
|
||||
/** Request to server sent, waiting for data response */
|
||||
fetchInProgress: ref(false),
|
||||
|
||||
helpPageVisible: ref(false)
|
||||
exercisePageVisible: ref(false),
|
||||
|
||||
/** All available exercise uuids are stored here */
|
||||
exerciseConfig: ref<Array<string>>(),
|
||||
}),
|
||||
|
||||
actions: {
|
||||
/**
|
||||
* Get all exercises and exercise groups from server
|
||||
*
|
||||
* @param firstLoad True sets all exercises as available, for first load after database was initialised
|
||||
*/
|
||||
async getAllExercises() {
|
||||
this.fetchInProgress = true
|
||||
async getAllExercises(firstLoad: boolean = false) {
|
||||
const preferencesStore = usePreferencesStore();
|
||||
this.fetchInProgress = true;
|
||||
|
||||
await fetchAllExerciseGroups()
|
||||
.then(result => {
|
||||
this.exercises = result.data
|
||||
await fetchAllExerciseGroups().then((result) => {
|
||||
this.exercises = result.data;
|
||||
|
||||
this.helpPageVisible = this.getExercise(1, 1).solved
|
||||
this.fetchInProgress = false
|
||||
})
|
||||
if (firstLoad) {
|
||||
preferencesStore.notAvailableExercises = []
|
||||
}
|
||||
|
||||
result.data.forEach((exercise) => {
|
||||
if (firstLoad) {
|
||||
exercise.available = true
|
||||
} else {
|
||||
exercise.available =
|
||||
preferencesStore.notAvailableExercises.find(
|
||||
(availableExercise: string) => {
|
||||
return availableExercise == exercise.uuid;
|
||||
}
|
||||
) == undefined;
|
||||
}
|
||||
});
|
||||
|
||||
this.helpPageVisible = this.getExercise(1, 1).solved;
|
||||
this.fetchInProgress = false;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Get a exercise by group and exercise number
|
||||
*
|
||||
*
|
||||
* @param exerciseGroupNr Number of group of exercise
|
||||
* @param exerciseNr Number of exercise in group
|
||||
*
|
||||
*
|
||||
* @returns ExerciseModel
|
||||
*/
|
||||
getExercise(exerciseGroupNr: number, exerciseNr: number): ExerciseModel {
|
||||
return this.exercises.find((exercise: ExerciseModel) => {
|
||||
return exercise.exerciseNr == exerciseNr && exercise.exerciseGroup.groupNr == exerciseGroupNr
|
||||
})
|
||||
return (
|
||||
exercise.exerciseNr == exerciseNr &&
|
||||
exercise.exerciseGroup.groupNr == exerciseGroupNr
|
||||
);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Mark an exercise as solved
|
||||
*
|
||||
*
|
||||
* @param exerciseGroupNr Exercise group number (0-3)
|
||||
* @param exerciseNr Exercise number (1-3)
|
||||
*/
|
||||
async solveExercise(exerciseGroupNr: number, exerciseNr: number) {
|
||||
// Request all exercises from server
|
||||
await this.getAllExercises()
|
||||
await this.getAllExercises();
|
||||
|
||||
const feedbackStore = useFeedbackStore()
|
||||
this.fetchInProgress = true
|
||||
const feedbackStore = useFeedbackStore();
|
||||
this.fetchInProgress = true;
|
||||
|
||||
// Change only if the exercise is not solved
|
||||
updateExercise(exerciseGroupNr, exerciseNr, true)
|
||||
.then(result => {
|
||||
if (result.data.changed) {
|
||||
updateExercise(exerciseGroupNr, exerciseNr, true).then((result) => {
|
||||
if (result.data.changed) {
|
||||
let bannerState = BannerStateEnum.ERROR;
|
||||
|
||||
let bannerState = BannerStateEnum.ERROR
|
||||
|
||||
switch(exerciseGroupNr) {
|
||||
case 0: {
|
||||
switch(exerciseNr) {
|
||||
case 1: bannerState = BannerStateEnum.EXERCISESOLVED01; break;
|
||||
case 2: bannerState = BannerStateEnum.EXERCISESOLVED02; break;
|
||||
case 3: bannerState = BannerStateEnum.EXERCISESOLVED03; break;
|
||||
}
|
||||
|
||||
break;
|
||||
switch (exerciseGroupNr) {
|
||||
case 0: {
|
||||
switch (exerciseNr) {
|
||||
case 1:
|
||||
bannerState = BannerStateEnum.EXERCISESOLVED01;
|
||||
break;
|
||||
case 2:
|
||||
bannerState = BannerStateEnum.EXERCISESOLVED02;
|
||||
break;
|
||||
case 3:
|
||||
bannerState = BannerStateEnum.EXERCISESOLVED03;
|
||||
break;
|
||||
}
|
||||
|
||||
case 1: {
|
||||
switch(exerciseNr) {
|
||||
case 1: bannerState = BannerStateEnum.EXERCISESOLVED11; break;
|
||||
case 2: bannerState = BannerStateEnum.EXERCISESOLVED12; break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 2: {
|
||||
switch(exerciseNr) {
|
||||
case 1: bannerState = BannerStateEnum.EXERCISESOLVED21; break;
|
||||
case 2: bannerState = BannerStateEnum.EXERCISESOLVED22; break;
|
||||
case 3: bannerState = BannerStateEnum.EXERCISESOLVED23; break;
|
||||
case 4: bannerState = BannerStateEnum.EXERCISESOLVED24; break;
|
||||
case 5: bannerState = BannerStateEnum.EXERCISESOLVED25; break;
|
||||
case 6: bannerState = BannerStateEnum.EXERCISESOLVED26; break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 3: {
|
||||
switch(exerciseNr) {
|
||||
case 1: bannerState = BannerStateEnum.EXERCISESOLVED31; break;
|
||||
case 2: bannerState = BannerStateEnum.EXERCISESOLVED32; break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
feedbackStore.addSnackbar(bannerState)
|
||||
this.getAllExercises()
|
||||
case 1: {
|
||||
switch (exerciseNr) {
|
||||
case 1:
|
||||
bannerState = BannerStateEnum.EXERCISESOLVED11;
|
||||
break;
|
||||
case 2:
|
||||
bannerState = BannerStateEnum.EXERCISESOLVED12;
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 2: {
|
||||
switch (exerciseNr) {
|
||||
case 1:
|
||||
bannerState = BannerStateEnum.EXERCISESOLVED21;
|
||||
break;
|
||||
case 2:
|
||||
bannerState = BannerStateEnum.EXERCISESOLVED22;
|
||||
break;
|
||||
case 3:
|
||||
bannerState = BannerStateEnum.EXERCISESOLVED23;
|
||||
break;
|
||||
case 4:
|
||||
bannerState = BannerStateEnum.EXERCISESOLVED24;
|
||||
break;
|
||||
case 5:
|
||||
bannerState = BannerStateEnum.EXERCISESOLVED25;
|
||||
break;
|
||||
case 6:
|
||||
bannerState = BannerStateEnum.EXERCISESOLVED26;
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 3: {
|
||||
switch (exerciseNr) {
|
||||
case 1:
|
||||
bannerState = BannerStateEnum.EXERCISESOLVED31;
|
||||
break;
|
||||
case 2:
|
||||
bannerState = BannerStateEnum.EXERCISESOLVED32;
|
||||
break;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
feedbackStore.addSnackbar(bannerState);
|
||||
this.getAllExercises();
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -3,7 +3,11 @@ import { useLocalStorage } from "@vueuse/core";
|
||||
import { ThemeEnum } from "../data/enums/themeEnums";
|
||||
import { LanguageEnum } from "../data/enums/languageEnum";
|
||||
import { ref } from "vue";
|
||||
import { fetchServerState,resetDatabase, resetExerciseProgress } from "@/data/api/mainApi";
|
||||
import {
|
||||
fetchServerState,
|
||||
resetDatabase,
|
||||
resetExerciseProgress,
|
||||
} from "@/data/api/mainApi";
|
||||
import { ServerStateEnum } from "@/data/enums/serverStateEnum";
|
||||
import { BannerStateEnum } from "@/data/enums/bannerStateEnum";
|
||||
import { useFeedbackStore } from "./feedback.store";
|
||||
@@ -12,13 +16,19 @@ import { useExerciseStore } from "./exercise.store";
|
||||
import { useAccountStore } from "./account.store";
|
||||
import { AccountApiModel } from "@/data/models/user/accountApiModel";
|
||||
|
||||
export const usePreferencesStore = defineStore('preferencesStore', {
|
||||
export const usePreferencesStore = defineStore("preferencesStore", {
|
||||
state: () => ({
|
||||
/** Selected theme by user */
|
||||
theme: useLocalStorage<ThemeEnum>("eventMaster/preferencesStore/theme", ThemeEnum.DARK),
|
||||
theme: useLocalStorage<ThemeEnum>(
|
||||
"eventMaster/preferencesStore/theme",
|
||||
ThemeEnum.DARK
|
||||
),
|
||||
|
||||
/** Selected language by user */
|
||||
language: useLocalStorage<LanguageEnum>("eventMaster/preferencesStore/language", LanguageEnum.GERMAN),
|
||||
language: useLocalStorage<LanguageEnum>(
|
||||
"eventMaster/preferencesStore/language",
|
||||
LanguageEnum.GERMAN
|
||||
),
|
||||
|
||||
/** Request to server sent, waiting for data response */
|
||||
fetchInProgress: ref(false),
|
||||
@@ -36,13 +46,27 @@ export const usePreferencesStore = defineStore('preferencesStore', {
|
||||
showFactoryResetDialog: ref(false),
|
||||
|
||||
/** Marks the first run of the app */
|
||||
firstStartup: useLocalStorage<Boolean>("eventMaster/preferencesStore/firstStartup", true),
|
||||
firstStartup: useLocalStorage<Boolean>(
|
||||
"eventMaster/preferencesStore/firstStartup",
|
||||
true
|
||||
),
|
||||
|
||||
/** Full name of student */
|
||||
studentName: useLocalStorage<string>("eventMaster/preferencesStore/studentName", ""),
|
||||
studentName: useLocalStorage<string>(
|
||||
"eventMaster/preferencesStore/studentName",
|
||||
""
|
||||
),
|
||||
|
||||
/** Matrikel number */
|
||||
registrationNumber: useLocalStorage<string>("eventMaster/preferencesStore/registrationNumber", "")
|
||||
registrationNumber: useLocalStorage<string>(
|
||||
"eventMaster/preferencesStore/registrationNumber",
|
||||
""
|
||||
),
|
||||
|
||||
notAvailableExercises: useLocalStorage<Array<string>>(
|
||||
"eventMaster/preferencesStore/notAvailableExercises",
|
||||
[]
|
||||
),
|
||||
}),
|
||||
|
||||
actions: {
|
||||
@@ -50,92 +74,90 @@ export const usePreferencesStore = defineStore('preferencesStore', {
|
||||
* Request the state of the backend server
|
||||
*/
|
||||
async getServerState() {
|
||||
this.fetchInProgress = true
|
||||
this.fetchInProgress = true;
|
||||
|
||||
fetchServerState()
|
||||
.then(result => {
|
||||
.then((result) => {
|
||||
if (result.status == 200) {
|
||||
this.serverState = ServerStateEnum.ONLINE
|
||||
this.serverState = ServerStateEnum.ONLINE;
|
||||
} else {
|
||||
this.serverState = ServerStateEnum.OFFLINE
|
||||
this.serverState = ServerStateEnum.OFFLINE;
|
||||
}
|
||||
|
||||
this.fetchInProgress = false
|
||||
})
|
||||
.catch(error => {
|
||||
this.serverState = ServerStateEnum.OFFLINE
|
||||
this.fetchInProgress = false
|
||||
this.fetchInProgress = false;
|
||||
})
|
||||
.catch((error) => {
|
||||
this.serverState = ServerStateEnum.OFFLINE;
|
||||
this.fetchInProgress = false;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Resets the database (without exercise tables)
|
||||
*/
|
||||
async resetDb() {
|
||||
const feedbackStore = useFeedbackStore()
|
||||
const accountStore = useAccountStore()
|
||||
const feedbackStore = useFeedbackStore();
|
||||
const accountStore = useAccountStore();
|
||||
|
||||
this.serverState = ServerStateEnum.PENDING
|
||||
this.fetchInProgress = true
|
||||
this.serverState = ServerStateEnum.PENDING;
|
||||
this.fetchInProgress = true;
|
||||
|
||||
// Logout user
|
||||
accountStore.logout()
|
||||
accountStore.logout();
|
||||
|
||||
await resetDatabase()
|
||||
.then(result => {
|
||||
if (result.status == 200) {
|
||||
feedbackStore.addSnackbar(BannerStateEnum.DATABASERESETSUCCESSFUL)
|
||||
this.serverState = ServerStateEnum.ONLINE
|
||||
}
|
||||
await resetDatabase().then((result) => {
|
||||
if (result.status == 200) {
|
||||
feedbackStore.addSnackbar(BannerStateEnum.DATABASERESETSUCCESSFUL);
|
||||
this.serverState = ServerStateEnum.ONLINE;
|
||||
}
|
||||
|
||||
this.fetchInProgress = false
|
||||
this.showDeleteDbDialog = false
|
||||
})
|
||||
this.fetchInProgress = false;
|
||||
this.showDeleteDbDialog = false;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Resets the exercise progress
|
||||
*/
|
||||
async resetExerciseProg() {
|
||||
const feedbackStore = useFeedbackStore()
|
||||
const exerciseStore = useExerciseStore()
|
||||
const feedbackStore = useFeedbackStore();
|
||||
const exerciseStore = useExerciseStore();
|
||||
|
||||
this.serverState = ServerStateEnum.PENDING
|
||||
this.fetchInProgress = true
|
||||
this.serverState = ServerStateEnum.PENDING;
|
||||
this.fetchInProgress = true;
|
||||
|
||||
await resetExerciseProgress()
|
||||
.then(result => {
|
||||
if (result.status == 200) {
|
||||
feedbackStore.addSnackbar(BannerStateEnum.EXERCISEPROGRESSRESETSUCCESSFUL)
|
||||
this.serverState = ServerStateEnum.ONLINE
|
||||
await resetExerciseProgress().then((result) => {
|
||||
if (result.status == 200) {
|
||||
feedbackStore.addSnackbar(
|
||||
BannerStateEnum.EXERCISEPROGRESSRESETSUCCESSFUL
|
||||
);
|
||||
this.serverState = ServerStateEnum.ONLINE;
|
||||
|
||||
exerciseStore.getAllExercises()
|
||||
}
|
||||
exerciseStore.getAllExercises(true);
|
||||
}
|
||||
|
||||
this.fetchInProgress = false
|
||||
this.showDeleteExerciseDialog = false
|
||||
})
|
||||
this.fetchInProgress = false;
|
||||
this.showDeleteExerciseDialog = false;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Reset all store values to factory state
|
||||
*/
|
||||
resetToFactorySettings() {
|
||||
const basketStore = useBasketStore()
|
||||
const accountStore = useAccountStore()
|
||||
const basketStore = useBasketStore();
|
||||
const accountStore = useAccountStore();
|
||||
|
||||
this.firstStartup = true
|
||||
this.studentName = ""
|
||||
this.registrationNumber = ""
|
||||
this.theme = "dark"
|
||||
this.language = LanguageEnum.GERMAN
|
||||
basketStore.itemsInBasket = []
|
||||
accountStore.userAccountToken = ""
|
||||
accountStore.userAccount = new AccountApiModel()
|
||||
this.firstStartup = true;
|
||||
this.studentName = "";
|
||||
this.registrationNumber = "";
|
||||
this.theme = "dark";
|
||||
this.language = LanguageEnum.GERMAN;
|
||||
basketStore.itemsInBasket = [];
|
||||
accountStore.userAccountToken = "";
|
||||
accountStore.userAccount = new AccountApiModel();
|
||||
|
||||
|
||||
|
||||
this.showFactoryResetDialog = false
|
||||
}
|
||||
}
|
||||
})
|
||||
this.showFactoryResetDialog = false;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user