holy ravioli

This commit is contained in:
Laurent
2025-03-20 17:36:46 +01:00
parent fd7bdace3b
commit 609ef2b705
28 changed files with 245 additions and 93 deletions

View File

@@ -5,7 +5,4 @@ import java.util.Set;
public class AttendeeDTO { public class AttendeeDTO {
public String name; public String name;
public Set<EventDateDTO> dates;
} }

View File

@@ -1,10 +1,11 @@
package be.naaturel.letsmeet.dto.httpRequest; package be.naaturel.letsmeet.dto.httpRequest;
import java.util.Map;
import java.util.Set; import java.util.Set;
public class EventDTO { public class EventDTO {
public String name; public String name;
public Set<AttendeeDTO> attendees; public Map<String, Set<AttendeeDTO>> dates;
} }

View File

@@ -22,13 +22,12 @@ public class AttendeeDTOMapper implements Mapper<Attendee, AttendeeDTO> {
public AttendeeDTO toEntity(Attendee d) { public AttendeeDTO toEntity(Attendee d) {
AttendeeDTO dto = new AttendeeDTO(); AttendeeDTO dto = new AttendeeDTO();
dto.name = d.getName(); dto.name = d.getName();
dto.dates = dateMapper.toEntities(d.getDates(), HashSet::new);
return dto; return dto;
} }
@Override @Override
public Attendee toModel(AttendeeDTO d) { public Attendee toModel(AttendeeDTO d) {
return new Attendee(d.name, dateMapper.toModels(d.dates, HashSet::new)); return null;
} }
@Override @Override

View File

@@ -1,30 +1,58 @@
package be.naaturel.letsmeet.mappers.requests; package be.naaturel.letsmeet.mappers.requests;
import be.naaturel.letsmeet.core.models.EventDate;
import be.naaturel.letsmeet.dto.httpRequest.EventDTO; import be.naaturel.letsmeet.dto.httpRequest.EventDTO;
import be.naaturel.letsmeet.dto.httpRequest.AttendeeDTO; import be.naaturel.letsmeet.dto.httpRequest.AttendeeDTO;
import be.naaturel.letsmeet.dto.httpRequest.EventDateDTO;
import be.naaturel.letsmeet.mappers.Mapper; import be.naaturel.letsmeet.mappers.Mapper;
import be.naaturel.letsmeet.core.models.Event; import be.naaturel.letsmeet.core.models.Event;
import be.naaturel.letsmeet.core.models.Attendee; import be.naaturel.letsmeet.core.models.Attendee;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set;
import java.util.function.Supplier; import java.util.function.Supplier;
public class EventDTOMapper implements Mapper<Event, EventDTO> { public class EventDTOMapper implements Mapper<Event, EventDTO> {
private final Mapper<Attendee, AttendeeDTO> attendeeMapper = new AttendeeDTOMapper(); private final Mapper<Attendee, AttendeeDTO> attendeeMapper = new AttendeeDTOMapper();
private final Mapper<EventDate, EventDateDTO> dateMapper = new EventDateDTOMapper();
@Override @Override
public EventDTO toEntity(Event event) { public EventDTO toEntity(Event event) {
EventDTO eventDTO = new EventDTO(); EventDTO eventDTO = new EventDTO();
eventDTO.name = event.getName(); eventDTO.name = event.getName();
eventDTO.attendees = attendeeMapper.toEntities(event.getAttendees(), HashSet::new); //eventDTO.dates = attendeeMapper.toEntities(event.getAttendees(), HashSet::new);
//TODO : MUST BE CLEANED
eventDTO.dates = new HashMap<>();
event.getAttendees().forEach(a -> {
a.getDates().forEach(d -> {
EventDateDTO dtoD = dateMapper.toEntity(d);
AttendeeDTO dtoA = attendeeMapper.toEntity(a);
String key = String.valueOf(d.getTimeStamp());
if(eventDTO.dates.containsKey(key)){
eventDTO.dates.get(key).add(dtoA);
} else {
Set<AttendeeDTO> s = new HashSet<>();
s.add(dtoA);
eventDTO.dates.put(key, s);
}
});
});
return eventDTO; return eventDTO;
} }
@Override @Override
public Event toModel(EventDTO dto) { public Event toModel(EventDTO dto) {
return new Event(dto.name, attendeeMapper.toModels(dto.attendees, HashSet::new)); //return new Event(dto.name, attendeeMapper.toModels(dto.dates, HashSet::new));
Set<Attendee> set = new HashSet<>();
for (String key : dto.dates.keySet()) {
set.addAll(attendeeMapper.toModels(dto.dates.get(key), HashSet::new));
}
return new Event(dto.name, set);
} }
@Override @Override

View File

@@ -1,6 +1,7 @@
package be.naaturel.letsmeet.mappers; package be.naaturel.letsmeet.mappers;
import be.naaturel.letsmeet.dto.database.EventDateEntity; import be.naaturel.letsmeet.dto.database.EventDateEntity;
import be.naaturel.letsmeet.mappers.Mapper;
import be.naaturel.letsmeet.mappers.database.EventDateMapper; import be.naaturel.letsmeet.mappers.database.EventDateMapper;
import be.naaturel.letsmeet.core.models.EventDate; import be.naaturel.letsmeet.core.models.EventDate;
import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeAll;

View File

@@ -0,0 +1,56 @@
package be.naaturel.letsmeet.mappers.dto;
import be.naaturel.letsmeet.core.models.Event;
import be.naaturel.letsmeet.dto.httpRequest.EventDTO;
import be.naaturel.letsmeet.mappers.Mapper;
import be.naaturel.letsmeet.mappers.requests.EventDTOMapper;
import be.naaturel.letsmeet.mock.dto.MockEventDTO;
import be.naaturel.letsmeet.mock.models.MockEventModel;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class EventDTOMapperTest {
private static MockEventDTO mockDto;
private static MockEventModel mockModel;
private static Mapper<Event, EventDTO> mapper;
@BeforeAll
static void setup(){
mockDto = new MockEventDTO();
mockModel = new MockEventModel();
mapper = new EventDTOMapper();
}
@Test
void single_dto_to_model_test(){
Event res = mapper.toModel(mockDto);
assertEquals(res.getName(), mockDto.name);
}
@Test
void single_model_to_dto_test(){
EventDTO res = mapper.toEntity(mockModel);
assertEquals(res.name, mockModel.getName());
}
@Test
void multiple_dto_to_models_test(){
List<Event> res = mapper.toModels(Set.of(mockDto), ArrayList::new);
assertEquals(res.size(), 1);
assertEquals(res.getFirst().getName(), mockDto.name);
}
@Test
void mulitple_models_to_dto_test(){
List<EventDTO> res = mapper.toEntities(Set.of(mockModel), ArrayList::new);
assertEquals(res.size(), 1);
assertEquals(res.getFirst().name, mockModel.getName());
}
}

View File

@@ -0,0 +1,11 @@
package be.naaturel.letsmeet.mock.dto;
import be.naaturel.letsmeet.dto.httpRequest.AttendeeDTO;
public class MockAttendeeDTO extends AttendeeDTO {
public MockAttendeeDTO(){
}
}

View File

@@ -0,0 +1,24 @@
package be.naaturel.letsmeet.mock.dto;
import be.naaturel.letsmeet.core.models.Attendee;
import be.naaturel.letsmeet.dto.httpRequest.AttendeeDTO;
import be.naaturel.letsmeet.dto.httpRequest.EventDTO;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;
public class MockEventDTO extends EventDTO {
public MockEventDTO() {
this.name = "Mocked event";
this.dates = new HashMap<>();
populate(0, new HashSet<>());
populate(1, new HashSet<>());
populate(2, new HashSet<>());
}
private void populate(long timestamp, Set<AttendeeDTO> attendees){
this.dates.put(String.valueOf(timestamp), attendees);
}
}

View File

@@ -0,0 +1,11 @@
package be.naaturel.letsmeet.mock.jpa;
import be.naaturel.letsmeet.dto.database.EventEntity;
public class MockEventEntity extends EventEntity {
public MockEventEntity(){
}
}

View File

@@ -0,0 +1,13 @@
package be.naaturel.letsmeet.mock.models;
import be.naaturel.letsmeet.core.models.Attendee;
import be.naaturel.letsmeet.core.models.Event;
import java.util.HashSet;
import java.util.Set;
public class MockEventModel extends Event {
public MockEventModel() {
super("Mock event model", new HashSet<>());
}
}

View File

Before

Width:  |  Height:  |  Size: 251 B

After

Width:  |  Height:  |  Size: 251 B

View File

Before

Width:  |  Height:  |  Size: 254 B

After

Width:  |  Height:  |  Size: 254 B

View File

Before

Width:  |  Height:  |  Size: 7.1 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

View File

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 3.1 KiB

View File

@@ -1,6 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import ArrowRight from "@/assets/arrow-right.svg" import ArrowRight from "@/assets/svg/arrow-right.svg"
import ArrowLeft from "@/assets/arrow-left.svg"; import ArrowLeft from "@/assets/svg/arrow-left.svg";
import {ref, onMounted, watch} from "vue"; import {ref, onMounted, watch} from "vue";
import { datePickerStore } from "@/stores/CalendarStore.ts"; import { datePickerStore } from "@/stores/CalendarStore.ts";
import { Calendar } from "@/models/Calendar.ts"; import { Calendar } from "@/models/Calendar.ts";

View File

@@ -10,7 +10,6 @@ const props = defineProps({
} }
}); });
// Method to handle the update
const updateValue = (event : any) => { const updateValue = (event : any) => {
emit('update:value', event.target.value); emit('update:value', event.target.value);
}; };

