Events can be joined and created

This commit is contained in:
Laurent
2025-03-20 20:32:21 +01:00
parent 135ba81bb6
commit 4bbbd94ad2
12 changed files with 195 additions and 99 deletions

View File

@@ -5,25 +5,25 @@
import { datePickerStore } from "@/stores/CalendarStore.ts";
import { Calendar } from "@/models/Calendar.ts";
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 });
const calendar = new Calendar();
const monthYear = ref("");
const dates = ref([] as (number | null)[]);
const selectedDays = ref(new Map<string, Date>());
const emit = defineEmits(['update:value']);
const emitNewDay = (timestamp : number) => {
emit('update:value', timestamp);
};
function selectDay(event : Event, day : number | null){
if(!(event.target instanceof HTMLElement) || day == null) return;
calendar.setDay(day);
toggleSelectedDay(event.target.id, calendar.getDate());
emitNewDay(calendar.getDate().getTime());
highlightSelectedDay(event.target);
}
@@ -80,8 +80,6 @@
<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"

View File

@@ -26,6 +26,7 @@ input
{
line-height: 100%;
text-align: center;
border: solid 2px black;
border-radius: var(--radius);
}

View File

@@ -4,5 +4,10 @@ export const API_PATHS = {
EVENTS: '/event/',
CREATE: '/create/',
JOIN: '/join/'
},
NAMES: {
EVENTS: 'event',
CREATE: 'create',
JOIN: 'join'
}
};

View File

