Preparing backend integration

This commit is contained in:
Laurent
2025-03-11 11:16:04 +01:00
parent 242b186cb3
commit f7b3cdfea7
45 changed files with 11 additions and 0 deletions

13
front/src/App.vue Normal file
View File

@@ -0,0 +1,13 @@
<script setup lang="ts">
import LayoutDefault from "@/layouts/LayoutDefault.vue";
</script>
<template>
<layout-default>
<router-view />
</layout-default>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512">
<path d="M9.4 233.4c-12.5 12.5-12.5 32.8 0 45.3l192 192c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L77.3 256 246.6 86.6c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0l-192 192z"/>
</svg>

After

Width:  |  Height:  |  Size: 251 B

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 512">
<path d="M310.6 233.4c12.5 12.5 12.5 32.8 0 45.3l-192 192c-12.5 12.5-32.8 12.5-45.3 0s-12.5-32.8 0-45.3L242.7 256 73.4 86.6c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0l192 192z"/>
</svg>

After

Width:  |  Height:  |  Size: 254 B

19
front/src/assets/base.css Normal file
View File

@@ -0,0 +1,19 @@
:root
{
--primary-color : #1b2021;
--secondary-color : #9BC53D;
--header-color : #FCFCFC;
--background-color : rgba(252, 252, 252, 0.43);
--radius : 3px;
--font-size : 1.3rem;
}
*
{
color: var(--primary-color);
text-decoration: none;
font-family: 'Afacad Flux', sans-serif;
transition: 0.3s all;
box-sizing: border-box;
}

152
front/src/assets/logo.svg Normal file
View File

