update du access token et update des cards
This commit is contained in:
parent
b36e3981ba
commit
d2b8c5ade4
22
src/App.vue
22
src/App.vue
@ -11,11 +11,13 @@ const { isAuthenticated, logout } = useAuth();
|
|||||||
const toastService = useToast();
|
const toastService = useToast();
|
||||||
const authenticationService = new AuthenticationService();
|
const authenticationService = new AuthenticationService();
|
||||||
|
|
||||||
|
const { login } = useAuth();
|
||||||
|
|
||||||
const fetchUser = async () => {
|
const fetchUser = async () => {
|
||||||
if (isAuthenticated.value) {
|
if (isAuthenticated.value) {
|
||||||
try {
|
try {
|
||||||
authenticatedUser.value = await authenticationService.findMe();
|
authenticatedUser.value = await authenticationService.findMe();
|
||||||
} catch (error) {
|
} catch (error: any) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -68,6 +70,11 @@ function handleLogout() {
|
|||||||
<style>
|
<style>
|
||||||
@import "styles.css";
|
@import "styles.css";
|
||||||
|
|
||||||
|
.nav-links-div {
|
||||||
|
display: flex;
|
||||||
|
gap: 10em;
|
||||||
|
}
|
||||||
|
|
||||||
.logout {
|
.logout {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
@ -131,8 +138,17 @@ nav {
|
|||||||
justify-content: space-around;
|
justify-content: space-around;
|
||||||
}
|
}
|
||||||
|
|
||||||
.nav-links-div {
|
@media (max-width: 820px) {
|
||||||
|
nav {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 10em;
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-links-div {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
gap: 0;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Book } from "@/models/book";
|
import type { Book } from "@/models/book";
|
||||||
import { BookService } from "@/services/book.service";
|
import { BookService } from "@/services/book.service";
|
||||||
|
import { convertState } from "@/utils";
|
||||||
import { onMounted, ref } from "vue";
|
import { onMounted, ref } from "vue";
|
||||||
|
|
||||||
const API_URL = import.meta.env.VITE_MBL_API_URL
|
const API_URL = import.meta.env.VITE_MBL_API_URL
|
||||||
@ -10,10 +11,23 @@ const bookService = new BookService();
|
|||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
books.value = await bookService.findBooks();
|
books.value = await bookService.findBooks();
|
||||||
|
|
||||||
|
books.value.map((book) => {
|
||||||
|
book.added_at = new Date(book.added_at);
|
||||||
|
});
|
||||||
|
books.value.sort((a, b) => {
|
||||||
|
if (a.updated_at > b.updated_at) {
|
||||||
|
return -1;
|
||||||
|
} else if (a.updated_at < b.updated_at) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
})
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
<main class="cards">
|
||||||
<div v-for="book in books" class="card">
|
<div v-for="book in books" class="card">
|
||||||
<img :src="API_URL + book.illustration" class="image"></img>
|
<img :src="API_URL + book.illustration" class="image"></img>
|
||||||
<div class="content">
|
<div class="content">
|
||||||
@ -21,16 +35,58 @@ onMounted(async () => {
|
|||||||
<span class="title">{{ book.title }}</span>
|
<span class="title">{{ book.title }}</span>
|
||||||
</a>
|
</a>
|
||||||
<p class="desc">
|
<p class="desc">
|
||||||
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Recusandae dolores, possimus
|
Livre écrit par <strong>{{ book.author }}</strong>
|
||||||
pariatur animi temporibus nesciunt praesentium
|
|
||||||
</p>
|
</p>
|
||||||
|
<p v-if="book.state === 'COMPLETED' || book.state === 'DROPPED'">Note {{ book.note }}/5</p>
|
||||||
|
<div class="state-div">
|
||||||
|
<p>{{ convertState(book.state) }}</p>
|
||||||
|
<div :class="book.state" class="state-indicator"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
|
.COMPLETED {
|
||||||
|
background-color: #2d4276;
|
||||||
|
}
|
||||||
|
|
||||||
|
.PLAN {
|
||||||
|
background-color: #747474;
|
||||||
|
}
|
||||||
|
|
||||||
|
.READING {
|
||||||
|
background-color: #338543;
|
||||||
|
}
|
||||||
|
|
||||||
|
.DROPPED {
|
||||||
|
background-color: #832f30;
|
||||||
|
}
|
||||||
|
|
||||||
|
.state-div {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.state-indicator {
|
||||||
|
width: 100%;
|
||||||
|
height: 10px;
|
||||||
|
border-radius: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cards {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
max-width: 300px;
|
max-width: 300px;
|
||||||
|
min-width: 300px;;
|
||||||
border-radius: 0.5rem;
|
border-radius: 0.5rem;
|
||||||
background-color: light-dark(#efedea, #282828);
|
background-color: light-dark(#efedea, #282828);
|
||||||
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
|
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
|
||||||
@ -46,6 +102,7 @@ onMounted(async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.image {
|
.image {
|
||||||
|
border-radius: 0.5rem;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 150px;
|
height: 150px;
|
||||||
|
|||||||
@ -1,33 +1,100 @@
|
|||||||
import type { User } from "@/models/user";
|
import type { User } from "@/models/user";
|
||||||
import axios from "axios";
|
import axios, { type AxiosInstance, type AxiosError } from "axios";
|
||||||
|
|
||||||
export class AuthenticationService {
|
export class AuthenticationService {
|
||||||
|
private client: AxiosInstance;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
const apiURL = import.meta.env.VITE_MBL_API_URL;
|
const apiURL = import.meta.env.VITE_MBL_API_URL;
|
||||||
axios.defaults.baseURL = `${apiURL}/api/`;
|
|
||||||
|
this.client = axios.create({
|
||||||
|
baseURL: `${apiURL}/api/`,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
this.setupInterceptors();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private setupInterceptors() {
|
||||||
|
this.client.interceptors.request.use(
|
||||||
|
(config) => {
|
||||||
|
const token = sessionStorage.getItem("access");
|
||||||
|
if (token) {
|
||||||
|
config.headers.Authorization = `Bearer ${token}`;
|
||||||
|
}
|
||||||
|
return config;
|
||||||
|
},
|
||||||
|
(error) => Promise.reject(error)
|
||||||
|
);
|
||||||
|
|
||||||
|
this.client.interceptors.response.use(
|
||||||
|
(response) => response,
|
||||||
|
async (error: AxiosError) => {
|
||||||
|
const originalRequest = error.config as any;
|
||||||
|
|
||||||
|
if (error.response?.status === 401 && !originalRequest._retry) {
|
||||||
|
originalRequest._retry = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const newData = await this.refreshToken();
|
||||||
|
|
||||||
|
sessionStorage.setItem("access", newData.access);
|
||||||
|
originalRequest.headers.Authorization = `Bearer ${newData.access}`;
|
||||||
|
|
||||||
|
return this.client(originalRequest);
|
||||||
|
} catch (refreshError) {
|
||||||
|
console.error("Session expirée, impossible de rafraîchir.");
|
||||||
|
sessionStorage.removeItem("access");
|
||||||
|
localStorage.removeItem("refresh");
|
||||||
|
return Promise.reject(refreshError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
async findUsers() {
|
async findUsers() {
|
||||||
return await axios.get('users').then((res) => {
|
return await this.client.get("users").then((res) => {
|
||||||
return res.data as User
|
return res.data as User;
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async findMe() {
|
async findMe() {
|
||||||
const headers = { 'Authorization': `Bearer ${sessionStorage.getItem('access')}` };
|
|
||||||
return await axios
|
return await this.client
|
||||||
.get('profile', { headers })
|
.get("profile")
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
return res.data as User;
|
return res.data as User;
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
throw error;
|
throw error;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async refreshToken() {
|
||||||
|
const refreshToken = localStorage.getItem("refresh");
|
||||||
|
const apiURL = import.meta.env.VITE_MBL_API_URL;
|
||||||
|
|
||||||
|
return await axios
|
||||||
|
.post(`${apiURL}/api/token/refresh/`, {
|
||||||
|
refresh: refreshToken,
|
||||||
})
|
})
|
||||||
|
.then((res) => {
|
||||||
|
return res.data;
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
throw error;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async authenticate(username: string, password: string) {
|
async authenticate(username: string, password: string) {
|
||||||
return await axios
|
return await this.client
|
||||||
.post('token/', {
|
.post("token/", {
|
||||||
username: username,
|
username: username,
|
||||||
password: password,
|
password: password,
|
||||||
})
|
})
|
||||||
@ -36,6 +103,6 @@ export class AuthenticationService {
|
|||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
throw error;
|
throw error;
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
11
src/utils.ts
Normal file
11
src/utils.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
export function convertState(state: string) {
|
||||||
|
if (state === 'COMPLETED') {
|
||||||
|
return "Complété"
|
||||||
|
} else if (state === 'PLAN') {
|
||||||
|
return "À lire"
|
||||||
|
} else if (state === 'READING') {
|
||||||
|
return "En lecture"
|
||||||
|
} else if (state === 'DROPPED') {
|
||||||
|
return "Lâché"
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user