update modal

This commit is contained in:
Guamss 2025-12-16 18:30:48 +01:00
parent 68d3b86816
commit 173b4636bc
5 changed files with 129 additions and 69 deletions

View File

@ -4,7 +4,7 @@ import type { Book } from '@/models/book';
import { convertState } from '@/utils'; import { convertState } from '@/utils';
import { ref, Transition } from 'vue'; import { ref, Transition } from 'vue';
const emit = defineEmits(["openDeleteModal", "concernedBook"]); const emit = defineEmits(["openDeleteModal", "concernedBookToDelete", "concernedBookToUpdate", "openUpdateModal"]);
const API_URL = import.meta.env.VITE_MBL_API_URL const API_URL = import.meta.env.VITE_MBL_API_URL
const props = defineProps<{ book: Book }>() const props = defineProps<{ book: Book }>()
@ -13,7 +13,6 @@ const { isAuthenticated } = useAuth();
function showButtonsOnHover() { function showButtonsOnHover() {
showButtons.value = true showButtons.value = true
} }
function unshowButtonOnLeave() { function unshowButtonOnLeave() {
@ -51,8 +50,8 @@ function unshowButtonOnLeave() {
</p> </p>
<Transition> <Transition>
<div class="buttons" v-show="showButtons"> <div class="buttons" v-show="showButtons">
<button class="update-button">Modifier</button> <button @click="$emit('openUpdateModal'); $emit('concernedBookToUpdate', props.book)" class="update-button">Modifier</button>
<button @click="$emit('openDeleteModal'); $emit('concernedBook', props.book)" class="delete-button"><font-awesome-icon icon="fa-trash-can" /></button> <button @click="$emit('openDeleteModal'); $emit('concernedBookToDelete', props.book)" class="delete-button"><font-awesome-icon icon="fa-trash-can" /></button>
</div> </div>
</Transition> </Transition>
</div> </div>

View File

@ -4,10 +4,15 @@ import type { Book } from "@/models/book";
import { User } from "@/models/user"; import { User } from "@/models/user";
import { AuthenticationService } from "@/services/authentication.service"; import { AuthenticationService } from "@/services/authentication.service";
import { BookService } from "@/services/book.service"; import { BookService } from "@/services/book.service";
import { onMounted, ref } from "vue"; import { onMounted, ref, watch } from "vue";
const props = defineProps<{ isActive: boolean }>(); const props = defineProps<{
const emit = defineEmits(["closeBookFormModal", "newBook"]); isActive: boolean;
isUpdating: boolean;
updatingBook: Book | undefined
}>();
const emit = defineEmits(["closeBookFormModal", "newBook", "updateBook"]);
const title = ref(""); const title = ref("");
const author = ref(""); const author = ref("");
@ -21,15 +26,28 @@ const bookService = new BookService();
const { isAuthenticated } = useAuth(); const { isAuthenticated } = useAuth();
onMounted(async () => { onMounted(async () => {
{ if (isAuthenticated.value) {
if (isAuthenticated.value) { try {
try { concernedUser.value = await authenticationService.findMe();
concernedUser.value = await authenticationService.findMe(); } catch (error: any) {
} catch (error: any) { console.error(error);
console.error(error); }
} }
});
watch(() => props.isActive, (isOpen) => {
if (isOpen) {
if (props.isUpdating && props.updatingBook) {
title.value = props.updatingBook.title;
author.value = props.updatingBook.author;
state.value = props.updatingBook.state;
note.value = props.updatingBook.note;
} else { } else {
concernedUser.value = undefined; title.value = "";
author.value = "";
state.value = "PLAN";
note.value = 0;
illustrationFile.value = null;
} }
} }
}); });
@ -47,13 +65,23 @@ async function sendBookData() {
form.append("author", author.value); form.append("author", author.value);
form.append("note", `${note.value}`); form.append("note", `${note.value}`);
form.append("state", state.value); form.append("state", state.value);
form.append("illustration", illustrationFile.value!);
console.log(concernedUser.value) if (illustrationFile.value) {
form.append("illustration", illustrationFile.value);
}
if (concernedUser.value?.id) { if (concernedUser.value?.id) {
form.append("user", concernedUser.value.id.toString()); form.append("user", concernedUser.value.id.toString());
} }
const newBook: Book = await bookService.createBook(form);
emit('newBook', newBook); if (props.isUpdating && props.updatingBook) {
const updatedBook = await bookService.updateBook(props.updatingBook.id, form);
emit("updateBook", updatedBook);
} else {
const newBook = await bookService.createBook(form);
emit("newBook", newBook);
}
emit("closeBookFormModal"); emit("closeBookFormModal");
} }
</script> </script>
@ -65,7 +93,7 @@ async function sendBookData() {
<div class="modale card"> <div class="modale card">
<button class="btn-close" @click="$emit('closeBookFormModal')">X</button> <button class="btn-close" @click="$emit('closeBookFormModal')">X</button>
<h2>Ajouter un livre</h2> <h2>{{ props.isUpdating ? 'Modifier le livre' : 'Ajouter un livre' }}</h2>
<form @submit.prevent="sendBookData"> <form @submit.prevent="sendBookData">
<label for="title">Titre</label> <label for="title">Titre</label>
@ -95,7 +123,9 @@ async function sendBookData() {
/> />
<div class="actions"> <div class="actions">
<button class="send" type="submit">Créer</button> <button class="send" type="submit">
{{ props.isUpdating ? 'Modifier' : 'Créer' }}
</button>
</div> </div>
</form> </form>
</div> </div>

View File

@ -66,6 +66,17 @@ export class BookService {
}); });
} }
async updateBook(id: number, formData: FormData) {
return await this.client
.put(`/${id}/`, formData)
.then((res) => {
return res.data as Book;
})
.catch((error) => {
throw error;
});
}
async deleteBookById(id: number) { async deleteBookById(id: number) {
return await this.client.delete(`/${id}/`).catch((error) => { return await this.client.delete(`/${id}/`).catch((error) => {
throw error; throw error;

View File

@ -3,34 +3,62 @@ import type { Book } from "@/models/book";
import { BookService } from "@/services/book.service"; import { BookService } from "@/services/book.service";
import { onMounted, ref } from "vue"; import { onMounted, ref } from "vue";
import BookCard from "@/components/BookCard.vue"; import BookCard from "@/components/BookCard.vue";
import CreateBookForm from "@/components/CreateBookForm.vue"; import CreateBookModal from "@/components/CreateBookModal.vue";
import { useAuth } from "@/composables/useAuth"; import { useAuth } from "@/composables/useAuth";
import { useToast } from "vue-toast-notification"; import { useToast } from "vue-toast-notification";
import DeleteBookForm from "@/components/DeleteBookForm.vue"; import DeleteBookModal from "@/components/DeleteBookModal.vue";
const books = ref<Book[]>([]); const books = ref<Book[]>([]);
const bookService = new BookService(); const bookService = new BookService();
const isCreatingBookFormOpened = ref<boolean>(false);
const isDeletingBookFormOpened = ref<boolean>(false);
const deletingBook = ref<Book | undefined>(undefined);
const { isAuthenticated } = useAuth(); const { isAuthenticated } = useAuth();
const toastService = useToast(); const toastService = useToast();
const isCreatingBookFormOpened = ref<boolean>(false);
const isDeletingBookFormOpened = ref<boolean>(false);
const deletingBook = ref<Book | undefined>(undefined);
const updatingBook = ref<Book | undefined>(undefined);
function sortBooks(a: Book, b: Book) { function sortBooks(a: Book, b: Book) {
if (a.updated_at > b.updated_at) { if (a.updated_at > b.updated_at) return -1;
return -1; if (a.updated_at < b.updated_at) return 1;
} else if (a.updated_at < b.updated_at) {
return 1;
}
return 0; return 0;
} }
function pushNewBook(book: Book) { function parseBookDates(book: Book) {
book.added_at = new Date(book.added_at); book.added_at = new Date(book.added_at);
book.updated_at = new Date(book.updated_at); book.updated_at = new Date(book.updated_at);
}
function openCreateModal() {
updatingBook.value = undefined;
isCreatingBookFormOpened.value = true;
}
function pushNewBook(book: Book) {
parseBookDates(book);
books.value.push(book); books.value.push(book);
books.value.sort(sortBooks); books.value.sort(sortBooks);
toastService.success(`Livre créé avec succès"`, {
toastService.success(`Livre créé avec succès`, {
position: "bottom-right",
pauseOnHover: true,
dismissible: true,
});
}
function handleBookUpdate(updatedBook: Book) {
parseBookDates(updatedBook);
const index = books.value.findIndex((b) => b.id === updatedBook.id);
if (index !== -1) {
books.value[index] = updatedBook;
books.value.sort(sortBooks);
}
updatingBook.value = undefined;
toastService.success(`Livre modifié avec succès`, {
position: "bottom-right", position: "bottom-right",
pauseOnHover: true, pauseOnHover: true,
dismissible: true, dismissible: true,
@ -39,17 +67,20 @@ function pushNewBook(book: Book) {
function closeBookFormModal() { function closeBookFormModal() {
isCreatingBookFormOpened.value = false; isCreatingBookFormOpened.value = false;
updatingBook.value = undefined;
} }
function closeDeleteBookFormModal() { function closeDeleteBookFormModal() {
isDeletingBookFormOpened.value = false; isDeletingBookFormOpened.value = false;
deletingBook.value = undefined;
} }
function deleteBookFromList(bool: boolean) { function deleteBookFromList(bool: boolean) {
if (deletingBook.value && bool) { if (deletingBook.value && bool) {
books.value = books.value.filter((book) => book.id !== deletingBook.value?.id); books.value = books.value.filter((book) => book.id !== deletingBook.value?.id);
deletingBook.value = undefined; closeDeleteBookFormModal();
toastService.success(`Livre supprimé avec succès"`, {
toastService.success(`Livre supprimé avec succès`, {
position: "bottom-right", position: "bottom-right",
pauseOnHover: true, pauseOnHover: true,
dismissible: true, dismissible: true,
@ -58,43 +89,51 @@ function deleteBookFromList(bool: boolean) {
} }
onMounted(async () => { onMounted(async () => {
books.value = await bookService.findBooks(); try {
const fetchedBooks = await bookService.findBooks();
books.value.map((book) => { fetchedBooks.forEach(parseBookDates);
book.added_at = new Date(book.added_at); books.value = fetchedBooks;
book.updated_at = new Date(book.updated_at); books.value.sort(sortBooks);
}); } catch (e) {
console.error("Erreur au chargement des livres", e);
books.value.sort(sortBooks); }
}); });
</script> </script>
<template> <template>
<CreateBookForm <CreateBookModal
v-if="isAuthenticated" v-if="isAuthenticated"
:is-active="isCreatingBookFormOpened" :is-active="isCreatingBookFormOpened"
:is-updating="updatingBook !== undefined"
:updating-book="updatingBook"
@new-book="(book) => pushNewBook(book)" @new-book="(book) => pushNewBook(book)"
@update-book="(book) => handleBookUpdate(book)"
@close-book-form-modal="closeBookFormModal" @close-book-form-modal="closeBookFormModal"
/> />
<DeleteBookForm
<DeleteBookModal
v-if="isAuthenticated" v-if="isAuthenticated"
:is-active="isDeletingBookFormOpened" :is-active="isDeletingBookFormOpened"
:concerned-book="deletingBook" :concerned-book="deletingBook"
@is-deleted="(bool) => deleteBookFromList(bool)" @is-deleted="(bool) => deleteBookFromList(bool)"
@close-delete-book-modal="closeDeleteBookFormModal" @close-delete-book-modal="closeDeleteBookFormModal"
/> />
<main class="cards"> <main class="cards">
<BookCard <BookCard
v-for="book in books" v-for="book in books"
:book="book" :book="book"
:key="book.id" :key="book.id"
@concerned-book="(book) => (deletingBook = book)" @open-update-modal="isCreatingBookFormOpened = true"
@concerned-book-to-delete="(book) => (deletingBook = book)"
@concerned-book-to-update="(book) => (updatingBook = book)"
@open-delete-modal="isDeletingBookFormOpened = true" @open-delete-modal="isDeletingBookFormOpened = true"
class="card" class="card"
/> />
</main> </main>
<div v-if="isAuthenticated" class="add-button-container"> <div v-if="isAuthenticated" class="add-button-container">
<button @click="isCreatingBookFormOpened = true" class="add-button"> <button @click="openCreateModal" class="add-button">
<h3><font-awesome-icon icon="fa-plus" /> Ajouter un livre</h3> <h3><font-awesome-icon icon="fa-plus" /> Ajouter un livre</h3>
</button> </button>
</div> </div>
@ -108,6 +147,7 @@ onMounted(async () => {
right: 0; right: 0;
margin-left: 2em; margin-left: 2em;
margin-bottom: 2em; margin-bottom: 2em;
z-index: 10;
} }
.add-button { .add-button {
@ -119,6 +159,7 @@ onMounted(async () => {
border-radius: 5px; border-radius: 5px;
cursor: pointer; cursor: pointer;
font-weight: bold; font-weight: bold;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
} }
.add-button:hover { .add-button:hover {
@ -131,28 +172,7 @@ onMounted(async () => {
flex-wrap: wrap; flex-wrap: wrap;
justify-content: center; justify-content: center;
gap: 2em; gap: 2em;
} padding-bottom: 5em;
.action {
display: inline-flex;
margin-top: 1rem;
color: #ffffff;
font-size: 0.875rem;
line-height: 1.25rem;
font-weight: 500;
align-items: center;
gap: 0.25rem;
background-color: #2563eb;
padding: 4px 8px;
border-radius: 4px;
}
.action span {
transition: 0.3s ease;
}
.action:hover span {
transform: translateX(4px);
} }
@media (max-width: 820px) { @media (max-width: 820px) {