@@ -0,0 +1,152 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg xmlns="http://www.w3.org/2000/svg"
width="2.33333in" height="2.1in"
viewBox="0 0 210 189">
<path id="Sélection"
fill="none" stroke="black" stroke-width="1"
d="M 0.00,0.00
C 0.00,0.00 7.00,0.00 7.00,0.00
7.00,0.00 7.00,53.00 7.00,53.00
7.00,53.00 28.00,53.00 28.00,53.00
28.00,53.00 28.00,60.00 28.00,60.00
28.00,60.00 0.00,60.00 0.00,60.00
0.00,60.00 0.00,0.00 0.00,0.00 Z
M 113.00,0.00
C 113.00,0.00 121.00,0.00 121.00,0.00
120.96,4.95 119.67,17.07 114.61,19.43
112.93,20.21 108.04,20.00 106.00,20.00
110.61,12.43 112.57,9.11 113.00,0.00 Z
M 87.00,6.00
C 87.00,6.00 95.00,6.00 95.00,6.00
95.00,6.00 95.00,20.00 95.00,20.00
95.00,20.00 105.00,20.00 105.00,20.00
105.00,20.00 105.00,26.00 105.00,26.00
105.00,26.00 95.00,26.00 95.00,26.00
95.00,26.00 95.00,60.00 95.00,60.00
95.00,60.00 87.00,60.00 87.00,60.00
87.00,60.00 87.00,26.00 87.00,26.00
87.00,26.00 78.00,26.00 78.00,26.00
78.00,26.00 78.00,20.00 78.00,20.00
78.00,20.00 87.00,20.00 87.00,20.00
87.00,20.00 87.00,6.00 87.00,6.00 Z
M 67.00,47.00
C 67.84,49.29 69.08,52.42 68.60,54.90
67.45,60.75 57.63,61.10 53.00,60.99
40.50,60.68 31.12,50.64 32.19,38.00
33.46,22.89 50.78,13.56 63.99,21.61
71.65,26.28 73.81,33.55 74.00,42.00
74.00,42.00 41.00,42.00 41.00,42.00
43.79,50.19 51.44,55.78 60.00,51.93
62.63,50.74 64.80,48.82 67.00,47.00 Z
M 128.00,48.00
C 130.26,50.82 133.06,53.63 137.00,53.64
140.71,53.65 143.50,50.35 141.92,47.39
139.34,42.55 126.19,43.12 125.11,32.00
124.16,22.22 131.26,18.42 140.00,19.09
142.99,19.32 147.38,20.30 148.89,23.22
150.14,25.66 148.71,29.52 148.00,32.00
145.29,30.13 141.32,26.45 138.02,26.45
134.70,26.45 131.68,29.34 134.42,32.73
137.84,36.97 147.42,36.60 150.30,45.01
152.83,52.42 147.55,59.78 140.00,60.81
133.40,61.71 129.67,59.91 124.00,57.00
124.00,57.00 126.00,48.00 126.00,48.00
126.00,48.00 128.00,48.00 128.00,48.00 Z
M 65.00,36.00
C 61.69,22.75 45.46,22.92 41.00,36.00
41.00,36.00 65.00,36.00 65.00,36.00 Z
M 168.00,43.00
C 168.00,43.00 173.00,43.00 173.00,43.00
173.00,43.00 174.00,55.00 174.00,55.00
174.00,55.00 169.00,55.00 169.00,55.00
169.00,55.00 168.00,43.00 168.00,43.00 Z
M 176.00,44.00
C 176.00,44.00 181.00,44.00 181.00,44.00
181.00,44.00 180.00,56.00 180.00,56.00
184.34,52.55 185.53,51.86 191.00,51.00
189.46,58.20 185.88,59.92 179.00,61.00
179.00,61.00 180.00,56.00 180.00,56.00
180.00,56.00 175.00,56.00 175.00,56.00
175.00,56.00 176.00,44.00 176.00,44.00 Z
M 157.00,52.00
C 162.95,52.30 166.12,52.81 168.00,59.00
162.05,58.70 158.88,58.19 157.00,52.00 Z
M 157.00,71.00
C 158.46,64.21 161.53,63.02 168.00,62.00
166.54,68.79 163.47,69.98 157.00,71.00 Z
M 194.00,95.00
C 191.87,99.14 187.49,102.17 184.42,96.85
184.42,96.85 177.32,73.00 177.32,73.00
176.88,70.29 176.58,65.71 179.31,64.06
181.50,62.75 184.80,63.93 187.00,64.70
190.72,66.00 205.24,73.30 207.69,75.63
211.89,79.66 209.99,84.15 205.00,86.00
206.84,89.66 210.65,98.44 202.96,98.55
200.44,98.59 196.38,96.14 194.00,95.00 Z
M 169.00,67.00
C 169.00,67.00 175.00,68.00 175.00,68.00
174.06,74.47 173.00,77.97 166.00,79.00
165.10,73.74 167.15,71.88 169.00,67.00 Z
M 54.00,126.00
C 54.00,126.00 55.00,146.00 55.00,146.00
55.00,146.00 55.00,188.00 55.00,188.00
55.00,188.00 47.00,188.00 47.00,188.00
47.00,179.60 47.91,156.72 46.00,150.00
46.00,150.00 28.00,177.00 28.00,177.00
28.00,177.00 26.00,177.00 26.00,177.00
26.00,177.00 7.00,150.00 7.00,150.00
7.00,150.00 7.00,188.00 7.00,188.00
7.00,188.00 0.00,188.00 0.00,188.00
0.00,188.00 0.00,127.00 0.00,127.00
0.00,127.00 28.00,163.00 28.00,163.00
28.00,163.00 54.00,126.00 54.00,126.00 Z
M 200.00,128.00
C 200.00,128.00 208.00,128.00 208.00,128.00
208.00,128.00 208.00,146.00 208.00,146.00
208.00,146.00 207.00,168.00 207.00,168.00
207.00,168.00 200.00,168.00 200.00,168.00
200.00,168.00 200.00,128.00 200.00,128.00 Z
M 168.00,148.00
C 168.00,135.23 166.96,134.12 176.00,134.00
176.00,134.00 176.00,148.00 176.00,148.00
176.00,148.00 186.00,148.00 186.00,148.00
186.00,148.00 186.00,154.00 186.00,154.00
186.00,154.00 176.00,154.00 176.00,154.00
176.00,154.00 176.00,188.00 176.00,188.00
176.00,188.00 168.00,188.00 168.00,188.00
168.00,188.00 168.00,154.00 168.00,154.00
168.00,154.00 159.00,154.00 159.00,154.00
159.00,154.00 159.00,148.00 159.00,148.00
159.00,148.00 168.00,148.00 168.00,148.00 Z
M 101.00,175.00
C 101.84,177.29 103.08,180.42 102.60,182.90
101.45,188.75 91.63,189.10 87.00,188.99
75.78,188.71 67.27,181.34 66.18,170.00
64.92,157.00 73.96,147.01 87.00,147.01
91.17,147.01 94.32,147.35 97.96,149.61
105.59,154.35 107.61,161.57 108.00,170.00
108.00,170.00 75.00,170.00 75.00,170.00
77.78,178.15 85.46,183.82 93.99,179.93
96.58,178.74 98.75,176.71 101.00,175.00 Z
M 149.00,175.00
C 149.32,177.26 150.50,181.85 149.68,183.79
146.97,190.19 131.32,189.78 126.00,187.47
117.33,183.71 113.20,175.15 113.76,166.00
114.80,149.05 135.00,140.81 147.91,151.53
154.02,156.60 154.93,162.57 155.00,170.00
155.00,170.00 122.00,170.00 122.00,170.00
125.38,177.40 131.25,183.06 140.00,180.35
143.04,179.41 146.29,176.79 149.00,175.00 Z
M 99.00,164.00
C 95.66,150.80 78.81,151.05 75.00,164.00
75.00,164.00 99.00,164.00 99.00,164.00 Z
M 147.00,164.00
C 140.75,150.34 126.34,151.31 122.00,164.00
122.00,164.00 147.00,164.00 147.00,164.00 Z
M 202.11,178.84
C 210.20,178.30 210.19,186.71 205.85,188.38
200.73,190.36 194.35,183.37 202.11,178.84 Z" />
</svg>

