Skip to content

Commit

Permalink
Add UserProfileView, update classes and resources accordingly.
Browse files Browse the repository at this point in the history
Change app title
  • Loading branch information
massijay committed Sep 11, 2022
1 parent 3a54076 commit 3ecdbcc
Show file tree
Hide file tree
Showing 16 changed files with 291 additions and 45 deletions.
2 changes: 1 addition & 1 deletion TripRecorder-vue/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
<title>TripRecorder</title>
</head>
<body>
<div id="app"></div>
Expand Down
6 changes: 6 additions & 0 deletions TripRecorder-vue/src/router/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import LoginView from '../views/LoginView.vue'
import RegisterView from '../views/RegisterView.vue'
import TripDetailView from '../views/TripDetailView.vue'
import TripFormView from '../views/TripFormView.vue'
import UserProfileView from '../views/UserProfileView.vue'
import {useAccountStore} from "../stores/account";

const router = createRouter({
Expand Down Expand Up @@ -41,6 +42,11 @@ const router = createRouter({
path: 'trips/:trip_id/edit',
name: 'trip_edit',
component: TripFormView
},
{
path: 'profile',
name: 'profile',
component: UserProfileView
}
]
},
Expand Down
8 changes: 7 additions & 1 deletion TripRecorder-vue/src/stores/account.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ export const useAccountStore = defineStore('account', () => {
return axios.post(import.meta.env.VITE_API_URL + '/auth/register', user);
}

function updateUserData(userData) {
return axios.patch(import.meta.env.VITE_API_URL + '/profile', userData)
.then(response => user.value = response.data)
.catch(err => console.log("Impossibile caricare i dati dell'utente", err));
}

function login(username, password) {
return axios.post(import.meta.env.VITE_API_URL + '/auth/login',
{
Expand Down Expand Up @@ -90,6 +96,6 @@ export const useAccountStore = defineStore('account', () => {
}
}

return {isAuthenticated, user, getUserData, login, register, logout};
return {isAuthenticated, user, getUserData, updateUserData, login, register, logout};

})
24 changes: 18 additions & 6 deletions TripRecorder-vue/src/views/MainView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,22 @@
</RouterLink>
</li>
</ul>
<span class="navbar-text me-2">
Ciao {{ accountStore.user?.username }}
</span>
<a class="nav-link active" @click.prevent="logout()" href="#">Logout</a>
<ul class="navbar-nav">
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
Ciao {{ accountStore.user?.username }}
</a>
<ul class="dropdown-menu">
<li>
<RouterLink class="dropdown-item" :to="{name: 'profile'}">Profilo</RouterLink>
</li>
<li>
<hr class="dropdown-divider">
</li>
<li><a class="dropdown-item" @click.prevent="logout()" href="#">Logout</a></li>
</ul>
</li>
</ul>
</div>
</div>
</nav>
Expand All @@ -31,8 +43,8 @@ import {useAccountStore} from "../stores/account";
import router from "../router";
const accountStore = useAccountStore();
onBeforeMount(() => {
accountStore.getUserData();
onBeforeMount(async () => {
await accountStore.getUserData();
});
function logout() {
Expand Down
2 changes: 2 additions & 0 deletions TripRecorder-vue/src/views/RegisterView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ async function register() {
error.value = 'Username già utilizzato';
} else if (err.response.data?.errorCode === 2) {
error.value = 'Indirizzo email già utilizzato';
} else if (err.response.data?.errorCode === 3) {
error.value = 'Indirizzo email non valido';
} else {
error.value = 'Dati inseriti non validi';
}
Expand Down
9 changes: 7 additions & 2 deletions TripRecorder-vue/src/views/TripsView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,28 @@
<small>{{ DateTime.fromMillis(trip.date).toLocaleString(DateTime.DATE_FULL) }}</small>
</RouterLink>
<RouterLink :to="{name: 'trip_create', query:{date: dateFilter}}"
class="list-group-item list-group-item-action"
class="list-group-item list-group-item-action disabled"
v-if="trips.length === 0 && !isLoading">
<p class="mb-1 text-center text-muted">Non sono stati trovati viaggi,
<span class="text-decoration-underline text-primary">aggiungine uno!</span>
</p>
</RouterLink>
<a href="#" class="list-group-item list-group-item-action" v-if="isLoading">
<a href="#" class="list-group-item list-group-item-action disabled" v-if="isLoading">
<p class="mb-1 text-center text-muted">Caricamento...</p>
</a>
</div>
<p class="mb-1 text-center text-muted mt-3" v-if="!isLoading && !dateFilter.value && trips.length">
<InformationCircleIcon style="width: 1rem;" class="me-1 mb-1"></InformationCircleIcon>
Stai visualizzando gli utlimi 10 viaggi, seleziona una data per filtrarli
</p>
</div>
</template>

<script setup>
import {onBeforeMount, ref, watch} from "vue";
import axios from "axios";
import {DateTime} from "luxon";
import {InformationCircleIcon} from '@heroicons/vue/24/outline'
const dateFilter = ref('');
const trips = ref([]);
Expand Down
110 changes: 110 additions & 0 deletions TripRecorder-vue/src/views/UserProfileView.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<template>
<div class="container my-5" style="max-width: 320px;">
<h1>Il tuo profilo</h1>
<div class="alert alert-danger" role="alert" v-if="error!==''">
{{ error }}
</div>
<div class="alert alert-success" role="alert" v-if="success!==''">
{{ success }}
</div>
<form @submit.prevent="updateProfile">
<div class="mb-3">
<label for="username" class="form-label">Username</label>
<input type="text" required class="form-control" id="username"
v-model="userAccount.username">
</div>
<div class="mb-3">
<label for="email" class="form-label">Email</label>
<input type="email" required class="form-control" id="email"
v-model="userAccount.email">
</div>
<div class="form-check mb-2">
<input class="form-check-input" type="checkbox" value="" id="passwordCheckbox" v-model="passwordChange">
<label class="form-check-label" for="passwordCheckbox">
Vuoi modificare la password?
</label>
</div>
<div class="mb-3" v-if="passwordChange">
<label for="password" class="form-label">Password</label>
<input type="password" required class="form-control" id="password" v-model="userAccount.password">
</div>
<div class="mb-3" v-if="passwordChange">
<label for="confirm_password" class="form-label">Conferma password</label>
<input type="password" required class="form-control" id="confirm_password" v-model="confirmPassword">
</div>
<div class="row justify-content-end align-items-center">
<div class="col-auto">
<button type="submit" class="btn btn-primary" :disabled="loading">Aggiorna</button>
</div>
</div>
</form>
</div>
</template>

<script setup>
import {ref, watch} from "vue";
import {useAccountStore} from "../stores/account";
const confirmPassword = ref('');
const loading = ref(false);
const error = ref('');
const success = ref('');
const accountStore = useAccountStore();
const passwordChange = ref(false);
const userAccount = ref(accountStore.user ? {...accountStore.user} : {
username: '',
email: '',
password: ''
});
watch(accountStore, (state) => {
userAccount.value = {...state.user};
})
async function updateProfile() {
if (passwordChange.value && userAccount.value.password !== confirmPassword.value) {
error.value = 'Le password non coincidono';
return;
}
loading.value = true;
error.value = '';
success.value = '';
accountStore.updateUserData({
username: userAccount.value.username,
email: userAccount.value.email,
password: passwordChange.value ? userAccount.value.password : undefined
})
.then(() => {
loading.value = false;
success.value = 'Profilo aggiornato con successo!';
userAccount.value.password = '';
confirmPassword.value = '';
passwordChange.value = false;
setTimeout(() => {
success.value = '';
}, 2000);
})
.catch((err) => {
loading.value = false;
switch (err.response.status) {
case 422:
if (err.response.data?.errorCode === 1) {
error.value = 'Username già utilizzato';
} else if (err.response.data?.errorCode === 2) {
error.value = 'Indirizzo email già utilizzato';
} else if (err.response.data?.errorCode === 3) {
error.value = 'Indirizzo email non valido';
} else {
error.value = 'Dati inseriti non validi';
}
break;
default:
console.log("Errore nella registrazione", err);
error.value = 'Errore, riprova più tardi';
break;
}
});
}
</script>
17 changes: 5 additions & 12 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>
<!-- <maven.compiler.target>1.8</maven.compiler.target>-->
<!-- <maven.compiler.source>1.8</maven.compiler.source>-->
<maven.compiler.release>17</maven.compiler.release>
<junit.version>5.8.2</junit.version>
</properties>

Expand All @@ -39,21 +40,11 @@
<artifactId>jersey-cdi2-se</artifactId>
<version>2.34</version>
</dependency>
<!-- <dependency>-->
<!-- <groupId>javax.ws.rs</groupId>-->
<!-- <artifactId>javax.ws.rs-api</artifactId>-->
<!-- <version>2.1.1</version>-->
<!-- </dependency>-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
</dependency>
<!-- <dependency>-->
<!-- <groupId>org.glassfish</groupId>-->
<!-- <artifactId>jakarta.el</artifactId>-->
<!-- <version>4.0.1</version>-->
<!-- </dependency>-->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
Expand Down Expand Up @@ -93,9 +84,11 @@
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<configuration>
<source>17</source>
<target>17</target>
<release>17</release>
</configuration>
</plugin>
</plugins>
Expand Down
6 changes: 4 additions & 2 deletions src/main/java/com/mcris/triprecorder/filters/AuthFilter.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,21 +34,23 @@ public void filter(ContainerRequestContext containerRequestContext) {
containerRequestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).build());
return;
}
// TODO: needs to be sanitized?
String token = parts[1];
Session session = DBProvider.getInstance().getSession(token);
if (session == null || session.getUser() == null) {
containerRequestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).build());
return;
}
// TODO: check time zone?
Instant now = Instant.now();
Timestamp tsNow = Timestamp.from(now);
if (tsNow.after(session.getExpireAt())) {
DBProvider.getInstance().deleteSession(session.getToken());
containerRequestContext.abortWith(Response.status(Response.Status.UNAUTHORIZED).build());
return;
}
// TODO: magic numbers
Instant instant = Instant.now().plusSeconds(1800);
session.setExpireAt(Timestamp.from(instant));
session = DBProvider.getInstance().updateSession(session);
containerRequestContext.setSecurityContext(new SessionSecurityContext(session));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@
@Table(name = "sessions", schema = "trip_recorder")
@NamedQueries({
@NamedQuery(name = "Session.byToken",
query = "select s from Session s where s.token = :tokenString"),
query = "select s from Session s where s.token = :tokenUUID"),
@NamedQuery(name = "Session.deleteByToken",
query = "delete from Session s where s.token = :tokenString")
query = "delete from Session s where s.token = :tokenUUID")
})
public class Session {
// https://www.codementor.io/@petrepopescu/how-to-use-string-uuid-in-hibernate-with-mysql-1jrhjh6ef5
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@
query = "select t from Trip t where t.id = :tripId and t.userId = :userId"),
@NamedQuery(name = "Trip.removeByIdIfCorrectUser",
query = "delete from Trip t where t.id = :tripId and t.userId = :userId"),
@NamedQuery(name = "Trip.getListByUserAndDate",
query = "select t from Trip t where t.userId = :userId and t.date >= :tripDate and t.date < :nextDay")
@NamedQuery(name = "Trip.getLatestByUser",
query = "select t from Trip t where t.userId = :userId order by t.date desc"),
@NamedQuery(name = "Trip.getLatestByUserAndDate",
query = "select t from Trip t where t.userId = :userId and t.date >= :tripDate and t.date < :nextDay order by t.date desc")
})

public class Trip {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,11 @@ public class User implements Principal {
private Collection<Trip> trips;
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
@Basic
@Column(name = "created_at")
@Column(name = "created_at", insertable = false, updatable = false)
private Timestamp createdAt;
@JsonProperty(access = JsonProperty.Access.READ_ONLY)
@Basic
@Column(name = "updated_at")
@Column(name = "updated_at", insertable = false, updatable = false)
private Timestamp updatedAt;

public int getId() {
Expand Down
Loading

0 comments on commit 3ecdbcc

Please sign in to comment.