Skip to content
Snippets Groups Projects
Commit 2b90a3ed authored by Martin Lehoux's avatar Martin Lehoux
Browse files

ready for prod

parent ef14943e
Branches
No related tags found
No related merge requests found
Showing with 450 additions and 38 deletions
......@@ -9,6 +9,7 @@ const config = require("./config.json");
const Client = require("./models/client");
const Produit = require("./models/produit");
// const Course = require("./models/course");
const app = express();
const store = new mongoDBStore({
......@@ -23,36 +24,145 @@ app.use(session({
saveUninitialized: false,
store
}));
app.use("/images", express.static('images'));
app.use("/static", express.static("static"));
app.use(bodyParser.urlencoded({ extended: false }));
app.use(fileUpload());
// Login checker
app.get("/", (req, res) => res.render("index"));
app.get("/", (req, res) => res.render("index", { loggedIn: Boolean(req.session.loggedIn) }));
app.post("/connexion", (req, res) => {
if (!req.body.password || req.body.password!==config.password) res.send("bad password");
else {
req.session.loggedIn = true;
res.redirect("/");
}
});
app.get("/deconnexion", (req, res) => {
req.session.destroy();
res.redirect("/");
});
app.use((req, res, next) => {
if (req.session.loggedIn) next();
else res.redirect("/");
});
app.get("/clients", (req, res) => {
Client.find({}, (err, clients) => res.render("clients", { clients }));
Client.find({}, (err, clients) => res.render("clients", { clients, loggedIn: Boolean(req.session.loggedIn) }));
});
app.get("/clients/nouveau", (req, res) => res.render("nouveau-client"));
app.post("/clients/nouveau", (req, res) => {
app.get("/clients/nouveau", (req, res) => res.render("nouveau-client", { loggedIn: Boolean(req.session.loggedIn) }));
app.post("/clients", (req, res) => {
if (req.body.membre) req.body.membre = true;
Client
.create(req.body)
.then(() => res.redirect("/clients"))
.catch(err => console.error(err));
.catch(err => res.send(err));
});
app.get("/clients/:id", (req, res) => {
Promise
.all([
Client.findById(req.params.id).populate("commandes.produit").exec(),
Produit.find().exec(),
])
.then(([client, produits]) => res.render("client", { client, produits, loggedIn: Boolean(req.session.loggedIn) }));
});
app.post("/clients/:id/operations", (req, res) => {
Client
.findOneAndUpdate({ _id: req.params.id }, { $push: { operations: { montant: req.body.montant } }, $inc: { solde: req.body.montant } }, (err, client) => console.log(err));
res.redirect("/clients/"+req.params.id);
});
app.get("/clients/:idClient/achat/:idProduit", (req, res) => {
Produit
.findById(req.params.idProduit).exec()
.then(produit => {
Client.findOneAndUpdate({ _id: req.params.idClient }, { $push: { commandes: { produit } }, $inc: { solde: -produit.prixUnitaire } }).exec();
res.redirect("/clients/"+req.params.idClient);
})
.catch(err => res.send(err));
});
app.get("/produits", (req, res) => {
Produit.find({}, (err, produits) => res.render("produits", { produits }));
Promise
.all([
Produit.find({ categorie: "boisson" }).exec(),
Produit.find({ categorie: "nourriture" }).exec(),
Produit.find({ categorie: "autre" }).exec()
])
.then(([boissons, nourritures, autres]) => res.render("produits", { boissons, nourritures, autres, loggedIn: Boolean(req.session.loggedIn) }));
});
app.get("/produits/nouveau", (req, res) => res.render("nouveau-produit"));
app.post("/produits/nouveau", (req, res) => {
app.get("/produits/nouveau", (req, res) => res.render("nouveau-produit", { loggedIn: Boolean(req.session.loggedIn) }));
app.post("/produits", (req, res) => {
const image = req.files.image;
if (image) {
image.mv("images/"+image.name);
}
Produit
.create({ ...req.body, image })
.create({ ...req.body, image: image && image.name })
.then(() => res.redirect("/produits"))
.catch(err => console.error(err));
.catch(err => res.send(err));
});
app.get("/produits/:id", (req, res) => {
Produit
.findById(req.params.id).exec()
.then(produit => res.render("produit", { produit, loggedIn: Boolean(req.session.loggedIn) }));
});
app.get("/produits/:id/modifier", (req, res) => {
Produit
.findById(req.params.id).exec()
.then(produit => res.render("modifier-produit", { produit, loggedIn: Boolean(req.session.loggedIn) }));
});
app.post("/produits/:id/modifier", (req, res) => {
const image = req.files.image;
if (image) {
image.mv("images/"+image.name);
Produit.findOneAndUpdate({ _id: req.params.id }, { image: image.name }).exec();
}
Produit.findOneAndUpdate({ _id: req.params.id }, { ...req.body }).exec();
res.redirect("/produits");
});
app.post("/produits/:id/supprimer", (req, res) => {
Produit.findOneAndDelete({ _id: req.params.id }).exec()
res.redirect("/produits");
});
// app.get("/courses", (req, res) => {
// Course
// .find().populate("conducteur").exec()
// .then(courses => res.render("courses", { courses }));
// });
// app.get("/courses/nouvelle", (req, res) => {
// Promise
// .all([
// Client.find({ membre: true }).exec(),
// Produit.find({}).exec()
// ])
// .then(([membres, produits]) => res.render("nouvelle-course", { membres, produits }));
// });
// app.post("/courses/nouvelle", (req, res) => {
// const produits = [];
// for (i = 0; i < parseInt(req.body.total); i++) {
// produits.push({
// produit: req.body[`produit[${i}]`],
// quantite: req.body[`quantite[${i}]`],
// prix: parseInt(req.body[`prix[${i}]`])
// });
// }
// const prix = parseInt(req.body.prixFournitures) + produits.reduce((total, now) => total + now.prix, 0);
// Course
// .create({
// conducteur: req.body.conducteur,
// produits,
// prixFournitures: req.body.prixFournitures,
// prix
// })
// .then(() => res.redirect("/courses"))
// .catch(err => res.send(err));
// });
// app.get("/courses/:id", (req, res) => {
// Course
// .findOne({ _id: req.params.id }).exec()
// .then(course => {
// res.render("course", { course });
// }); //
// });
mongoose.connect('mongodb://localhost/bonober', err => {
if (err) {
......
images/kinder-bueno.png

139 KiB

images/orangina.jpg

140 KiB

......@@ -2,11 +2,7 @@ const mongoose = require("mongoose");
const Commande = new mongoose.Schema({
date: { type: Date, default: Date.now },
prix: { type: Number, required: true },
produits: [{
nom: { type: String, required: true },
quantite: { type: Number, required: true }
}]
produit: { type: mongoose.Schema.Types.ObjectId, ref: 'Produit', required: true }
});
module.exports = Commande;
\ No newline at end of file
......@@ -7,7 +7,9 @@ const Course = new mongoose.Schema({
quantite: { type: Number, required: true },
prix: { type: Number, required: true }
}],
prixFournitures: { type: Number, default: 0 }
prixFournitures: { type: Number, default: 0 },
date: { type: Date, default: Date.now },
prix: { type: Number, required: true }
});
module.exports = mongoose.model("Course", Course);
\ No newline at end of file
......@@ -3,7 +3,7 @@ const mongoose = require("mongoose");
const Operation = new mongoose.Schema({
date: { type: Date, default: Date.now },
montant: { type: Number, required: true },
motif: { type: String }
motif: { type: String, default: "recharge" }
});
module.exports = Operation;
\ No newline at end of file
......@@ -2,7 +2,7 @@ const mongoose = require("mongoose");
const Produit = new mongoose.Schema({
nom: { type: String, required: true },
stocks: { type: Number, default: 0 },
stock: { type: Number, default: 0 },
prixUnitaire: { type: Number, required: true },
categorie: { type: String, enum: ["boisson", "nourriture", "autre"], default: "autre" },
image: { type: String }
......
function ajoutProduit() {
if (typeof window.counter == 'undefined') window.counter = 0;
console.log(window.counter);
const produit = document.getElementById("produit").value;
const quantite = document.getElementById("quantite").value;
const prix = document.getElementById("prix").value;
console.log(produit, quantite, prix);
const row = document.createElement("tr");
const inputProduit = document.createElement("input");
inputProduit.setAttribute("type", "hidden");
inputProduit.name = `produit[${window.counter}]`;
inputProduit.value = produit;
const produitTD = document.createElement("td");
produitTD.innerText = document.getElementById("produit").options[document.getElementById("produit").selectedIndex].text;
const inputQuantite = document.createElement("input");
inputQuantite.setAttribute("type", "hidden");
inputQuantite.name = `quantite[${window.counter}]`;
inputQuantite.value = quantite;
const quantiteTD = document.createElement("td");
quantiteTD.innerText = quantite;
const inputPrix = document.createElement("input");
inputPrix.setAttribute("type", "hidden");
inputPrix.name = `prix[${window.counter}]`;
inputPrix.value = prix;
const prixTD = document.createElement("td");
prixTD.innerText = prix;
row.appendChild(inputProduit);
row.appendChild(produitTD);
row.appendChild(inputQuantite);
row.appendChild(quantiteTD);
row.appendChild(inputPrix);
row.appendChild(prixTD);
document.getElementById("produits").appendChild(row);
window.counter ++;
document.getElementById("total").value = window.counter;
}
\ No newline at end of file
extends layout.pug
block content
.ui.container
h1.ui.header #{client.prenom} #{client.nom}
.ui.grid
.six.wide.column
.ui.statistics
.statistic
.value #{client.solde.toFixed(2)} €
.label Solde
form(action="/clients/"+client._id+"/supprimer" method="post" onsubmit="return confirm('Supprimer "+client.prenom+" "+client.nom+" ?');")
a.ui.button.icon(href="/clients/"+client._id+"/modifier")
i.edit.icon
button.ui.button.icon(type="submit")
i.trash.icon
button.ui.button(type="button" onclick="$('#commande').modal('show')") Commande
button.ui.button(type="button" onclick="$('#recharge').modal('show')") Recharge
.ui.modal#commande
.header #{client.prenom} #{client.nom}
.content.scrollable
form.ui.form#commande-form(action="/clients/"+client._id+"/commandes" method="post")
table.ui.table
tbody#produits
.ui.tiny.images
each produit in produits
a.ui.tiny.image(href="/clients/"+client._id+"/achat/"+produit._id)
img(src="/images/"+produit.image)
.ui.modal#recharge
.header #{client.prenom} #{client.nom}
.content
form.ui.form#recharge-form(action="/clients/"+client._id+"/operations" method="post")
.field
label Montant de l'opération
input(type="number" step="0.01" placeholder="Montant" name="montant")
.actions
button.ui.button(type="button" onclick="$('#recharge').modal('hide')") Annuler
button.ui.button(type="submit" form="recharge-form") Valider
.ten.wide.column
h3.ui.header Dernières opérations
table.ui.table
thead
tr
th Date
th Montant
th Motif
tbody
each operation in client.operations.slice(-5).reverse()
tr
td= operation.date.toLocaleDateString()
td #{operation.montant.toFixed(2)} €
td= operation.motif
//- p= client.commandes
h3.ui.header Dernières commandes
table.ui.table
thead
tr
th Date
th Produit
th Prix
tbody
each commande in client.commandes.slice(-5).reverse()
tr
td= commande.date.toLocaleDateString()
td #{commande.produit.nom}
td #{commande.produit.prixUnitaire.toFixed(2)} €
script $(".ui.modal").modal();
\ No newline at end of file
......@@ -9,5 +9,11 @@ block content
tbody
each client in clients
tr
td #{client.prenom} #{client.nom}
.ui.label.float #{client.solde} €
td.selectable
a(href="/clients/"+client._id).class #{client.prenom} #{client.nom}
td.right.align.collapsing
.ui.label #{client.solde.toFixed(2)} €
if client.membre
.ui.label
i.certificate.icon
| Toucan™
extends layout.pug
block content
.ui.container
h1.ui.header Courses du #{course.date.toLocaleDateString()}
table.ui.table
thead
tr
th Produit
th Quantité
th Prix total
tbody
each produit in course.produits
tr
td= produit.id
//- img(src="/images/"+produit.image)
//- | #{produit.nom}
td= produit.quantite
td #{produit.prix} €
tr
td Fournitures
td
td #{course.prixFournitures} €
extends layout.pug
block content
.ui.container
h1.ui.header Courses
a.ui.button(href="/courses/nouvelle") Créer de nouvelles courses
table.ui.table
tbody
each course in courses
tr
td.selectable
a(href="/courses/"+course._id) Courses du #{course.date.toLocaleDateString()} par #{course.conducteur.prenom} #{course.conducteur.nom}
td.right.align.collapsing
.ui.label #{course.prix} €
extends layout.pug
block content
.ui.container
.ui.grid.one.column.centered
.column.four.wide
if !loggedIn
form.ui.form(action="/connexion", method="post")
.field
label Mot de passe
input(type="password" name="password" placeholder="test")
else
h3.ui.header Connecté
......@@ -7,17 +7,21 @@ html(lang="en")
title Bonober
link(rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.min.css" integrity="sha256-9mbkOfVho3ZPXfM7W8sV2SndrGDuh7wuyLjtsWeTI1Q=" crossorigin="anonymous")
body
script(src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha256-3edrmyuQ0w65f8gfBsqowzjJe2iM6n0nKciPUp8y+7E=" crossorigin="anonymous")
script(src="https://code.jquery.com/jquery-3.3.1.min.js" crossorigin="anonymous")
script(src="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.min.js" integrity="sha256-t8GepnyPmw9t+foMh3mKNvcorqNHamSKtKRxxpUEgFI=" crossorigin="anonymous")
script(src="/static/script.js")
block navbar
.ui.pointing.menu
a.item(href="/") Bonober
.item.header Bonober
a.item(href="/clients") Clients
a.item(href="/produits") Produits
a.item(href="/courses") Courses
a.item(href="/statistiques") Statistiques
//- a.item(href="/courses") Courses
//- a.item(href="/statistiques") Statistiques
.right.menu
a.item(href="/connexion") Connexion
if !loggedIn
a.item(href="/") Connexion
else
a.item(href="/deconnexion") Déconnexion
block content
\ No newline at end of file
extends layout.pug
block content
.ui.container
h1.ui.header Modifier #{produit.nom}
form.ui.form(action="/produits/"+produit._id+"/modifier" method="post" enctype="multipart/form-data")
.ui.field
label Nom
input(name="nom" placeholder="Nom" type="text" value=produit.nom)
.ui.field
label Stock
input(name="stock" placeholder="Quantité en stock" type="number" value=produit.stock)
.ui.field
label Prix unitaire
input(name="prixUnitaire" placeholder="Prix unitaire" type="number" step="0.1" value=produit.prixUnitaire)
.ui.inline.fields
label Catégorie :
.field
.ui.checkbox.radio
input(type="radio" name="categorie" value="boisson" checked=(produit.categorie == "boisson"))
label Boisson
.field
.ui.checkbox.radio
input(type="radio" name="categorie" value="nourriture" checked=(produit.categorie == "nourriture"))
label Nourriture
.field
.ui.checkbox.radio
input(type="radio" name="categorie" value="autre" checked=(produit.categorie == "autre"))
label Autre
.ui.fields
.field.two.wide
img.ui.tiny.image(src="/images/"+produit.image)
.field.fourteen.wide
label Image
input(type="file" name="image")
.ui.field
input.ui.button(type="submit" value="Modifier")
\ No newline at end of file
......@@ -4,13 +4,13 @@ block content
.ui.container
h1.ui.header Nouveau client
form.ui.form(action="/clients/nouveau" method="post")
.ui.field
label Nom
input(name="nom" placeholder="Nom" type="text")
form.ui.form(action="/clients" method="post")
.ui.field
label Prénom
input(name="prenom" placeholder="Prénom" type="text")
.ui.field
label Nom
input(name="nom" placeholder="Nom" type="text")
.ui.field
label Surnom
input(name="surnom" placeholder="Surnom (optionnel)" type="text")
......
......@@ -4,13 +4,13 @@ block content
.ui.container
h1.ui.header Nouveau produit
form.ui.form(action="/produits/nouveau" method="post" enctype="multipart/form-data")
form.ui.form(action="/produits" method="post" enctype="multipart/form-data")
.ui.field
label Nom
input(name="nom" placeholder="Nom" type="text")
.ui.field
label Stocks
input(name="stocks" placeholder="Quantité en stocks" type="number")
label Stock
input(name="stock" placeholder="Quantité en stock" type="number")
.ui.field
label Prix unitaire
input(name="prixUnitaire" placeholder="Prix unitaire" type="number" step="0.1")
......
extends layout.pug
block content
.ui.container
h1.ui.header Nouvelles courses
form.ui.form(action="/courses/nouvelle" method="post")
.ui.field
label Conducteur
select.ui.fluid.search.dropdown(name="conducteur")
each membre in membres
option(value=membre._id) #{membre.prenom} #{membre.nom}
table.ui.table
thead
tr
th Produit
th Quantité
th Prix total
th
tbody#produits
tr
td
select.ui.fluid.search.dropdown#produit(name="nouveau_produit")
each produit in produits
option(value=produit._id) #{produit.nom}
td
input#quantite(type="number" name="quantite" placeholder="Quantité")
td
input#prix(type="number" step="0.01" name="prix" placeholder="Prix")
td
button.ui.icon.button(onclick="ajoutProduit()" type="button")
i.add.icon
.ui.field
label Prix des fournitures
input(type="number" step="0.01" name="prixFournitures" placeholder="Fournitures")
.ui.field
input#total(type="hidden" value="0" name="total")
input.ui.button(type="submit" value="Enregistrer")
script $('select').dropdown();
extends layout.pug
block content
.ui.container
h1.ui.header
| #{produit.nom}
span.ui.label= produit.categorie
.ui.grid
.four.wide.column
img.ui.image.bordered.rounded(src="/images/"+produit.image)
.twelve.wide.column
.ui.statistics
.statistic
.value= produit.stock
.label En stock
.statistic
.value #{produit.prixUnitaire} €
.label L'unité
.ui.container.center.aligned
form(action="/produits/"+produit._id+"/supprimer" method="post" onsubmit="return confirm('Supprimer "+produit.nom+" ?');")
a.ui.button.icon(href="/produits/"+produit._id+"/modifier")
i.edit.icon
button.ui.button.icon(type="submit")
i.trash.icon
......@@ -4,15 +4,39 @@ block content
.ui.container
h1.ui.header Produits
a.ui.button(href="/produits/nouveau") Créer un produit
.ui.six.cards
each produit in produits
.card
h2.ui.header Boissons
.ui.eight.cards
each produit in boissons
a.card(href="/produits/"+produit._id)
.content
.header= produit.nom
.ui.label.floating #{produit.prixUnitaire}€
.image
img(src="https://www.google.com/url?sa=i&rct=j&q=&esrc=s&source=images&cd=&cad=rja&uact=8&ved=2ahUKEwjT2J-lhO_gAhUHzIUKHQVwAG0QjRx6BAgBEAU&url=https%3A%2F%2Fwww.lachaiselongue.fr%2Ffrigo-canette-coca-6l.html&psig=AOvVaw0ajbIFVueMaG631yr-H1j0&ust=1552013851276147", alt="")
.ui.tiny.image.centered
img(src="/images/"+produit.image alt="")
.content
.description #{produit.stocks} en stock
.description #{produit.stock} en stock
h2.ui.header Nourriture
.ui.eight.cards
each produit in nourritures
a.card(href="/produits/"+produit._id)
.content
.header= produit.nom
.ui.label.floating #{produit.prixUnitaire}€
.ui.tiny.image.centered
img(src="/images/"+produit.image alt="")
.content
.description #{produit.stock} en stock
h2.ui.header Autres
.ui.eight.cards
each produit in autres
a.card(href="/produits/"+produit._id)
.content
.header= produit.nom
.ui.label.floating #{produit.prixUnitaire}€
.ui.tiny.image.centered
img(src="/images/"+produit.image alt="")
.content
.description #{produit.stock} en stock
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment