♻️ Refactor frontend
This commit is contained in:
@ -1 +1 @@
|
|||||||
<svg id="screenshot" viewBox="0 0 512 511.9999999999999" width="512" height="511.9999999999999" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="-webkit-print-color-adjust: exact;"><g id="shape-881e7b40-cbde-11ec-b1da-f1565ad79f08" clip-path="url(#frame-clip-881e7b40-cbde-11ec-b1da-f1565ad79f08-c54471c0-cbe1-11ec-98cd-6dc23a2e2ce9)"><defs><clipPath id="frame-clip-881e7b40-cbde-11ec-b1da-f1565ad79f08-c54471c0-cbe1-11ec-98cd-6dc23a2e2ce9" class="frame-clip"><rect x="0" y="0" width="512" height="512"/></clipPath></defs><g id="shape-8db28d31-cbde-11ec-b1da-f1565ad79f08"><rect rx="50" ry="50" x="0" y="0" transform="matrix(1,0,0,1,0,0)" width="512" height="512" style="fill: rgb(53, 53, 53); fill-opacity: 1;"/></g><g id="shape-90cb4970-cbdf-11ec-b1da-f1565ad79f08"><g id="shape-40af0440-cbdf-11ec-b1da-f1565ad79f08"><g id="shape-2d3a8c40-cbdf-11ec-b1da-f1565ad79f08"><g id="shape-2d3a8c41-cbdf-11ec-b1da-f1565ad79f08"><rect rx="5" ry="5" x="147.25" y="340.56163194444434" transform="matrix(1,0,0,1,0,0)" width="319" height="77" style="fill: rgb(20, 88, 126); fill-opacity: 1;"/></g><g id="shape-2d3a8c42-cbdf-11ec-b1da-f1565ad79f08"><rect rx="5" ry="5" x="46.25" y="340.56163194444434" transform="matrix(1,0,0,1,0,0)" width="77" height="77" style="fill: rgb(20, 88, 126); fill-opacity: 1;"/></g></g></g><g id="shape-7a8b2e50-cbdf-11ec-b1da-f1565ad79f08"><g id="shape-7a8b2e51-cbdf-11ec-b1da-f1565ad79f08"><g id="shape-7a8b2e52-cbdf-11ec-b1da-f1565ad79f08"><rect rx="5" ry="5" x="146.75" y="216.36979166666652" transform="matrix(1,0,0,1,0,0)" width="319" height="77" style="fill: rgb(20, 88, 126); fill-opacity: 1;"/></g><g id="shape-7a8b2e53-cbdf-11ec-b1da-f1565ad79f08"><rect rx="5" ry="5" x="45.75" y="216.36979166666652" transform="matrix(1,0,0,1,0,0)" width="77" height="77" style="fill: rgb(20, 88, 126); fill-opacity: 1;"/></g></g></g><g id="shape-7d7a37f0-cbdf-11ec-b1da-f1565ad79f08"><g id="shape-7d7a37f1-cbdf-11ec-b1da-f1565ad79f08"><g id="shape-7d7a37f2-cbdf-11ec-b1da-f1565ad79f08"><rect rx="5" ry="5" x="146.75" y="93.68489583333314" transform="matrix(1,0,0,1,0,0)" width="319" height="77" style="fill: rgb(125, 43, 45); fill-opacity: 1;"/></g><g id="shape-7d7a37f3-cbdf-11ec-b1da-f1565ad79f08"><rect rx="5" ry="5" x="45.75" y="93.68489583333314" transform="matrix(1,0,0,1,0,0)" width="77" height="77" style="fill: rgb(125, 43, 45); fill-opacity: 1;"/></g></g></g></g></g></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" style="-webkit-print-color-adjust:exact" viewBox="0 0 512 512"><g clip-path="url(#a)"><rect rx="50" ry="50" width="512" height="512" fill="#353535"/><rect rx="5" ry="5" x="147" y="341" width="319" height="77" fill="#14587e"/><rect rx="5" ry="5" x="46" y="341" width="77" height="77" fill="#14587e"/><rect rx="5" ry="5" x="147" y="216" width="319" height="77" fill="#14587e"/><rect rx="5" ry="5" x="46" y="216" width="77" height="77" fill="#14587e"/><rect rx="5" ry="5" x="147" y="94" width="319" height="77" fill="#7d2b2d"/><rect rx="5" ry="5" x="46" y="94" width="77" height="77" fill="#7d2b2d"/></g></svg>
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 647 B |
@ -37,8 +37,8 @@ const isDataView = computed(() => {
|
|||||||
<DateSelector v-show="isDataView" />
|
<DateSelector v-show="isDataView" />
|
||||||
<RouterView />
|
<RouterView />
|
||||||
</main>
|
</main>
|
||||||
|
<BottomNavbar v-show="$route.name != 'title.login'" />
|
||||||
</div>
|
</div>
|
||||||
<BottomNavbar v-show="$route.name != 'title.login'" />
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -32,18 +32,16 @@ import { RouterLink } from "vue-router";
|
|||||||
.bottomnav {
|
.bottomnav {
|
||||||
width: calc(90% - 40px);
|
width: calc(90% - 40px);
|
||||||
height: 50px;
|
height: 50px;
|
||||||
background-color: var(--bottomnav-color);
|
max-width: 800px;
|
||||||
border-radius: 10px;
|
padding: 0px 20px;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 20px;
|
bottom: 20px;
|
||||||
left: 50%;
|
|
||||||
transform: translateX(-50%);
|
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-auto-columns: 1fr;
|
grid-auto-columns: 1fr;
|
||||||
grid-auto-flow: column;
|
grid-auto-flow: column;
|
||||||
padding: 0px 20px;
|
|
||||||
max-width: 800px;
|
|
||||||
z-index: 100;
|
z-index: 100;
|
||||||
|
border-radius: 10px;
|
||||||
|
background-color: var(--bottomnav-color);
|
||||||
box-shadow: var(--bottomnav-shadow);
|
box-shadow: var(--bottomnav-shadow);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
import { selectedDate, selectedDay, changeDay, changeDate } from "../store";
|
import { selectedDate, selectedDay, changeDay, changeDate } from "../store";
|
||||||
import ArrowIcon from "./icons/arrow-icon.vue";
|
import ArrowIcon from "./icons/arrow-icon.vue";
|
||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
|
import { getDateSkippingWeekend } from "../util";
|
||||||
|
|
||||||
const dayNames = [
|
const dayNames = [
|
||||||
"days.sunday",
|
"days.sunday",
|
||||||
@ -16,15 +17,15 @@ const dayNames = [
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="selector">
|
<div class="selector">
|
||||||
<ArrowIcon @click="() => (changeDay -= 1)" />
|
<ArrowIcon @click="changeDay--" />
|
||||||
<span class="day" @click="changeDate = dayjs().toDate()">
|
<span
|
||||||
|
class="day"
|
||||||
|
@click="changeDate = getDateSkippingWeekend(dayjs().toDate(), true)"
|
||||||
|
>
|
||||||
{{ $t(dayNames[selectedDay + 1]) }},
|
{{ $t(dayNames[selectedDay + 1]) }},
|
||||||
{{ dayjs(selectedDate).format("DD.MM.YYYY") }}
|
{{ dayjs(selectedDate).format("DD.MM.YYYY") }}
|
||||||
</span>
|
</span>
|
||||||
<ArrowIcon
|
<ArrowIcon style="transform: rotate(180deg)" @click="changeDay++" />
|
||||||
style="transform: rotate(180deg)"
|
|
||||||
@click="() => (changeDay += 1)"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ import { selectedDate, changeDate, changeDay } from "../store";
|
|||||||
import { Swiper, SwiperSlide } from "swiper/vue";
|
import { Swiper, SwiperSlide } from "swiper/vue";
|
||||||
import { Virtual } from "swiper";
|
import { Virtual } from "swiper";
|
||||||
import { ref, watch } from "vue";
|
import { ref, watch } from "vue";
|
||||||
|
import { getDateSkippingWeekend } from "../util";
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
element: {
|
element: {
|
||||||
@ -26,20 +27,25 @@ const loadedDates = ref([
|
|||||||
|
|
||||||
// Load left or right date on slide change
|
// Load left or right date on slide change
|
||||||
function slideChange(swiper) {
|
function slideChange(swiper) {
|
||||||
|
const activeSlide = swiper.activeIndex;
|
||||||
// Only trigger data refresh if date is different
|
// Only trigger data refresh if date is different
|
||||||
const newSelectedDate = loadedDates.value[swiper.activeIndex];
|
const newSelectedDate = loadedDates.value[activeSlide];
|
||||||
if (selectedDate.value.getTime() != newSelectedDate.getTime())
|
if (selectedDate.value.getTime() != newSelectedDate.getTime())
|
||||||
selectedDate.value = newSelectedDate;
|
selectedDate.value = newSelectedDate;
|
||||||
|
// Check if current slide is the last one
|
||||||
const activeSlide = swiper.activeIndex;
|
|
||||||
if (activeSlide == loadedDates.value.length - 1) {
|
if (activeSlide == loadedDates.value.length - 1) {
|
||||||
const lastDate = loadedDates.value[loadedDates.value.length - 1];
|
// Append next day to loadedDates
|
||||||
|
const lastDate = loadedDates.value.at(-1);
|
||||||
loadedDates.value.push(getDateSkippingWeekend(calculateDate(lastDate, 1)));
|
loadedDates.value.push(getDateSkippingWeekend(calculateDate(lastDate, 1)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function slideChangeEnd(swiper) {
|
function slideChangeEnd(swiper) {
|
||||||
const activeSlide = swiper.activeIndex;
|
const activeSlide = swiper.activeIndex;
|
||||||
|
// Check if the current slide is the first one
|
||||||
if (activeSlide == 0) {
|
if (activeSlide == 0) {
|
||||||
|
// Prepend the previous day to loadedDates
|
||||||
|
// and slide to the next slide without transition
|
||||||
|
// so the same day as before is shown
|
||||||
const lastDate = loadedDates.value[0];
|
const lastDate = loadedDates.value[0];
|
||||||
loadedDates.value = [
|
loadedDates.value = [
|
||||||
getDateSkippingWeekend(calculateDate(lastDate, -1)),
|
getDateSkippingWeekend(calculateDate(lastDate, -1)),
|
||||||
@ -48,6 +54,12 @@ function slideChangeEnd(swiper) {
|
|||||||
swiper.slideTo(activeSlide + 1, 0);
|
swiper.slideTo(activeSlide + 1, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Also check if a day needs to be prepended
|
||||||
|
// if the user releases the card later because
|
||||||
|
// a different event is fired for that
|
||||||
|
function slideResetEnd(swiper) {
|
||||||
|
slideChangeEnd(swiper);
|
||||||
|
}
|
||||||
|
|
||||||
// Helper functions
|
// Helper functions
|
||||||
function calculateDate(date, offset) {
|
function calculateDate(date, offset) {
|
||||||
@ -60,6 +72,8 @@ function slideToDate(date) {
|
|||||||
const slide = calculateSlide(date);
|
const slide = calculateSlide(date);
|
||||||
if (slide != -1) swiperElement.value.slideTo(slide);
|
if (slide != -1) swiperElement.value.slideTo(slide);
|
||||||
else {
|
else {
|
||||||
|
// If the slide is not loaded yet reset
|
||||||
|
// loaded dates and load that date
|
||||||
loadedDates.value = [calculateDate(date, -1), date, calculateDate(date, 1)];
|
loadedDates.value = [calculateDate(date, -1), date, calculateDate(date, 1)];
|
||||||
swiperElement.value.slideTo(1, 0);
|
swiperElement.value.slideTo(1, 0);
|
||||||
}
|
}
|
||||||
@ -76,16 +90,6 @@ watch(changeDay, (change) => {
|
|||||||
watch(changeDate, (date) => {
|
watch(changeDate, (date) => {
|
||||||
slideToDate(new Date(date.setUTCHours(0, 0, 0, 0)));
|
slideToDate(new Date(date.setUTCHours(0, 0, 0, 0)));
|
||||||
});
|
});
|
||||||
|
|
||||||
function getDateSkippingWeekend(date) {
|
|
||||||
var calculatedDate = date;
|
|
||||||
// Skip weekend
|
|
||||||
if (calculatedDate.getDay() == 6)
|
|
||||||
calculatedDate = new Date(calculatedDate.getTime() + 86400000 * 2);
|
|
||||||
if (calculatedDate.getDay() == 0)
|
|
||||||
calculatedDate = new Date(calculatedDate.getTime() - 86400000 * 2);
|
|
||||||
return calculatedDate;
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -96,6 +100,7 @@ function getDateSkippingWeekend(date) {
|
|||||||
:initial-slide="1"
|
:initial-slide="1"
|
||||||
:virtual="true"
|
:virtual="true"
|
||||||
@slide-change-transition-end="slideChangeEnd"
|
@slide-change-transition-end="slideChangeEnd"
|
||||||
|
@slide-reset-transition-end="slideResetEnd"
|
||||||
@slide-change="slideChange"
|
@slide-change="slideChange"
|
||||||
@swiper="setSwiper"
|
@swiper="setSwiper"
|
||||||
>
|
>
|
||||||
|
@ -15,6 +15,7 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const loadingElement = ref(null);
|
||||||
const computedProgress = ref(0);
|
const computedProgress = ref(0);
|
||||||
const visible = ref(true);
|
const visible = ref(true);
|
||||||
var hideTimeout;
|
var hideTimeout;
|
||||||
@ -38,6 +39,7 @@ watch(
|
|||||||
hideTimeout = setTimeout(() => {
|
hideTimeout = setTimeout(() => {
|
||||||
if (!props.active) {
|
if (!props.active) {
|
||||||
visible.value = false;
|
visible.value = false;
|
||||||
|
loadingElement.value.classList.remove("transition");
|
||||||
computedProgress.value = 0;
|
computedProgress.value = 0;
|
||||||
}
|
}
|
||||||
}, 500);
|
}, 500);
|
||||||
@ -48,7 +50,8 @@ watch(
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
class="bar"
|
class="bar transition"
|
||||||
|
ref="loadingElement"
|
||||||
:class="(active ? 'active' : '') + (error ? ' error' : '')"
|
:class="(active ? 'active' : '') + (error ? ' error' : '')"
|
||||||
:style="`width: ${computedProgress * 100}%;`"
|
:style="`width: ${computedProgress * 100}%;`"
|
||||||
></div>
|
></div>
|
||||||
@ -57,9 +60,12 @@ watch(
|
|||||||
<style scoped>
|
<style scoped>
|
||||||
.bar {
|
.bar {
|
||||||
width: 0%;
|
width: 0%;
|
||||||
opacity: 0;
|
opacity: 0.2;
|
||||||
background-color: var(--loader-color);
|
background-color: var(--loader-color);
|
||||||
height: 5px;
|
height: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bar.transition {
|
||||||
transition: 0.5s;
|
transition: 0.5s;
|
||||||
transition-property: width opacity background-color;
|
transition-property: width opacity background-color;
|
||||||
}
|
}
|
||||||
@ -67,6 +73,7 @@ watch(
|
|||||||
.bar.active {
|
.bar.active {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bar.error {
|
.bar.error {
|
||||||
background-color: var(--loader-error-color);
|
background-color: var(--loader-error-color);
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,6 @@ const substitutions = computed(() => {
|
|||||||
<style scoped>
|
<style scoped>
|
||||||
.substitution {
|
.substitution {
|
||||||
display: grid;
|
display: grid;
|
||||||
background-color: var(--substitution-background-unchanged);
|
|
||||||
min-height: 50px;
|
min-height: 50px;
|
||||||
border-radius: 11px;
|
border-radius: 11px;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
|
@ -41,7 +41,7 @@ defineProps({
|
|||||||
width: max-content;
|
width: max-content;
|
||||||
height: max-content;
|
height: max-content;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
z-index: 10;
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.content {
|
.content {
|
||||||
@ -76,7 +76,7 @@ span.description {
|
|||||||
|
|
||||||
select {
|
select {
|
||||||
margin-top: 15px;
|
margin-top: 15px;
|
||||||
padding: 5px 0px;
|
padding: 5px 5px;
|
||||||
outline: none;
|
outline: none;
|
||||||
border: 2px solid var(--element-border-input);
|
border: 2px solid var(--element-border-input);
|
||||||
border-radius: 5px;
|
border-radius: 5px;
|
@ -1,5 +1,4 @@
|
|||||||
import { computed } from "@vue/reactivity";
|
import { ref, watch, computed } from "vue";
|
||||||
import { ref, watch } from "vue";
|
|
||||||
import router from "./router";
|
import router from "./router";
|
||||||
import i18n from "./main";
|
import i18n from "./main";
|
||||||
|
|
||||||
|
20
src/util.js
20
src/util.js
@ -1,11 +1,4 @@
|
|||||||
import {
|
import { classFilter } from "./store";
|
||||||
classFilter,
|
|
||||||
fetchSubstitutions,
|
|
||||||
fetchHistory,
|
|
||||||
loading,
|
|
||||||
selectedDate,
|
|
||||||
} from "./store";
|
|
||||||
import dayjs from "dayjs";
|
|
||||||
|
|
||||||
export function getSubstitutionText(substitution) {
|
export function getSubstitutionText(substitution) {
|
||||||
const includeClass = !classFilter.value || classFilter.value == "none";
|
const includeClass = !classFilter.value || classFilter.value == "none";
|
||||||
@ -29,3 +22,14 @@ export function getSubstitutionColor(substitution) {
|
|||||||
return "background-color: var(--substitution-background-cancellation);";
|
return "background-color: var(--substitution-background-cancellation);";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getDateSkippingWeekend(date, onlyNext) {
|
||||||
|
// Go from saturday to next monday
|
||||||
|
if (date.getDay() == 6) return new Date(date.getTime() + 86400000 * 2);
|
||||||
|
// Go from sunday to last friday or next monday
|
||||||
|
if (date.getDay() == 0)
|
||||||
|
return new Date(
|
||||||
|
onlyNext ? date.getTime() + 86400000 * 1 : date.getTime() - 86400000 * 2
|
||||||
|
);
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.login {
|
.login {
|
||||||
padding: 90px 10px 80px 10px;
|
padding: 50px 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.login {
|
.login {
|
||||||
@ -31,13 +31,18 @@ form {
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
|
width: 250px;
|
||||||
|
}
|
||||||
|
|
||||||
|
form * {
|
||||||
|
height: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
input {
|
input {
|
||||||
border: 0px;
|
border: 0px;
|
||||||
outline: none;
|
outline: none;
|
||||||
background-color: var(--element-color);
|
background-color: var(--element-color);
|
||||||
padding: 5px 5px;
|
padding: 0px 5px;
|
||||||
font-size: 15px;
|
font-size: 15px;
|
||||||
margin: 5px 0;
|
margin: 5px 0;
|
||||||
border: 2px solid var(--element-border-input);
|
border: 2px solid var(--element-border-input);
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import TimetableSetup from "../components/initial-setup.vue";
|
import TimetableSetup from "../components/timetable-setup.vue";
|
||||||
import { classList, classFilter } from "../store";
|
import { classList, classFilter } from "../store";
|
||||||
import DayCarousel from "../components/day-carousel.vue";
|
import DayCarousel from "../components/day-carousel.vue";
|
||||||
import TimetableList from "../components/timetable-list.vue";
|
import TimetableList from "../components/timetable-list.vue";
|
||||||
|
Reference in New Issue
Block a user