View File

@@ -1,17 +1,17 @@
import type {Attendee} from "@/models/Attendee.ts"; import type {Attendee} from "@/models/Attendee.ts";
import type {TimeStamp} from "@/models/TimeStamp.ts"; import type {EventDate} from "@/models/EventDate.ts";
import type {AttendeeDto} from "@/dto/AttendeeDto.ts"; import type {AttendeeDto} from "@/dto/AttendeeDto.ts";
export class EventDto { export class EventDto {
public name: string; public name: string;
public token: string; public token: string;
public attendees: AttendeeDto[]; public dates : Map<EventDate, Attendee[]>
public constructor(name: string, token: string, attendees: AttendeeDto[]) { public constructor(name: string, token: string, dates: Map<EventDate, Attendee[]>) {
this.name = name; this.name = name;
this.token = token; this.token = token;
this.attendees = attendees; this.dates = dates;
} }
} }

View File

@@ -1,25 +1,18 @@
import type {TimeStamp, TimeStampState} from "@/models/TimeStamp.ts"; import type {EventDate, EventDateState} from "@/models/EventDate.ts";
export class Attendee { export class Attendee {
private name: string; private name: string;
private dates: TimeStamp[];
public constructor(name: string, dates: TimeStamp[]) { public constructor(name: string) {
this.name = name; this.name = name;
this.dates = dates;
} }
public getName() : string { public getName() : string {
return this.name; return this.name;
} }
public getDates() : TimeStamp[] {
return this.dates;
}
} }
export interface AttendeeState { export interface AttendeeState {
name : string name : string
dates : TimeStampState[]
} }