@@ -3,11 +3,9 @@ import type {TimeStampDto} from "@/dto/TimeStampDto.ts";
export class AttendeeDto {
public name: string;
public dates: TimeStampDto[];
public constructor(name: string, dates: TimeStampDto[]) {
public constructor(name: string) {
this.name = name;
this.dates = dates;
}
}

View File

@@ -6,9 +6,9 @@ export class EventDto {
public name: string;
public token: string;
public dates : Map<EventDate, Attendee[]>
public dates : Map<number, AttendeeDto[]>
public constructor(name: string, token: string, dates: Map<EventDate, Attendee[]>) {
public constructor(name: string, token: string, dates: Map<number, AttendeeDto[]>) {
this.name = name;
this.token = token;
this.dates = dates;

View File

@@ -29,6 +29,6 @@ export class Event {
export interface EventState {
name : string
token : string
dates: Map<EventDateState, AttendeeState[]>;
dates: Map<number, AttendeeState[]>;
}

View File

@@ -30,14 +30,15 @@ export class EventRequests {
.catch(error => console.error(error));
}
public async createEvent(event : EventDto){
public async createEvent(event : EventDto) : Promise<void | string> {
let url = this.formatUrl([this.baseUrl, this.endpoints.CREATE]);
return fetch(url, {
method: "POST",
headers: {"Content-Type": "application/json", "Accept": "application/json"},
body : JSON.stringify(event)
})
.then(response => response.json())
.then(data => new EventDto(data.name, data.token, data.attendees))
.then(data => data.value)
.catch(error => console.error(error));
}

View File

@@ -0,0 +1,103 @@
import {defineStore} from 'pinia'
import {EventRequests} from "@/requests/EventRequests.ts";
import {EventDto} from "@/dto/EventDto.ts";
import {Event, type EventState} from "@/models/Event.ts";
import {Attendee, type AttendeeState} from "@/models/Attendee.ts";
import type {AttendeeDto} from "@/dto/AttendeeDto.ts";
const requests = new EventRequests();
function mapToDto(state : EventState) : EventDto{
let dates : Map<number, AttendeeDto[]> = new Map<number, AttendeeDto[]>();
for (let [date, attendeesState] of state.dates.entries()) {
dates.set(date, []);
}
return new EventDto(state.name, state.token, dates);
}
function mapToModel(state : EventState) : Event{
let dates: Map<number, Attendee[]> = new Map();
for (let [date, attendeesState] of state.dates.entries()) {
let attendees : Attendee[] = attendeesState.map(attendee => {
return new Attendee(attendee.name);
})
dates.set(date, attendees);
}
return new Event(state.name, state.token, dates);
}
function mapToState(dto : EventDto) : EventState {
let event : EventState = {name : "", token : "", dates: new Map<number, AttendeeState[]>()};
event.name = dto.name;
event.token = dto.token;
Object.entries(dto.dates).forEach(([key, attendees]) => {
event.dates.set(Number(key), attendees);
});
return event;
}
export const eventCreationStore = defineStore('eventStore', {
state: (): {event : EventState} => {
return {
event : {
name: "",
token: "",
dates: new Map<number, AttendeeState[]>()
}};
},
getters : {
getEvent(state) : Event {
return mapToModel(state.event);
}
},
actions: {
setName(name : string){
this.event.name = name;
},
addDate(timestamp : number){
this.event.dates.set(timestamp, []);
},
removeDate(timestamp : number){
this.event.dates.delete(timestamp);
},
addAttendee(timestamp : number, name : string){
let dates :Map<number, AttendeeState[]> = this.event.dates;
if(dates.has(timestamp)){
dates.get(timestamp)?.push({name : name});
} else {
let attendees : AttendeeState[] = [{name : name}];
dates.set(timestamp, attendees);
}
},
toggleDate(timestamp : number){
let dates :Map<number, AttendeeState[]> = this.event.dates;
if(dates.has(timestamp)){
this.removeDate(timestamp);
} else {
this.addDate(timestamp);
}
},
async fetch(token: string): Promise<void> {
try{
let data : EventDto | void = await requests.queryEvent(token);
if(!data) throw new Error("No event found");
this.event = mapToState(data);
} catch (error) {
console.error(error);
throw new Error("Unable to fetch. " + error);
}
},
async createEvent() : Promise<string> {
try {
let event = mapToDto(this.event);
let res = await requests.createEvent(event)
if(res) return res;
throw new Error("Unable to create event");
} catch (error){
console.error(error);
throw new Error("Unable to post. " + error);
}
}
},
});

View File

@@ -1,69 +0,0 @@
import {defineStore} from 'pinia'
import {EventRequests} from "@/requests/EventRequests.ts";
import {EventDto} from "@/dto/EventDto.ts";
import {Event, type EventState} from "@/models/Event.ts";
import {Attendee, type AttendeeState} from "@/models/Attendee.ts";
import {EventDate, type EventDateState} from "@/models/EventDate.ts";
const requests = new EventRequests();
function mapToDto() : EventDto{
return new EventDto("", "", new Map());
}
function mapToModel(state : EventState) : Event{
let dates: Map<number, Attendee[]> = new Map();
for (let [date, attendeesState] of state.dates.entries()) {
let attendees : Attendee[] = attendeesState.map(attendee => {
return new Attendee(attendee.name);
})
dates.set(date.value, attendees);
}
return new Event(state.name, state.token, dates);
}
function mapToState(dto : EventDto) : EventState {
let event : EventState = {name : "", token : "", dates: new Map<EventDateState, AttendeeState[]>()};
event.name = dto.name;
event.token = dto.token;
Object.entries(dto.dates).forEach(([key, attendees]) => {
let dateState : EventDateState = { value: Number(key) };
event.dates.set(dateState, attendees);
});
return event;
}
export const eventStore = defineStore('eventStore', {
state: (): {event : EventState} => {
return {
event : {
name: "",
token: "",
dates: new Map<EventDateState, AttendeeState[]>()
}};
},
getters : {
getEvent(state) : Event {
return mapToModel(state.event);
}
},
actions: {
setName(name : string){
this.event.name = name;
},
async fetch(token: string): Promise<void> {
try{
let data : EventDto | void = await requests.queryEvent(token);
if(!data) throw new Error("No event found");
this.event = mapToState(data);
} catch (error) {
console.error(error);
throw new Error("Unable to fetch. " + error);
}
},
async create(){
let event = mapToDto();
await requests.createEvent(event)
}
},
});

View File

@@ -2,11 +2,58 @@
import Calendar from "@/components/Calendar.vue";
import TextBlock from "@/components/TextBlock.vue";
import NavLink from "@/components/NavLink.vue";
import InputField from "@/components/InputField.vue";
import {eventStore} from "@/stores/EventStore.ts";
import {eventCreationStore} from "@/stores/EventCreationStore.ts";
import Button from "@/components/Button.vue";
import {ref} from "vue";
import {useRouter} from "vue-router";
import {API_PATHS} from "@/config/ApiConfig.ts";
const store = eventStore();
const router = useRouter();
const store = eventCreationStore();
const errorMessage = ref("");
async function createEvent() {
if(!store.event.name){
displayError("Please enter a valid name.", getNameField());
return;
}
if(store.event.dates.size == 0){
displayError("Please select at least one date", getCalendar());
return;
}
let createdToken = await store.createEvent();
await router.push({name: API_PATHS.NAMES.EVENTS, params: {token: createdToken}});
}
function updateName(value: string) {
resetError(getNameField());
store.setName(value);
}
function updateDates(value: number) {
resetError(getCalendar());
store.toggleDate(value)
}
function displayError(message: string, target : Element) {
target.style.border = "2px solid red";
errorMessage.value = message;
}
function resetError(target : Element){
target.style.border = "2px solid black";
errorMessage.value = "";
}
function getNameField() : Element {
return document.getElementsByClassName("input-field")[0];
}
function getCalendar() : Element {
return document.getElementsByClassName("calendar-container")[0];
}
</script>
@@ -17,9 +64,12 @@ const store = eventStore();
<h1>Create an <span class="colored-text">event</span></h1>
</TextBlock>
<div class="event-form">
<InputField placeholder="Event name" @update:value="(newValue) => { store.setName(newValue) }"/>
<Calendar class="calendar"/>
<NavLink name="Create" description="Create" class="create-button"></NavLink>
<div class="whatever-the-fuck-this-is">
<InputField placeholder="Event name" @update:value="(newValue) => { updateName(newValue) }"/>
<div>{{ errorMessage }}</div>
</div>
<Calendar class="calendar" @update:value="(newValue) => { updateDates(newValue) }"/>
<Button @click="createEvent" description="Create" class="create-button" ></Button>
</div>
</div>
@@ -46,13 +96,21 @@ const store = eventStore();
min-height: 50px;
}
.nav-link
.button
{
width: 50%;
height: 50px;
min-height: 50px;
}
.whatever-the-fuck-this-is
{
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
}
/* MEDIA QUERIES */
@media screen and (min-width: 1001px) {
@@ -79,7 +137,7 @@ const store = eventStore();
@media screen and (max-width: 1000px) {
.title, .event-form, .input-field, .nav-link {
.title, .event-form, .input-field, .button {
width: 100%;
}

View File

@@ -1,5 +1,5 @@
<script setup lang="ts">
import {eventStore} from "@/stores/EventStore.ts";
import {eventCreationStore} from "@/stores/EventCreationStore.ts";
import {onBeforeMount, ref} from "vue";
import {useRoute} from "vue-router";
import {DateHelper} from "@/helpers/DateHelper.ts";
@@ -7,7 +7,7 @@ import {Event} from "@/models/Event.ts";
import ErrorBlock from "@/components/ErrorBlock.vue";
const route = useRoute();
const store = eventStore();
const store = eventCreationStore();
const token = extractToken();
let event = ref<Event | undefined>();