Append icons in toolbar shows tooltips, exercise page icon adds open exercises badge

This commit is contained in:
2025-08-30 13:39:16 +02:00
parent 052bb7694a
commit cde3c84bc7
5 changed files with 134 additions and 57 deletions

View File

@@ -98,7 +98,7 @@
"nameEn": "Upgrade your privileges", "nameEn": "Upgrade your privileges",
"exerciseNr": 4, "exerciseNr": 4,
"descriptionDe": "Jetzt bearbeiten wir unseren eigenen Account. Schreibe hierfür einen >>UPDATE<<-SQL-Befehl, welcher die >>accountRoleId<< auf das Niveau eines >>Admin<< erhöht für deinen Account-Namen.", "descriptionDe": "Jetzt bearbeiten wir unseren eigenen Account. Schreibe hierfür einen >>UPDATE<<-SQL-Befehl, welcher die >>accountRoleId<< auf das Niveau eines >>Admin<< erhöht für deinen Account-Namen.",
"descriptionEn": "Change the privileges of your account" "descriptionEn": "Now we'll edit our own account. To do this, write an >>UPDATE<< SQL command that elevates the >>accountRoleId<< to the level of >>Admin<< for your account name."
}, },
{ {
"uuid": "sql-injection-capture-account", "uuid": "sql-injection-capture-account",

View File

@@ -2,54 +2,121 @@
import { useAccountStore } from "@/stores/account.store"; import { useAccountStore } from "@/stores/account.store";
import { useBasketStore } from "@/stores/basket.store"; import { useBasketStore } from "@/stores/basket.store";
import { useExerciseStore } from "@/stores/exercise.store"; import { useExerciseStore } from "@/stores/exercise.store";
import { ref, watch } from "vue";
const accountStore = useAccountStore(); const accountStore = useAccountStore();
const basketStore = useBasketStore(); const basketStore = useBasketStore();
const exerciseStore = useExerciseStore(); const exerciseStore = useExerciseStore();
const basketItems = ref(0);
exerciseStore.getAllExercises(); exerciseStore.getAllExercises();
watch(
() => basketStore.itemsInBasket,
() => {
basketItems.value = basketStore.itemsInBasket.reduce((tot, item) => {
return tot + item.seats.length;
}, 0);
}
);
</script> </script>
<template> <template>
<v-btn variant="plain" icon="mdi-magnify" to="/search" /> <!-- Global search -->
<v-tooltip :text="$t('misc.search.globalsearch')" location="bottom">
<template #activator="{ props }">
<v-btn v-bind="props" variant="plain" icon="mdi-magnify" to="/search" />
</template>
</v-tooltip>
<v-btn <!-- Account -->
v-if="accountStore.userAccountToken == ''" <v-tooltip :text="$t('account.account')" location="bottom">
variant="plain" <template #activator="{ props }">
icon="mdi-account" <v-btn
to="/account/login" v-if="accountStore.userAccountToken == ''"
/> v-bind="props"
variant="plain"
icon="mdi-account"
to="/account/login"
/>
<v-btn v-else variant="plain" icon="mdi-account-check" to="/account/home" /> <v-btn
v-else
v-bind="props"
variant="plain"
icon="mdi-account-check"
to="/account/home"
/>
</template>
</v-tooltip>
<div> <!-- Basket -->
<v-badge <v-tooltip :text="$t('basket.basket')" location="bottom">
:content=" <template #activator="{ props }">
basketStore.itemsInBasket.reduce((tot, item) => { <v-badge
return tot + item.seats.length; v-if="basketItems > 0"
}, 0) :content="basketItems"
" color="error"
color="error" offset-x="8"
offset-x="8" offset-y="8"
offset-y="8" >
> <v-btn v-bind="props" variant="plain" icon="mdi-cart" to="/basket" />
<v-btn variant="plain" icon="mdi-cart" to="/basket" /> </v-badge>
</v-badge>
</div>
<v-btn <v-btn
v-if="accountStore.adminPanelVisible" v-else
variant="plain" v-bind="props"
icon="mdi-table-cog" variant="plain"
to="/admin" icon="mdi-cart"
/> to="/basket"
/>
</template>
</v-tooltip>
<v-btn <!-- Exercise page -->
v-if="exerciseStore.exercisePageVisible" <v-tooltip :text="$t('misc.firstStartup.exercises')" location="bottom">
variant="plain" <template #activator="{ props }">
icon="mdi-book-open-blank-variant" <v-badge
to="/help" v-if="exerciseStore.exercisePageVisible"
/> :content="
exerciseStore.exercises.reduce((tot, exercise) => {
if (exercise.available && !exercise.solved) {
return tot + 1;
} else {
return tot;
}
}, 0)
"
color="error"
offset-x="8"
offset-y="8"
>
<v-btn
v-bind="props"
variant="plain"
icon="mdi-book-open-blank-variant"
to="/help"
/>
</v-badge>
</template>
</v-tooltip>
<v-btn variant="plain" icon="mdi-cog" to="/preferences" /> <!-- Admin panel -->
<v-tooltip :text="$t('admin.adminpanel')" location="bottom">
<template #activator="{ props }">
<v-btn
v-if="accountStore.adminPanelVisible"
v-bind="props"
variant="plain"
icon="mdi-table-cog"
to="/admin"
/>
</template>
</v-tooltip>
<v-tooltip :text="$t('preferences.preferences')" location="bottom">
<template #activator="{ props }">
<v-btn v-bind="props" variant="plain" icon="mdi-cog" to="/preferences" />
</template>
</v-tooltip>
</template> </template>

View File

@@ -179,7 +179,8 @@
"selectConfigFile": "Konfigurations-Datei auswählen", "selectConfigFile": "Konfigurations-Datei auswählen",
"download": "Konfiguration exportieren", "download": "Konfiguration exportieren",
"upload": "Datei hochladen" "upload": "Datei hochladen"
} },
"preferences": "Einstellungen"
}, },
"help": { "help": {
"scoreBoard": { "scoreBoard": {
@@ -280,7 +281,8 @@
"empty": { "empty": {
"headline": "So leer hier..." "headline": "So leer hier..."
}, },
"searchterm": "Suchbegriff" "searchterm": "Suchbegriff",
"globalsearch": "Globale Suche"
}, },
"submit": "Absenden", "submit": "Absenden",
"content": "Inhalt", "content": "Inhalt",
@@ -295,5 +297,8 @@
}, },
"genre": { "genre": {
"withoutBand": "ohne Band" "withoutBand": "ohne Band"
},
"admin": {
"adminpanel": "Admin Panel"
} }
} }