View File

@@ -1,18 +1,16 @@
import type {Attendee, AttendeeState} from "@/models/Attendee.ts"; import type {Attendee, AttendeeState} from "@/models/Attendee.ts";
import type {TimeStamp} from "@/models/TimeStamp.ts"; import type {EventDate, EventDateState} from "@/models/EventDate.ts";
export class Event { export class Event {
private name: string; private name: string;
private token: string; private token: string;
private attendees: Attendee[]; private dates: Map<number, Attendee[]>;
private groups : Map<number, string[]>;
public constructor(name: string, token: string, attendees: Attendee[]) { public constructor(name: string, token: string, dates : Map<number, Attendee[]>) {
this.name = name; this.name = name;
this.token = token; this.token = token;
this.attendees = attendees; this.dates = dates;
this.groups = this.byDates();
} }
public getName() : string { public getName() : string {
@@ -23,34 +21,14 @@ export class Event {
return this.token; return this.token;
} }
public getAttendees() : Attendee[] { public getDates() : Map<number, Attendee[]> {
return this.attendees; return this.dates;
} }
public getGroups() : Map<number, string[]> { return this.groups; }
private byDates() : Map<number, string[]> {
let res : Map<number, string[]> = new Map();
this.attendees.forEach((attendee: Attendee) => {
attendee.getDates().forEach((timeStamp: TimeStamp) => {
let value : number = timeStamp.getValue();
if(res.has(value)) {
res.get(value)?.push(attendee.getName());
} else {
res.set(value, []);
res.get(value)?.push(attendee.getName());
}
});
});
return res;
}
} }
export interface EventState { export interface EventState {
name : String name : string
token : String token : string
attendees: AttendeeState[]; dates: Map<EventDateState, AttendeeState[]>;
} }

View File

@@ -1,4 +1,4 @@
export class TimeStamp { export class EventDate {
private readonly value : number; private readonly value : number;
@@ -9,8 +9,13 @@ export class TimeStamp {
public getValue() : number{ public getValue() : number{
return this.value; return this.value;
} }
public toString() {
this.value.toString();
} }
export interface TimeStampState { }
export interface EventDateState {
value : number; value : number;
} }

View File

@@ -23,8 +23,22 @@ export class EventRequests {
let url = this.formatUrl([this.baseUrl, this.endpoints.EVENTS, token]); let url = this.formatUrl([this.baseUrl, this.endpoints.EVENTS, token]);
return fetch(url) return fetch(url)
.then(response => response.json())
.then(data => {
return new EventDto(data.name, data.token, data.dates)
})
.catch(error => console.error(error));
}
public async createEvent(event : EventDto){
let url = this.formatUrl([this.baseUrl, this.endpoints.CREATE]);
return fetch(url, {
method: "POST",
body : JSON.stringify(event)
})
.then(response => response.json()) .then(response => response.json())
.then(data => new EventDto(data.name, data.token, data.attendees)) .then(data => new EventDto(data.name, data.token, data.attendees))
.catch(error => console.error(error)); .catch(error => console.error(error));
} }
} }

