From 8d3e68340270798ed5736c64d0be1f0e8696c59e Mon Sep 17 00:00:00 2001 From: Filip Borum Poulsen Date: Wed, 19 Apr 2023 11:05:49 +0200 Subject: [PATCH] Added available timeslot overview and filter selector --- client/package-lock.json | 69 +++++- client/package.json | 3 + client/src/components/CenterSelector.vue | 83 +++++++ client/src/components/NavBar.vue | 2 +- client/src/components/TrainerSelector.vue | 100 ++++++++ client/src/interfaces/center.ts | 7 + client/src/interfaces/timeslot.ts | 4 + client/src/interfaces/trainer.ts | 7 + client/src/interfaces/trainerWithTimeslots.ts | 20 ++ client/src/main.ts | 7 +- client/src/views/Home.vue | 214 +++++++++++++++++- server/src/interfaces/timeslot.ts | 4 +- server/src/migrations/index.ts | 4 +- server/src/routes/timeslot.ts | 14 +- 14 files changed, 524 insertions(+), 14 deletions(-) create mode 100644 client/src/components/CenterSelector.vue create mode 100644 client/src/components/TrainerSelector.vue create mode 100644 client/src/interfaces/center.ts create mode 100644 client/src/interfaces/timeslot.ts create mode 100644 client/src/interfaces/trainer.ts create mode 100644 client/src/interfaces/trainerWithTimeslots.ts diff --git a/client/package-lock.json b/client/package-lock.json index c67cd12..7291d72 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -8,8 +8,11 @@ "name": "merit-opgave-client", "version": "0.0.0", "dependencies": { + "@popperjs/core": "^2.11.7", "bootstrap": "^5.2.3", + "dayjs": "^1.11.7", "pinia": "^2.0.34", + "v-calendar": "^3.0.3", "vue": "^3.2.47", "vue-router": "^4.1.6" }, @@ -390,18 +393,27 @@ "version": "2.11.7", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.7.tgz", "integrity": "sha512-Cr4OjIkipTtcXKjAsm8agyleBuDHvxzeBoa1v543lbv1YaIwQjESsVcmjiWiPEbC1FIeHOG/Op9kdCmAmiS3Kw==", - "peer": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/popperjs" } }, + "node_modules/@types/lodash": { + "version": "4.14.194", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.194.tgz", + "integrity": "sha512-r22s9tAS7imvBt2lyHC9B8AGwWnXaYb1tY09oyLkXDs4vArpYJzw09nj8MLx5VfciBPGIb+ZwG0ssYnEPJxn/g==" + }, "node_modules/@types/node": { "version": "18.15.11", "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.11.tgz", "integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==", "dev": true }, + "node_modules/@types/resize-observer-browser": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/@types/resize-observer-browser/-/resize-observer-browser-0.1.7.tgz", + "integrity": "sha512-G9eN0Sn0ii9PWQ3Vl72jDPgeJwRWhv2Qk/nQkJuWmRmOB4HX3/BhD5SE1dZs/hzPZL/WKnvF0RHdTSG54QJFyg==" + }, "node_modules/@vitejs/plugin-vue": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.1.0.tgz", @@ -754,6 +766,31 @@ "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz", "integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==" }, + "node_modules/date-fns": { + "version": "2.29.3", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.29.3.tgz", + "integrity": "sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA==", + "engines": { + "node": ">=0.11" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/date-fns" + } + }, + "node_modules/date-fns-tz": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/date-fns-tz/-/date-fns-tz-1.3.8.tgz", + "integrity": "sha512-qwNXUFtMHTTU6CFSFjoJ80W8Fzzp24LntbjFFBgL/faqds4e5mo9mftoRLgr3Vi1trISsg4awSpYVsOQCRnapQ==", + "peerDependencies": { + "date-fns": ">=2.0.0" + } + }, + "node_modules/dayjs": { + "version": "1.11.7", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.7.tgz", + "integrity": "sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ==" + }, "node_modules/de-indent": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", @@ -1374,6 +1411,11 @@ "node": ">=4" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, "node_modules/magic-string": { "version": "0.25.9", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", @@ -1970,6 +2012,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/v-calendar": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/v-calendar/-/v-calendar-3.0.3.tgz", + "integrity": "sha512-Skpp/nMoFqFadm94aWj0oOfazoux5T5Ug3/pbRbdolkoDrnVcL7Ronw1/SGFRUPGOwnLdYwhKPhrhSE1segW6w==", + "dependencies": { + "@types/lodash": "^4.14.165", + "@types/resize-observer-browser": "^0.1.7", + "date-fns": "^2.16.1", + "date-fns-tz": "^1.0.12", + "lodash": "^4.17.20", + "vue-screen-utils": "^1.0.0-beta.13" + }, + "peerDependencies": { + "@popperjs/core": "^2.0.0", + "vue": "^3.2.0" + } + }, "node_modules/validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -2055,6 +2114,14 @@ "vue": "^3.2.0" } }, + "node_modules/vue-screen-utils": { + "version": "1.0.0-beta.13", + "resolved": "https://registry.npmjs.org/vue-screen-utils/-/vue-screen-utils-1.0.0-beta.13.tgz", + "integrity": "sha512-EJ/8TANKhFj+LefDuOvZykwMr3rrLFPLNb++lNBqPOpVigT2ActRg6icH9RFQVm4nHwlHIHSGm5OY/Clar9yIg==", + "peerDependencies": { + "vue": "^3.2.0" + } + }, "node_modules/vue-template-compiler": { "version": "2.7.14", "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.14.tgz", diff --git a/client/package.json b/client/package.json index 41cb51d..fbaef0d 100644 --- a/client/package.json +++ b/client/package.json @@ -10,8 +10,11 @@ "type-check": "vue-tsc --noEmit" }, "dependencies": { + "@popperjs/core": "^2.11.7", "bootstrap": "^5.2.3", + "dayjs": "^1.11.7", "pinia": "^2.0.34", + "v-calendar": "^3.0.3", "vue": "^3.2.47", "vue-router": "^4.1.6" }, diff --git a/client/src/components/CenterSelector.vue b/client/src/components/CenterSelector.vue new file mode 100644 index 0000000..899cfcb --- /dev/null +++ b/client/src/components/CenterSelector.vue @@ -0,0 +1,83 @@ + + + + + diff --git a/client/src/components/NavBar.vue b/client/src/components/NavBar.vue index 14e1046..af6e3f3 100644 --- a/client/src/components/NavBar.vue +++ b/client/src/components/NavBar.vue @@ -26,7 +26,7 @@ const { loggedIn } = storeToRefs(loginStore); diff --git a/client/src/interfaces/center.ts b/client/src/interfaces/center.ts new file mode 100644 index 0000000..b6729d8 --- /dev/null +++ b/client/src/interfaces/center.ts @@ -0,0 +1,7 @@ +export interface Center { + id: number + name: string + city: string + zip_code: number + address: string +} \ No newline at end of file diff --git a/client/src/interfaces/timeslot.ts b/client/src/interfaces/timeslot.ts new file mode 100644 index 0000000..eee7ded --- /dev/null +++ b/client/src/interfaces/timeslot.ts @@ -0,0 +1,4 @@ +export interface Timeslot { + startDate: Date + endDate: Date +} \ No newline at end of file diff --git a/client/src/interfaces/trainer.ts b/client/src/interfaces/trainer.ts new file mode 100644 index 0000000..60fa483 --- /dev/null +++ b/client/src/interfaces/trainer.ts @@ -0,0 +1,7 @@ +export interface Trainer { + id: number + first_name: string + last_name: string + center_id: number + center_name: string +} \ No newline at end of file diff --git a/client/src/interfaces/trainerWithTimeslots.ts b/client/src/interfaces/trainerWithTimeslots.ts new file mode 100644 index 0000000..ff731c6 --- /dev/null +++ b/client/src/interfaces/trainerWithTimeslots.ts @@ -0,0 +1,20 @@ +import type { Timeslot } from "./timeslot"; +import type { Trainer } from "./trainer"; + +export interface TrainerWithTimeslots extends Trainer { + timeslots: Timeslot[] +} + +export interface TrainerWithDateGroupedTimeslots extends Trainer { + dates: DateGroupedTimeslotsList + timeslots: Timeslot[] +} + +export interface DateGroupedTimeslotsList { + [key: string]: DateGroupedTimeslots +} + +export interface DateGroupedTimeslots { + timeslots: Timeslot[] + date: string +} \ No newline at end of file diff --git a/client/src/main.ts b/client/src/main.ts index fe7d946..97789f4 100644 --- a/client/src/main.ts +++ b/client/src/main.ts @@ -2,15 +2,16 @@ import { createApp } from 'vue' import { createPinia } from 'pinia' import App from './App.vue' import router from './router' +import VCalendar from 'v-calendar'; import 'bootstrap/dist/css/bootstrap.css'; +import 'v-calendar/style.css'; const pinia = createPinia() const app = createApp(App) app.use(router) app.use(pinia) +app.use(VCalendar, {}) -app.mount('#app') - -import "bootstrap/dist/js/bootstrap.js"; \ No newline at end of file +app.mount('#app') \ No newline at end of file diff --git a/client/src/views/Home.vue b/client/src/views/Home.vue index 27e0f69..6ca63f1 100644 --- a/client/src/views/Home.vue +++ b/client/src/views/Home.vue @@ -1,3 +1,213 @@ \ No newline at end of file +
+ + + + +
+
+
+ {{ trainer.first_name }} {{ trainer.last_name }} +
+
+
+ {{ date.date }} +
+
+ {{ formatTime(timeslot.startDate) }} - {{ formatTime(timeslot.endDate) }} +
+
+
+
+
+
+ +
+ + + + + \ No newline at end of file diff --git a/server/src/interfaces/timeslot.ts b/server/src/interfaces/timeslot.ts index d8a8e6e..caded19 100644 --- a/server/src/interfaces/timeslot.ts +++ b/server/src/interfaces/timeslot.ts @@ -1,6 +1,6 @@ export interface Timeslot { - startTime: Date - endTime: Date + startDate: Date + endDate: Date } export interface WeeklyTimeslot { diff --git a/server/src/migrations/index.ts b/server/src/migrations/index.ts index 1145b0f..65a71c0 100644 --- a/server/src/migrations/index.ts +++ b/server/src/migrations/index.ts @@ -1,4 +1,5 @@ import dotenv from "dotenv"; +import { exit } from "process"; dotenv.config(); import { client } from "../db"; @@ -80,5 +81,6 @@ CREATE TABLE public.orders ); `) .then(()=>{ - return client.end(); + client.end(); + exit(0); }) \ No newline at end of file diff --git a/server/src/routes/timeslot.ts b/server/src/routes/timeslot.ts index e840c8f..9b17ab4 100644 --- a/server/src/routes/timeslot.ts +++ b/server/src/routes/timeslot.ts @@ -54,10 +54,16 @@ router.get("/timeslot", async (req: Request, res: Response) => { } const timeslotFilters: TimeslotFilters = validation.value; - - const filterStartDate: dayjs.Dayjs = dayjs(timeslotFilters.startDate).utcOffset(0).startOf("date"); + + let filterStartDate: dayjs.Dayjs = dayjs(timeslotFilters.startDate).utcOffset(0).startOf("date"); const filterEndDate: dayjs.Dayjs = dayjs(timeslotFilters.endDate).utcOffset(0).startOf("date"); + const nowDate: dayjs.Dayjs = dayjs().utcOffset(0).startOf("date"); + + if (filterStartDate.isBefore(nowDate)) { + filterStartDate = nowDate; + } + let weekdays = []; for (let day: dayjs.Dayjs = filterStartDate; !day.isAfter(filterEndDate); day = day.add(1, "day")) { weekdays.push(day.isoWeekday()); @@ -163,8 +169,8 @@ router.get("/timeslot", async (req: Request, res: Response) => { } } trainerWithAvailableTimeslots.timeslots.push({ - startTime: startTime.toDate(), - endTime: endTime.toDate() + startDate: startTime.toDate(), + endDate: endTime.toDate() }); } }