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

Commit f2e8ae4

Browse files
committed
chore: solve conflits
2 parents 7c0ab13 + e62743c commit f2e8ae4

15 files changed

+225
-298
lines changed

frontend/src/commons/utils.ts

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
* Contains utility functions used by multiple components.
33
*/
44

5+
import type {Voting} from "@/stores/voting";
6+
57
/** An enum representing the possible roles of a user. */
68
export enum Role { User = 'user', Admin = 'admin' }
79

@@ -17,12 +19,13 @@ export function toRole(roleString: string | null): Role | null {
1719
/**
1820
* Formats the given date as a string with the format "dd MMM yy" w.r.t. italian timezone (e.g. "01 Jan 21")
1921
* @param date the date to format
22+
* @param yearsDigit the number of digits to use for the year (2 or 4)
2023
*/
21-
export function formatDate(date: Date): string {
24+
export function formatDate(date: Date, yearsDigit: '2-digit' | 'numeric' | undefined = '2-digit'): string {
2225
const options: Intl.DateTimeFormatOptions = {
2326
day: '2-digit',
2427
month: 'short',
25-
year: '2-digit',
28+
year: yearsDigit,
2629
};
2730
return new Intl.DateTimeFormat('it-IT', options).format(date).toString();
2831
}
@@ -55,3 +58,29 @@ export function highestOf(data: Record<string, number>): { key: string, value: n
5558
}
5659
return { key: maxKey, value: maxValue as number };
5760
}
61+
62+
/**
63+
* Capitalizes the first letter of the given string.
64+
* @param str the string to capitalize.
65+
*/
66+
export function capitalizeFirstLetter(str: string) {
67+
if (str === '') {
68+
return str;
69+
}
70+
return str.charAt(0).toUpperCase() + str.slice(1);
71+
}
72+
73+
/**
74+
* Returns the status of the given election w.r.t. the given date.
75+
* @param election the election to analyze.
76+
* @param now the date to use as reference.
77+
*/
78+
export function getStatus(election: Voting, now: number): string {
79+
if (now >= election.start.getTime() && now < election.end.getTime()) {
80+
return "open";
81+
} else if (now >= election.end.getTime()) {
82+
return "closed";
83+
} else {
84+
return "soon";
85+
}
86+
}

frontend/src/components/CarouselComponent.vue

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<template>
22
<Carousel v-bind="settings" :breakpoints="breakpoints">
33
<Slide v-for="election in elections" :key="String(election.id)">
4-
<ElectionCard :election="election"/>
4+
<ElectionCard :election="election" :time="time"/>
55
</Slide>
66
<template #pagination="{ pagesCount, currentPage, setCurrentPage }">
77
<div class="pagination">
@@ -25,10 +25,11 @@
2525
import ElectionCard from "@/components/ElectionCardComponent.vue";
2626
import { Carousel, Navigation, Slide } from 'vue3-carousel'
2727
import 'vue3-carousel/dist/carousel.css'
28-
import type {VotingWithStatus} from "@/stores/voting";
28+
import type {Voting} from "@/stores/voting";
2929
30-
const props = defineProps<{
31-
elections: VotingWithStatus[]
30+
defineProps<{
31+
elections: Voting[],
32+
time: number,
3233
}>()
3334
3435
const settings = {

frontend/src/components/ElectionCardComponent.vue

Lines changed: 24 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,38 +5,35 @@
55
<hr class="solid"/>
66
<ul class="election-props">
77
<li>
8-
<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) }}
8+
<strong>Start:</strong> {{formatDate(election.start, 'numeric')}} <br/> {{ formatTime(election.start) }}
99
</li>
1010
<li>
11-
<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) }}
12-
</li>
13-
<li>
14-
<div class="card links mx-auto">
15-
<ul>
16-
<li>
17-
<a :href="`/elections/${election.id}`">See details</a>
18-
</li>
19-
<li v-if="isOpen(election)">
20-
<a :href="`/vote/${election.id}`">Cast a vote</a>
21-
</li>
22-
</ul>
23-
</div>
11+
<strong>End:</strong> {{formatDate(election.end, 'numeric')}} <br/> {{ formatTime(election.end) }}
2412
</li>
2513
</ul>
14+
<div class="d-flex flex-column links">
15+
<a :href="`/elections/${election.id}`">See details</a>
16+
<a :href="`/vote/${election.id}`">Cast a vote</a>
17+
</div>
2618
</div>
2719
</div>
2820
</template>
2921