View File

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

View File

@@ -1,50 +1,69 @@
import {defineStore} from 'pinia' import {defineStore} from 'pinia'
import {EventRequests} from "@/requests/EventRequests.ts"; import {EventRequests} from "@/requests/EventRequests.ts";
import type {EventDto} from "@/dto/EventDto.ts"; import {EventDto} from "@/dto/EventDto.ts";
import {Event, type EventState} from "@/models/Event.ts"; import {Event, type EventState} from "@/models/Event.ts";
import type {AttendeeDto} from "@/dto/AttendeeDto.ts";
import {Attendee, type AttendeeState} from "@/models/Attendee.ts"; import {Attendee, type AttendeeState} from "@/models/Attendee.ts";
import {TimeStamp, type TimeStampState} from "@/models/TimeStamp.ts"; import {EventDate, type EventDateState} from "@/models/EventDate.ts";
import type {TimeStampDto} from "@/dto/TimeStampDto.ts";
const requests = new EventRequests(); 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', { export const eventStore = defineStore('eventStore', {
state: (): {event : EventState} => { state: (): {event : EventState} => {
return { return {
event : { event : {
name: "", name: "",
token: "", token: "",
attendees: [] as AttendeeState[] dates: new Map<EventDateState, AttendeeState[]>()
}}; }};
}, },
getters : { getters : {
getEvent(state) : Event { getEvent(state) : Event {
let attendees : Attendee[] = state.event.attendees.map((a : AttendeeState) => { return mapToModel(state.event);
let dates: TimeStamp[] = a.dates.map(d => new TimeStamp(new Date(d.value)))
return new Attendee(a.name, dates);
});
return new Event(state.event.name.toString(), "", attendees);
} }
}, },
actions: { actions: {
setName(name : string){
this.event.name = name;
},
async fetch(token: string): Promise<void> { async fetch(token: string): Promise<void> {
try{ try{
let data : EventDto | void = await requests.queryEvent(token); let data : EventDto | void = await requests.queryEvent(token);
if(!data) throw new Error("No event found"); if(!data) throw new Error("No event found");
this.event.name = data.name; this.event = mapToState(data);
this.event.token = data.token;
this.event.attendees = data.attendees.map((a: AttendeeDto) => ({
name: a.name,
dates: a.dates.map((date: TimeStampDto) => ({
value: date.timestamp
})) as TimeStampState[]
}));
} catch (error) { } catch (error) {
console.error(error); console.error(error);
throw new Error("Unable to fetch."); throw new Error("Unable to fetch. " + error);
} }
}, },
async create(){
let event = mapToDto();
await requests.createEvent(event)
}
}, },
}); });

