Added trainer create/delete weeklytimeslot view and endpoint
continuous-integration/drone/push Build is passing Details

main
Filip Borum Poulsen 3 years ago
parent 5eacfe7167
commit 0f8d73b4cc

@ -1,4 +1,11 @@
export interface Timeslot {
startDate: Date
endDate: Date
}
export interface WeeklyTimeslot {
id: number
day_of_week: number
start_time: string
end_time: string
}

@ -24,6 +24,14 @@ const router = createRouter({
// this generates a separate chunk (About.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import('../views/Register.vue')
},
{
path: '/trainer/schedule',
name: 'TrainerSchedule',
// route level code-splitting
// this generates a separate chunk (About.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import('../views/trainer/Schedule.vue')
}
]
})

@ -0,0 +1,201 @@
<template>
<div class="Schedule">
<div class="days">
<div class="day" v-for="day in days">
{{ day.name }}
<div class="timeslots">
<div class="timeslot" v-for="timeslot in day.timeslots" @click="deleteTimeslot(timeslot.id)">
{{ timeslot.start_time.split(":").slice(0, 2).join(":") }} -
{{ timeslot.end_time.split(":").slice(0, 2).join(":") }}
</div>
</div>
<div class="newTimeslot">
<label class="startLabel" for="startHour">Start</label>
<input class="startHour" type="number" placeholder="HH" name="startHour" min="0" max="23"
v-model="newDayInput[day.day_of_week - 1].startHour" />
<input class="startMinute" type="number" placeholder="MM" name="startMinute" min="0" max="59"
v-model="newDayInput[day.day_of_week - 1].startMinute" />
<label class="endLabel" for="endHour">Slut</label>
<input class="endHour" type="number" placeholder="HH" name="endHour" min="0" max="23"
v-model="newDayInput[day.day_of_week - 1].endHour" />
<input class="endMinute" type="number" placeholder="MM" name="endMinute" min="0" max="59"
v-model="newDayInput[day.day_of_week - 1].endMinute" />
<input class="submit" type="submit" name="submit" value="Submit" @click="addTimeslot(day.day_of_week)">
</div>
</div>
</div>
</div>
</template>
<style scoped>
.Schedule {
margin: 30px;
border: 1px solid #cbd5e1;
border-radius: 0.5rem;
padding: 20px;
}
.newTimeslot {
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-template-rows: repeat(5, 1fr);
grid-column-gap: 0px;
grid-row-gap: 0px;
width: fit-content;
margin: auto;
font-size: 0.9em;
}
.startLabel {
grid-area: 1 / 1 / 2 / 3;
}
.startHour {
grid-area: 2 / 1 / 3 / 2;
}
.startMinute {
grid-area: 2 / 2 / 3 / 3;
}
.endLabel {
grid-area: 3 / 1 / 4 / 3;
}
.endHour {
grid-area: 4 / 1 / 5 / 2;
}
.endMinute {
grid-area: 4 / 2 / 5 / 3;
}
.submit {
grid-area: 5 / 1 / 6 / 3;
}
.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: lightcoral;
}
</style>
<script lang="ts">
import dayjs from 'dayjs';
import LocalizedFormat from "dayjs/plugin/localizedFormat"
import utc from "dayjs/plugin/utc"
import { } from "dayjs/locale/da";
import type { WeeklyTimeslot } from '@/interfaces/timeslot';
dayjs.extend(LocalizedFormat);
dayjs.extend(utc);
interface Day {
name: string
timeslots: WeeklyTimeslot[]
day_of_week: number
}
interface NewDayInput {
startHour: number | undefined
startMinute: number | undefined
endHour: number | undefined
endMinute: number | undefined
}
export default {
name: "TrainerSchedule",
data() {
return {
newDayInput: Array.from({ length: 7 }, () => {
return {
startHour: undefined,
startMinute: undefined,
endHour: undefined,
endMinute: undefined
}
}) as NewDayInput[],
weeklyTimeslots: [] as WeeklyTimeslot[]
}
},
async mounted() {
this.fetchSchedule();
},
computed: {
days(): Day[] {
const days: Day[] = [
{ day_of_week: 1, name: "Mandag", timeslots: [] },
{ day_of_week: 2, name: "Tirsdag", timeslots: [] },
{ day_of_week: 3, name: "Onsdag", timeslots: [] },
{ day_of_week: 4, name: "Torsdag", timeslots: [] },
{ day_of_week: 5, name: "Fredag", timeslots: [] },
{ day_of_week: 6, name: "Lørdag", timeslots: [] },
{ day_of_week: 7, name: "Søndag", timeslots: [] }
];
this.weeklyTimeslots.forEach((timeslot: WeeklyTimeslot) => {
days[timeslot.day_of_week - 1].timeslots.push(timeslot);
});
return days;
}
},
methods: {
async addTimeslot(day_of_week: number) {
console.log(this.newDayInput[day_of_week - 1]);
const input: NewDayInput = this.newDayInput[day_of_week - 1];
const start_time = `${input.startHour}:${input.startMinute ? input.startMinute : 0}`;
const end_time = `${input.endHour}:${input.endMinute ? input.endMinute : 0}`;
const res = await fetch(`${import.meta.env.VITE_BASE_API_URL}/trainer/timeslot/`, {
credentials: import.meta.env.DEV ? "include" : undefined,
method: "POST",
body: JSON.stringify({
day_of_week,
start_time,
end_time
}),
headers: {
"Content-Type": "Application/json"
}
});
if (res.status === 401 || res.status === 403) {
this.$router.push({ path: "/login", query: { ref: this.$route.path } });
}
this.fetchSchedule();
},
async deleteTimeslot(id: number) {
const res = await fetch(`${import.meta.env.VITE_BASE_API_URL}/trainer/timeslot/${id}`, {
credentials: import.meta.env.DEV ? "include" : undefined,
method: "DELETE"
});
if (res.status === 401 || res.status === 403) {
this.$router.push({ path: "/login", query: { ref: this.$route.path } });
}
this.fetchSchedule();
},
async fetchSchedule() {
// TODO: remove these credentials
const res = await fetch(`${import.meta.env.VITE_BASE_API_URL}/trainer/timeslot`, import.meta.env.DEV ? { credentials: "include" } : {});
if (res.status === 200) {
this.weeklyTimeslots = await res.json();
} else if (res.status === 401 || res.status === 403) {
this.$router.push({ path: "/login", query: { ref: this.$route.path } });
}
}
}
}
</script>

