diff --git a/public/javascript/adDelete.js b/public/javascript/adDelete.js index 0c95fa64a07548879686018c4ba0c2aaa3f698b5..d233b8d48150b3fb9b6842af22460b258c3a12d8 100644 --- a/public/javascript/adDelete.js +++ b/public/javascript/adDelete.js @@ -1,5 +1,12 @@ -function deleteAd(id) { - const response = fetch(`delete/${id}`, { +/** + * Send HTTP request to delete ad, and follow redirection afterwise. + * + * @param {Event} e + */ +function deleteAd(e) { + const id = e.target.dataset.id; + + fetch(`/ads/delete/${id}`, { method: 'DELETE', mode: 'cors', cache: 'no-cache', @@ -9,9 +16,13 @@ function deleteAd(id) { }, redirect: 'follow', referrer: '', - }); - - response - .then((value) => window.location = value.url) + }) + .then((value) => (window.location = value.url)) .catch((reason) => console.error(reason)); } + +document.addEventListener('DOMContentLoaded', function() { + document.querySelectorAll('.delete-button').forEach((btn) => { + btn.addEventListener('click', deleteAd); + }); +}); diff --git a/public/stylesheets/style.css b/public/stylesheets/style.css index 085847b7b9e2cb19ef921cf214e276b096120f6d..fcd20d811ce04625bfb8566e8c1ab00f0f3dbd0e 100644 --- a/public/stylesheets/style.css +++ b/public/stylesheets/style.css @@ -53,14 +53,17 @@ span.error-message { .ad-photos { display: flex; + align-items: center; width: 100%; + max-width: 750px; justify-content: space-around; } .ad-photos > img { - max-width: 20%; - max-height: 100%; - border: solid 2px #333; + box-sizing: border-box; + border: solid 2px black; padding: 2px; + max-width: 25%; + height: 100%; margin-bottom: 15px; } \ No newline at end of file diff --git a/routes/ads.js b/routes/ads.js index 537b5d67a41618a6a0cff2b6e6d1618feafcbbf1..5f6be6bbea8dc8227704b12334bac6b70bfb9ac9 100644 --- a/routes/ads.js +++ b/routes/ads.js @@ -10,11 +10,15 @@ const upload = multer({ }); /* GET to get to get to the ad creation form */ -router.get('/create', checkPermissions('agent'), function(req, res, next) { - res.render('ad_create'); -}) - .post('/create', checkPermissions('agent'), - upload.array('pictures', 3), function(req, res, next) { +router + .get('/create', checkPermissions('agent'), function(req, res) { + res.render('ads/create'); + }) + .post( + '/create', + checkPermissions('agent'), + upload.array('pictures', 3), + function(req, res, next) { const body = req.body; const id = body.id; @@ -25,53 +29,39 @@ router.get('/create', checkPermissions('agent'), function(req, res, next) { price: body.price.replace(',', '.'), published: body.published === 'on', description: body.description, - availabilityDate: body.availabilityDate === '' ? - null : body.availabilityDate, + availabilityDate: + body.availabilityDate === '' ? null : body.availabilityDate, }; if (req.files.length) { - formData.pictures = req - .files - .map((f) => ({name: f.fieldName, body: f.buffer})); + formData.pictures = req.files.map((f) => ({ + name: f.fieldName, + body: f.buffer, + })); } if (id) { // Peut-être charger l'objet en amont et le retourner si erreur ? - adModel.Ad.updateOne({_id: id}, {$set: formData}).exec() + adModel.Ad.updateOne({_id: id}, {$set: formData}) + .exec() .then((value) => res.redirect('/ads/')) .catch((reason) => { - res.render('ad_create', reason); + res.render('ads/create', reason); }); } else { const newAd = new adModel.Ad(formData); - newAd.save() + newAd + .save() .then((value) => res.redirect('/ads/')) .catch((reason) => { console.log(reason); - res.render('ad_create', reason); + res.render('ads/create', reason); }); } - }) - .get('/', function(req, res, next) { - adModel.Ad.find({published: true}).exec() - .then((value) => { - // on trie les annonces par ordre alphabétique - res.render('ad_view', { - ads: value.sort((a, b) => a.title.localeCompare(b.title)), - }); - }) - .catch((_) => { - console.log(_); - res.render('ad_view', { - ads: [], - errors: { - load: 'Un problème est survenu lors du chargement des données', - }, - }); - }); - }) + }, + ) .get('/update/:id', checkPermissions('agent'), function(req, res, next) { const id = req.params.id; @@ -84,21 +74,11 @@ router.get('/create', checkPermissions('agent'), function(req, res, next) { errors.push('L\'annonce cherchée n\'a pas été trouvée'); } - res.render('ad_create', {ad: ad, errors_update: errors}); + res.render('ads/create', {ad: ad, errors_update: errors}); }); }) - .delete('/delete/:id', checkPermissions('agent'), function(req, res, next) { - const id = req.params.id; - - adModel.Ad.deleteOne({_id: id}).exec() - .then((value) => { - req.flash('success', 'L\'annonce a bien été supprimée'); - }) - .catch((reason) => { - req.flash('error', 'L\'annonce n\'a pas pu être supprimée'); - }) - .finally(() => res.redirect(303, '/ads/')); - }) + .get('/delete/:id', checkPermissions('agent'), deleteAdAction) + .delete('/delete/:id', checkPermissions('agent'), deleteAdAction) .get('/:id/picture/:index', function(req, res, next) { adModel.Ad.findOne({_id: req.params.id}) .then((ad) => { @@ -106,6 +86,89 @@ router.get('/create', checkPermissions('agent'), function(req, res, next) { res.send(ad.pictures[parseInt(req.params.index)].body); }) .catch(next); + }) + .get('/draft/:id', checkPermissions('agent'), renderAdAction(false)) + .get('/:id', renderAdAction(true)) + .get('/', function(req, res) { + adModel.Ad.find() + .then((ads) => { + const publishedAds = ads + .filter((ad) => ad.published) + .sort((a, b) => a.title.localeCompare(b.title)); + + const notPublishedAds = ads + .filter((ad) => !ad.published) + .sort((a, b) => a.title.localeCompare(b.title)); + + // on trie les annonces par ordre alphabétique + res.render('ads/index', { + ads: { + published: publishedAds, + notPublished: notPublishedAds, + }, + messages: req.flash('info'), + }); + }) + .catch((err) => { + console.error(err); + res.status(500); + res.render('ads/index', { + ads: [], + messages: [ + ...req.flash('info'), + 'Un problème est survenu lors du chargement des données', + ], + }); + }); }); +/** + * Renders a single ad. + * @param {*} published + * if the ad to find must be published. + * @return {Function} ad serving route function. + */ +function renderAdAction(published) { + return function(req, res) { + adModel.Ad.findOne({_id: req.params.id, published}) + .then((ad) => { + if (ad) { + res.render('ads/show', {ad}); + } else { + req.flash('info', 'L\'annonce demandé n\'a pas été trouvé'); + res.status(404); + res.redirect('/'); + } + }) + .catch((err) => { + console.error(err); + req.flash( + 'info', + 'Un problème est survenu lors du chargement des données', + ); + res.status(500); + res.redirect('/'); + }); + }; +} + +/** + * Delete a given ad. + * @param {*} req + * @param {*} res + * @param {Function} next + */ +function deleteAdAction(req, res, next) { + const id = req.params.id; + + adModel.Ad.deleteOne({_id: id}) + .then(() => { + req.flash('success', 'L\'annonce a bien été supprimée'); + }) + .catch((reason) => { + req.flash('error', 'L\'annonce n\'a pas pu être supprimée'); + }) + .finally(() => res.redirect(303, '/ads/')); +} + module.exports = router; diff --git a/src/permissions.js b/src/permissions.js index 266724e7f4d6a854200262ba7964bac262e45b78..6ec3053fd1fb767b42ab395251165c7b23d9e607 100644 --- a/src/permissions.js +++ b/src/permissions.js @@ -20,6 +20,7 @@ function checkPermissions(level) { if (!requiredPermissionLevel) { console.error(`checkPermissions: Le rôle ${level} n'existe pas !`); req.flash('info', 'Erreur dev : Rôle non défini (' + level + ')'); + res.status(401); res.redirect('/'); return; } diff --git a/views/ad_view.hbs b/views/ad_view.hbs deleted file mode 100644 index 8ac9e129d0fa048a51bc7f08b6c990b42320a7a6..0000000000000000000000000000000000000000 --- a/views/ad_view.hbs +++ /dev/null @@ -1,27 +0,0 @@ -

Liste des annonces immobilières

- -{{#each ads}} -
-

{{title}}

-

{{dateToLocaleString availabilityDate}}

- - - -
- {{#if description}} - - {{/if}} -
- - {{#authorize "agent"}} - Mettre à jour - - {{/authorize}} -
-{{/each}} - - \ No newline at end of file diff --git a/views/ad_create.hbs b/views/ads/create.hbs similarity index 100% rename from views/ad_create.hbs rename to views/ads/create.hbs diff --git a/views/ads/index.hbs b/views/ads/index.hbs new file mode 100644 index 0000000000000000000000000000000000000000..66ef9cbfb17511e239bdf07bbc2de34c14873e67 --- /dev/null +++ b/views/ads/index.hbs @@ -0,0 +1,49 @@ +{{#authorize "agent"}} +

Annonces non publiées

+ +Nouvelle annonce + +{{#each ads.notPublished}} +
+
+

{{title}}

+
+

Type : {{getEnumValue "type" type}}

+

Statut : {{getEnumValue "transactionStatus" transactionStatus}}

+

Prix : {{formatPrice price}} €

+
+ +
+ +
+{{/each}} +{{/authorize}} + +

Liste des annonces immobilières

+ +{{#each ads.published}} +
+
+

{{title}}

+
+

Type : {{getEnumValue "type" type}}

+

Statut : {{getEnumValue "transactionStatus" transactionStatus}}

+

Prix : {{formatPrice price}} €

+
+ +
+ +
+{{/each}} + +{{#authorize "agent"}} + +{{/authorize}} \ No newline at end of file diff --git a/views/ads/show.hbs b/views/ads/show.hbs new file mode 100644 index 0000000000000000000000000000000000000000..aa875079d4a4731abcda8331156ab7c04e8f7c3d --- /dev/null +++ b/views/ads/show.hbs @@ -0,0 +1,29 @@ +
+
+

Propriété : {{ad.title}}

+
+

Type : {{getEnumValue "type" ad.type}}

+

Statut : {{getEnumValue "transactionStatus" ad.transactionStatus}}

+

Prix : {{formatPrice ad.price}} €

+
+ +
+
+

Images

+
+ {{#with ad as |ad|}} + {{#each ad.pictures}} + {{title}} + {{/each}} + {{/with}} +
+

Description

+

{{ad.description}}

+
+ {{#authorize "agent"}} + + {{/authorize}} +
\ No newline at end of file diff --git a/views/layout.hbs b/views/layout.hbs index 7ec3c00b830ff6931bc8ea678fc3fc0ceea3b346..ebb36f2e6901710c24d51bc2a80780d95ac9094a 100644 --- a/views/layout.hbs +++ b/views/layout.hbs @@ -5,9 +5,9 @@ -
+
{{{body}}} -
+ {{#if messages}}
{{#each messages}}