🚧 💄 Implement new settings menu and 3 subpages

- Implement new main settings menu
- Implement "filtering" page
- Implement "appearance" page
- Implement "about" page
- Make the Git hash a link to the Git repo
This commit is contained in:
2023-06-06 22:39:08 +02:00
parent 7183f467b4
commit 1ed9af766d
11 changed files with 253 additions and 113 deletions

View File

@ -1,7 +1,7 @@
.app.theme-dark {
--bg-color: #353535;
--element-color: #333030;
--element-color-hover: #292424;
--element-color: #212121;
--element-color-hover: #1b1b1b;
--element-border-input: #4e7a3a;
--element-border-focus: #9ac982;
--element-border-action: #1f5b63;

View File

@ -0,0 +1,41 @@
<script setup>
import { RouterLink } from "vue-router";
defineProps({
name: {
required: true,
},
icon: {
required: true,
},
route: {
required: true,
},
});
</script>
<template>
<RouterLink :to="route" class="card">
<component :is="icon" /><span>{{ name }}</span>
</RouterLink>
</template>
<style scoped>
.card {
text-decoration: unset;
color: unset;
display: flex;
background-color: var(--element-color);
padding: 15px;
border-radius: 8px;
font-size: 18px;
gap: 15px;
transition: 0.2s;
cursor: pointer;
box-shadow: 2px 2px 10px 0px rgba(0, 0, 0, 0.2);
}
.card:hover {
background-color: var(--element-color-hover);
}
</style>

View File

@ -0,0 +1,30 @@
<script setup>
import { CircleIcon, CheckCircleIcon } from "lucide-vue-next";
defineProps(["options", "values", "modelValue"]);
defineEmits(["update:modelValue"]);
</script>
<template>
<div
class="button"
v-for="(value, index) in values"
:key="value"
@click="$emit('update:modelValue', value)"
>
<CheckCircleIcon v-if="modelValue == value" />
<CircleIcon v-else />
<span class="text">{{ options[index] }}</span>
</div>
</template>
<style scoped>
.button {
display: flex;
align-items: center;
gap: 8px;
padding: 10px 5px;
cursor: pointer;
}
</style>

View File

@ -9,8 +9,8 @@ const router = useRouter();
const routeName = computed(() => route.name);
function goBack() {
if (!routeName.value.startsWith("title.settings")) return;
if (lastDataRoute.value.name) router.push(lastDataRoute.value.path);
else router.push("/timetable");
if (lastDataRoute.value) router.push(lastDataRoute.value.path);
else router.push("/");
}
</script>

View File

@ -7,6 +7,9 @@ import HistoryView from "@/views/HistoryView.vue";
import SettingsView from "@/views/SettingsView.vue";
import LoginView from "@/views/LoginView.vue";
import TokenView from "@/views/TokenView.vue";
import FilteringSettings from "@/views/settings/FilteringSettings.vue";
import AppearanceSettings from "@/views/settings/AppearanceSettings.vue";
import AboutPage from "@/views/settings/AboutPage.vue";
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
@ -41,8 +44,25 @@ const router = createRouter({
},
{
path: "/settings",
name: "title.settings",
name: "title.settings.main",
component: SettingsView,
children: [
{
path: "filtering",
name: "title.settings.filtering",
component: FilteringSettings,
},
{
path: "appearance",
name: "title.settings.appearance",
component: AppearanceSettings,
},
{
path: "about",
name: "title.settings.about",
component: AboutPage,
},
],
},
{
path: "/login",

View File

@ -4,9 +4,14 @@ export const strings = {
timetable: "Timetable",
substitutions: "Substitutions",
history: "History",
settings: "Settings",
login: "Login",
token: "Token",
settings: {
main: "Settings",
filtering: "Filtering",
appearance: "Appearance",
about: "About",
},
},
settings: {
heading: {
@ -28,6 +33,7 @@ export const strings = {
"Select a Theme to change the colors of the app. The 'Auto' option selects a theme based on your system preferences.",
},
other: "Other",
back: "Back",
none: "None",
version: "Version",
theme: {
@ -102,8 +108,13 @@ export const strings = {
timetable: "Stundenplan",
substitutions: "Vertretungsplan",
history: "Verlauf",
settings: "Einstellungen",
login: "Anmelden",
settings: {
main: "Einstellungen",
filtering: "Filter",
appearance: "Aussehen",
about: "Über",
},
},
settings: {
heading: {
@ -125,6 +136,7 @@ export const strings = {
"Wähle ein Farbschema aus, um die Farben dieser Anwendung anzupassen. Die Option 'Automatisch' wählt ein Farbschema basierend auf den Einstellungen deines Systems aus.",
},
other: "Andere",
back: "Zurück",
none: "Keine",
version: "Version",
theme: {

View File

@ -2,126 +2,71 @@
import ScrollableContainer from "@/components/scrollable-container.vue";
import PageCard from "@/components/settings/page-card.vue";
import {
classList,
classFilter,
possibleTimetableGroups,
timetableGroups,
theme,
} from "../store";
import { language } from "../i18n";
// eslint-disable-next-line no-undef
const gitHash = GITVERSION;
FilterIcon,
PaletteIcon,
InfoIcon,
ChevronLeft,
} from "lucide-vue-next";
</script>
<template>
<ScrollableContainer class="settings">
<!-- Filtering -->
<h2>{{ $t("settings.heading.filtering") }}</h2>
<p>{{ $t("settings.text.filtering") }}</p>
<select v-model="classFilter">
<option value="none">{{ $t("timetable.setup.prompt") }}</option>
<option
v-for="option in classList.length > 0 ? classList : [classFilter]"
:value="option"
:key="option"
>
{{ option }}
</option>
<option value="other">{{ $t("settings.other") }}</option>
</select>
<div class="spacer"></div>
<!-- Timetable Groups -->
<h2>{{ $t("settings.heading.timetableGroups") }}</h2>
<p>{{ $t("settings.text.timetableGroups") }}</p>
<select v-model="timetableGroups" multiple>
<option value="none">{{ $t("settings.none") }}</option>
<option
v-for="option in possibleTimetableGroups"
:value="option"
:key="option"
>
{{ option }}
</option>
</select>
<div class="spacer"></div>
<!-- Language -->
<h2>{{ $t("settings.heading.language") }}</h2>
<p>{{ $t("settings.text.language") }}</p>
<select v-model="language">
<option
v-for="locale in $i18n.availableLocales"
:key="`locale-${locale}`"
:value="locale"
>
{{ locale }}
</option>
</select>
<div class="spacer"></div>
<!-- Theme -->
<h2>{{ $t("settings.heading.theme") }}</h2>
<p>{{ $t("settings.text.theme") }}</p>
<select v-model="theme">
<option value="auto">{{ $t("settings.theme.auto") }}</option>
<option value="dark">{{ $t("settings.theme.dark") }}</option>
<option value="light">{{ $t("settings.theme.light") }}</option>
</select>
<div class="spacer"></div>
<!-- About -->
<h2>{{ $t("settings.heading.about") }}</h2>
<p>{{ $t("settings.text.about") }}</p>
<div class="spacer"></div>
<p class="gray">{{ $t("settings.version") }}: {{ gitHash }}</p>
<div class="wrapper" v-if="$route.name == 'title.settings.main'">
<div class="cards">
<PageCard
:name="$t('title.settings.filtering')"
:icon="FilterIcon"
route="settings/filtering"
/>
<PageCard
:name="$t('title.settings.appearance')"
:icon="PaletteIcon"
route="settings/appearance"
/>
<PageCard
:name="$t('title.settings.about')"
:icon="InfoIcon"
route="settings/about"
/>
</div>
</div>
<div class="subroute" v-else>
<RouterLink to="/settings" class="back">
<ChevronLeft />
<span>{{ $t("settings.back") }}</span>
</RouterLink>
<RouterView />
</div>
</ScrollableContainer>
</template>
<style scoped>
.settings {
padding: 15px 10px 0px 10px;
padding: 20px 10px 0px 10px;
}
h2 {
margin: 0px;
.wrapper {
flex-direction: column;
align-items: center;
display: flex;
}
p {
margin: 5px 0px;
.cards {
display: flex;
flex-direction: column;
width: 100%;
max-width: 500px;
gap: 10px;
}
select {
padding: 5px 0px;
margin: 5px 0px;
outline: none;
border: 2px solid var(--element-border-input);
border-radius: 5px;
background-color: var(--element-color);
transition: 0.2s;
transition-property: background-color border-color;
color: var(--text-color);
}
select:hover {
background-color: var(--element-color-hover);
}
select:focus {
border-color: var(--element-border-focus);
}
select[multiple] {
min-width: 90px;
}
.spacer {
display: block;
margin: 15px 0;
}
.gray {
opacity: 0.5;
.back {
display: flex;
align-items: center;
cursor: pointer;
user-select: none;
width: max-content;
text-decoration: unset;
color: unset;
margin-bottom: 8px;
}
</style>

View File

@ -0,0 +1,37 @@
<script setup>
// eslint-disable-next-line no-undef
const gitHash = GITVERSION;
// eslint-disable-next-line no-undef
const gitRepo = GITURL;
</script>
<template>
<h2>{{ $t("settings.heading.about") }}</h2>
<p>{{ $t("settings.text.about") }}</p>
<div class="spacer"></div>
<p class="gray">
{{ $t("settings.version") }}:
<a :href="gitRepo" target="_blank">{{ gitHash }}</a>
</p>
</template>
<style>
h2 {
margin: 0px;
}
p {
margin: 5px 0px;
line-height: 140%;
}
.gray {
opacity: 0.5;
}
a {
text-decoration-style: dotted;
text-underline-offset: 3px;
color: unset;
}
</style>

View File

@ -0,0 +1,28 @@
<script setup>
import { theme } from "@/store";
import RadioButtons from "@/components/settings/radio-buttons.vue";
</script>
<template>
<h2>{{ $t("settings.heading.theme") }}</h2>
<p>{{ $t("settings.text.theme") }}</p>
<RadioButtons
:options="[
$t('settings.theme.auto'),
$t('settings.theme.light'),
$t('settings.theme.dark'),
]"
:values="['auto', 'light', 'dark']"
v-model="theme"
/>
</template>
<style scoped>
h2 {
margin: 0px;
}
p {
margin: 5px 0px;
}
</style>

View File

@ -0,0 +1,24 @@
<script setup>
import { classList, classFilter } from "@/store";
import RadioButtons from "@/components/settings/radio-buttons.vue";
</script>
<template>
<h2>{{ $t("settings.heading.filtering") }}</h2>
<p>{{ $t("settings.text.filtering") }}</p>
<RadioButtons
:options="['None', ...classList]"
:values="['none', ...classList]"
v-model="classFilter"
/>
</template>
<style scoped>
h2 {
margin: 0px;
}
p {
margin: 5px 0px;
}
</style>

View File

@ -11,6 +11,9 @@ export default defineConfig({
GITVERSION: JSON.stringify(
child.execSync("git rev-parse --short HEAD").toString()
),
GITURL: JSON.stringify(
child.execSync("git config --get remote.origin.url").toString()
),
},
plugins: [
vue(),