@ -4,6 +4,7 @@ export interface Timeslot {
}
export interface WeeklyTimeslot {
id: number
day_of_week: number
start_time: string
end_time: string

@ -5,7 +5,7 @@ const router: Router = express.Router();
import login from "./login"
import register from "./register"
import center from "./center";
import trainer from "./trainer";
import trainer from "./trainer/index";
import timeslot from "./timeslot";
import order from "./order";

@ -80,6 +80,7 @@ router.get("/timeslot", async (req: Request, res: Response) => {
'center_name',centers.name
) as trainer,
json_agg(json_build_object(
'id',weekly_timeslots.id,
'day_of_week',day_of_week,
'start_time',start_time,
'end_time',end_time)

@ -0,0 +1,10 @@
import express, { Express, Router } from "express";
const router: Router = express.Router();
import trainer from "./trainer";
import weeklyTimeslot from "./weeklyTimeslot";
router.use(weeklyTimeslot);
router.use(trainer);
export default router;

@ -1,12 +1,12 @@
import express, { Router, Request, Response } from "express";
import Joi from "joi"
import { client } from "../db";
import { client } from "../../db";
import { DatabaseError } from "pg";
import Trainer from "../interfaces/trainer";
import { AdminAuth } from "../middlewares/auth";
import { trainerExists } from "../middlewares/trainer";
import { idSchema } from "../schemas";
import Trainer from "../../interfaces/trainer";
import { AdminAuth } from "../../middlewares/auth";
import { trainerExists } from "../../middlewares/trainer";
import { idSchema } from "../../schemas";
const router: Router = express.Router();

@ -0,0 +1,109 @@
import express, { Router, Response } from "express";
import Joi from "joi"
import { client } from "../../db";
import { DatabaseError } from "pg";
import { TrainerAuth } from "../../middlewares/auth";
import { timeSchema } from "../../schemas";
import { AuthedRequest } from "../../interfaces/auth";
import { WeeklyTimeslot } from "../../interfaces/timeslot";
const router: Router = express.Router();
async function getTrainerIdFromUserId(userId: number | undefined): Promise<number | undefined> {
const trainerLookupResult = await client.query(`
SELECT id FROM trainers
WHERE user_id = $1;
`, [
userId
]);
return trainerLookupResult.rows[0]?.id;
}
router.get("/trainer/timeslot", TrainerAuth, async (req: AuthedRequest, res: Response) => {
try {
const trainerId: number | undefined = await getTrainerIdFromUserId(req.user?.userId);
if (trainerId === undefined)
return res.sendStatus(403);
const databaseResult = await client.query(`
SELECT id, day_of_week, start_time, end_time FROM weekly_timeslots
WHERE trainer_id = $1;
`, [
trainerId
]);
const timeslots: WeeklyTimeslot[] = databaseResult.rows;
return res.status(200).send(timeslots);
} catch (error: DatabaseError | Error | any) {
console.error(error);
return res.sendStatus(500);
}
})
const weeklyTimeslotSchema = Joi.object({
day_of_week: Joi.number().integer().min(1).max(7).required(),
start_time: timeSchema.required(),
end_time: timeSchema.required()
});
router.post("/trainer/timeslot", TrainerAuth, async (req: AuthedRequest, res: Response) => {
try {
const trainerId: number | undefined = await getTrainerIdFromUserId(req.user?.userId);
if (trainerId === undefined)
return res.sendStatus(403);
const validation = weeklyTimeslotSchema.validate(req.body, { abortEarly: false });
if (validation.error !== undefined) {
return res.status(400).send(validation.error.details);
}
const weeklyTimeslot: WeeklyTimeslot = validation.value;
const databaseResult = await client.query(`
INSERT INTO weekly_timeslots (trainer_id, day_of_week, start_time, end_time)
VALUES ($1, $2, $3, $4)
RETURNING *;
`, [
trainerId,
weeklyTimeslot.day_of_week,
weeklyTimeslot.start_time,
weeklyTimeslot.end_time
]);
const timeslots: WeeklyTimeslot = databaseResult.rows[0];
return res.status(200).send(timeslots);
} catch (error: DatabaseError | Error | any) {
console.error(error);
return res.sendStatus(500);
}
})
router.delete("/trainer/timeslot/:id", TrainerAuth, async (req: AuthedRequest, res: Response) => {
try {
const trainerId: number | undefined = await getTrainerIdFromUserId(req.user?.userId);
if (trainerId === undefined)
return res.sendStatus(403);
const databaseResult = await client.query(`
DELETE FROM weekly_timeslots
WHERE trainer_id = $1
AND id = $2;
`, [
trainerId,
req.params.id
]);
if (databaseResult.rowCount < 1)
return res.sendStatus(404);
return res.sendStatus(204);
} catch (error: DatabaseError | Error | any) {
console.error(error);
return res.sendStatus(500);
}
})
export default router;
Loading…
Cancel
Save