Créer une annonce immobilière
- - - {{#each errors_update}} -{{this}}
- {{/each}} - -diff --git a/app.js b/app.js
index ace05dc1b3762f670805a908b67ba7b3d5160038..77be80c51a8d33e32e17f4fa3bdd30f849aaca54 100644
--- a/app.js
+++ b/app.js
@@ -112,7 +112,7 @@ app.use(function(err, req, res, next) {
// render the error page
res.status(err.status || 500);
- res.render('error');
+ res.render('error', {title: 'ERREUR'});
});
module.exports = app;
diff --git a/public/stylesheets/style.css b/public/stylesheets/style.css
index 910a8befcb44ab516ae713550aaf053beb33dbf0..ced89f440400f3337ac25ebd46aae8aae923618a 100644
--- a/public/stylesheets/style.css
+++ b/public/stylesheets/style.css
@@ -1,37 +1,81 @@
+:root {
+ --bg-color: #e3e7f1;
+ --sh-color: #494ca2;
+ --fg-color: #222;
+ --hl-color: #8186d5;
+}
+
body {
margin: 0;
padding: 0;
font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;
+ background: var(--bg-color);
+}
+
+body > main {
+ box-sizing: border-box;
+ padding: 25px;
}
.msgs {
display: flex;
flex-direction: column;
- position: absolute;
- left:0;
+ position: fixed;
+ right: 0;
bottom: 0;
- margin: 0 0 15px 15px;
+ margin: 0 15px 15px 0;
min-width: 150px;
}
-#create-form {
- overflow: hidden;
- width: min-content;
-}
-
-form > fieldset, form > button {
- margin-top: 5%;
+fieldset, form > button {
+ margin-top: 15px;
}
div.form-input {
- display: inline-block;
margin-bottom: 15px;
+ color: var(--bg-color);
+ font-weight: bold;
}
span.error-message {
color: red;
}
+textarea {
+ border: solid 2px var(--fg-color);
+ border-radius: 5px;
+}
+
+fieldset {
+ border-color: var(--sh-color);
+}
+
+figure {
+ margin: 15px 7px;
+}
+
+legend {
+ color: var(--bg-color);
+ font-size: 1.2em;
+ font-weight: bold;
+}
+
+.form-input > label {
+ font-weight: bold;
+ color: var(--fg-color);
+ font-size: 1.2em;
+}
+
+.form-input > ul {
+ display: flex;
+ flex-direction: column;
+ padding-left: 0;
+}
+
+.form-input > ul > li {
+ list-style-type: none;
+}
+
.msg {
display: flex;
justify-content: space-between;
@@ -67,23 +111,260 @@ span.error-message {
cursor: pointer;
}
-.ad-photos {
+.ad-pictures {
display: flex;
align-items: center;
- width: 100%;
- max-width: 750px;
justify-content: space-around;
+ /*Forced to specify a width here, otherwise container warps outside of parent*/
+ width: 610px;
}
-.ad-photos > img {
+.ad-pictures > img {
box-sizing: border-box;
- border: solid 2px black;
- padding: 2px;
- max-width: 25%;
- height: 100%;
+ border: solid 5px var(--bg-color);
+ border-radius: 5px;
+ background: var(--sh-color);
+ flex: 1;
+ height: auto;
margin-bottom: 15px;
+ box-shadow: 0 0 5px var(--fg-color);
+ margin: 0 7px;
}
.reply-form {
display: none;
+}
+
+.ad {
+ box-sizing: border-box;
+ box-shadow: 0 0 8px var(--fg-color);
+ border-radius: 15px;
+ padding: 25px;
+ width: 700px;
+ background: var(--hl-color);
+ color: var(--fg-color);
+ display: flex;
+ flex-direction: column;
+ margin-bottom: 30px;
+}
+
+.ad-preview {
+ width: 400px;
+}
+
+.ad-infos {
+ margin-bottom: 15px;
+}
+
+.infos-title > h2 {
+ margin: 0;
+ margin-bottom: 0.33em;
+}
+
+.infos-title > h1 {
+ color: var(--bg-color);
+ font-size: 1.8em;
+ margin: 0;
+}
+
+.infos-details {
+ width: 100%;
+ color: var(--bg-color);
+ font-size: 1.2em;
+ display: grid;
+ grid-template-columns: 50% 50%;
+}
+
+.ad-details > h2 {
+ margin: 0;
+}
+
+.infos-item > p, .infos-item > time {
+ margin: 0;
+ font-size: 1.3em;
+}
+
+.infos-item > h4 {
+ margin-bottom: 5px;
+ color: var(--fg-color)
+}
+
+.ad-description {
+ color: var(--bg-color);
+ font-size: 1.2em;
+}
+
+button, input[type=submit], a {
+ box-sizing: border-box;
+ background: var(--fg-color);
+ color: var(--bg-color);
+ border: none;
+ border-radius: 5px;
+ padding: 10px 20px;
+ font-size: 1.2em;
+ text-decoration: none;
+ cursor: pointer;
+ display: inline-block;
+ width: max-content;
+ font-family: "Lucida Grande", Helvetica, Arial, sans-serif;
+}
+
+button.alt, input.alt[type=submit], a.alt {
+ background: var(--hl-color);
+ color: var(--bg-color);
+ font-weight: bold;
+}
+
+button:hover, input[type=submit]:hover, a:hover {
+ color: var(--hl-color);
+}
+
+button.alt:hover, input.alt[type=submit]:hover, a.alt:hover {
+ background: var(--sh-color);
+ color: var(--bg-color);
+ font-weight: bold;
+}
+
+button:active, input[type=submit]:active, a:active {
+ background: var(--sh-color);
+ color: var(--fg-color);
+}
+
+.ad-questions {
+ margin-bottom: 15px;
+}
+
+.questions-details > .question:not(:last-child) {
+ margin-bottom: 10px;
+}
+
+.question {
+ background: var(--sh-color);
+ padding: 15px;
+ border-radius: 15px;
+ border: solid 3px var(--fg-color);
+ color: var(--bg-color);
+}
+
+.question > main, .reply > main {
+ font-size: 1.2em;
+}
+
+.question > time, .reply > time {
+ font-weight: bold;
+ margin-left: 3em;
+}
+
+.reply {
+ margin-left: 2em;
+}
+
+.reply:not(:last-child) {
+ margin-bottom: 10px;
+}
+
+.reply-control {
+ margin-top: 5px;
+}
+
+.reply-btn {
+ font-weight: bold;
+ font-size: 1em;
+}
+
+.m-t {
+ margin-top: 5px;
+}
+
+.m-b {
+ margin-bottom: 5px;
+}
+
+.m-l {
+ margin-left: 5px;
+}
+
+.m-r {
+ margin-right: 5px;
+}
+
+.m-t-lg {
+ margin-top: 20px;
+}
+
+.m-b-lg {
+ margin-bottom: 20px;
+}
+
+.m-l-lg {
+ margin-left: 20px;
+}
+
+.m-r-lg {
+ margin-right: 20px;
+}
+
+.ads {
+ width: 100%;
+ display: flex;
+ flex-wrap: wrap;
+ justify-content: space-around;
+}
+
+nav {
+ display: flex;
+ justify-content: space-between;
+ background: var(--hl-color);
+ box-shadow: 0 2px 5px black;
+ margin-bottom: 5px;
+ padding-top: 5px;
+}
+
+nav>.auth-links, nav>.nav-links {
+ display: flex;
+ align-items: center;
+}
+
+.nav-links a {
+ background: transparent;
+ color: var(--bg-color);
+ font-weight: bold;
+ font-size: 1.4em;
+ cursor: pointer;
+}
+
+.nav-links a:hover {
+ color: var(--fg-color);
+}
+
+.nav-links a:active {
+ color: var(--sh-color);
+}
+
+.auth-links {
+ font-size: 1.2em;
+ color: var(--bg-color);
+}
+
+.auth-links a, .auth-links button {
+ box-sizing: border-box;
+ font-size: 0.9em;
+ padding: 5px 10px;
+ height: max-content;
+ font-weight: bold;
+}
+
+.auth-form {
+ width: 300px;
+ display: flex;
+ flex-direction: column;
+}
+
+.auth-form > .form-input {
+ display: flex;
+ flex-direction: column;
+}
+
+.form-input > select {
+ width: min-content;
}
\ No newline at end of file
diff --git a/routes/ads.js b/routes/ads.js
index 7f8b76d8b7d2c5dcf2977b9d3e6928e1d862eab3..660be336cd46899f3c800b7d195dddb7e41f17e8 100644
--- a/routes/ads.js
+++ b/routes/ads.js
@@ -10,56 +10,67 @@ const upload = multer({
});
/* GET to get to get to the ad creation form */
-router.get('/create', authorize('agent'), function(req, res, next) {
- 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 && body.price.replace(',', '.'),
- published: body.published === 'on',
- description: body.description,
- availabilityDate: body.availabilityDate === '' ?
- null : body.availabilityDate,
- };
-
- errCallback = (reason) => res.status(406).render('ads/create', reason);
+router
+ .get('/create', authorize('agent'), function(req, res) {
+ res.render('ads/create', {title: 'Créer annonce'});
+ })
+ .post('/create', authorize('agent'), upload.array('pictures', 3), function(
+ req,
+ res,
+ ) {
+ 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,
+ };
- if (req.files && req.files.length) {
- formData.pictures = req.files.map((f) => ({
- name: f.fieldName,
- body: f.buffer,
- }));
- }
+ 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', {
+ title: 'Modifier annonce',
+ errors: reason.errors,
+ });
+ });
+ } else {
+ const newAd = new adModel.Ad(formData);
- if (id) {
- // Peut-être charger l'objet en amont et le retourner si erreur ?
- adModel.Ad.updateOne({_id: id}, {$set: formData}).exec()
- .then((value) => {
- req.flash('success', 'Mise à jour réussie !');
- res.redirect('/ads/');
- })
- .catch((reason) => {
- errCallback(reason);
- });
- } else {
- const newAd = new adModel.Ad(formData);
-
- newAd.save()
- .then((value) => {
- res.redirect('/ads/');
- })
- .catch((reason) => {
- errCallback(reason);
- });
- }
- })
+ newAd
+ .save()
+ .then(() => {
+ req.flash('success', 'Mise à jour réussie !');
+ res.redirect('/ads/');
+ })
+ .catch((reason) => {
+ console.error(reason);
+ res.render('ads/create', {
+ title: 'Créer annonce',
+ errors: reason.errors,
+ });
+ });
+ }
+ })
.get('/update/:id', authorize('agent'), function(req, res, next) {
const id = req.params.id;
let status;
@@ -78,8 +89,7 @@ router.get('/create', authorize('agent'), function(req, res, next) {
req.flash('success', 'Mise à jour réussie !');
}
- res.status(status)
- .render('ads/create', {ad: ad, errors_update: errors});
+ res.render('ads/create', {title: 'Modifier annonce', ad: ad, errors_update: errors});
});
})
.get('/delete/:id', authorize('agent'), deleteAdAction)
@@ -169,6 +179,7 @@ router.get('/create', authorize('agent'), function(req, res, next) {
// on trie les annonces par ordre alphabétique
res.render('ads/index', {
+ title: 'Annonces',
ads: {
published: publishedAds,
notPublished: notPublishedAds,
@@ -179,6 +190,7 @@ router.get('/create', authorize('agent'), function(req, res, next) {
console.error(err);
res.status(500);
res.render('ads/index', {
+ title: 'Annonces',
ads: [],
_messages: [
...res.locals._messages,
@@ -199,7 +211,7 @@ function renderAdAction(published) {
adModel.Ad.findOne({_id: req.params.id, published}, {'pictures.body': 0})
.then((ad) => {
if (ad) {
- res.render('ads/show', {ad});
+ res.render('ads/show', {title: ad.title, ad});
} else {
req.flash('error', 'L\'annonce demandé n\'a pas été trouvé');
res.redirect('/');
diff --git a/routes/auth.js b/routes/auth.js
index eff9aabe56034cc7d2dd88f0fd053dd33cdcd043..797ba6d76bb8dc2ae525fa2b6ca78ee250fa2515 100644
--- a/routes/auth.js
+++ b/routes/auth.js
@@ -9,6 +9,7 @@ const users = require('../models/user');
router
.get('/register', function(req, res, next) {
res.render('register', {
+ title: 'Inscription',
roles: Object.values(users.USER_ROLE),
});
})
@@ -42,7 +43,7 @@ router
.catch(next);
})
.get('/login', function(req, res, next) {
- res.render('login');
+ res.render('login', {title: 'Connexion'});
})
.post('/login', function(req, res, next) {
passport.authenticate('local', function(err, user, info) {
diff --git a/views/ads/create.hbs b/views/ads/create.hbs
index 1572d361eae40df247ad9cbd740694455ee3e3f6..0ed3e582395fe204a1065f12b90c787d5577b720 100644
--- a/views/ads/create.hbs
+++ b/views/ads/create.hbs
@@ -1,105 +1,113 @@
- {{this}}Créer une annonce immobilière
-
-
- {{#each errors_update}}
-
Type : {{getEnumValue "type" type}}
-Statut : {{getEnumValue "transactionStatus" transactionStatus}}
-Prix : {{formatPrice price}} €
-{{getEnumValue "type" type}}
+{{getEnumValue "transactionStatus" transactionStatus}}
+{{formatPrice price}} €
+Type : {{getEnumValue "type" type}}
-Statut : {{getEnumValue "transactionStatus" transactionStatus}}
-Prix : {{formatPrice price}} €
-{{getEnumValue "type" type}}
+{{getEnumValue "transactionStatus" transactionStatus}}
+{{formatPrice price}} €
+Type : {{getEnumValue "type" ad.type}}
-Statut : {{getEnumValue "transactionStatus" ad.transactionStatus}}
-Prix : {{formatPrice ad.price}} €
+{{getEnumValue "type" ad.type}}
+{{getEnumValue "transactionStatus" ad.transactionStatus}}
+{{formatPrice ad.price}} €
+{{ad.description}}
+{{ad.description}}
{{body}}
+ {{body}}{{body}}
-Aucune réponse
{{/if}} +Aucune question.
{{/each}} {{/with}}