After

Width:  |  Height:  |  Size: 7.1 KiB

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 250 250">
<defs>
<style>
</style>
</defs>
<g id="Header">
<path class="cls-2" d="M225.05,120.76c-2.74,0-4.79-.85-6.15-2.55-1.36-1.7-2.03-3.84-2.03-6.41s.73-4.71,2.2-6.41c1.46-1.7,3.48-2.55,6.04-2.55s4.61.73,6.15,2.2c1.54,1.47,2.31,3.62,2.31,6.47s-.73,5.11-2.2,6.77c-1.46,1.66-3.57,2.49-6.31,2.49ZM225.44,98.43c-.67,0-1.21-.54-1.21-1.21v-9.01c0-6.81-1.19-14.9-3.57-24.29-2.38-9.38-3.57-15.77-3.57-19.18s.86-5.86,2.58-7.36c1.72-1.5,3.82-2.26,6.31-2.26,5.56,0,8.34,3.21,8.34,9.62,0,3.72-1.28,10.21-3.84,19.48-2.56,9.26-3.84,17.3-3.84,24.11v8.89c0,.67-.54,1.21-1.21,1.21h0Z"/>
<g>
<path class="cls-2" d="M80.03,35.29h6.81v34.14h17.12v.9h-23.93v-35.04Z"/>
<path class="cls-2" d="M104.36,57.82c0-7.41,5.71-13.02,13.37-13.02,6.21,0,11.21,3.55,11.21,8.91,0,.95-.15,1.8-.4,2.6h-17.12v.1c0,7.46,4.1,12.02,9.56,12.02,4.71,0,8.26-3.3,9.21-8.01h.95c-1.1,6.06-6.51,10.41-13.32,10.41-7.66,0-13.47-5.61-13.47-13.02ZM111.46,55.42h10.61c.35-1.05.55-2.15.55-3.2,0-3.4-2-6.41-4.91-6.41-3.4,0-6.01,4.31-6.26,9.61Z"/>
<path class="cls-2" d="M134.89,62.58v-16.37h-3.3v-.9h3.5l5.61-7.76h.9v7.76h7.36v.9h-7.36v17.97c0,3.15,1.8,4.86,4.01,4.86s4-1.75,4-4.16c0-.7-.15-1.45-.5-2.2h.9c.4.9.5,1.6.5,2.2,0,3.5-3.2,5.96-7.36,5.96-4.76,0-8.26-3.2-8.26-8.26Z"/>
<path class="cls-2" d="M152.96,49.26v-1.05c.65.45,1.65.75,2.65.75,3.5,0,5.46-3.5,5.46-6.36,0-.2,0-.5-.05-.8-.6,1.1-2,1.95-3.7,1.95-2.5,0-4.46-1.95-4.46-4.51s1.95-4.46,4.61-4.46c3.1,0,4.86,2.65,4.86,6.56,0,4.86-2.65,8.61-6.51,8.61-1,0-2.1-.25-2.85-.7Z"/>
<path class="cls-2" d="M163.77,63.83c0-.8.15-1.65.45-2.45h.95c-.3.8-.45,1.65-.45,2.35,0,3.35,3.75,6.01,8.36,6.01,4.16,0,6.81-2.1,6.81-4.71,0-6.31-15.42-3.81-15.42-12.77,0-4.81,4.36-7.46,9.36-7.46,5.61,0,9.46,3.2,9.46,7.46h-.95c0-3.65-3.45-6.36-7.51-6.36-3.3,0-5.46,1.85-5.46,4,0,5.81,15.42,3.2,15.42,12.67,0,5.26-4.76,8.26-10.76,8.26s-10.26-3-10.26-7.01Z"/>
<path class="cls-2" d="M80.03,84.03h7.01l11.01,26.73,11.31-26.73h6.81v35.04h-6.81v-32.34l-13.72,32.34h-.9l-13.72-32.49v32.49h-1v-35.04Z"/>
<path class="cls-2" d="M120.97,106.56c0-7.41,5.71-13.02,13.37-13.02,6.21,0,11.21,3.55,11.21,8.91,0,.95-.15,1.8-.4,2.6h-17.12v.1c0,7.46,4.1,12.02,9.56,12.02,4.71,0,8.26-3.3,9.21-8.01h.95c-1.1,6.06-6.51,10.41-13.32,10.41-7.66,0-13.47-5.61-13.47-13.02ZM128.08,104.15h10.61c.35-1.05.55-2.15.55-3.2,0-3.4-2-6.41-4.91-6.41-3.4,0-6.01,4.31-6.26,9.61Z"/>
<path class="cls-2" d="M149.56,106.56c0-7.41,5.71-13.02,13.37-13.02,6.21,0,11.21,3.55,11.21,8.91,0,.95-.15,1.8-.4,2.6h-17.12v.1c0,7.46,4.1,12.02,9.56,12.02,4.71,0,8.26-3.3,9.21-8.01h.95c-1.1,6.06-6.51,10.41-13.32,10.41-7.66,0-13.47-5.61-13.47-13.02ZM156.67,104.15h10.61c.35-1.05.55-2.15.55-3.2,0-3.4-2-6.41-4.91-6.41-3.4,0-6.01,4.31-6.26,9.61Z"/>
<path class="cls-2" d="M180.09,111.31v-16.37h-3.3v-.9h3.5l5.61-7.76h.9v7.76h7.36v.9h-7.36v17.97c0,3.15,1.8,4.86,4.01,4.86s4-1.75,4-4.16c0-.7-.15-1.45-.5-2.2h.9c.4.9.5,1.6.5,2.2,0,3.5-3.2,5.96-7.36,5.96-4.76,0-8.26-3.2-8.26-8.26Z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.1 KiB

