You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
210 lines
6.4 KiB
Vue
210 lines
6.4 KiB
Vue
<template>
|
|
<div class="Home">
|
|
<VDatePicker class="dateSelector" v-model.range="range" mode="date" :rules="datePickerRules" timezone="utc"
|
|
:input-debounce="50" />
|
|
<CenterSelector class="centerSelector" @selectionChanged="centerSelectionChanged"></CenterSelector>
|
|
<TrainerSelector class="trainerSelector" v-bind:centers="centers" @selectionChanged="trainerSelectionChanged">
|
|
</TrainerSelector>
|
|
<div class="trainers">
|
|
<div class="trainer" v-for="trainer in trainersWithDateGroupedTimeslots" :key="trainer.id">
|
|
<div class="top">
|
|
{{ trainer.first_name }} {{ trainer.last_name }}
|
|
</div>
|
|
<div class="dates">
|
|
<div class="date" v-for="date in trainer.dates">
|
|
{{ date.date }}
|
|
<div class="timeslots">
|
|
<div class="timeslot" v-for="timeslot in date.timeslots"
|
|
@click="createOrder(trainer, timeslot)">
|
|
{{ formatTime(timeslot.startDate) }} - {{ formatTime(timeslot.endDate) }}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- {{ trainersWithTimeslots }} -->
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.Home {
|
|
display: grid;
|
|
grid-template-columns: 1fr 3fr 1fr;
|
|
grid-template-rows: auto auto auto;
|
|
grid-column-gap: 50px;
|
|
grid-row-gap: 30px;
|
|
padding: 50px;
|
|
}
|
|
|
|
.dateSelector {
|
|
grid-area: 1 / 1 / 2 / 2;
|
|
}
|
|
|
|
.centerSelector {
|
|
grid-area: 1 / 3 / 2 / 4;
|
|
}
|
|
|
|
.trainerSelector {
|
|
grid-area: 2 / 3 / 3 / 4;
|
|
}
|
|
|
|
.trainers {
|
|
grid-area: 1 / 2 / 4 / 3;
|
|
display: flex;
|
|
gap: 10px;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.trainer .top {
|
|
border-bottom: 1px solid #cbd5e1;
|
|
padding: 20px;
|
|
font-size: large;
|
|
}
|
|
|
|
.trainer {
|
|
border: 1px solid #cbd5e1;
|
|
border-radius: 0.5rem;
|
|
}
|
|
|
|
.dates {
|
|
padding: 20px;
|
|
}
|
|
|
|
.timeslots {
|
|
border-top: 1px solid #cbd5e1;
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.timeslot {
|
|
padding: 5px;
|
|
border: 1px solid #cbd5e1;
|
|
margin: 5px;
|
|
cursor: pointer;
|
|
}
|
|
|
|
.timeslot:hover {
|
|
border-color: black;
|
|
}
|
|
</style>
|
|
|
|
<script lang="ts">
|
|
import CenterSelector from '@/components/CenterSelector.vue';
|
|
import TrainerSelector from '@/components/TrainerSelector.vue';
|
|
import type { Timeslot } from '@/interfaces/timeslot';
|
|
import type { DateGroupedTimeslots, DateGroupedTimeslotsList, TrainerWithDateGroupedTimeslots, TrainerWithTimeslots } from '@/interfaces/trainerWithTimeslots';
|
|
import dayjs from 'dayjs';
|
|
import LocalizedFormat from "dayjs/plugin/localizedFormat"
|
|
import utc from "dayjs/plugin/utc"
|
|
import { } from "dayjs/locale/da";
|
|
import type { Trainer } from '@/interfaces/trainer';
|
|
|
|
dayjs.extend(LocalizedFormat);
|
|
dayjs.extend(utc);
|
|
|
|
export default {
|
|
name: "Home",
|
|
components: {
|
|
CenterSelector,
|
|
TrainerSelector
|
|
},
|
|
data() {
|
|
return {
|
|
range: {
|
|
start: new Date(),
|
|
end: new Date()
|
|
},
|
|
datePickerRules: {
|
|
hours: 0,
|
|
minutes: 0,
|
|
seconds: 0,
|
|
milliseconds: 0
|
|
},
|
|
centers: [] as number[],
|
|
trainers: [] as number[],
|
|
trainersWithTimeslots: [] as TrainerWithTimeslots[]
|
|
}
|
|
},
|
|
computed: {
|
|
trainersWithDateGroupedTimeslots() {
|
|
return this.trainersWithTimeslots.map(trainer => {
|
|
const dates: DateGroupedTimeslotsList = {};
|
|
trainer.timeslots.forEach(timeslot => {
|
|
const day: string = new Date(timeslot.startDate).toISOString().split("T")[0];
|
|
if (!dates[day]) {
|
|
dates[day] = {
|
|
date: this.formatDate(new Date(day)),
|
|
timeslots: [] as Timeslot[]
|
|
};
|
|
}
|
|
dates[day].timeslots.push(timeslot);
|
|
});
|
|
const trainerWithDateGroupedTimeslots: TrainerWithDateGroupedTimeslots = { ...trainer, dates };
|
|
return trainerWithDateGroupedTimeslots;
|
|
});
|
|
}
|
|
},
|
|
watch: {
|
|
centers: {
|
|
handler() {
|
|
this.fetchTimeslots();
|
|
},
|
|
deep: true
|
|
},
|
|
trainers: {
|
|
handler() {
|
|
this.fetchTimeslots();
|
|
},
|
|
deep: true
|
|
},
|
|
range: {
|
|
handler() {
|
|
this.fetchTimeslots();
|
|
},
|
|
deep: true
|
|
}
|
|
},
|
|
methods: {
|
|
createOrder(trainer: Trainer, timeslot: Timeslot) {
|
|
this.$router.push({
|
|
path: "/createOrder", query: {
|
|
trainer: trainer.id,
|
|
startDate: new Date(timeslot.startDate).toISOString(),
|
|
endDate: new Date(timeslot.endDate).toISOString()
|
|
}
|
|
});
|
|
},
|
|
formatDate(date: Date): string {
|
|
let output: string = dayjs(date).locale("da").format("dddd D. MMM YYYY");
|
|
let outputStringArray = output.split("");
|
|
outputStringArray[0] = outputStringArray[0].toLocaleUpperCase();
|
|
output = outputStringArray.join("");
|
|
|
|
return output;
|
|
},
|
|
formatTime(date: Date): string {
|
|
let output: string = dayjs(date).utcOffset(0).locale("da").format("HH:mm");
|
|
|
|
return output;
|
|
},
|
|
centerSelectionChanged(selection: number[]) {
|
|
this.centers = selection;
|
|
},
|
|
trainerSelectionChanged(selection: number[]) {
|
|
this.trainers = selection;
|
|
},
|
|
async fetchTimeslots() {
|
|
const centerFilters = this.centers.map(c => `center=${c}`);
|
|
const trainerFilters = this.trainers.map(c => `trainer=${c}`);
|
|
const startDateFilter = `startDate=${this.range.start.toISOString()}`;
|
|
const endDateFilter = `endDate=${this.range.end.toISOString()}`;
|
|
const filters = [...centerFilters, ...trainerFilters, startDateFilter, endDateFilter].join("&");
|
|
const res = await fetch(`${import.meta.env.VITE_BASE_API_URL}/timeslot?${filters}`);
|
|
if (res.status === 200) {
|
|
this.trainersWithTimeslots = await res.json();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</script> |