Events can be joined and created
This commit is contained in:
@@ -6,6 +6,7 @@ import java.util.Set;
|
|||||||
public class EventDTO {
|
public class EventDTO {
|
||||||
|
|
||||||
public String name;
|
public String name;
|
||||||
|
public String token;
|
||||||
public Map<String, Set<AttendeeDTO>> dates;
|
public Map<String, Set<AttendeeDTO>> dates;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,25 +5,25 @@
|
|||||||
import { datePickerStore } from "@/stores/CalendarStore.ts";
|
import { datePickerStore } from "@/stores/CalendarStore.ts";
|
||||||
import { Calendar } from "@/models/Calendar.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(() => {
|
onMounted(() => {
|
||||||
setupCalendar();
|
setupCalendar();
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(selectedDays, (newValue) => {
|
const calendar = new Calendar();
|
||||||
let dates = Array.from(newValue.values())
|
const monthYear = ref("");
|
||||||
datePicker.update(dates);
|
const dates = ref([] as (number | null)[]);
|
||||||
}, { deep: true });
|
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){
|
function selectDay(event : Event, day : number | null){
|
||||||
if(!(event.target instanceof HTMLElement) || day == null) return;
|
if(!(event.target instanceof HTMLElement) || day == null) return;
|
||||||
calendar.setDay(day);
|
calendar.setDay(day);
|
||||||
toggleSelectedDay(event.target.id, calendar.getDate());
|
toggleSelectedDay(event.target.id, calendar.getDate());
|
||||||
|
emitNewDay(calendar.getDate().getTime());
|
||||||
highlightSelectedDay(event.target);
|
highlightSelectedDay(event.target);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -80,8 +80,6 @@
|
|||||||
<div>S</div>
|
<div>S</div>
|
||||||
</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 class="day-picker">
|
||||||
<div v-on:click="selectDay($event, day)"
|
<div v-on:click="selectDay($event, day)"
|
||||||
v-for ="(day) in dates"
|
v-for ="(day) in dates"
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ input
|
|||||||
{
|
{
|
||||||
line-height: 100%;
|
line-height: 100%;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
border: solid 2px black;
|
||||||
border-radius: var(--radius);
|
border-radius: var(--radius);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,5 +4,10 @@ export const API_PATHS = {
|
|||||||
EVENTS: '/event/',
|
EVENTS: '/event/',
|
||||||
CREATE: '/create/',
|
CREATE: '/create/',
|
||||||
JOIN: '/join/'
|
JOIN: '/join/'
|
||||||
|
},
|
||||||
|
NAMES: {
|
||||||
|
EVENTS: 'event',
|
||||||
|
CREATE: 'create',
|
||||||
|
JOIN: 'join'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,11 +3,9 @@ import type {TimeStampDto} from "@/dto/TimeStampDto.ts";
|
|||||||
export class AttendeeDto {
|
export class AttendeeDto {
|
||||||
|
|
||||||
public name: string;
|
public name: string;
|
||||||
public dates: TimeStampDto[];
|
|
||||||
|
|
||||||
public constructor(name: string, dates: TimeStampDto[]) {
|
public constructor(name: string) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.dates = dates;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,9 +6,9 @@ export class EventDto {
|
|||||||
|
|
||||||
public name: string;
|
public name: string;
|
||||||
public token: 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.name = name;
|
||||||
this.token = token;
|
this.token = token;
|
||||||
this.dates = dates;
|
this.dates = dates;
|
||||||
|
|||||||
@@ -29,6 +29,6 @@ export class Event {
|
|||||||
export interface EventState {
|
export interface EventState {
|
||||||
name : string
|
name : string
|
||||||
token : string
|
token : string
|
||||||
dates: Map<EventDateState, AttendeeState[]>;
|
dates: Map<number, AttendeeState[]>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,14 +30,15 @@ export class EventRequests {
|
|||||||
.catch(error => console.error(error));
|
.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]);
|
let url = this.formatUrl([this.baseUrl, this.endpoints.CREATE]);
|
||||||
return fetch(url, {
|
return fetch(url, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
|
headers: {"Content-Type": "application/json", "Accept": "application/json"},
|
||||||
body : JSON.stringify(event)
|
body : JSON.stringify(event)
|
||||||
})
|
})
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => new EventDto(data.name, data.token, data.attendees))
|
.then(data => data.value)
|
||||||
.catch(error => console.error(error));
|
.catch(error => console.error(error));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
103
front/src/stores/EventCreationStore.ts
Normal file
103
front/src/stores/EventCreationStore.ts
Normal 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -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)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@@ -2,11 +2,58 @@
|
|||||||
|
|
||||||
import Calendar from "@/components/Calendar.vue";
|
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 InputField from "@/components/InputField.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>
|
</script>
|
||||||
|
|
||||||
@@ -17,9 +64,12 @@ const store = eventStore();
|
|||||||
<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" @update:value="(newValue) => { store.setName(newValue) }"/>
|
<div class="whatever-the-fuck-this-is">
|
||||||
<Calendar class="calendar"/>
|
<InputField placeholder="Event name" @update:value="(newValue) => { updateName(newValue) }"/>
|
||||||
<NavLink name="Create" description="Create" class="create-button"></NavLink>
|
<div>{{ errorMessage }}</div>
|
||||||
|
</div>
|
||||||
|
<Calendar class="calendar" @update:value="(newValue) => { updateDates(newValue) }"/>
|
||||||
|
<Button @click="createEvent" description="Create" class="create-button" ></Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -46,13 +96,21 @@ const store = eventStore();
|
|||||||
min-height: 50px;
|
min-height: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-link
|
.button
|
||||||
{
|
{
|
||||||
width: 50%;
|
width: 50%;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
min-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 QUERIES */
|
||||||
@media screen and (min-width: 1001px) {
|
@media screen and (min-width: 1001px) {
|
||||||
|
|
||||||
@@ -79,7 +137,7 @@ const store = eventStore();
|
|||||||
|
|
||||||
@media screen and (max-width: 1000px) {
|
@media screen and (max-width: 1000px) {
|
||||||
|
|
||||||
.title, .event-form, .input-field, .nav-link {
|
.title, .event-form, .input-field, .button {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {eventStore} from "@/stores/EventStore.ts";
|
import {eventCreationStore} from "@/stores/EventCreationStore.ts";
|
||||||
import {onBeforeMount, ref} from "vue";
|
import {onBeforeMount, ref} from "vue";
|
||||||
import {useRoute} from "vue-router";
|
import {useRoute} from "vue-router";
|
||||||
import {DateHelper} from "@/helpers/DateHelper.ts";
|
import {DateHelper} from "@/helpers/DateHelper.ts";
|
||||||
@@ -7,7 +7,7 @@ import {Event} from "@/models/Event.ts";
|
|||||||
import ErrorBlock from "@/components/ErrorBlock.vue";
|
import ErrorBlock from "@/components/ErrorBlock.vue";
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const store = eventStore();
|
const store = eventCreationStore();
|
||||||
const token = extractToken();
|
const token = extractToken();
|
||||||
let event = ref<Event | undefined>();
|
let event = ref<Event | undefined>();
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user