48
front/src/assets/main.css Normal file
View File

@@ -0,0 +1,48 @@
@import './base.css';
html, body, #app, .layout {
margin: 0;
height: 100vh;
width: 100vw;
}
body
{
background-color: white;
font-size: var(--font-size);
}
.layout
{
background: var(--background-color);
}
.container
{
display: flex;
padding: 0 1vw 3vh 1vw;
}
@media screen and (min-width: 1501px) {
.container
{
flex-direction: row;
}
}
@media screen and (max-width: 1500px) {
.container {
flex-direction: column;
}
}
.colored-text
{
color: var(--secondary-color);
}
footer
{
position:absolute;
bottom:0.5vw;
}

View File

@@ -0,0 +1,86 @@
/* color palette from <https://github.com/vuejs/theme> */
:root {
--vt-c-white: #ffffff;
--vt-c-white-soft: #f8f8f8;
--vt-c-white-mute: #f2f2f2;
--vt-c-black: #181818;
--vt-c-black-soft: #222222;
--vt-c-black-mute: #282828;
--vt-c-indigo: #2c3e50;
--vt-c-divider-light-1: rgba(60, 60, 60, 0.29);
--vt-c-divider-light-2: rgba(60, 60, 60, 0.12);
--vt-c-divider-dark-1: rgba(84, 84, 84, 0.65);
--vt-c-divider-dark-2: rgba(84, 84, 84, 0.48);
--vt-c-text-light-1: var(--vt-c-indigo);
--vt-c-text-light-2: rgba(60, 60, 60, 0.66);
--vt-c-text-dark-1: var(--vt-c-white);
--vt-c-text-dark-2: rgba(235, 235, 235, 0.64);
}
/* semantic color variables for this project */
:root {
--color-background: var(--vt-c-white);
--color-background-soft: var(--vt-c-white-soft);
--color-background-mute: var(--vt-c-white-mute);
--color-border: var(--vt-c-divider-light-2);
--color-border-hover: var(--vt-c-divider-light-1);
--color-heading: var(--vt-c-text-light-1);
--color-text: var(--vt-c-text-light-1);
--section-gap: 160px;
}
@media (prefers-color-scheme: dark) {
:root {
--color-background: var(--vt-c-black);
--color-background-soft: var(--vt-c-black-soft);
--color-background-mute: var(--vt-c-black-mute);
--color-border: var(--vt-c-divider-dark-2);
--color-border-hover: var(--vt-c-divider-dark-1);
--color-heading: var(--vt-c-text-dark-1);
--color-text: var(--vt-c-text-dark-2);
}
}
*,
*::before,
*::after {
box-sizing: border-box;
margin: 0;
font-weight: normal;
}
body {
min-height: 100vh;
color: var(--color-text);
background: var(--color-background);
transition:
color 0.5s,
background-color 0.5s;
line-height: 1.6;
font-family:
Inter,
-apple-system,
BlinkMacSystemFont,
'Segoe UI',
Roboto,
Oxygen,
Ubuntu,
Cantarell,
'Fira Sans',
'Droid Sans',
'Helvetica Neue',
sans-serif;
font-size: 15px;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

View File

@@ -0,0 +1,35 @@
@import './base.css';
#app {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
font-weight: normal;
}
a,
.green {
text-decoration: none;
color: hsla(160, 100%, 37%, 1);
transition: 0.4s;
padding: 3px;
}
@media (hover: hover) {
a:hover {
background-color: hsla(160, 100%, 37%, 0.2);
}
}
@media (min-width: 1024px) {
body {
display: flex;
place-items: center;
}
#app {
display: grid;
grid-template-columns: 1fr 1fr;
padding: 0 2rem;
}
}