3022
<script setup lang="ts">
3123
32-
import type {VotingWithStatus} from "@/stores/voting";
24+
import type {Voting} from "@/stores/voting";
25+
import {ref} from "vue";
26+
import {formatDate, formatTime, getStatus} from "@/commons/utils";
3327
34-
defineProps<{
35-
election: VotingWithStatus
28+
const props = defineProps<{
29+
election: Voting,
30+
time: number,
3631
}>()
3732
38-
function isOpen(election: VotingWithStatus): boolean {
39-
return election.status === 'open';
33+
const now = ref(props.time);
34+
35+
function isOpen(election: Voting): boolean {
36+
return getStatus(election, now.value) === 'open';
4037
}
4138
</script>
4239

@@ -55,14 +52,16 @@ function isOpen(election: VotingWithStatus): boolean {
5552
text-decoration: none;
5653
}
5754
58-
div.links {
59-
display: inline-block;
60-
padding: 4%;
55+
div.links a {
56+
padding: 6px 0;
57+
margin: 4px 0;
58+
border-radius: 15px;
59+
background-color: #edede9;
6160
}
6261
63-
.links ul {
64-
margin-left: 0;
65-
padding-left: 0;
62+
div.links a:hover {
63+
font-weight: bold;
64+
box-shadow: 1px 2px 5px rgba(200, 200, 200, 0.82);
6665
}
6766
6867
.card {
Lines changed: 61 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,60 @@
11
<template>
2-
<table>
3-
<tr>
4-
<td>
5-
<div class="msg text-center">
6-
<div class="row">
7-
<p class="name"><a :href="`/election/${election.id}`" class="name">{{ election.goal }}</a></p>
8-
</div>
9-
<div class="row">
10-
<p class="links-container">
11-
<a :href="`/elections/${election.id}`" class="useful-link">Details</a>
12-
<a :href="`/vote/${election.id}`" class="useful-link" v-if="isOpen(election)">Vote</a>
13-
</p>
14-
</div>
15-
</div>
16-
</td>
17-
<td>
18-
<div class="date" aria-label="start date">
19-
<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">
20-
<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"/>
21-
</svg> {{ ("0" + election.start.getUTCDate()).slice(-2) }}
22-
</span><br/>
23-
<span>{{ election.start.toLocaleString('default', { month: 'short' }) }} {{election.start.getFullYear() }}</span><br/>
24-
<span>{{ ("0" + election.start.getHours()).slice(-2) }}:{{ ("0" + election.start.getMinutes()).slice(-2) }}</span>
25-
</div>
26-
</td>
27-
<td>
28-
<div class="date" aria-label="end date">
29-
<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">
30-
<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"/>
31-
</svg> {{ ("0" + election.end.getUTCDate()).slice(-2) }}
32-
</span><br/>
33-
<span>{{ election.end.toLocaleString('default', { month: 'short' }) }} {{election.end.getFullYear() }}</span><br/>
34-
<span>{{ ("0" + election.end.getHours()).slice(-2) }}:{{ ("0" + election.end.getMinutes()).slice(-2) }}</span>
35-
</div>
36-
</td>
37-
</tr>
38-
</table>
2+
<div class="row">
3+
<div class="col msg text-center">
4+
<div class="row">
5+
<p class="name"><a :href="`/elections/${election.id}`" class="name">{{ election.goal }}</a></p>
6+
</div>
7+
<div class="d-flex justify-content-center flex-wrap">
8+
<a :href="`/elections/${election.id}`" class="useful-link">Details</a>
9+
<a :href="`/vote/${election.id}`" class="useful-link" v-if="isOpen(election)">Vote</a>
10+
</div>
11+
</div>
12+
<div class="col date my-auto" aria-label="start date">
13+
<span class="first">
14+
<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" fill="currentColor" class="bi bi-hourglass-top" viewBox="0 0 16 16">
15+
<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"/>
16+
</svg>
17+
{{formatDate(election.start).substring(0, 2)}}
18+
</span>
19+
<br/>
20+
<span>{{capitalizeFirstLetter(formatDate(election.start, 'numeric').substring(3, 11))}}</span>
21+
<br/>
22+
<span>{{formatTime(election.start)}}</span>
23+
</div>
24+
<div class="col date my-auto" aria-label="end date">
25+
<span class="first date">
26+
<svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" fill="currentColor" class="bi bi-hourglass-bottom" viewBox="0 0 16 16">
27+
<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"/>
28+
</svg> {{ ("0" + election.end.getUTCDate()).slice(-2) }}
29+
</span>
30+
<br/>
31+
<span>{{capitalizeFirstLetter(formatDate(election.end, 'numeric').substring(3, 11))}}</span>
32+
<br/>
33+
<span>{{formatTime(election.end)}}</span>
34+
</div>
35+
</div>
3936
</template>
4037

4138
<script setup lang="ts">
42-
import type {VotingWithStatus} from "@/stores/voting";
39+
import type {Voting} from "@/stores/voting";
40+
import {capitalizeFirstLetter, formatDate, formatTime, getStatus} from "@/commons/utils";
41+
import {ref} from "vue";
4342
44-
function isOpen(election: VotingWithStatus): boolean {
45-
return election.status === 'open';
43+
function isOpen(election: Voting): boolean {
44+
return getStatus(election, now.value) === 'open';
4645
}
47-
defineProps<{
48-
election: VotingWithStatus
46+
const props = defineProps<{
47+
election: Voting,
48+
time: number,
4949
}>()
50+
51+
const now = ref(props.time);
5052
</script>
5153

5254
<style scoped>
53-
54-
table td + td {
55-
border-left: 2px solid red;
56-
}
57-
5855
div.date {
5956
color: #0d6efd;
57+
border-left: 2px solid #e6308a;
6058
span.first {
6159
font-weight: bold;
6260
font-size: 2em;
@@ -69,28 +67,31 @@ div.date {
6967
7068
p.name {
7169
font-weight: bold;
72-
font-size: 1.1em;
73-
color: #e6308a;
70+
font-size: 1.2em;
7471
margin: 4% 0;
75-
}
76-
77-
p.links-container {
78-
display: flex;
79-
justify-content: space-between;
80-
padding: 0 15%;
72+
text-transform: uppercase;
8173
}
8274
8375
a {
8476
color: #e6308a;
8577
text-decoration: none;
8678
}
8779
88-
a.useful-link {
89-
margin: 0 10px;
90-
}
91-
9280
a:hover {
9381
text-decoration: underline;
9482
}
9583
96-
</style>
84+
a.useful-link {
85+
padding: 6px 10px;
86+
margin: 6px;
87+
border-radius: 15px;
88+
border: 1px solid #0d6efd;
89+
text-decoration: none;
90+
color: #0d6efd;
91+
}
92+
93+
a.useful-link:hover {
94+
font-weight: bold;
95+
box-shadow: 1px 2px 5px rgba(200, 200, 200, 0.82);
96+
}
97+
</style>

frontend/src/components/UserPropertyComponent.vue

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import {useForm} from "vee-validate";
4545
import * as yup from "yup";
4646
import {type User, useUserStore} from "@/stores/user";
47+
import {capitalizeFirstLetter} from "../commons/utils";
4748
4849
const props = defineProps<{
4950
property: string,
@@ -59,10 +60,6 @@
5960
6061
const isReadOnly = ref(true);
6162
62-
function capitalizeFirstLetter(str: string) {
63-
return str.charAt(0).toUpperCase() + str.slice(1);
64-
}
65-
6663
const {meta, errors, handleSubmit, defineField } = useForm({
6764
validationSchema: yup.object({
6865
refValue: props.validation,

frontend/src/router/index.ts

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,6 @@ import CodeInsertionView from '@/views/CodeInsertionView.vue';
1414
import VoteView from "@/views/VoteView.vue";
1515
import ElectionsView from "@/views/ElectionsView.vue";
1616
import {useAuthStore} from "@/stores/auth";
17-
import ErrorView from "@/views/ErrorView.vue";
18-
import NoPermissionView from "@/views/NoPermissionView.vue";
1917
import {Role} from "@/commons/utils";
2018
import 'vue-router'
2119
import {useNotificationsStore} from "@/stores/notificationsStore";
@@ -116,26 +114,11 @@ const router = createRouter({
116114
path: '/user/notifications',
117115
name: 'notifications',
118116
component: NotificationsView,
119-
beforeEnter: async (to, from) => {
120-
const notificationsStore = useNotificationsStore();
121-
await notificationsStore.getAllNotifications();
122-
},
117+
beforeEnter: async () => await useNotificationsStore().getAllNotifications(),
123118
meta: {
124119
allowed: [Role.User, Role.Admin]
125120
}
126121
},
127-
{
128-
// TODO: change path
129-
path: '/error',
130-
name: 'error',
131-
component: ErrorView,
132-
},
133-
{
134-
// TODO: change path
135-
path: '/no-permission',
136-
name: 'no-permission',
137-
component: NoPermissionView,
138-
},
139122
{
140123
path: '/:pathMatch(.*)*',
141124
name: 'not-found',

frontend/src/stores/voting.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,6 @@ export interface Voting {
1919
results: Record<string, number>;
2020
}
2121

22-
export interface VotingWithStatus extends Voting {
23-
status?: string
24-
}
25-
2622
export interface VotingCreation {
2723
goal: string
2824
voters: string

0 commit comments

Comments
 (0)