View File

@@ -1,11 +1,11 @@
import type {TimeStamp} from "@/models/TimeStamp.ts"; import type {EventDate} from "@/models/EventDate.ts";
import type {Attendee} from "@/models/Attendee.ts"; import type {Attendee} from "@/models/Attendee.ts";
/** /**
* I hate JavaScript and TypeScript with every single inch of my body * I hate JavaScript and TypeScript with every single inch of my body
*/ */
export class CustomMap extends Map<TimeStamp, Attendee> { export class CustomMap extends Map<EventDate, Attendee> {
has(obj: TimeStamp): boolean { has(obj: EventDate): boolean {
obj.getValue() obj.getValue()
return false; return false;
} }

View File

@@ -4,6 +4,10 @@ import Calendar from "@/components/Calendar.vue";
import TextBlock from "@/components/TextBlock.vue"; import TextBlock from "@/components/TextBlock.vue";
import NavLink from "@/components/NavLink.vue"; import NavLink from "@/components/NavLink.vue";
import InputField from "@/components/InputField.vue"; import InputField from "@/components/InputField.vue";
import {eventStore} from "@/stores/EventStore.ts";
const store = eventStore();
</script> </script>
<template> <template>
@@ -13,7 +17,7 @@ import InputField from "@/components/InputField.vue";
<h1>Create an <span class="colored-text">event</span></h1> <h1>Create an <span class="colored-text">event</span></h1>
</TextBlock> </TextBlock>
<div class="event-form"> <div class="event-form">
<InputField placeholder="Event name"/> <InputField placeholder="Event name" @update:value="(newValue) => { store.setName(newValue) }"/>
<Calendar class="calendar"/> <Calendar class="calendar"/>
<NavLink name="Create" description="Create" class="create-button"></NavLink> <NavLink name="Create" description="Create" class="create-button"></NavLink>
</div> </div>

View File

@@ -43,12 +43,11 @@ function formatDate(timestamp : number) : String{
<div v-else> <div v-else>
Name : {{ event.getName() }} <br> Name : {{ event.getName() }} <br>
<div v-for="(timestamp) in event.getGroups().keys()" > <div v-for="(date) in event.getDates().keys()">
{{ formatDate(timestamp) }} {{formatDate(date)}}
<div v-for="(attendee) in event.getGroups().get(timestamp)"> <div v-for="(attendee) in event.getDates().get(date)">
{{ attendee }} {{attendee.getName()}}
</div> </div>
=================
</div> </div>
</div> </div>

View File

@@ -11,7 +11,7 @@ import TextBlock from "@/components/TextBlock.vue";
<h1>Welcome !</h1> <h1>Welcome !</h1>
<p> <p>
This website is currently under <span class="colored-text">development</span> and might look ugly as fuck 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. bla bla bla I'm just writing things nobody will read to fill the space and see how it looks.
</p> </p>
<p> <p>
Yes I know i could have use Yes I know i could have use