View File

@@ -0,0 +1,176 @@
<script setup lang="ts">
import ArrowRight from "@/assets/arrow-right.svg"
import ArrowLeft from "@/assets/arrow-left.svg";
import {ref, onMounted, watch} from "vue";
import { datePickerStore } from "@/stores/CalendarStore.ts";
import { Calendar } from "@/models/Calendar.ts";
import router from "@/router";
const calendar = new Calendar();
const datePicker = datePickerStore();
const monthYear = ref("");
const dates = ref([] as (number | null)[]);
const selectedDays = ref(new Map<string, Date>());
onMounted(() => {
setupCalendar();
})
watch(selectedDays, (newValue) => {
let dates = Array.from(newValue.values())
datePicker.update(dates);
}, { deep: true });
function selectDay(event : Event, day : number | null){
if(!(event.target instanceof HTMLElement) || day == null) return;
calendar.setDay(day);
toggleSelectedDay(event.target.id, calendar.getDate());
highlightSelectedDay(event.target);
}
function toggleSelectedDay(id : string, date : Date) : void {
if(selectedDays.value.has(id)){
selectedDays.value.delete(id);
} else {
selectedDays.value.set(id, date);
}
}
function highlightSelectedDay(element : HTMLElement){
element.classList.toggle("item-selected");
}
function setupCalendar(){
dates.value = calendar.datesOfCurrentMonth();
updateMonth();
}
function clickPrev(){
dates.value = calendar.datesOfPrevMonth();
updateMonth();
}
function clickNext(){
dates.value = calendar.datesOfNextMonth();
updateMonth();
}
function updateMonth(){
monthYear.value = calendar.getMonthYear();
}
</script>
<template>
<div class="calendar-container">
<div class="month-picker">
<div>{{ monthYear }}</div>
<div class="arrows">
<div @click="clickPrev" class="prevMonth"><ArrowLeft class="arrow-left" /></div>
<div @click="clickNext" class="nextMonth"><ArrowRight class="arrow-right" /></div>
</div>
</div>
<div class="day-names">
<div>M</div>
<div>T</div>
<div>W</div>
<div>T</div>
<div>F</div>
<div>S</div>
<div>S</div>
</div>
<!--Elements are not recreated ? Does only the id change ? -->
<!--Keep track of all selected ids and update view when month is update ? -->
<div class="day-picker">
<div v-on:click="selectDay($event, day)"
v-for ="(day) in dates"
:id ="day !== null ? `${day}-${monthYear}` : undefined"
:class ="{
'item': day !== null,
'item-selected' : selectedDays.has(`${day}-${monthYear}`)
}">
{{ day }}
</div>
</div>
</div>
</template>
<style scoped>
.calendar-container {
display: flex;
flex-direction: column;
justify-content: center;
border: solid 2px;
border-radius: var(--radius);
padding: 0 20px 20px 20px;
}
.month-picker, .day-picker, .day-names {
justify-items: center;
}
.month-picker {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
border-bottom:solid 1px;
padding: 20px;
}
.arrows {
display: flex;
gap: 10px;
}
.arrow-left, .arrow-right {
width: 30px;
border-radius: var(--radius);
padding: 5px 7px 5px 7px;
}
.arrow-left:hover, .arrow-right:hover {
transform: scale(1.2);
background-color: rgb(239,239,239);
}
.day-picker, .day-names {
display: grid;
grid-template-columns: repeat(7, 1fr);
row-gap: 20px;
width: 100%;
}
.day-picker {
grid-template-rows: repeat(6, 1fr);
}
.day-names {
padding: 20px 0 20px 0;
}
.item
{
text-align: center;
width: 90%;
min-width: 30px;
height: 100%;
border: solid 1px;
border-radius: 5px;
user-select: none;
}
.item-selected
{
background-color: var(--secondary-color);
}
.item:hover
{
transform: scale(1.1);
}
</style>

