Skip to content
This repository has been archived by the owner on May 10, 2024. It is now read-only.

Commit

Permalink
Merge branch 'frontend/refactor' into develop
Browse files Browse the repository at this point in the history
  • Loading branch information
Luca1313 committed Jan 31, 2024
2 parents c69e0aa + 691c587 commit 32aa328
Show file tree
Hide file tree
Showing 8 changed files with 112 additions and 76 deletions.
33 changes: 31 additions & 2 deletions frontend/src/commons/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
* Contains utility functions used by multiple components.
*/

import type {Voting} from "@/stores/voting";

/** An enum representing the possible roles of a user. */
export enum Role { User = 'user', Admin = 'admin' }

Expand All @@ -17,12 +19,13 @@ export function toRole(roleString: string | null): Role | null {
/**
* Formats the given date as a string with the format "dd MMM yy" w.r.t. italian timezone (e.g. "01 Jan 21")
* @param date the date to format
* @param yearsDigit the number of digits to use for the year (2 or 4)
*/
export function formatDate(date: Date): string {
export function formatDate(date: Date, yearsDigit: '2-digit' | 'numeric' | undefined = '2-digit'): string {
const options: Intl.DateTimeFormatOptions = {
day: '2-digit',
month: 'short',
year: '2-digit',
year: yearsDigit,
};
return new Intl.DateTimeFormat('it-IT', options).format(date).toString();
}
Expand Down Expand Up @@ -55,3 +58,29 @@ export function highestOf(data: Record<string, number>): { key: string, value: n
}
return { key: maxKey, value: maxValue as number };
}

/**
* Capitalizes the first letter of the given string.
* @param str the string to capitalize.
*/
export function capitalizeFirstLetter(str: string) {
if (str === '') {
return str;
}
return str.charAt(0).toUpperCase() + str.slice(1);
}

/**
* Returns the status of the given election w.r.t. the given date.
* @param election the election to analyze.
* @param now the date to use as reference.
*/
export function getStatus(election: Voting, now: number): string {
if (now >= election.start.getTime() && now < election.end.getTime()) {
return "open";
} else if (now >= election.end.getTime()) {
return "closed";
} else {
return "soon";
}
}
6 changes: 3 additions & 3 deletions frontend/src/components/CarouselComponent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@
import ElectionCard from "@/components/ElectionCardComponent.vue";
import { Carousel, Navigation, Slide } from 'vue3-carousel'
import 'vue3-carousel/dist/carousel.css'
import type {VotingWithStatus} from "@/stores/voting";
import type {Voting} from "@/stores/voting";
const props = defineProps<{
elections: VotingWithStatus[]
defineProps<{
elections: Voting[]
}>()
const settings = {
Expand Down
27 changes: 21 additions & 6 deletions frontend/src/components/ElectionCardComponent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@
<hr class="solid"/>
<ul class="election-props">
<li>
<strong>Start:</strong> {{ ("0" + election.start.getUTCDate()).slice(-2) }} {{ election.start.toLocaleString('default', { month: 'short' }) }} {{election.start.getFullYear() }} {{ election.start.getHours() }}:{{ ("0" + election.start.getMinutes()).slice(-2) }}
<strong>Start:</strong> {{formatDate(election.start, 'numeric')}} <br/> {{ formatTime(election.start) }}
</li>
<li>
<strong>End:</strong> {{ ("0" + election.end.getUTCDate()).slice(-2) }} {{ election.end.toLocaleString('default', { month: 'short' }) }} {{election.end.getFullYear() }} {{ election.end.getHours() }}:{{ ("0" + election.end.getMinutes()).slice(-2) }}
<strong>End:</strong> {{formatDate(election.end, 'numeric')}} <br/> {{ formatTime(election.end) }}
</li>
</ul>
<div class="d-flex flex-column links">
Expand All @@ -21,14 +21,29 @@

<script setup lang="ts">
import type {VotingWithStatus} from "@/stores/voting";
import type {Voting} from "@/stores/voting";
import {ref} from "vue";
import {formatDate, formatTime, getStatus} from "@/commons/utils";
defineProps<{
election: VotingWithStatus
election: Voting
}>()
function isOpen(election: VotingWithStatus): boolean {
return election.status === 'open';
const now = ref(new Date().getTime());
function scheduleUpdateNow() {
setTimeout(updateNow, 1000);
}
function updateNow() {
now.value = new Date().getTime();
scheduleUpdateNow();
}
scheduleUpdateNow();
function isOpen(election: Voting): boolean {
return getStatus(election, now.value) === 'open';
}
</script>

Expand Down
33 changes: 24 additions & 9 deletions frontend/src/components/ElectionComponent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@
<div class="date" aria-label="start date">
<span class="first"><svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" fill="currentColor" class="bi bi-hourglass-top" viewBox="0 0 16 16">
<path d="M2 14.5a.5.5 0 0 0 .5.5h11a.5.5 0 1 0 0-1h-1v-1a4.5 4.5 0 0 0-2.557-4.06c-.29-.139-.443-.377-.443-.59v-.7c0-.213.154-.451.443-.59A4.5 4.5 0 0 0 12.5 3V2h1a.5.5 0 0 0 0-1h-11a.5.5 0 0 0 0 1h1v1a4.5 4.5 0 0 0 2.557 4.06c.29.139.443.377.443.59v.7c0 .213-.154.451-.443.59A4.5 4.5 0 0 0 3.5 13v1h-1a.5.5 0 0 0-.5.5m2.5-.5v-1a3.5 3.5 0 0 1 1.989-3.158c.533-.256 1.011-.79 1.011-1.491v-.702s.18.101.5.101.5-.1.5-.1v.7c0 .701.478 1.236 1.011 1.492A3.5 3.5 0 0 1 11.5 13v1z"/>
</svg> {{ ("0" + election.start.getUTCDate()).slice(-2) }}
</svg> {{formatDate(election.start).substring(0, 2)}}
</span><br/>
<span>{{ election.start.toLocaleString('default', { month: 'short' }) }} {{election.start.getFullYear() }}</span><br/>
<span>{{ ("0" + election.start.getHours()).slice(-2) }}:{{ ("0" + election.start.getMinutes()).slice(-2) }}</span>
<span>{{capitalizeFirstLetter(formatDate(election.start, 'numeric').substring(3, 11))}}</span><br/>
<span>{{formatTime(election.start)}}</span>
</div>
</td>
<td>
Expand All @@ -30,22 +30,37 @@
<path d="M2 1.5a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-1v1a4.5 4.5 0 0 1-2.557 4.06c-.29.139-.443.377-.443.59v.7c0 .213.154.451.443.59A4.5 4.5 0 0 1 12.5 13v1h1a.5.5 0 0 1 0 1h-11a.5.5 0 1 1 0-1h1v-1a4.5 4.5 0 0 1 2.557-4.06c.29-.139.443-.377.443-.59v-.7c0-.213-.154-.451-.443-.59A4.5 4.5 0 0 1 3.5 3V2h-1a.5.5 0 0 1-.5-.5m2.5.5v1a3.5 3.5 0 0 0 1.989 3.158c.533.256 1.011.791 1.011 1.491v.702s.18.149.5.149.5-.15.5-.15v-.7c0-.701.478-1.236 1.011-1.492A3.5 3.5 0 0 0 11.5 3V2z"/>
</svg> {{ ("0" + election.end.getUTCDate()).slice(-2) }}
</span><br/>
<span>{{ election.end.toLocaleString('default', { month: 'short' }) }} {{election.end.getFullYear() }}</span><br/>
<span>{{ ("0" + election.end.getHours()).slice(-2) }}:{{ ("0" + election.end.getMinutes()).slice(-2) }}</span>
<span>{{capitalizeFirstLetter(formatDate(election.end, 'numeric').substring(3, 11))}}</span><br/>
<span>{{formatTime(election.end)}}</span>
</div>
</td>
</tr>
</table>
</template>

<script setup lang="ts">
import type {VotingWithStatus} from "@/stores/voting";
import type {Voting} from "@/stores/voting";
import {capitalizeFirstLetter, formatDate, formatTime, getStatus} from "@/commons/utils";
import {ref} from "vue";
function isOpen(election: VotingWithStatus): boolean {
return election.status === 'open';
const now = ref(new Date().getTime());
function scheduleUpdateNow() {
setTimeout(updateNow, 1000);
}
function updateNow() {
now.value = new Date().getTime();
scheduleUpdateNow();
}
scheduleUpdateNow();
function isOpen(election: Voting): boolean {
return getStatus(election, now.value) === 'open';
}
defineProps<{
election: VotingWithStatus
election: Voting
}>()
</script>

Expand Down
5 changes: 1 addition & 4 deletions frontend/src/components/UserPropertyComponent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import {useForm} from "vee-validate";
import * as yup from "yup";
import {type User, useUserStore} from "@/stores/user";
import {capitalizeFirstLetter} from "../commons/utils";
const props = defineProps<{
property: string,
Expand All @@ -59,10 +60,6 @@
const isReadOnly = ref(true);
function capitalizeFirstLetter(str: string) {
return str.charAt(0).toUpperCase() + str.slice(1);
}
const {meta, errors, handleSubmit, defineField } = useForm({
validationSchema: yup.object({
refValue: props.validation,
Expand Down
4 changes: 0 additions & 4 deletions frontend/src/stores/voting.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,6 @@ export interface Voting {
results: Record<string, number>;
}

export interface VotingWithStatus extends Voting {
status?: string
}

export interface VotingCreation {
goal: string
voters: string
Expand Down
40 changes: 16 additions & 24 deletions frontend/src/views/DashboardView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,45 +14,37 @@ import PageTitle from "@/components/PageTitleComponent.vue";
import Breadcrumb from "@/components/BreadcrumbComponent.vue";
import {computed, onMounted, reactive, ref, type Ref} from "vue";
import router from "@/router";
import {useVotingStore, type Voting, type VotingWithStatus} from "@/stores/voting";
import {useVotingStore, type Voting} from "@/stores/voting";
import {capitalizeFirstLetter, getStatus} from "@/commons/utils";
const votingStore = useVotingStore();
const data: Ref<VotingWithStatus[] | null> = ref(null);
const data: Ref<Voting[] | null> = ref(null);
onMounted(async () => {
await getVotings();
scheduleUpdateNow();
});
function getStatus(election: Voting): string {
const now = new Date();
if (now >= election.start && now < election.end) {
return "open";
} else if (now >= election.end) {
return "closed";
} else {
return "soon";
}
const now = ref(new Date().getTime());
function scheduleUpdateNow() {
setTimeout(updateNow, 1000);
}
function updateNow() {
now.value = new Date().getTime();
scheduleUpdateNow();
}
async function getVotings() {
try {
data.value = await votingStore.getVotings();
for (const voting of data.value) {
voting['status'] = getStatus(voting);
}
} catch (e: any) {
console.error(e);
await router.push({name: "not-found"})
}
}
function capitalizeFirstLetter(str: string) {
if (str === '') {
return str;
}
return str.charAt(0).toUpperCase() + str.slice(1);
}
function sortElectionsByDate(elections: Voting[], prop: keyof Voting = 'start'): Voting[] {
return elections.sort((a: Voting, b: Voting) => a[prop as keyof typeof a] - b[prop as keyof typeof b]);
}
Expand All @@ -66,23 +58,23 @@ const reactiveVotings = computed(() => {
const getOpen = computed(() => {
if (reactiveVotings.value) {
return reactiveVotings.value.filter((election: VotingWithStatus) => election.status === 'open')
return reactiveVotings.value.filter((election: Voting) => getStatus(election, now.value) === 'open')
} else {
return []
}
});
const getClosed = computed(() => {
if (reactiveVotings.value) {
return reactiveVotings.value.filter((election: VotingWithStatus) => election.status === 'closed')
return reactiveVotings.value.filter((election: Voting) => getStatus(election, now.value) === 'closed')
} else {
return []
}
});
const getSoon = computed(() => {
if (reactiveVotings.value) {
return reactiveVotings.value.filter((election: VotingWithStatus) => election.status === 'soon')
return reactiveVotings.value.filter((election: Voting) => getStatus(election, now.value) === 'soon')
} else {
return []
}
Expand Down
40 changes: 16 additions & 24 deletions frontend/src/views/ElectionsView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,33 +5,32 @@ import PageTitle from '@/components/PageTitleComponent.vue'
import {useRoute} from "vue-router";
import {computed, onMounted, reactive, type Ref, ref} from 'vue'
import ElectionComponent from "@/components/ElectionComponent.vue";
import {useVotingStore, type Voting, type VotingWithStatus} from "@/stores/voting";
import {useVotingStore, type Voting} from "@/stores/voting";
import router from "@/router";
import {capitalizeFirstLetter, getStatus} from "@/commons/utils";
const votingStore = useVotingStore();
const data: Ref<VotingWithStatus[] | null> = ref(null);
const data: Ref<Voting[] | null> = ref(null);
onMounted(async () => {
await getVotings();
scheduleUpdateNow();
})
function getStatus(election: Voting): string {
const now = new Date();
if (now >= election.start && now < election.end) {
return "Open";
} else if (now >= election.end) {
return "Closed";
} else {
return "Soon";
}
const now = ref(new Date().getTime());
function scheduleUpdateNow() {
setTimeout(updateNow, 1000);
}
function updateNow() {
now.value = new Date().getTime();
scheduleUpdateNow();
}
async function getVotings() {
try {
data.value = await votingStore.getVotings();
for (const voting of data.value) {
voting['status'] = getStatus(voting);
}
} catch (e: any) {
console.error(e);
await router.push({name: "not-found"})
Expand All @@ -46,13 +45,6 @@ if (qualifier && ['all', 'open', 'closed', 'soon'].includes(qualifier)) {
picked.value = qualifier;
}
function capitalizeFirstLetter(str: string) {
if (str === '') {
return str;
}
return str.charAt(0).toUpperCase() + str.slice(1);
}
function sortElectionsByDate(elections: Voting[], prop: keyof Voting = 'start'): Voting[] {
return elections.sort((a: Voting, b: Voting) => a[prop as keyof typeof a] - b[prop as keyof typeof b]);
}
Expand All @@ -72,23 +64,23 @@ const reactiveVotings = computed(() => {
const getOpen = computed(() => {
if (reactiveVotings.value) {
return reactiveVotings.value.filter((election: VotingWithStatus) => election.status === 'open')
return reactiveVotings.value.filter((election: Voting) => getStatus(election, now.value) === 'open')
} else {
return []
}
});
const getClosed = computed(() => {
if (reactiveVotings.value) {
return reactiveVotings.value.filter((election: VotingWithStatus) => election.status === 'closed')
return reactiveVotings.value.filter((election: Voting) => getStatus(election, now.value) === 'closed')
} else {
return []
}
});
const getSoon = computed(() => {
if (reactiveVotings.value) {
return reactiveVotings.value.filter((election: VotingWithStatus) => election.status === 'soon')
return reactiveVotings.value.filter((election: Voting) => getStatus(election, now.value) === 'soon')
} else {
return []
}
Expand Down

0 comments on commit 32aa328

Please sign in to comment.