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

Commit

Permalink
chore: solve conflits
Browse files Browse the repository at this point in the history
  • Loading branch information
giovaz94 committed Jan 31, 2024
2 parents 7c0ab13 + e62743c commit f2e8ae4
Show file tree
Hide file tree
Showing 15 changed files with 225 additions and 298 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";
}
}
9 changes: 5 additions & 4 deletions frontend/src/components/CarouselComponent.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<template>
<Carousel v-bind="settings" :breakpoints="breakpoints">
<Slide v-for="election in elections" :key="String(election.id)">
<ElectionCard :election="election"/>
<ElectionCard :election="election" :time="time"/>
</Slide>
<template #pagination="{ pagesCount, currentPage, setCurrentPage }">
<div class="pagination">
Expand All @@ -25,10 +25,11 @@
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[],
time: number,
}>()
const settings = {
Expand Down
49 changes: 24 additions & 25 deletions frontend/src/components/ElectionCardComponent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,38 +5,35 @@
<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) }}
</li>
<li>
<div class="card links mx-auto">
<ul>
<li>
<a :href="`/elections/${election.id}`">See details</a>
</li>
<li v-if="isOpen(election)">
<a :href="`/vote/${election.id}`">Cast a vote</a>
</li>
</ul>
</div>
<strong>End:</strong> {{formatDate(election.end, 'numeric')}} <br/> {{ formatTime(election.end) }}
</li>
</ul>
<div class="d-flex flex-column links">
<a :href="`/elections/${election.id}`">See details</a>
<a :href="`/vote/${election.id}`">Cast a vote</a>
</div>
</div>
</div>
</template>

<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
const props = defineProps<{
election: Voting,
time: number,
}>()
function isOpen(election: VotingWithStatus): boolean {
return election.status === 'open';
const now = ref(props.time);
function isOpen(election: Voting): boolean {
return getStatus(election, now.value) === 'open';
}
</script>

Expand All @@ -55,14 +52,16 @@ function isOpen(election: VotingWithStatus): boolean {
text-decoration: none;
}
div.links {
display: inline-block;
padding: 4%;
div.links a {
padding: 6px 0;
margin: 4px 0;
border-radius: 15px;
background-color: #edede9;
}
.links ul {
margin-left: 0;
padding-left: 0;
div.links a:hover {
font-weight: bold;
box-shadow: 1px 2px 5px rgba(200, 200, 200, 0.82);
}
.card {
Expand Down
121 changes: 61 additions & 60 deletions frontend/src/components/ElectionComponent.vue
Original file line number Diff line number Diff line change
@@ -1,62 +1,60 @@
<template>
<table>
<tr>
<td>
<div class="msg text-center">
<div class="row">
<p class="name"><a :href="`/election/${election.id}`" class="name">{{ election.goal }}</a></p>
</div>
<div class="row">
<p class="links-container">
<a :href="`/elections/${election.id}`" class="useful-link">Details</a>
<a :href="`/vote/${election.id}`" class="useful-link" v-if="isOpen(election)">Vote</a>
</p>
</div>
</div>
</td>
<td>
<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) }}
</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>
</div>
</td>
<td>
<div class="date" aria-label="end date">
<span class="first date"><svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" fill="currentColor" class="bi bi-hourglass-bottom" viewBox="0 0 16 16">
<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>
</div>
</td>
</tr>
</table>
<div class="row">
<div class="col msg text-center">
<div class="row">
<p class="name"><a :href="`/elections/${election.id}`" class="name">{{ election.goal }}</a></p>
</div>
<div class="d-flex justify-content-center flex-wrap">
<a :href="`/elections/${election.id}`" class="useful-link">Details</a>
<a :href="`/vote/${election.id}`" class="useful-link" v-if="isOpen(election)">Vote</a>
</div>
</div>
<div class="col date my-auto" 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>
{{formatDate(election.start).substring(0, 2)}}
</span>
<br/>
<span>{{capitalizeFirstLetter(formatDate(election.start, 'numeric').substring(3, 11))}}</span>
<br/>
<span>{{formatTime(election.start)}}</span>
</div>
<div class="col date my-auto" aria-label="end date">
<span class="first date">
<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" fill="currentColor" class="bi bi-hourglass-bottom" viewBox="0 0 16 16">
<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>{{capitalizeFirstLetter(formatDate(election.end, 'numeric').substring(3, 11))}}</span>
<br/>
<span>{{formatTime(election.end)}}</span>
</div>
</div>
</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';
function isOpen(election: Voting): boolean {
return getStatus(election, now.value) === 'open';
}
defineProps<{
election: VotingWithStatus
const props = defineProps<{
election: Voting,
time: number,
}>()
const now = ref(props.time);
</script>

<style scoped>
table td + td {
border-left: 2px solid red;
}
div.date {
color: #0d6efd;
border-left: 2px solid #e6308a;
span.first {
font-weight: bold;
font-size: 2em;
Expand All @@ -69,28 +67,31 @@ div.date {
p.name {
font-weight: bold;
font-size: 1.1em;
color: #e6308a;
font-size: 1.2em;
margin: 4% 0;
}
p.links-container {
display: flex;
justify-content: space-between;
padding: 0 15%;
text-transform: uppercase;
}
a {
color: #e6308a;
text-decoration: none;
}
a.useful-link {
margin: 0 10px;
}
a:hover {
text-decoration: underline;
}
</style>
a.useful-link {
padding: 6px 10px;
margin: 6px;
border-radius: 15px;
border: 1px solid #0d6efd;
text-decoration: none;
color: #0d6efd;
}
a.useful-link:hover {
font-weight: bold;
box-shadow: 1px 2px 5px rgba(200, 200, 200, 0.82);
}
</style>
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
19 changes: 1 addition & 18 deletions frontend/src/router/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@ import CodeInsertionView from '@/views/CodeInsertionView.vue';
import VoteView from "@/views/VoteView.vue";
import ElectionsView from "@/views/ElectionsView.vue";
import {useAuthStore} from "@/stores/auth";
import ErrorView from "@/views/ErrorView.vue";
import NoPermissionView from "@/views/NoPermissionView.vue";
import {Role} from "@/commons/utils";
import 'vue-router'
import {useNotificationsStore} from "@/stores/notificationsStore";
Expand Down Expand Up @@ -116,26 +114,11 @@ const router = createRouter({
path: '/user/notifications',
name: 'notifications',
component: NotificationsView,
beforeEnter: async (to, from) => {
const notificationsStore = useNotificationsStore();
await notificationsStore.getAllNotifications();
},
beforeEnter: async () => await useNotificationsStore().getAllNotifications(),
meta: {
allowed: [Role.User, Role.Admin]
}
},
{
// TODO: change path
path: '/error',
name: 'error',
component: ErrorView,
},
{
// TODO: change path
path: '/no-permission',
name: 'no-permission',
component: NoPermissionView,
},
{
path: '/:pathMatch(.*)*',
name: 'not-found',
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
Loading

0 comments on commit f2e8ae4

Please sign in to comment.