View File

@@ -0,0 +1,34 @@
<script setup lang="ts">
const props = defineProps<{
path: string;
description: string;
}>();
</script>
<template>
<router-link :to=path><div class="button">{{ description }}</div></router-link>
</template>
<style scoped>
.button {
color: var(--primary-color);
text-align: center;
align-content: center;
border-radius: var(--radius);
background-color: var(--secondary-color);
word-break: break-word;
min-height: 7vh;
}
.button:hover
{
transform: scale(1.1);
background-color: var(--header-color);
filter: drop-shadow(0px 4px 4px rgba(0, 0, 0, 0.25));
}
</style>

View File

@@ -0,0 +1,42 @@
<script setup lang="ts">
</script>
<template>
<div class="text-block">
<slot></slot>
</div>
</template>
<style scoped>
.text-block {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
/*justify-content: start;*/
word-break: break-word;
height: fit-content;
border-radius: var(--radius);
border-left: solid 4px;
background: rgb(239, 239, 239);
}
@media screen and (max-width: 1501px) {
.text-block {
padding: 5px 15px 5px 15px;
}
}
@media screen and (min-width: 1500px) {
.text-block {
padding: 10px 30px 10px 30px;
}
}
</style>

View File

@@ -0,0 +1,10 @@
export abstract class ArrayHelper {
public static contains(array : any[], element : any) : boolean {
return array.indexOf(element) > -1;
}
public static indexOf(array : any[], element : any) : number {
return array.indexOf(element);
}
}

View File

@@ -0,0 +1,95 @@
<script setup lang="ts">
import NavLink from "@/components/NavLink.vue";
</script>
<template>
<div class="layout">
<nav class="nav">
<p>Let's<br>Meet</p>
<div class="nav-actions-group">
<NavLink path="/" description="Home"></NavLink>
<NavLink path="/login" description="Log in"></NavLink>
<NavLink path="/about" description="About"></NavLink>
</div>
</nav>
<main class="main">
<slot/>
</main>
<!--<footer class="footer">Blank footer</footer>-->
</div>
</template>
<style scoped>
.layout
{
display: flex;
flex-direction: column;
}
nav {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
min-height: 10vh;
border-bottom: 1px solid;
border-radius: var(--radius);
background-color: var(--header-color);
filter: drop-shadow(0px 4px 4px rgba(0, 0, 0, 0.25));
padding: 0 3vw 0 2vw;
margin-bottom: 20px;
}
nav p {
font-size: calc(var(--font-size) * 1.1);
margin: 4px 0 4px 0;
}
.nav-actions-group {
display: flex;
flex-direction: row;
gap: 1vw;
}
::v-deep(nav .button) {
font-size: calc(var(--font-size) * 0.8);
background-color: var(--header-color);
border: solid 2px;
height: fit-content;
}
@media screen and (min-width: 951px) {
::v-deep(nav .button) {
width: 5vw;
min-width: 5vw;
min-height: 4vh;
}
}
@media screen and (max-width: 950px) {
::v-deep(nav .button) {
width: 7vw;
min-width: 60px;
min-height: 3vh;
}
}
.main {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
flex-grow: 1;
}
</style>

