Skip to content

Commit

Permalink
Add response page and favicon and title.
Browse files Browse the repository at this point in the history
  • Loading branch information
etopiei committed Sep 10, 2024
1 parent 70b1577 commit 5c1bcc0
Show file tree
Hide file tree
Showing 12 changed files with 146 additions and 104 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ start_server
*.local.cr
.env
/tmp
frontend/node_modules
frontend/node_modules
.DS_Store
4 changes: 2 additions & 2 deletions frontend/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<link rel="icon" type="image/png" href="/converge.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + Vue + TS</title>
<title>Converge</title>
</head>
<body>
<div id="app"></div>
Expand Down
Binary file added frontend/public/converge.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 0 additions & 1 deletion frontend/public/vite.svg

This file was deleted.

12 changes: 11 additions & 1 deletion frontend/src/App.vue
Original file line number Diff line number Diff line change
@@ -1,21 +1,30 @@
<script setup lang="ts">
import CreateEvent from './components/CreateEvent.vue';
import ResultView from './components/ResultView.vue';
import GuestView from './components/GuestView.vue';
import ResultView from './components/ResultView.vue';
import SelectView from './components/SelectView.vue';
let renderResultView = false;
let guestCreationView = false;
let eventCreationView = false;
let selectView = false;
let event_uuid: string | null = "";
let guest_id: string | null = "";
let params = new URLSearchParams(document.location.search);
if (params.has("event_uuid")) {
event_uuid = params.get("event_uuid");
}
if (params.has("guest_id")) {
guest_id = params.get("guest_id");
}
if (params.has("page") && params.get("page") === "results") {
renderResultView = true;
} else if (event_uuid && guest_id) {
selectView = true;
} else if (event_uuid) {
guestCreationView = true;
} else {
Expand All @@ -30,6 +39,7 @@ if (params.has("page") && params.get("page") === "results") {
<ResultView v-if="renderResultView" :eventUuid="event_uuid"/>
<CreateEvent v-if="eventCreationView"/>
<GuestView v-if="guestCreationView" :eventUuid="event_uuid"/>
<SelectView v-if="selectView" :eventUuid="event_uuid" :guestId="guest_id"/>
</template>

<style scoped>
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ type EventData = {
type EventCreateResponse = {
name: string,
host_name: string,
host_id: number,
event_uuid: string
};

Expand Down Expand Up @@ -71,7 +72,7 @@ type OkResponse = {
ok: true
};

const API_BASE = "http://localhost:3000"
const API_BASE = "https://converge.etopiei.com"

export const createEvent: (eventData: EventData) => Promise<EventCreateResponse> = async (eventData: EventData) => {
const response = await fetch(API_BASE + "/api/events", {method: "POST", headers: {"Content-Type": "application/json"}, body: JSON.stringify(eventData)});
Expand Down
12 changes: 6 additions & 6 deletions frontend/src/components/CreateEvent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ const dates: Ref<any[] | undefined> = ref();
const eventName = ref('');
const hostName = ref('');
const eventUuid = ref('');
const hostId = ref(0);
const loading = ref(false);
const copiedFlash = ref(false);
const LINK_BASE = "http://localhost:5173/?event_uuid=";
const LINK_BASE = "https://converge.etopiei.com/?event_uuid=";
const createEventFromData = async () => {
if (eventName.value === '' || hostName.value === '' || dates.value === undefined) {
Expand All @@ -29,13 +30,12 @@ const createEventFromData = async () => {
});
eventUuid.value = eventResponse.event_uuid;
hostId.value = eventResponse.host_id;
loading.value = false;
};
const goToEventPage = () => {
// TODO: Ideally we would remember that we know the hosts name and register them
// This might require splitting up the pages more though.
window.location.href = `/?event_uuid=${eventUuid.value}`;
const goToResponseSelectionPage = () => {
window.location.href = `/?event_uuid=${eventUuid.value}&guest_id=${hostId.value}`;
};
const copyLink = async () => {
Expand Down Expand Up @@ -66,7 +66,7 @@ const copyLink = async () => {
<p>
Once you've shared the link, set your own availability:
</p>
<button @click="goToEventPage">Set Responses</button>
<button @click="goToResponseSelectionPage">Set Responses</button>
</div>
</template>

Expand Down
97 changes: 6 additions & 91 deletions frontend/src/components/GuestView.vue
Original file line number Diff line number Diff line change
@@ -1,44 +1,23 @@
<script setup lang="ts">
import { Ref, onMounted, ref } from 'vue';
import { ResponseValue, SlotWithResponses, getEventData, getGuestList, registerGuest, sendResponses } from '../api/api';
import BigDate from './BigDate.vue';
import TapResponse from './TapResponse.vue';
import { getEventData, getGuestList, registerGuest } from '../api/api';
const props = defineProps(['eventUuid'])
const guestName = ref('');
const guestId = ref(0);
const guestError = ref('');
const eventData = ref();
const slotData: Ref<Record<number, ResponseValue>> = ref({});
const modeSelected = ref(false);
const mode = ref('');
const guestOptions: Ref<{text: string, value: string}[]> = ref([]);
const buttonClicked = ref(false);
onMounted(async () => {
if (props.eventUuid) {
eventData.value = await getEventData(props.eventUuid);
}
});
const setSlotDataForGuest = () => {
eventData.value.slots.forEach((slot: SlotWithResponses) => {
let guestResponse: ResponseValue;
let currentResponseForSlot = slot.responses.find(response => response.guest.id === Number(guestId.value));
if (currentResponseForSlot) {
guestResponse = currentResponseForSlot.response;
} else {
guestResponse = "yes";
}
slotData.value[slot.id] = guestResponse;
});
};
const setButtonClicked = () => {
buttonClicked.value = true;
setSlotDataForGuest();
};
const setMode = async (newMode: string) => {
mode.value = newMode;
Expand All @@ -57,44 +36,18 @@ const createGuest = async () => {
guestError.value = "Name has already been used for this event.";
} else {
guestId.value = response.guest_id
setButtonClicked();
}
};
const submit = async () => {
const responses = Object.entries(slotData.value).map(([slotId, response]) => {
return {
slot_id: Number(slotId),
response: response.toUpperCase() as ResponseValue,
};
});
const guestResponses = { guest_id: Number(guestId.value), responses };
const response = await sendResponses(props.eventUuid, guestResponses);
if (response.ok) {
window.location.href = `${window.location.protocol}//${window.location.hostname}${(location.port ? ':'+location.port: '')}/?page=results&event_uuid=${props.eventUuid}`;
goToSelectView();
}
};
const setYes = (slotId: number) => {
slotData.value[slotId] = "yes";
const goToSelectView = () => {
window.location.href = `/?event_uuid=${props.eventUuid}&guest_id=${guestId.value}`;
};
const setMaybe = (slotId: number) => {
slotData.value[slotId] = "maybe";
};
const setNo = (slotId: number) => {
slotData.value[slotId] = "no";
};
const selectAll = (going: boolean) => {
const funcToCall = going ? setYes : setNo;
Object.keys(slotData.value).forEach(slotId => funcToCall(Number(slotId)));
};
</script>

<template>
<div v-if="!guestId || !buttonClicked" class="guest-reg">
<div class="guest-reg">
<h3>Guest</h3>
<p>You have been invited to
<span :class="{eventName: eventData}">{{ eventData ? eventData.name : ' an event' }}</span>
Expand All @@ -119,36 +72,13 @@ const selectAll = (going: boolean) => {
</option>
</select>
</div>
<button @click="setButtonClicked">Edit Days Available</button>
<button @click="goToSelectView">Edit Days Available</button>
</div>
</div>
<div v-else-if="buttonClicked" class="response-view">
<button class="all-buttons yes" @click="selectAll(true)">Select 'Yes' For All</button>
<button class="all-buttons no" @click="selectAll(false)">Select 'No' For All</button>
<p>Click coloured tiles to change response.</p>
<div class="slots">
<div class="slot-row" v-if="guestId && slotData && Object.keys(slotData).length === eventData.slots.length" v-for="slot in eventData.slots">
<BigDate :date=slot.start />
<TapResponse
@yes="setYes(slot.id)"
@no="setNo(slot.id)"
@maybe="setMaybe(slot.id)"
:response=slotData[slot.id]
/>
</div>
</div>
<button @click="submit">Submit Responses</button>
</div>
<a :href="`/?page=results&event_uuid=${props.eventUuid}`">Check responses</a>
</template>

<style scoped>
.slot-row {
display: flex;
gap: 16px;
margin-top: 8px;
margin-bottom: 8px;
}
.input-container {
display: inline-flex;
flex-direction: column;
Expand Down Expand Up @@ -176,19 +106,4 @@ const selectAll = (going: boolean) => {
.errorText {
color: red;
}
.slots {
display: flex;
flex-direction: column;
align-items: center;
}
.all-buttons {
margin-left: 4px;
margin-right: 4px;
}
.yes {
background-color: #68ac52;
}
.no {
background-color: red;
}
</style>
109 changes: 109 additions & 0 deletions frontend/src/components/SelectView.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
<script setup lang="ts">
import BigDate from './BigDate.vue';
import TapResponse from './TapResponse.vue';
import { sendResponses, ResponseValue, getEventData, SlotWithResponses } from '../api/api';
import { Ref, onMounted, ref } from 'vue';
const props = defineProps(['eventUuid', 'guestId'])
const eventData = ref();
const slotData: Ref<Record<number, ResponseValue>> = ref({});
onMounted(async () => {
if (props.eventUuid) {
eventData.value = await getEventData(props.eventUuid);
setSlotDataForGuest();
}
});
const setSlotDataForGuest = () => {
eventData.value.slots.forEach((slot: SlotWithResponses) => {
let guestResponse: ResponseValue;
let currentResponseForSlot = slot.responses.find(response => response.guest.id === Number(props.guestId));
if (currentResponseForSlot) {
guestResponse = currentResponseForSlot.response;
} else {
guestResponse = "yes";
}
slotData.value[slot.id] = guestResponse;
});
};
const submit = async () => {
const responses = Object.entries(slotData.value).map(([slotId, response]) => {
return {
slot_id: Number(slotId),
response: response.toUpperCase() as ResponseValue,
};
});
const guestResponses = { guest_id: Number(props.guestId), responses };
const response = await sendResponses(props.eventUuid, guestResponses);
if (response.ok) {
window.location.href = `${window.location.protocol}//${window.location.hostname}${(location.port ? ':'+location.port: '')}/?page=results&event_uuid=${props.eventUuid}`;
}
};
const setYes = (slotId: number) => {
slotData.value[slotId] = "yes";
};
const setMaybe = (slotId: number) => {
slotData.value[slotId] = "maybe";
};
const setNo = (slotId: number) => {
slotData.value[slotId] = "no";
};
const selectAll = (going: boolean) => {
const funcToCall = going ? setYes : setNo;
Object.keys(slotData.value).forEach(slotId => funcToCall(Number(slotId)));
};
</script>

<template>
<div v-if="eventData">
<button class="all-buttons yes" @click="selectAll(true)">Select 'Yes' For All</button>
<button class="all-buttons no" @click="selectAll(false)">Select 'No' For All</button>
<p>Click coloured tiles to change response.</p>
<div class="slots">
<div class="slot-row" v-if="props.guestId && slotData && Object.keys(slotData).length === eventData.slots.length" v-for="slot in eventData.slots">
<BigDate :date=slot.start />
<TapResponse
@yes="setYes(slot.id)"
@no="setNo(slot.id)"
@maybe="setMaybe(slot.id)"
:response=slotData[slot.id]
/>
</div>
</div>
<button @click="submit">Submit Responses</button>
</div>
<div v-else>
... Loading ...
</div>
</template>

<style scoped>
.slot-row {
display: flex;
gap: 16px;
margin-top: 8px;
margin-bottom: 8px;
}
.all-buttons {
margin-left: 4px;
margin-right: 4px;
}
.yes {
background-color: #68ac52;
}
.no {
background-color: red;
}
.slots {
display: flex;
flex-direction: column;
align-items: center;
}
</style>
4 changes: 3 additions & 1 deletion src/actions/event/event.cr
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ class Api::Event::Create < ApiAction
post "/api/events/" do
serialized_event = EventCreateSerializer.from_json(params.body)
event = SaveEvent.create!(serialized_event)
json EventSerializer.new(event)
SaveGuestHost.create!(event_id: event.id, name: event.host_name)
event_with_host = EventQuery.new.preload_guests.event_uuid(event.event_uuid).first
json EventSerializer.new(event_with_host)
end
end

Expand Down
Loading

0 comments on commit 5c1bcc0

Please sign in to comment.