View File

@@ -181,7 +181,8 @@
"selectConfigFile": "Select config file", "selectConfigFile": "Select config file",
"upload": "Upload file", "upload": "Upload file",
"download": "Export config" "download": "Export config"
} },
"preferences": "Preferences"
}, },
"help": { "help": {
"scoreBoard": { "scoreBoard": {
@@ -280,7 +281,8 @@
"empty": { "empty": {
"headline": "So empty here..." "headline": "So empty here..."
}, },
"searchterm": "Search term" "searchterm": "Search term",
"globalsearch": "Global Search"
}, },
"submit": "Submit", "submit": "Submit",
"content": "Content", "content": "Content",
@@ -295,5 +297,8 @@
}, },
"genre": { "genre": {
"withoutBand": "without Band" "withoutBand": "without Band"
},
"admin": {
"adminpanel": "Admin Panel"
} }
} }

View File

@@ -1,24 +1,24 @@
<script setup lang="ts"> <script setup lang="ts">
import { useConcertStore } from '@/stores/concert.store'; import { useConcertStore } from "@/stores/concert.store";
import { useLocationStore } from '@/stores/location.store'; import { useLocationStore } from "@/stores/location.store";
import bandSection from './bandsSection.vue'; import bandSection from "./bandsSection.vue";
import UpcomingConcertsSection from './upcomingConcertsSection.vue'; import UpcomingConcertsSection from "./upcomingConcertsSection.vue";
import TopLocationsSection from './topLocationsSection.vue'; import TopLocationsSection from "./topLocationsSection.vue";
import { usePreferencesStore } from '@/stores/preferences.store'; import { usePreferencesStore } from "@/stores/preferences.store";
import welcomeDialog from './welcomeDialog/dialog.vue'; import welcomeDialog from "./welcomeDialog/dialog.vue";
import { ref } from 'vue'; import { ref } from "vue";
const concertStore = useConcertStore() const concertStore = useConcertStore();
const locationStore = useLocationStore() const locationStore = useLocationStore();
const preferencesStore = usePreferencesStore() const preferencesStore = usePreferencesStore();
const showWelcomeDialog = ref(false) const showWelcomeDialog = ref(false);
concertStore.getUpcomingConcerts() concertStore.getUpcomingConcerts();
locationStore.getTopLocations() locationStore.getTopLocations();
// First startup // First startup
showWelcomeDialog.value = true
if (preferencesStore.firstStartup) { if (preferencesStore.firstStartup) {
showWelcomeDialog.value = true;
} }
</script> </script>
@@ -33,7 +33,7 @@ if (preferencesStore.firstStartup) {
<v-col cols="10"> <v-col cols="10">
<upcoming-concerts-section /> <upcoming-concerts-section />
<top-locations-section /> <top-locations-section />
</v-col> </v-col>
@@ -42,4 +42,4 @@ if (preferencesStore.firstStartup) {
</v-container> </v-container>
<welcome-dialog :model-value="showWelcomeDialog" /> <welcome-dialog :model-value="showWelcomeDialog" />
</template> </template>