14
front/src/main.ts Normal file
View File

@@ -0,0 +1,14 @@
import './assets/main.css'
import {createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
import router from './router'
const app = createApp(App)
app.use(createPinia())
app.use(router)
app.mount('#app')

View File

@@ -0,0 +1,82 @@
export class Calendar {
private readonly monthsName : string[] = [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December"
];
private year : number;
private month : number;
private day : number;
public constructor() {
let date = new Date();
this.year = date.getFullYear();
this.month = date.getMonth();
this.day = date.getDay();
}
public getDate(): Date {
return new Date(this.year, this.month, this.day);
}
public getMonthYear(){
return `${this.monthsName[this.month]} ${this.year}`;
}
public setDay(day : number) { this.day = day; }
public datesOfCurrentMonth(){
return this.datesOf();
}
public datesOfNextMonth(){
if(this.month+1 > 11){
this.year += 1;
this.month = 0;
} else {
this.month++;
}
return this.datesOf();
}
public datesOfPrevMonth(){
if(this.month-1 < 0){
this.year -= 1;
this.month = 11;
} else {
this.month--;
}
return this.datesOf();
}
private datesOf() {
let dates = [];
let daysInMonth = new Date(this.year, this.month+1, 0).getDate();
let firstDayOfMonth = (new Date(this.year, this.month, 1).getDay() + 6) % 7; // Adjust to start from Monday
for (let i = 0; i < firstDayOfMonth; i++) {
dates.push(null);
}
for (let day = 1; day <= daysInMonth; day++) {
dates.push(day);
}
return dates;
}
public toString(){
return `${this.day}-${this.monthsName[this.month]}-${this.year}`
}
}

View File

@@ -0,0 +1,12 @@
export class TimeStamp {
private readonly value : number;
public constructor(date : Date) {
this.value = date.getTime();
}
public getValue() : number{
return this.value;
}
}

20
front/src/router/index.ts Normal file
View File

@@ -0,0 +1,20 @@
import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
import AboutView from '../views/AboutView.vue'
import CreateView from "../views/CreateView.vue";
import JoinView from "../views/JoinView.vue";
import ErrorView from '../views/ErrorView.vue';
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{ path: '/', name: 'home', component: HomeView},
{ path: '/about', name: 'about', component: AboutView},
{ path: '/create', name: 'create', component: CreateView},
{ path: '/join', name: 'join', component: JoinView},
{ path: '/error', name: 'error', component: ErrorView},
{ path: '/:pathMatch(.*)*', component: ErrorView }
]
})
export default router

View File

@@ -0,0 +1,22 @@
import { defineStore } from 'pinia'
import {TimeStamp} from "@/models/TimeStamp.ts";
export const datePickerStore = defineStore('datePicker', {
state: () => {
return { dates: [] as TimeStamp[] }
},
getters: {
value: (state) : TimeStamp[] => state.dates as TimeStamp[],
},
actions: {
getValue(){
return this.dates;
},
update(dates : Date[]) : void {
this.dates = dates.map(date => new TimeStamp(date));
},
clear() : void {
this.dates = [] as TimeStamp[];
},
},
})

View File

@@ -0,0 +1,28 @@
<script setup lang="ts">
import TextBlock from "@/components/TextBlock.vue";
</script>
<template>
<TextBlock>
<h1>About</h1>
<p>
Inspired by <a href="https://when2meet.com">when2meet</a>, Let's meet is a free and open-source
app that allows you and your friends to find a date to meet. From now on, there is no need to spend
hours and hours trying to find a suitable day for everyone.
</p>
<p>
Developed and hosted by Naaturel<br>
Check my links out !<br>
<a href="https://github.com/">Github</a><br>
<a href="https://bsky.app/profile/naaturel.be">Blue Sky</a>
</p>
</TextBlock>
</template>
<style scoped>
</style>

View File

