diff --git a/app.js b/app.js index d4b545b3adbdd0f7c0e83130866a542013810afe..37888bdfff393e34c53a51dd93c09868edf59b83 100644 --- a/app.js +++ b/app.js @@ -9,7 +9,6 @@ const flash = require('connect-flash'); const LocalStrategy = require('passport-local').Strategy; const authRouter = require('./routes/auth'); -const usersRouter = require('./routes/users'); const adsRouter = require('./routes/ads'); const app = express(); @@ -22,6 +21,7 @@ require('dotenv').config(); mongoose.connect(process.env.DB_CONNECTION_STRING, { useNewUrlParser: true, useUnifiedTopology: true, + useFindAndModify: false, }); const User = require('./models/user').User; @@ -95,7 +95,6 @@ app.use(function(req, res, next) { }); app.use('/', authRouter); -app.use('/users', usersRouter); app.use('/ads', adsRouter); // catch 404 and forward to error handler diff --git a/models/ad.js b/models/ad.js index 7ba797671c530435d835c397b3daa936110e6d26..cd5374a37096bd50ac9f0b7033db993d6b2839a3 100644 --- a/models/ad.js +++ b/models/ad.js @@ -37,7 +37,6 @@ const postTemplate = { }, date: { type: Date, - get: (d) => d && d.toLocaleDateString(), }, }; diff --git a/public/javascript/script.js b/public/javascript/script.js index b588a882079c3b262ed8a5307a4e78fbe1d59681..9b034bfd7024dae081566b4f76d9ebd8c44a4ae5 100644 --- a/public/javascript/script.js +++ b/public/javascript/script.js @@ -1,3 +1,6 @@ +/** + * Registers listeners to messages so that they can be closed. + */ function setMessagesListeners() { const messages = document.querySelector('.msgs'); if (messages) { @@ -11,6 +14,31 @@ function setMessagesListeners() { } } +/** + * Registers listeners to answer buttons so that it displays reply form. + */ +function setReplyListeners() { + const replyButtons = document.querySelectorAll('.reply-btn'); + + replyButtons.forEach((btn) => { + const hiddenForm = btn.parentNode && btn.parentNode.querySelector('.reply-form'); + if (hiddenForm) { + btn.addEventListener('click', (e) => { + if (btn.dataset.shown === 't') { + btn.dataset.shown = 'f'; + btn.innerText = 'Répondre'; + hiddenForm.style.display = 'none'; + } else { + btn.dataset.shown = 't'; + btn.innerText = 'Cacher'; + hiddenForm.style.display = 'block'; + } + }); + } + }); +} + document.addEventListener('DOMContentLoaded', function() { setMessagesListeners(); + setReplyListeners(); }); diff --git a/public/stylesheets/style.css b/public/stylesheets/style.css index 2d6029d0814209d29c5d2c9ace51dd79555f9cd6..910a8befcb44ab516ae713550aaf053beb33dbf0 100644 --- a/public/stylesheets/style.css +++ b/public/stylesheets/style.css @@ -82,4 +82,8 @@ span.error-message { max-width: 25%; height: 100%; margin-bottom: 15px; +} + +.reply-form { + display: none; } \ No newline at end of file diff --git a/routes/ads.js b/routes/ads.js index 28879a956c5e087572f5454e524ec6527bb39d87..aa55cf5ef6606ba6ef1ace0fb6966f44bd21ba4a 100644 --- a/routes/ads.js +++ b/routes/ads.js @@ -14,59 +14,57 @@ router .get('/create', authorize('agent'), function(req, res) { res.render('ads/create'); }) - .post( - '/create', - authorize('agent'), - upload.array('pictures', 3), - function(req, res, next) { - const body = req.body; - const id = body.id; - - const formData = { - title: body.title, - type: body.type, - transactionStatus: body.transactionStatus, - price: body.price.replace(',', '.'), - published: body.published === 'on', - description: body.description, - availabilityDate: - body.availabilityDate === '' ? null : body.availabilityDate, - }; + .post('/create', authorize('agent'), upload.array('pictures', 3), function( + req, + res, + ) { + const body = req.body; + const id = body.id; - if (req.files.length) { - formData.pictures = req.files.map((f) => ({ - name: f.fieldName, - body: f.buffer, - })); - } + const formData = { + title: body.title, + type: body.type, + transactionStatus: body.transactionStatus, + price: body.price.replace(',', '.'), + published: body.published === 'on', + description: body.description, + availabilityDate: + body.availabilityDate === '' ? null : body.availabilityDate, + }; - if (id) { - // Peut-être charger l'objet en amont et le retourner si erreur ? - adModel.Ad.updateOne({_id: id}, {$set: formData}) - .exec() - .then(() => { - req.flash('success', 'Mise à jour réussie !'); - res.redirect('/ads/'); - }) - .catch((reason) => { - res.render('ads/create', reason); - }); - } else { - const newAd = new adModel.Ad(formData); - - newAd - .save() - .then(() => { - req.flash('success', 'Mise à jour réussie !'); - res.redirect('/ads/'); - }) - .catch((reason) => { - console.error(reason); - res.render('ads/create', reason); - }); - } - }, - ) + if (req.files.length) { + 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() + .then(() => { + req.flash('success', 'Mise à jour réussie !'); + res.redirect('/ads/'); + }) + .catch((reason) => { + res.render('ads/create', reason); + }); + } else { + const newAd = new adModel.Ad(formData); + + newAd + .save() + .then(() => { + req.flash('success', 'Mise à jour réussie !'); + res.redirect('/ads/'); + }) + .catch((reason) => { + console.error(reason); + res.render('ads/create', reason); + }); + } + }) .get('/update/:id', authorize('agent'), function(req, res, next) { const id = req.params.id; @@ -85,6 +83,71 @@ router }) .get('/delete/:id', authorize('agent'), deleteAdAction) .delete('/delete/:id', authorize('agent'), deleteAdAction) + .post('/:id/questions/create', authorize('client'), function(req, res) { + const question = { + author: req.user.name, + body: req.body.body, + date: new Date(), + }; + + adModel.Ad.findByIdAndUpdate( + req.params.id, + { + $push: { + questions: question, + }, + }, + ) + .then((ad) => { + res.status(201); + req.flash('success', 'Question publiée.'); + res.redirect('/ads/' + ad.id); + }) + .catch((err) => { + console.error(err); + req.flash( + 'error', + 'Une erreur est survenue lors du chargement des données.', + ); + res.redirect('/ads/' + ad.id); + }); + }) + .post( + '/:id/questions/:questionId/answers/create', + authorize('client'), + function(req, res) { + const answer = { + author: req.user.name, + body: req.body.body, + date: new Date(), + }; + + adModel.Ad.findByIdAndUpdate( + req.params.id, + { + $push: { + 'questions.$[target].answers': answer, + }, + }, + { + arrayFilters: [{'target._id': req.params.questionId}], + }, + ) + .then((ad) => { + res.status(201); + req.flash('success', 'Réponse publiée.'); + res.redirect('/ads/' + ad.id); + }) + .catch((err) => { + console.error(err); + req.flash( + 'error', + 'Une erreur est survenue lors du chargement des données.', + ); + res.redirect('/ads/' + ad.id); + }); + }, + ) .get('/:id/picture/:index', function(req, res, next) { adModel.Ad.findOne({_id: req.params.id}) .then((ad) => { diff --git a/routes/users.js b/routes/users.js deleted file mode 100644 index 7bea77ff87f5ab901ed9e69934986a398c3660b5..0000000000000000000000000000000000000000 --- a/routes/users.js +++ /dev/null @@ -1,9 +0,0 @@ -const express = require('express'); -const router = new express.Router(); - -/* GET users listing. */ -router.get('/', function(req, res, next) { - res.send('respond with a resource'); -}); - -module.exports = router; diff --git a/views/ads/show.hbs b/views/ads/show.hbs index aa875079d4a4731abcda8331156ab7c04e8f7c3d..40ff5b160a4fcec00e93645941726d03f19b87e1 100644 --- a/views/ads/show.hbs +++ b/views/ads/show.hbs @@ -20,6 +20,69 @@

Description

{{ad.description}}

+
+
+

Questions

+
+ {{#authorize "client"}} +
+

Poser une question

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

+ Voir les questions +

+
+ {{#with ad as |ad|}} + {{#each ad.questions}} +
+
+

{{body}}

+
+ + {{#authorize "agent"}} +
+ +
+
+
+ +
+
+
+ {{/authorize}} + {{#if this.answers.length}} +
+ Réponses + {{#each this.answers}} +
+
+

{{body}}

+
+ +
+ {{/each}} +
+
+ {{else}} +

Aucune réponse

+ {{/if}} + {{else}} +

Aucune question.

+ {{/each}} + {{/with}} +
+
{{#authorize "agent"}}