From 7e56936a36be9d0e92f7264c2f1ca6cfb6d19c07 Mon Sep 17 00:00:00 2001 From: Hajar RAHMOUNI Date: Sat, 7 Oct 2023 12:21:29 +0200 Subject: [PATCH 1/2] photos added --- routes/announcement.js | 138 ---------------------------------- views/add_announcement.pug | 99 ++++++++++++------------ views/drag_drop_script.pug | 109 +++++++++++++++++++++++++++ views/update_announcement.pug | 64 +++++++++++++--- 4 files changed, 212 insertions(+), 198 deletions(-) delete mode 100644 routes/announcement.js create mode 100644 views/drag_drop_script.pug diff --git a/routes/announcement.js b/routes/announcement.js deleted file mode 100644 index 55efdc0..0000000 --- a/routes/announcement.js +++ /dev/null @@ -1,138 +0,0 @@ -const express = require('express'); -const router = express.Router(); -const models = require('../schemas/models'); -const routes = require('./index'); -const upload = require('../multer-config') - - -const Announcement = models.Announcement; -const isAuthenticated = routes.isAuthenticated; - -// Route pour récupérer toutes les annonces -router.get('/', async (req, res) => { - try { - const announcements = await Announcement.find(); - let isAuthenticated = false; - if (req.isAuthenticated()) { - isAuthenticated = true; - } - console.log(req.user,' ---------------------- ') - res.render('announcements', { announcements: announcements, isAuthenticated: isAuthenticated , user : req.user},); - } catch (error) { - res.status(500).json({ message: error.message }); - } -}); - -// Route pour afficher la page d'ajout d'annonce -router.get('/add-announcement', isAuthenticated, (req, res) => { - res.render('add_announcement',{ user : req.user }); -}); - -// details de l'annonce -router.get('/:id', async (req, res) => { - try { - const announcementId = req.params.id; - const announcementDetails = await Announcement.findById(announcementId); - res.render('announcement_details', { announcement: announcementDetails ,user : req.user }); - } catch (error) { - res.status(500).json({ message: error.message }); - } -}); - -router.post('/add-announcement', isAuthenticated, upload.array('photos', ), async (req, res) => { - const { title, propertyType, publicationStatus, propertyStatus, description, price, availabilityDate} = req.body; - const photos = req.files.map(file => file.filename); - - const user = req.user; - - const newAnnouncement = new Announcement({ - title, - propertyType, - publicationStatus, - propertyStatus, - description, - price, - availabilityDate, - photos, - userName: user.username - }); - - try { - await newAnnouncement.save(); - const announcements = await Announcement.find(); - res.redirect('/announcements'); - } catch (error) { - res.status(500).json({ message: error.message }); - } -}); - -// route to display form to update an ad -router.get('/update/:id', isAuthenticated, async (req, res) => { - try { - const announcementId = req.params.id; - const announcementDetails = await Announcement.findById(announcementId); - - if (!announcementDetails) { - return res.status(404).json({ message: 'Announcement not found' }); - } - - res.render('update_announcement', { announcement: announcementDetails, user: req.user }); - } catch (error) { - res.status(500).json({ message: error.message }); - } -}); - -// Route to handle update of an ad -router.post('/update/:id', isAuthenticated, upload.array('photos', ), async (req, res) => { - console.log(req.body); - const announcementId = req.params.id; - const { title, propertyType, publicationStatus, propertyStatus, description, price, availabilityDate } = req.body; - photos = []; - if (req.files && req.files.length > 0) { - photos = req.files.map(file => file.filename); - } - - try { - const updatedAnnouncement = await Announcement.findByIdAndUpdate( - announcementId, - { - title, - propertyType, - publicationStatus, - propertyStatus, - description, - price, - availabilityDate, - photos, - }, - { new: true } - ); - - if (!updatedAnnouncement) { - return res.status(404).json({ message: 'annonce non trouvée' }); - } - - res.redirect('/announcements'); - } catch (error) { - res.status(500).json({ message: error.message }); - } -}); - -router.get('/delete/:id', isAuthenticated, async (req, res) => { - try { - const announcementId = req.params.id; - const deletedAnnouncement = await Announcement.findByIdAndRemove(announcementId); - console.log(deletedAnnouncement,'-------------') - if (!deletedAnnouncement) { - return res.status(404).json({ message: 'annonce non trouvée' }); - } - - res.redirect('/announcements'); - } catch (error) { - res.status(500).json({ message: error.message }); - } -}); - - - -module.exports = router; diff --git a/views/add_announcement.pug b/views/add_announcement.pug index a90cd8f..ca94cc7 100644 --- a/views/add_announcement.pug +++ b/views/add_announcement.pug @@ -1,51 +1,54 @@ extends layout block content - .container.mt-5 - - h1 Ajouter une Annonce - - form(action="/announcements/add-announcement", method="post", enctype="multipart/form-data") - .form-group - label(for="title") Titre - input#title.form-control(type="text", name="title", required) - - .form-group - label(for="propertyType") Type de Bien - select#propertyType.form-control(name="propertyType", required) - option(value="À la vente") À la vente - option(value="À la location") À la location - - .form-group - label(for="price") Prix - input#price.form-control(type="text", name="price", required, pattern="[0-9]+(\.[0-9]{1,2})?", title="Veuillez entrer un nombre") - - .form-group - label(for="availabilityDate") Date de disponibilité - input#availabilityDate.form-control(type="date", name="availabilityDate", required) - - - .form-group - label(for="description") Description - textarea#description.form-control(name="description", required) - - .form-group - label(for="publicationStatus") Statut Publication - select#publicationStatus.form-control(name="publicationStatus", required) - option(value="Publiée") Publiée - option(value="Non publiée") Non publiée - - .form-group - label(for="propertyStatus") Statut Bien - select#propertyStatus.form-control(name="propertyStatus", required) - option(value="Disponible") Disponible - option(value="Loué") Loué - option(value="Vendu") Vendu - - .form-group - label(for="photos") Photos - input#photos.form-control(type="file", name="photos", accept="image/*", multiple) - - button.btn.btn-primary(type="submit") Ajouter l'annonce - a.btn.btn-secondary(href="/announcements") Annuler - + .container.mt-5 + + h1 Ajouter une Annonce + +form(action="/announcements/add-announcement", method="post", enctype="multipart/form-data") + .form-group.mb-4 + label(for="title") Titre + input#title.form-control(type="text", name="title", required) + + .form-group.mb-4 + label(for="propertyType") Type de Bien + select#propertyType.form-control(name="propertyType", required) + option(value="À la vente") À la vente + option(value="À la location") À la location + + .form-group.mb-4 + label(for="price") Prix + input#price.form-control(type="text", name="price", required, pattern="[0-9]+(\.[0-9]{1,2})?", title="Veuillez entrer un nombre") + + .form-group.mb-4 + label(for="availabilityDate") Date de disponibilité + input#availabilityDate.form-control(type="date", name="availabilityDate", required) + + .form-group.mb-4 + label(for="description") Description + textarea#description.form-control(name="description", required) + + .form-group.mb-4 + label(for="publicationStatus") Statut Publication + select#publicationStatus.form-control(name="publicationStatus", required) + option(value="Publiée") Publiée + option(value="Non publiée") Non publiée + + .form-group.mb-4 + label(for="propertyStatus") Statut Bien + select#propertyStatus.form-control(name="propertyStatus", required) + option(value="Disponible") Disponible + option(value="Loué") Loué + option(value="Vendu") Vendu + + .form-group.mb-4 + label(for="photos") Photos (Glisser-déposer) + .control(style="border: 3px #0a0a0a dotted; padding: 40px") + #photo-dropzone.dropzone + | Glissez-déposez des fichiers ici ou cliquez pour sélectionner. + input#file-input.form-control(type="file", name="photos", accept="image/*", multiple) + + button.btn.btn-primary(type="submit") Ajouter l'annonce + a.btn.btn-secondary(href="/announcements") Annuler + + include drag_drop_script.pug diff --git a/views/drag_drop_script.pug b/views/drag_drop_script.pug new file mode 100644 index 0000000..d846ccb --- /dev/null +++ b/views/drag_drop_script.pug @@ -0,0 +1,109 @@ +script. + const dropzone = document.getElementById("photo-dropzone"); + const fileInput = document.getElementById("file-input"); + const selectedPhotosDiv = document.getElementById("selected-photos"); + + // resize image + function resizeImage(file, maxWidth, maxHeight) { + return new Promise((resolve, reject) => { + const img = new Image(); + img.src = URL.createObjectURL(file); + + img.onload = () => { + const canvas = document.createElement("canvas"); + const ctx = canvas.getContext("2d"); + let width = img.width; + let height = img.height; + + if (width > maxWidth) { + height *= maxWidth / width; + width = maxWidth; + } + + if (height > maxHeight) { + width *= maxHeight / height; + height = maxHeight; + } + + canvas.width = width; + canvas.height = height; + ctx.drawImage(img, 0, 0, width, height); + + canvas.toBlob((blob) => { + resolve(new File([blob], file.name, {type: file.type})); + }, file.type); + }; + + img.onerror = (error) => { + reject(error); + }; + }); + } + + // drag over (survol) + dropzone.addEventListener("dragover", (e) => { + e.preventDefault(); + dropzone.classList.add("dragover"); // Ajoute une classe pour indiquer le survol + }); + + // evenement dragleave + dropzone.addEventListener("dragleave", () => { + dropzone.classList.remove("dragover"); + }); + + // Gestionnaire d'événement pour l'événement "drop" (déposer des fichiers) + dropzone.addEventListener("drop", async (e) => { + e.preventDefault(); + e.stopPropagation(); + dropzone.classList.remove("dragover"); + const droppedFiles = e.dataTransfer.files; + const newInputDataTransfer = new DataTransfer(); + + for (let i = 0; i < droppedFiles.length; i++) { + const resizedFile = await resizeImage(droppedFiles[i], 250, 250); // Set the maximum dimensions here + newInputDataTransfer.items.add(resizedFile); + addImageWithDeleteButton(resizedFile, selectedPhotosDiv); + } + + fileInput.files = newInputDataTransfer.files; + }); + + // importer fichiers (boite de dialogue) + dropzone.addEventListener("click", () => { + fileInput.click(); + }); + + // selection des photos + fileInput.addEventListener("change", async () => { + const files = Array.from(fileInput.files); + console.log("Fichiers sélectionnés :", files); + + const selectedPhotosDiv = document.getElementById("selected-photos"); + + for (const file of files) { + const resizedFile = await resizeImage(file, 250, 250); // Set the maximum dimensions here + addImageWithDeleteButton(resizedFile, selectedPhotosDiv); + } + }); + + function addImageWithDeleteButton(file, selectedPhotosDiv) { + const img = document.createElement("img"); + const deleteButton = document.createElement("button"); + img.src = URL.createObjectURL(file); + img.classList.add("selected-image"); + img.style.width = "250px"; + img.style.height = "250px"; + img.style.marginRight = "10px"; + img.style.marginBottom = "10px"; + img.style.border = "2px solid #ccc"; + deleteButton.innerHTML = ''; + + deleteButton.addEventListener("click", (e) => { + img.remove(); + deleteButton.remove(); + }); + + selectedPhotosDiv.appendChild(img); + selectedPhotosDiv.appendChild(deleteButton); + } + diff --git a/views/update_announcement.pug b/views/update_announcement.pug index c596303..f159390 100644 --- a/views/update_announcement.pug +++ b/views/update_announcement.pug @@ -2,48 +2,88 @@ extends layout block content .container.mt-5 - h1 Modifier une Annonce form(action=`/announcements/update/${announcement._id}`, method="post", enctype="multipart/form-data") - .form-group + .form-group.mb-4 label(for="title") Titre input#title.form-control(type="text", name="title", required, value=announcement.title) - .form-group + .form-group.mb-4 label(for="propertyType") Type de Bien select#propertyType.form-control(name="propertyType", required) option(value="À la vente", selected=announcement.propertyType === "À la vente") À la vente option(value="À la location", selected=announcement.propertyType === "À la location") À la location - .form-group + .form-group.mb-4 label(for="price") Prix input#price.form-control(type="text", name="price", required, pattern="[0-9]+(\.[0-9]{1,2})?", title='Veuillez entrer un nombre', value=announcement.price) - .form-group + .form-group.mb-4 label(for="availabilityDate") Date de disponibilité - input#availabilityDate.form-control(type="date", name="availabilityDate", required, value=announcement.availabilityDate) + input#availabilityDate.form-control(type="date", name="availabilityDate", required, value=announcement.availabilityDate.toISOString().split('T')[0]) - .form-group + .form-group.mb-4 label(for="description") Description textarea#description.form-control(name="description", required)= announcement.description - .form-group + .form-group.mb-4 label(for="publicationStatus") Statut Publication select#publicationStatus.form-control(name="publicationStatus", required) option(value="Publiée", selected=announcement.publicationStatus === "Publiée") Publiée option(value="Non publiée", selected=announcement.publicationStatus === "Non publiée") Non publiée - .form-group + .form-group.mb-4 label(for="propertyStatus") Statut Bien select#propertyStatus.form-control(name="propertyStatus", required) option(value="Disponible", selected=announcement.propertyStatus === "Disponible") Disponible option(value="Loué", selected=announcement.propertyStatus === "Loué") Loué option(value="Vendu", selected=announcement.propertyStatus === "Vendu") Vendu - .form-group - label(for="photos") Photos - input#photos.form-control(type="file", name="photos", accept="image/*", multiple) + .form-group.mb-4 + label(for="photos") Photos (Glisser-déposer) + .control(style="border: 3px #0a0a0a dotted; padding: 20px") + #photo-dropzone.dropzone + | Glissez-déposez des fichiers ici ou cliquez pour sélectionner. + input#file-input.form-control(type="file", name="photos", multiple) + + #selected-photos + each photo in announcement.photos + .selected-photo.mb-3 + img(src=`/uploads/${photo}`, alt="Announcement Photo", class="img-thumbnail") + button.delete-photo.btn.btn-danger(type="button", data-photo-filename=`${photo}`, data-announcement-id=`${announcement._id}`) Supprimer button.btn.btn-primary(type="submit") Modifier l'annonce a.btn.btn-secondary(href="/announcements") Annuler + + include drag_drop_script.pug + + script. + // Add this JavaScript code to your client-side script file + document.addEventListener('DOMContentLoaded', function () { + const deleteButtons = document.querySelectorAll('.delete-photo'); + + deleteButtons.forEach((button) => { + button.addEventListener('click', async (event) => { + event.preventDefault(); + + const photoFilename = button.dataset.photoFilename; + const announcementId = button.dataset.announcementId; + + try { + const response = await fetch(`/announcements/delete-photo/${announcementId}/${photoFilename}`, { + method: 'DELETE', + }); + + if (response.ok) { + button.parentNode.remove(); + } else { + // Handle errors if needed + console.error('Failed to delete photo'); + } + } catch (error) { + console.error('An error occurred:', error); + } + }); + }); + }); -- GitLab From 8dd8467a85794753570f90739d163cc833a73501 Mon Sep 17 00:00:00 2001 From: Hajar RAHMOUNI Date: Sat, 7 Oct 2023 16:55:51 +0200 Subject: [PATCH 2/2] feat : drag and drop photos with deletion --- database/connection.js | 2 +- routes/announcement.js | 195 +++++++++++++++++++++++++++++++++ views/add_announcement.pug | 102 ++++++++--------- views/announcement_details.pug | 6 +- views/drag_drop_script.pug | 162 +++++++++++++-------------- views/update_announcement.pug | 54 ++++----- 6 files changed, 358 insertions(+), 163 deletions(-) create mode 100644 routes/announcement.js diff --git a/database/connection.js b/database/connection.js index f692cc8..5c201cb 100644 --- a/database/connection.js +++ b/database/connection.js @@ -2,7 +2,7 @@ const mongoose = require('mongoose'); require('dotenv').config(); const dbName = process.env.DB_NAME; -const url = `mongodb://172.18.0.2:27017/${dbName}`; +const url = `mongodb://mongodb:27017/${dbName}`; const connectToDatabase = async () => { try { diff --git a/routes/announcement.js b/routes/announcement.js new file mode 100644 index 0000000..653aed6 --- /dev/null +++ b/routes/announcement.js @@ -0,0 +1,195 @@ +const express = require('express'); +const router = express.Router(); +const models = require('../schemas/models'); +const routes = require('./index'); +const upload = require('../multer-config'); +const fs = require('fs'); +const path = require('path'); + + +const Announcement = models.Announcement; +const isAuthenticated = routes.isAuthenticated; + +// Route pour récupérer toutes les annonces +router.get('/', async (req, res) => { + try { + const announcements = await Announcement.find(); + let isAuthenticated = false; + if (req.isAuthenticated()) { + isAuthenticated = true; + } + console.log(req.user, ' ---------------------- ') + res.render('announcements', { announcements: announcements, isAuthenticated: isAuthenticated, user: req.user }); + } catch (error) { + res.status(500).json({ message: error.message }); + } +}); + +// Route pour afficher la page d'ajout d'annonce +router.get('/add-announcement', isAuthenticated, (req, res) => { + res.render('add_announcement', { user: req.user }); +}); + +// Details de l'annonce +router.get('/:id', async (req, res) => { + try { + const announcementId = req.params.id; + const announcementDetails = await Announcement.findById(announcementId); + res.render('announcement_details', { announcement: announcementDetails, user: req.user }); + } catch (error) { + res.status(500).json({ message: error.message }); + } +}); + +router.post('/add-announcement', isAuthenticated, upload.array('photos'), async (req, res) => { + const { title, propertyType, publicationStatus, propertyStatus, description, price, availabilityDate } = req.body; + const photos = []; + + req.files.forEach(file => { + photos.push(file.filename); + }); + + const user = req.user; + + const newAnnouncement = new Announcement({ + title, + propertyType, + publicationStatus, + propertyStatus, + description, + price, + availabilityDate, + photos, + userName: user.username + }); + + try { + await newAnnouncement.save(); + const announcements = await Announcement.find(); + res.redirect('/announcements'); + } catch (error) { + res.status(500).json({ message: error.message }); + } +}); + + +// Route to display form to update an ad +router.get('/update/:id', isAuthenticated, async (req, res) => { + try { + const announcementId = req.params.id; + const announcementDetails = await Announcement.findById(announcementId); + console.log(' announcement object :', announcementDetails) + if (!announcementDetails) { + return res.status(404).json({ message: 'Announcement not found' }); + } + + res.render('update_announcement', { announcement: announcementDetails, user: req.user }); + } catch (error) { + res.status(500).json({ message: error.message }); + } +}); + +// Route to handle update of an ad +router.post('/update/:id', isAuthenticated, upload.array('photos'), async (req, res) => { + const announcementId = req.params.id; + const { title, propertyType, publicationStatus, propertyStatus, description, price, availabilityDate } = req.body; + let photos = []; + + const oldPhotos = req.body.oldPhotos || []; + + req.files.forEach(file => { + photos.push(file.filename); + }); + + photos = photos.concat(oldPhotos); + + try { + const updatedAnnouncement = await Announcement.findByIdAndUpdate( + announcementId, + { + title, + propertyType, + publicationStatus, + propertyStatus, + description, + price, + availabilityDate, + photos, // we combine old and new photos + }, + { new: true } + ); + + if (!updatedAnnouncement) { + return res.status(404).json({ message: 'annonce non trouvée' }); + } + + res.redirect('/announcements'); + } catch (error) { + res.status(500).json({ message: error.message }); + } +}); + + + +// route to delete an ad +router.get('/delete/:id', isAuthenticated, async (req, res) => { + try { + const announcementId = req.params.id; + const deletedAnnouncement = await Announcement.findByIdAndRemove(announcementId); + console.log(deletedAnnouncement, '-------------') + if (!deletedAnnouncement) { + return res.status(404).json({ message: 'annonce non trouvée' }); + } + + res.redirect('/announcements'); + } catch (error) { + res.status(500).json({ message: error.message }); + } +}); + + +// Route to delete a photo +router.delete('/delete-photo/:announcementId/:filename', isAuthenticated, async (req, res) => { + try { + const filename = req.params.filename; + const announcementId = req.params.announcementId; + const announcement = await Announcement.findById(announcementId); + + if (!announcement) { + return res.status(404).json({ message: 'Annonce non trouvée' }); + } + + if (!announcement.photos.includes(filename)) { + return res.status(404).json({ message: 'Photo non trouvée' }); + } + + const filePath = path.join('public', 'uploads', filename); + + console.log('filePath:', filePath); + console.log('File exists:', fs.existsSync(filePath)); + + if (fs.existsSync(filePath)) { + fs.unlinkSync(filePath); + + let updatedPhotos = [...announcement.photos]; + + updatedPhotos = updatedPhotos.filter((photo) => photo !== filename); + announcement.photos = updatedPhotos; + console.log(announcement.photos,'photos !!'); + await announcement.save(); + + return res.status(200).json({ message: 'Photo supprimée avec succès' }); + } else { + return res.status(404).json({ message: 'Fichier introuvable' }); + } + } catch (error) { + console.error('Une erreur s\'est produite :', error); + return res.status(500).json({ message: 'Erreur interne du serveur' }); + } +}); + + + + + +module.exports = router; \ No newline at end of file diff --git a/views/add_announcement.pug b/views/add_announcement.pug index ca94cc7..edfd02a 100644 --- a/views/add_announcement.pug +++ b/views/add_announcement.pug @@ -1,54 +1,54 @@ extends layout block content - .container.mt-5 - - h1 Ajouter une Annonce - -form(action="/announcements/add-announcement", method="post", enctype="multipart/form-data") - .form-group.mb-4 - label(for="title") Titre - input#title.form-control(type="text", name="title", required) - - .form-group.mb-4 - label(for="propertyType") Type de Bien - select#propertyType.form-control(name="propertyType", required) - option(value="À la vente") À la vente - option(value="À la location") À la location - - .form-group.mb-4 - label(for="price") Prix - input#price.form-control(type="text", name="price", required, pattern="[0-9]+(\.[0-9]{1,2})?", title="Veuillez entrer un nombre") - - .form-group.mb-4 - label(for="availabilityDate") Date de disponibilité - input#availabilityDate.form-control(type="date", name="availabilityDate", required) - - .form-group.mb-4 - label(for="description") Description - textarea#description.form-control(name="description", required) - - .form-group.mb-4 - label(for="publicationStatus") Statut Publication - select#publicationStatus.form-control(name="publicationStatus", required) - option(value="Publiée") Publiée - option(value="Non publiée") Non publiée - - .form-group.mb-4 - label(for="propertyStatus") Statut Bien - select#propertyStatus.form-control(name="propertyStatus", required) - option(value="Disponible") Disponible - option(value="Loué") Loué - option(value="Vendu") Vendu - - .form-group.mb-4 - label(for="photos") Photos (Glisser-déposer) - .control(style="border: 3px #0a0a0a dotted; padding: 40px") - #photo-dropzone.dropzone - | Glissez-déposez des fichiers ici ou cliquez pour sélectionner. - input#file-input.form-control(type="file", name="photos", accept="image/*", multiple) - - button.btn.btn-primary(type="submit") Ajouter l'annonce - a.btn.btn-secondary(href="/announcements") Annuler - - include drag_drop_script.pug + .container.mt-5 + h1 Ajouter une Annonce + + form(action="/announcements/add-announcement", method="post", enctype="multipart/form-data") + .form-group.mb-4 + label(for="title") Titre + input#title.form-control(type="text", name="title", required) + + .form-group.mb-4 + label(for="propertyType") Type de Bien + select#propertyType.form-control(name="propertyType", required) + option(value="À la vente") À la vente + option(value="À la location") À la location + + .form-group.mb-4 + label(for="price") Prix + input#price.form-control(type="text", name="price", required, pattern="[0-9]+(\.[0-9]{1,2})?", title="Veuillez entrer un nombre") + + .form-group.mb-4 + label(for="availabilityDate") Date de disponibilité + input#availabilityDate.form-control(type="date", name="availabilityDate", required) + + .form-group.mb-4 + label(for="description") Description + textarea#description.form-control(name="description", required) + + .form-group.mb-4 + label(for="publicationStatus") Statut Publication + select#publicationStatus.form-control(name="publicationStatus", required) + option(value="Publiée") Publiée + option(value="Non publiée") Non publiée + + .form-group.mb-4 + label(for="propertyStatus") Statut Bien + select#propertyStatus.form-control(name="propertyStatus", required) + option(value="Disponible") Disponible + option(value="Loué") Loué + option(value="Vendu") Vendu + + .form-group.mb-4 + label(for="photos") Photos (Glisser-déposer) + .control(style="border: 3px #0a0a0a dotted; padding: 20px") + #photo-dropzone.dropzone Glissez-déposez des fichiers ici ou cliquez pour sélectionner. + input#file-input.form-control(type="file", name="photos", multiple) + + #selected-photos + + button.btn.btn-primary(type="submit") Ajouter l'annonce + a.btn.btn-secondary(href="/announcements") Annuler + + include drag_drop_script.pug diff --git a/views/announcement_details.pug b/views/announcement_details.pug index 89c6228..d96b1b4 100644 --- a/views/announcement_details.pug +++ b/views/announcement_details.pug @@ -21,5 +21,7 @@ block content each photo in announcement.photos img.card-img-top(src=`/uploads/${photo}`, alt=announcement.title) - a.btn.btn-primary(href=`/announcements/update/${announcement._id}`) Modifier l'annonce - a.btn.btn-danger(href=`/announcements/delete/${announcement._id}`) Supprimer l'annonce + // Add spacing between buttons + div(style="margin-top: 10px;") + a.btn.btn-primary.m-3(href=`/announcements/update/${announcement._id}`) Modifier l'annonce + a.btn.btn-danger.m-3(href=`/announcements/delete/${announcement._id}`) Supprimer l'annonce diff --git a/views/drag_drop_script.pug b/views/drag_drop_script.pug index d846ccb..5721eb7 100644 --- a/views/drag_drop_script.pug +++ b/views/drag_drop_script.pug @@ -2,108 +2,102 @@ script. const dropzone = document.getElementById("photo-dropzone"); const fileInput = document.getElementById("file-input"); const selectedPhotosDiv = document.getElementById("selected-photos"); + const deleteButtons = document.querySelectorAll('.delete-photo'); // Select all delete buttons - // resize image - function resizeImage(file, maxWidth, maxHeight) { - return new Promise((resolve, reject) => { - const img = new Image(); - img.src = URL.createObjectURL(file); - - img.onload = () => { - const canvas = document.createElement("canvas"); - const ctx = canvas.getContext("2d"); - let width = img.width; - let height = img.height; - - if (width > maxWidth) { - height *= maxWidth / width; - width = maxWidth; - } - - if (height > maxHeight) { - width *= maxHeight / height; - height = maxHeight; - } - - canvas.width = width; - canvas.height = height; - ctx.drawImage(img, 0, 0, width, height); - - canvas.toBlob((blob) => { - resolve(new File([blob], file.name, {type: file.type})); - }, file.type); - }; - - img.onerror = (error) => { - reject(error); - }; - }); - } - - // drag over (survol) + // Add dragover event listener dropzone.addEventListener("dragover", (e) => { - e.preventDefault(); - dropzone.classList.add("dragover"); // Ajoute une classe pour indiquer le survol + e.preventDefault(); + dropzone.classList.add("dragover"); }); - // evenement dragleave + // Add dragleave event listener dropzone.addEventListener("dragleave", () => { - dropzone.classList.remove("dragover"); + dropzone.classList.remove("dragover"); }); - // Gestionnaire d'événement pour l'événement "drop" (déposer des fichiers) - dropzone.addEventListener("drop", async (e) => { - e.preventDefault(); - e.stopPropagation(); - dropzone.classList.remove("dragover"); - const droppedFiles = e.dataTransfer.files; const newInputDataTransfer = new DataTransfer(); - for (let i = 0; i < droppedFiles.length; i++) { - const resizedFile = await resizeImage(droppedFiles[i], 250, 250); // Set the maximum dimensions here - newInputDataTransfer.items.add(resizedFile); - addImageWithDeleteButton(resizedFile, selectedPhotosDiv); - } + // Add drop event listener + dropzone.addEventListener("drop", async (e) => { + e.preventDefault(); + e.stopPropagation(); + dropzone.classList.remove("dragover"); + + const droppedFiles = e.dataTransfer.files; + console.log('dropped: ', droppedFiles); + + for (let i = 0; i < droppedFiles.length; i++) { + newInputDataTransfer.items.add(droppedFiles[i]); + addImageWithDeleteButton(droppedFiles[i], selectedPhotosDiv); + } - fileInput.files = newInputDataTransfer.files; + + fileInput.files = newInputDataTransfer.files; + + deleteButtons.forEach(button => { + button.addEventListener("click", (e) => { + handleDeleteButtonClick(e, selectedPhotosDiv); + }); + }); }); - // importer fichiers (boite de dialogue) dropzone.addEventListener("click", () => { - fileInput.click(); + fileInput.click(); }); - // selection des photos - fileInput.addEventListener("change", async () => { - const files = Array.from(fileInput.files); - console.log("Fichiers sélectionnés :", files); + // Add change event listener to the file input + fileInput.addEventListener("change", () => { + const files = Array.from(fileInput.files); + console.log("Selected Files:", files); - const selectedPhotosDiv = document.getElementById("selected-photos"); - - for (const file of files) { - const resizedFile = await resizeImage(file, 250, 250); // Set the maximum dimensions here - addImageWithDeleteButton(resizedFile, selectedPhotosDiv); - } + files.forEach((file) => { + addImageWithDeleteButton(file, selectedPhotosDiv); + }); }); function addImageWithDeleteButton(file, selectedPhotosDiv) { - const img = document.createElement("img"); - const deleteButton = document.createElement("button"); - img.src = URL.createObjectURL(file); - img.classList.add("selected-image"); - img.style.width = "250px"; - img.style.height = "250px"; - img.style.marginRight = "10px"; - img.style.marginBottom = "10px"; - img.style.border = "2px solid #ccc"; - deleteButton.innerHTML = ''; - - deleteButton.addEventListener("click", (e) => { - img.remove(); - deleteButton.remove(); - }); - - selectedPhotosDiv.appendChild(img); - selectedPhotosDiv.appendChild(deleteButton); + const img = document.createElement("img"); + const deleteButton = document.createElement("button"); + img.src = URL.createObjectURL(file); + img.classList.add("selected-image"); + img.style.width = "250px"; + img.style.height = "250px"; + img.style.marginRight = "10px"; + img.style.marginBottom = "10px"; + img.style.border = "2px solid #ccc"; + deleteButton.innerHTML = ''; + + deleteButton.addEventListener("click", (e) => { + img.remove(); + deleteButton.remove(); + handleDeleteButtonClick(e, selectedPhotosDiv); + }); + + selectedPhotosDiv.appendChild(img); + selectedPhotosDiv.appendChild(deleteButton); } + // Function to handle delete photo click + async function handleDeleteButtonClick(event, selectedPhotosDiv) { + event.preventDefault(); + + const photoFilename = event.target.dataset.photoFilename; + const announcementId = event.target.dataset.announcementId; + + try { + const response = await fetch(`/announcements/delete-photo/${announcementId}/${photoFilename}`, { + method: 'DELETE', + }); + + if (response.ok) { + const deletedPhoto = selectedPhotosDiv.querySelector(`[src*="${photoFilename}"]`); + if (deletedPhoto) { + deletedPhoto.remove(); + } + } else { + console.error('Failed to delete photo'); + } + } catch (error) { + console.error('An error occurred:', error); + } + } \ No newline at end of file diff --git a/views/update_announcement.pug b/views/update_announcement.pug index f159390..470ab9b 100644 --- a/views/update_announcement.pug +++ b/views/update_announcement.pug @@ -40,6 +40,9 @@ block content option(value="Loué", selected=announcement.propertyStatus === "Loué") Loué option(value="Vendu", selected=announcement.propertyStatus === "Vendu") Vendu + each oldPhoto in announcement.photos + input(type="hidden", name="oldPhotos[]", value=oldPhoto) + .form-group.mb-4 label(for="photos") Photos (Glisser-déposer) .control(style="border: 3px #0a0a0a dotted; padding: 20px") @@ -58,32 +61,33 @@ block content include drag_drop_script.pug - script. - // Add this JavaScript code to your client-side script file - document.addEventListener('DOMContentLoaded', function () { - const deleteButtons = document.querySelectorAll('.delete-photo'); - - deleteButtons.forEach((button) => { - button.addEventListener('click', async (event) => { - event.preventDefault(); - - const photoFilename = button.dataset.photoFilename; - const announcementId = button.dataset.announcementId; - - try { - const response = await fetch(`/announcements/delete-photo/${announcementId}/${photoFilename}`, { - method: 'DELETE', - }); - - if (response.ok) { - button.parentNode.remove(); - } else { - // Handle errors if needed - console.error('Failed to delete photo'); + script. + document.addEventListener('DOMContentLoaded', function () { + const deleteButtons = document.querySelectorAll('.delete-photo'); + deleteButtons.forEach((button) => { + button.addEventListener('click', async (event) => { + event.preventDefault(); + const photoFilename = button.dataset.photoFilename; + const announcementId = button.dataset.announcementId; + + try { + const response = await fetch(`/announcements/delete-photo/${announcementId}/${photoFilename}`, { + method: 'DELETE', + }); + + if (response.ok) { + button.parentNode.remove(); + + const oldPhotosInput = document.querySelector('input[name="oldPhotos[]"][value="' + photoFilename + '"]'); + if (oldPhotosInput) { + oldPhotosInput.remove(); } - } catch (error) { - console.error('An error occurred:', error); + } else { + console.error('Failed'); } - }); + } catch (error) { + console.error('error : ', error); + } }); }); + }); -- GitLab