@@ -0,0 +1,66 @@
<script setup lang="ts">
import Calendar from "@/components/Calendar.vue";
import TextBlock from "@/components/TextBlock.vue";
import NavLink from "@/components/NavLink.vue";
</script>
<template>
<div class="container">
<TextBlock class="title">
<h1>Create an <span class="colored-text">event</span></h1>
</TextBlock>
<div class="event-form">
<Calendar class="calendar"/>
<NavLink path="/create" description="Create" class="create-button"></NavLink>
</div>
</div>
</template>
<style scoped>
.container
{
width: 80%;
gap: 10vw;
}
.event-form {
display: flex;
flex-direction: column;
width: 45%;
gap: 20px;
}
/* MEDIA QUERIES */
@media screen and (min-width: 1501px) {
.title {
width: 50%;
}
.title h1
{
font-size: 4rem;
}
.calendar {
width: 100%;
min-width: 100%;
}
}
@media screen and (max-width: 1500px) {
.title, .event-form {
width: 100%;
}
.title {
height: fit-content;
}
}
</style>

View File

@@ -0,0 +1,22 @@
<script setup lang="ts">
import TextBlock from "@/components/TextBlock.vue";
</script>
<template>
<TextBlock>
<h1>Oopsie</h1>
<p>Something went wrong</p>
</TextBlock>
</template>
<style scoped>
.text-block {
border-left: solid 4px red;
width: 35%;
height: 35%;
}
</style>

View File

@@ -0,0 +1,151 @@
<script setup lang="ts">
import NavLink from "@/components/NavLink.vue";
import TextBlock from "@/components/TextBlock.vue";
</script>
<template>
<div class="container">
<TextBlock class="introduction">
<h1>Welcome !</h1>
<p>
This website is currently under <span class="colored-text">development</span> and might look ugly as fuck
bla bla bla I'm just writting things nobody will read to fill the space and see how it looks.
</p>
<p>
Yes I know i could have use
a Lorem Ipsum but it seems more natural with words everyone can understand
</p>
</TextBlock>
<div class="actions-group">
<h1>Meet up <span class="colored-text">now</span> !</h1>
<NavLink path="/create" description="Create" class="button"></NavLink>
<NavLink path="/join" description="Join" class="button"></NavLink>
</div>
</div>
</template>
<style scoped>
.container, .actions-group {
display: flex;
justify-content: center;
align-items: center;
height: 100%;
}
.container {
width: 80%;
gap: 10vw;
}
.introduction p
{
width: 80%;
text-align: justify;
}
.actions-group {
flex-direction: column;
min-width: 250px;
}
.actions-group {
justify-content: center;
gap: 4vh;
height: fit-content;
border-radius: var(--radius);
filter: drop-shadow(0px 0px 0px rgba(0, 0, 0, 0.0));
margin-bottom: 5vh;
}
.actions-group h1
{
margin-top: 0;
}
.actions-group::before,
.actions-group::after {
content: "";
position: absolute;
width: 5%;
height: 15%;
border: 4px solid black;
}
.actions-group::before {
top: 0;
left: 0;
border-radius: var(--radius);
border-right: none;
border-bottom: none;
}
.actions-group::after {
bottom: 0;
right: 0;
border-radius: var(--radius);
border-left: none;
border-top: none;
}
/* MEDIA QUERIES */
@media screen and (min-width: 1501px) {
.introduction {
padding: 0 0 1vw 0;
width: 50%;
min-height: 40vh;
}
.actions-group {
width: 35%;
min-height: 40vh;
}
.introduction h1
{
font-size: 4rem;
}
}
@media screen and (max-width: 1500px) {
.introduction, .actions-group {
width: 100%;
}
.introduction {
min-height: 25vh;
}
.actions-group {
min-height: 40vh;
}
.introduction h1
{
font-size: 3rem;
}
}
@media screen and (min-width: 801px) {
.button {
width: 15vw;
min-width: 15vw;
}
}
@media screen and (max-width: 800px) {
.button {
width: 50vw;
min-width: 50vw;
}
}
</style>

View File

@@ -0,0 +1,24 @@
<script setup lang="ts">
import TextBlock from "@/components/TextBlock.vue";
</script>
<template>
<TextBlock>
<h1>Join an event</h1>
<p>
This website is currently under <span class="colored-text">development</span> and might look ugly as fuck
bla bla bla I'm just writing things nobody will read to fill the space and see how it looks.
</p>
<p>
Yes I know i could have use
a Lorem Ipsum but it seems more natural with words everyone can understand
</p>
</TextBlock>
</template>
<style scoped>
</style>