diff --git a/data/menu.json b/data/menu.json index 046a19f..da6bdad 100644 --- a/data/menu.json +++ b/data/menu.json @@ -26,8 +26,8 @@ }, { "name": "90er-Reis-Plattenscheibe", - "price": 51.9, - "desc": "Für Retrofreunde die seltene Platte \"Blue Eyes\" von \"Brooky Ryes\" bestückt mit 5 verschiedenen Reissorten dazu etwas Gemüse in Vinyl-Optik" + "desc": "Für Retrofreunde die seltene Platte \"Blue Eyes\" von \"Brooky Ryes\" bestückt mit 5 verschiedenen Reissorten dazu etwas Gemüse in Vinyl-Optik", + "price": 51.9 } ] }, @@ -47,18 +47,18 @@ }, { "name": "Üppige Suppe", - "price": 2.2, - "desc": "Üppige Suppe mit üppigem Gemüse gewuppt mit zerruppten Gestrüpp" + "desc": "Üppige Suppe mit üppigem Gemüse gewuppt mit zerruppten Gestrüpp", + "price": 2.2 }, { "name": "Henker's Mahlzeit", - "price": 6.8, - "desc": "Roter Reis mit scharfem Chili mit tomaten und roter Paprika" + "desc": "Roter Reis mit scharfem Chili mit tomaten und roter Paprika", + "price": 6.8 }, { "name": "Nudelsuppe", - "price": 3.3, - "desc": "Leissuppe mit liesigel Poltion klingliger Knödel" + "desc": "Leissuppe mit liesigel Poltion klingliger Knödel", + "price": 3.3 } ] }, @@ -83,8 +83,8 @@ }, { "name": "RisiBisi", - "price": 17.3, - "desc": "Lila Reis aus kontrolliertem Bio-Anbau mit knallgrünen erbsen, orangene Paprika und gelber Mais als Beilage mit sauerstoffarmem, blauem Schweinefleisch von glücklichen Tieren" + "desc": "Lila Reis aus kontrolliertem Bio-Anbau mit knallgrünen erbsen, orangene Paprika und gelber Mais als Beilage mit sauerstoffarmem, blauem Schweinefleisch von glücklichen Tieren", + "price": 17.3 }, { "name": "Burger Duck", @@ -119,8 +119,8 @@ }, { "name": "Spagetti Reisonese", - "price": 5.6, - "desc": "Spaghetti mit Tomatensoße und Reis" + "desc": "Spaghetti mit Tomatensoße und Reis", + "price": 5.6 } ] }, diff --git a/data/reservation.json b/data/reservation.json new file mode 100644 index 0000000..55ab0a1 --- /dev/null +++ b/data/reservation.json @@ -0,0 +1,31 @@ +{ + "items": [ + { + "name": "Hans Bauer", + "desc": "Kleine Familienfeier zu meinem 80. Geburtstag", + "email": "hans@gmx.de", + "person": 20, + "time": "20:15", + "date": "16.05.2016", + "read": false + }, + { + "name": "Micha Müller", + "desc": "Ich will meiner geliebten Ursula einen Heiratsantrag machen. Ich suche ein gemütliches Eckchen.", + "email": "micha@mueller.de", + "person": 3, + "time": "21:00", + "date": "14.3.2016", + "read": true + }, + { + "name": "Spa Mer", + "desc": "Das ist eine versuchte Injection\r\nmit mehreren Zeilen.", + "email": "ano.ny@mus.com", + "person": 1337, + "time": "00:00", + "date": "01.01.2016", + "read": true + } + ] +} \ No newline at end of file diff --git a/data/reservation.json.example b/data/reservation.json.example new file mode 100644 index 0000000..55ab0a1 --- /dev/null +++ b/data/reservation.json.example @@ -0,0 +1,31 @@ +{ + "items": [ + { + "name": "Hans Bauer", + "desc": "Kleine Familienfeier zu meinem 80. Geburtstag", + "email": "hans@gmx.de", + "person": 20, + "time": "20:15", + "date": "16.05.2016", + "read": false + }, + { + "name": "Micha Müller", + "desc": "Ich will meiner geliebten Ursula einen Heiratsantrag machen. Ich suche ein gemütliches Eckchen.", + "email": "micha@mueller.de", + "person": 3, + "time": "21:00", + "date": "14.3.2016", + "read": true + }, + { + "name": "Spa Mer", + "desc": "Das ist eine versuchte Injection\r\nmit mehreren Zeilen.", + "email": "ano.ny@mus.com", + "person": 1337, + "time": "00:00", + "date": "01.01.2016", + "read": true + } + ] +} \ No newline at end of file diff --git a/data/users.json b/data/users.json new file mode 100644 index 0000000..aa70f1f --- /dev/null +++ b/data/users.json @@ -0,0 +1,20 @@ +{ + "Richard Reis": { + "pwd": "secret", + "rights": [ + null, + true, + true, + true + ] + }, + "Ursula Ulstein": { + "pwd": "ultimativ", + "rights": [ + null, + true, + true, + false + ] + } +} \ No newline at end of file diff --git a/data/users.json.example b/data/users.json.example new file mode 100644 index 0000000..ee85ad0 --- /dev/null +++ b/data/users.json.example @@ -0,0 +1,20 @@ +{ + "Richard Reis": { + "pwd": "secret", + "rights": [ + null, + true, + true, + true + ] + }, + "Ursula Ulstein": { + "pwd": "ultimativ", + "rights": [ + null, + true, + true, + false + ] + }, +} \ No newline at end of file diff --git a/main.js b/main.js index 5cb5c1b..4ac0172 100644 --- a/main.js +++ b/main.js @@ -4,7 +4,7 @@ var bodyParser = require('body-parser'); var session = require('express-session'); var loginMod = require('./modules/login'); var menuMod = require('./modules/menu'); -//var path = require('path'); +var reservationMod = require('./modules/reservation'); var fs = require('fs'); var app = express(); @@ -17,7 +17,7 @@ var app = express(); // ************************** app.set('view engine', 'jade'); -app.set('rundir', '.'); +app.set('rundir', __dirname); app.set('views', app.get('rundir') + '/views'); @@ -46,7 +46,9 @@ app.use('/bin', express.static(app.get('rundir') + '/public')); // INIT // ************************** +loginMod.init(app.get('rundir') + '/data/users.json'); menuMod.init(app.get('rundir') + '/data/menu.json'); +reservationMod.init(app.get('rundir') + '/data/reservation.json'); @@ -87,22 +89,91 @@ app.post('/speisekarte', function(req, res) { error = menuMod.updateValue(name, desc, price, nametag); else if (action == menuMod.MenuAction.DELETE) error = menuMod.deleteValue(nametag); - console.log(error); } res.render('menu', { title: 'Restaurant Reiskorn | Speisekarte', loginName: loginMod.getName(req), isAdmin: loginMod.hasAccess(req, loginMod.AdminRight.MENU), - menuJSON: menuMod.getJSON() + menuJSON: menuMod.getJSON(), + statusMessage: menuMod.getStatusMessage(error, action, nametag) }); }); app.get('/kontakt', function(req, res) { + var showItem = null; + var nametag = req.query.nametag; + if (loginMod.hasAccess(req, loginMod.AdminRight.RESERVATION)) { + var action = reservationMod.determineAction(nametag); + if (action == reservationMod.MenuAction.SHOW) { + showItem = reservationMod.showValue(nametag); + } + } res.render('contact', { title: 'Restaurant Reiskorn | Kontakt', loginName: loginMod.getName(req), - menuJSON: menuMod.getJSON() + isAdmin: loginMod.hasAccess(req, loginMod.AdminRight.RESERVATION), + menuJSON: menuMod.getJSON(), + editItem: showItem, + listItem: reservationMod.getJSON()["items"] + }); +}); + +app.post('/kontakt', function(req, res) { + var nametag = req.body.nametag; + var name = req.body.name; + var desc = req.body.desc; + var email = req.body.email; + var person = req.body.person; + var time = req.body.time; + var date = req.body.date; + + var action = reservationMod.determineAction(nametag); + var error = 0; + var showItem = null; + + if (action == reservationMod.MenuAction.CREATE) { + error = reservationMod.addValue(name, desc, person, email, date, time, nametag); + if (error) { + showItem = { + name: name, + desc: desc, + email: email, + person: person, + time: time, + date: date + }; + } + } + else if (loginMod.hasAccess(req, loginMod.AdminRight.RESERVATION)) { + if (action == reservationMod.MenuAction.UPDATE) { + error = reservationMod.updateValue(name, desc, person, email, date, time, nametag); + if (error) { + showItem = { + name: name, + desc: desc, + email: email, + person: person, + time: time, + date: date, + id: nametag + }; + } + } + else if (action == reservationMod.MenuAction.DELETE) + error = reservationMod.deleteValue(nametag); + else if (action == reservationMod.MenuAction.READ) + error = reservationMod.readValue(nametag); + } + + res.render('contact', { + title: 'Restaurant Reiskorn | Kontakt', + loginName: loginMod.getName(req), + isAdmin: loginMod.hasAccess(req, loginMod.AdminRight.RESERVATION), + menuJSON: menuMod.getJSON(), + editItem: showItem, + listItem: reservationMod.getJSON()["items"], + statusMessage: reservationMod.getStatusMessage(error, action) }); }); @@ -147,5 +218,5 @@ var server = app.listen(3000, function() { var host = server.address().address; var port = server.address().port; - console.log('Example app listening at http://%s:%s', host, port); + console.log('App listening at http://%s:%s', host, port); }); \ No newline at end of file diff --git a/modules/login.js b/modules/login.js index c168298..60f5580 100644 --- a/modules/login.js +++ b/modules/login.js @@ -1,20 +1,20 @@ -var admins = { - "Richard Reis": { - pwd: "secret", - rights: { - 1: true, - 2: true, - 3: true - } - }, - "Ursula Ulstein": { - pwd: "ultimativ", - rights: { - 1: true, - 2: true, - 3: false - } - }, +var fs = require('fs'); + +var admins = {}; + +var FILENAME = false; + +module.exports.init = function(userfile) { + FILENAME = userfile; + module.exports.loadFile(); +} + +module.exports.loadFile = function() { + if (!FILENAME) throw "No file for user JSON specified"; + fs.readFile(FILENAME, 'utf8', function(err, data) { + if (err) throw err; + admins = JSON.parse(data); + }); } module.exports.AdminRight = { diff --git a/modules/menu.js b/modules/menu.js index 27ef2ed..b18b455 100644 --- a/modules/menu.js +++ b/modules/menu.js @@ -8,7 +8,28 @@ var menuJSON = { var REGEX_CREATE = /^[fd]-[0-9]+-new$/; var REGEX_UPDATE = /^[fd]-[0-9]+-[0-9]+$/; var REGEX_DELETE = /^[fd]-[0-9]+-[0-9]+-del$/; -var REGEX_FLOAT = /^(\-|\+)?([0-9]+(\.[0-9]+)?)$/; + +var REGEX_FLOAT = /^([0-9]+(\.[0-9]+)?)$/; + +var LANG = {}; +LANG["ACTIONS"] = { + 1: "Das Ertellen des Menüeintrags", + 2: "Das Aktualisieren der Informationen", + 3: "Das Löschen des Menüeintrags" +}; +LANG["ERROR_MESSAGES"] = { + 1: "Das Feld \"Name\" darf nicht leer sein.", + 2: "Das Feld \"Beschreibung\" darf nicht leer sein.", + 4: "Das Feld \"Preis\" darf nicht leer und muss eine positive Zahl sein.", + 8: "Beim Verarbeiten der Anfrage ist ein interner Fehler aufgetreten." +}; +LANG["MESSAGES"] = { + "success": "%action% war erfolgreich.", + "failure": "%action% konnte nicht durchgeführt werden." +}; +LANG["INTERNAL"] = { + "noinput": "No input file for menu JSON specified!" +}; var FILENAME = false; @@ -18,7 +39,7 @@ module.exports.init = function(menufile) { } module.exports.loadFile = function() { - if (!FILENAME) throw "No file for menu JSON specified"; + if (!FILENAME) throw LANG["MESSAGES"]["noinput"]; fs.readFile(FILENAME, 'utf8', function(err, data) { if (err) throw err; menuJSON = JSON.parse(data); @@ -26,7 +47,7 @@ module.exports.loadFile = function() { } module.exports.saveJSON = function() { - if (!FILENAME) throw "No file for menu JSON specified"; + if (!FILENAME) throw LANG["MESSAGES"]["noinput"]; fs.writeFile(FILENAME, JSON.stringify(menuJSON, null, 4), function(err) { if(err) { console.log(err); @@ -50,6 +71,36 @@ module.exports.ErrorCode = { NOFLOAT_PRICE: 4, WRONG_ACTION: 8 } +module.exports.ErrorCode["MAX"] = sum(module.exports.ErrorCode); + +module.exports.getStatusMessage = function(errorCode, action, nametag) { + if (action) { + var position = splitNameTag(nametag, action); + if (!errorCode) { + return { + type: "success", + message: LANG["MESSAGES"]["success"].replace("%action%", LANG["ACTIONS"][action]), + reasons: null, + anchor: menuJSON[position.category][position.section]["anchor"] + } + } else { + var reasons = []; + for (var i = 1; i < module.exports.ErrorCode["MAX"]; i=i*2) { + if (errorCode & i) { // bit-wise comparing + reasons.push(LANG["ERROR_MESSAGES"][i]); + } + } + return { + type: "danger", + message: LANG["MESSAGES"]["failure"].replace("%action%", LANG["ACTIONS"][action]), + reasons: reasons, + anchor: menuJSON[position.category][position.section]["anchor"] + } + } + } else { + return null; + } +} module.exports.determineAction = function(nametag) { if (nametag && REGEX_CREATE.test(nametag)) { // new @@ -136,6 +187,16 @@ var filterFloat = function(value) { return NaN; } +function sum(obj) { + var sum = 0; + for(var el in obj) { + if(obj.hasOwnProperty(el)) { + sum += parseFloat(obj[el]); + } + } + return sum; +} + var splitNameTag = function(nametag, action) { if (action) { // create, update & delete var splitArr = nametag.split("-"); diff --git a/modules/reservation.js b/modules/reservation.js new file mode 100644 index 0000000..f4558fb --- /dev/null +++ b/modules/reservation.js @@ -0,0 +1,272 @@ +var fs = require('fs'); + +var reservationJSON = { + "items": [] +}; + +var REGEX_CREATE = /^new$/; +var REGEX_UPDATE = /^[0-9]+$/; +var REGEX_DELETE = /^[0-9]+-del$/; +var REGEX_SHOW = /^[0-9]+-edit$/; +var REGEX_READ = /^[0-9]+-read$/; + +var REGEX_INT = /^(\-|\+)?([0-9]+)$/; +var REGEX_EMAIL = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; +var REGEX_TIME = /^(([01][0-9]|2[0-4]):[0-5][0-9])$/; +var REGEX_DATE = /^(([0-2][0-9]|3[01]).(0[0-9]|1[012]).[0-9]{4})$/; + +var LANG = {}; +LANG["ACTIONS"] = { + 1: "Das Reservieren", + 2: "Das Aktualisieren der Informationen", + 3: "Das Löschen der Reservierung", + 4: "Das Anzeigen der Inforationen", + 5: "Das Setzen des Gelesen-Status" +}; +LANG["ERROR_MESSAGES"] = { + 1: "Das Feld \"Name\" darf nicht leer sein.", + 2: "Das Feld \"Kommentar\" darf nicht leer sein.", + 4: "Das Feld \"Anzahl der Personen\" darf nicht leer und muss eine ganze, positive Zahl sein.", + 8: "Das Feld \"Datum\" darf nicht leer sein und muss einem gültigen Datum im Format TT.MM.JJJJ entsprechen.", + 16: "Das Feld \"Uhrzeit\" darf nicht leer sein und muss einer gültigen Uhrzeit im Format HH:MM entsprechen.", + 32: "Das Feld \"E-Mail\" darf nicht leer und muss eine gültige E-Mail-Adresse sein.", + 64: "Beim Verarbeiten der Anfrage ist ein interner Fehler aufgetreten." +}; +LANG["MESSAGES"] = { + "success": "%action% war erfolgreich.", + "failure": "%action% konnte nicht durchgeführt werden." +}; +LANG["INTERNAL"] = { + "noinput": "No input file for reservation JSON specified!" +}; + +var FILENAME = false; + +module.exports.init = function(reservationfile) { + FILENAME = reservationfile; + module.exports.loadFile(); +} + +module.exports.loadFile = function() { + if (!FILENAME) throw LANG["MESSAGES"]["noinput"]; + fs.readFile(FILENAME, 'utf8', function(err, data) { + if (err) throw err; + reservationJSON = JSON.parse(data); + }); +} + +module.exports.saveJSON = function() { + if (!FILENAME) throw LANG["MESSAGES"]["noinput"]; + fs.writeFile(FILENAME, JSON.stringify(reservationJSON, null, 4), function(err) { + if(err) { + console.log(err); + return false; + } + }); + return true; +} + +module.exports.MenuAction = { + ERROR: 0, + CREATE: 1, + UPDATE: 2, + DELETE: 3, + SHOW: 4, + READ: 5 +} + +module.exports.ErrorCode = { + SUCCESS: 0, + NOSTRING_NAME: 1, + NOSTRING_DESC: 2, + NONUMBER_PERS: 4, + NODATE_DATE: 8, + NOTIME_TIME: 16, + NOEMAIL_MAIL: 32, + WRONG_ACTION: 64 +} +module.exports.ErrorCode["MAX"] = sum(module.exports.ErrorCode); + +module.exports.getStatusMessage = function(errorCode, action) { + if (action) { + if (!errorCode) { + return { + type: "success", + message: LANG["MESSAGES"]["success"].replace("%action%", LANG["ACTIONS"][action]), + reasons: null + } + } else { + var reasons = []; + for (var i = 1; i < module.exports.ErrorCode["MAX"]; i=i*2) { + if (errorCode & i) { // bit-wise comparing + reasons.push(LANG["ERROR_MESSAGES"][i]); + } + } + return { + type: "danger", + message: LANG["MESSAGES"]["failure"].replace("%action%", LANG["ACTIONS"][action]), + reasons: reasons + } + } + } else { + return null; + } +} + +module.exports.determineAction = function(nametag) { + if (nametag && REGEX_CREATE.test(nametag)) { // new + return module.exports.MenuAction.CREATE; + } else if (nametag && REGEX_UPDATE.test(nametag)) { // update + return module.exports.MenuAction.UPDATE; + } else if (nametag && REGEX_DELETE.test(nametag)) { // delete + return module.exports.MenuAction.DELETE; + } else if (nametag && REGEX_SHOW.test(nametag)) { // show + return module.exports.MenuAction.SHOW; + } else if (nametag && REGEX_READ.test(nametag)) { // read + return module.exports.MenuAction.READ; + } + return module.exports.MenuAction.ERROR; +} + +module.exports.checkValidity = function(name, desc, person, email, date, time) { + var errorCode = 0; + + if (!name || !(typeof name === 'string' || name instanceof String)) + errorCode += module.exports.ErrorCode.NOSTRING_NAME; + if (!desc || !(typeof desc === 'string' || desc instanceof String)) + errorCode += module.exports.ErrorCode.NOSTRING_DESC; + if (isNaN(filterInt(person))) + errorCode += module.exports.ErrorCode.NONUMBER_PERS; + if (!REGEX_EMAIL.test(email)) + errorCode += module.exports.ErrorCode.NOEMAIL_MAIL; + if (!REGEX_DATE.test(date)) + errorCode += module.exports.ErrorCode.NODATE_DATE; + if (!REGEX_TIME.test(time)) + errorCode += module.exports.ErrorCode.NOTIME_TIME; + + return errorCode; +} + +module.exports.addValue = function(name, desc, person, email, date, time, nametag) { + var errorCode = module.exports.checkValidity(name, desc, person, email, date, time); + var action = module.exports.determineAction(nametag); + if (action !== module.exports.MenuAction.CREATE) { + errorCode += module.exports.ErrorCode.WRONG_ACTION; + } + if (!errorCode) { + person = filterInt(person); + reservationJSON["items"].push({ + name: name, + desc: desc, + email: email, + person: person, + time: time, + date: date, + read: false + }); + module.exports.saveJSON(); + } + return errorCode; +} + +module.exports.updateValue = function(name, desc, person, email, date, time, nametag) { + var errorCode = module.exports.checkValidity(name, desc, person, email, date, time); + var action = module.exports.determineAction(nametag); + if (action !== module.exports.MenuAction.UPDATE) { + errorCode += module.exports.ErrorCode.WRONG_ACTION; + } + if (!errorCode) { + person = filterInt(person); + var position = splitNameTag(nametag, action); + reservationJSON["items"][position.id] = { + name: name, + desc: desc, + email: email, + person: person, + time: time, + date: date, + read: true + }; + module.exports.saveJSON(); + } + return errorCode; +} + +module.exports.deleteValue = function(nametag) { + var errorCode = module.exports.ErrorCode.SUCCESS; + var action = module.exports.determineAction(nametag); + if (action !== module.exports.MenuAction.DELETE) { + errorCode += module.exports.ErrorCode.WRONG_ACTION; + } + if (!errorCode) { + var position = splitNameTag(nametag, action); + reservationJSON["items"].splice(position.id, 1); + module.exports.saveJSON(); + } + return errorCode; +} + +module.exports.showValue = function(nametag) { + var errorCode = module.exports.ErrorCode.SUCCESS; + var action = module.exports.determineAction(nametag); + if (action !== module.exports.MenuAction.SHOW) { + errorCode += module.exports.ErrorCode.WRONG_ACTION; + } + if (!errorCode) { + var position = splitNameTag(nametag, action); + result = reservationJSON["items"][position.id]; + result.id = position.id; + return result; + } + return errorCode; +} + +module.exports.readValue = function(nametag) { + var errorCode = module.exports.ErrorCode.SUCCESS; + var action = module.exports.determineAction(nametag); + if (action !== module.exports.MenuAction.READ) { + errorCode += module.exports.ErrorCode.WRONG_ACTION; + } + if (!errorCode) { + var position = splitNameTag(nametag, action); + reservationJSON["items"][position.id]["read"] = !reservationJSON["items"][position.id]["read"]; + module.exports.saveJSON(); + } + return errorCode; +} + +module.exports.getJSON = function() { + return reservationJSON; +} + +var filterInt = function(value) { + if(REGEX_INT.test(value)) + return Number(value); + return NaN; +} + +function sum(obj) { + var sum = 0; + for(var el in obj) { + if(obj.hasOwnProperty(el)) { + sum += parseFloat(obj[el]); + } + } + return sum; +} + +var splitNameTag = function(nametag, action) { + if (action) { // create, update, show, read & delete + var splitArr = nametag.split("-"); + var result = {}; + result.type = action; + if (action == module.exports.MenuAction.CREATE) { // create + result.id = null; + } else { // update, show, read & delete + result.id = parseInt(splitArr[0]); + } + return result; + } else { + return false; + } +} \ No newline at end of file diff --git a/public/css/style.css b/public/css/style.css index bb0e42c..2ff735d 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -81,7 +81,6 @@ textarea { #carousel-home_carousel { max-width: 1170px; margin: auto; - padding: 0 15px; } .btn { @@ -119,4 +118,21 @@ textarea { #loginDropdown { min-width: 300px; } +} + +/* reservation form */ + +@media only screen and (min-width : 992px) { + .no-padding-md { + padding: 0; + } +} + +/* +Carousel Fix +*/ + +.carousel-fullwidth { + padding-left: 0; + padding-right: 0; } \ No newline at end of file diff --git a/views/contact.jade b/views/contact.jade index 2ce3958..25cef16 100644 --- a/views/contact.jade +++ b/views/contact.jade @@ -1,4 +1,97 @@ extends ./wrapper + +mixin reservation-form(id, name, desc, email, person, time, date, message) + .row + form.col-md-10.col-md-offset-1.orange(method="post",action="/kontakt") + if message + +status-message(message.type, message.message, message.reasons) + .row + .col-md-6 + +input-val("name","id-name","Max Mustermann","Name:","name",name) + +input-val("number","id-person","2","Anzahl der Personen:","person",person) + .col-md-6.no-padding-md + .row + .col-sm-6 + +input-val("date","id-date","TT.MM.JJJJ","Datum:","date",date) + .col-sm-6 + +input-val("datetime-local","id-time","HH:MM","Uhrzeit:","time",time) + .row + .col-md-12 + +input-val("email","id-mail","max.mustermann@mail.de","E-Mail-Adresse:","email",email) + .row + .col-md-12 + +textarea-val("id-desc","Weitere Kontaktdaten, Spezielle Essens-, Platzwünsche, ...","Kommentare:","desc",desc,"7") + .row + .col-md-3.col-md-offset-3.text-center + input(type='hidden',name='nametag',value='#{id}') + if id="new" + button(type="submit",class="btn btn-success") Reservieren + else + button(type="submit",class="btn btn-success") Reservierung ändern + .col-md-3.text-center + button(type="reset",class="btn btn-danger") Eingaben löschen + +mixin reservation-list(entries, message) + .row + if entries.length + .col-md-10.col-md-offset-1.stripe + if message + +status-message(message.type, message.message, message.reasons) + each entry, index in entries + +reservation-entry(entry.name, entry.desc, entry.person, entry.time, entry.date, entry.read, entry.email, index) + else + .col-md-10.col-md-offset-1.orange Keine Reservierungen vorhanden. + +mixin reservation-entry(name, desc, person, time, date, read, email, index) + .row + .col-md-1 + form(style="display:inline",method="post",action="") + input(type='hidden',name='nametag',value="#{index+'-del'}") + button.btn.btn-sm.btn-danger(type="submit") + +icon("remove") + span.hidden-md.hidden-lg Entfernen + form(style="display:inline",method="get",action="") + input(type='hidden',name='nametag',value="#{index+'-edit'}") + button.btn.btn-sm.btn-primary(type="submit") + +icon("edit") + span.hidden-md.hidden-lg Bearbeiten + form(style="display:inline",method="post",action="") + input(type='hidden',name='nametag',value="#{index+'-read'}") + button.btn.btn-sm.btn-info(type="submit") + if read + +icon("check") + span.hidden-md.hidden-lg Als ungelesen markieren + else + +icon("unchecked") + span.hidden-md.hidden-lg Als gelesen markieren + .col-md-7 + span.lead= name + br + span.small !{desc.substr(0, 300).replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"').replace(/\n/g, '
')} + .col-md-4 + .row + .col-md-12= person + .row + .col-md-12= time + .row + .col-md-12= date + .row + .col-md-12= email + + + +mixin reservation(item, list, isadmin, message) + if isadmin + if item + +reservation-form(item.id, item.name, item.desc, item.email, item.person, item.time, item.date, message) + else + +reservation-list(list, message) + else + if item + +reservation-form("new", item.name, item.desc, item.email, item.person, item.time, item.date, message) + else + +reservation-form("new", "", "", "", "", "", "", message) + block vars - var pageTitle = 'Kontakt' block content @@ -6,25 +99,7 @@ block content a(name="reservierung") h1 Reservierung .container - .row - .col-md-10.col-md-offset-1.orange - .row - .col-md-6 - +input("name","id-name","Max Mustermann","Name:") - +input("number","id-person","2","Anzahl der Personen:") - .col-md-6 - +input("datetime-local","id-date","TT.MM.JJJJ HH:MM","Datum und Uhrzeit des Besuchs:") - +input("email","id-mail","max.mustermann@mail.de","E-Mail-Adresse:") - .row - .col-md-12 - .form-group - label(for="id-desc") Kommentare: - textarea(id="id-desc",class="form-control",type="datetime-local",placeholder="Weitere Kontaktdaten, Spezielle Essens-, Platzwünsche, ...",rows="7") - .row - .col-md-2.col-md-offset-4 - button(type="submit",class="btn btn-success") Submit - .col-md-2 - button(type="reset",class="btn btn-danger") Submit + +reservation(editItem, listItem, isAdmin, statusMessage) a(name="anfahrt") h1 Anfahrt .container @@ -46,7 +121,7 @@ block content p | 030 110 112 1337 br - a(href='reis@korn.de') reis@korn.de + a(href='mailto://reis@korn.de') reis@korn.de a(name="impressum") h1 Impressum .container diff --git a/views/index.jade b/views/index.jade index b7ad40f..81fd5a0 100644 --- a/views/index.jade +++ b/views/index.jade @@ -2,45 +2,47 @@ extends ./wrapper block vars - var pageTitle = 'Home' block content - +carousel("home_carousel",[ - { - image:"/bin/image/restaurant-1-chairs.jpg", - h1:"Romantisches Dinner", - p:"Wie wäre es mit einem Kerzenabend zu zweit?", - button:{ - caption:"Reservieren", - url:"/kontakt#reservieren" - } - }, - { - image:"/bin/image/restaurant-2-hall.jpg", - h1:"Konferenzraum", - p:"Viel Platz für über 200 Gäste", - button:{ - caption:"Reservieren", - url:"/kontakt#reservieren" - } - }, - { - image:"/bin/image/restaurant-3-pillar.jpg", - h1:"Gemütliche Räumlichkeiten", - p:"Bei schlechten Wetter verträumt im fernen Osten", - button:{ - caption:"Anfahrt", - url:"/kontakt#anfahrt" - } - }, - { - image:"/bin/image/restaurant-4-buffet.jpg", - h1:"Buffet", - p:"Vielfältige Auswahl an Speisen und Getränken", - button:{ - caption:"Speisekarte", - url:"/speisekarte" - } - } - ]) .container + .row + .col-md-12.carousel-fullwidth + +carousel("home_carousel",[ + { + image:"/bin/image/restaurant-1-chairs.jpg", + h1:"Romantisches Dinner", + p:"Wie wäre es mit einem Kerzenabend zu zweit?", + button:{ + caption:"Reservieren", + url:"/kontakt#reservieren" + } + }, + { + image:"/bin/image/restaurant-2-hall.jpg", + h1:"Konferenzraum", + p:"Viel Platz für über 200 Gäste", + button:{ + caption:"Reservieren", + url:"/kontakt#reservieren" + } + }, + { + image:"/bin/image/restaurant-3-pillar.jpg", + h1:"Gemütliche Räumlichkeiten", + p:"Bei schlechten Wetter verträumt im fernen Osten", + button:{ + caption:"Anfahrt", + url:"/kontakt#anfahrt" + } + }, + { + image:"/bin/image/restaurant-4-buffet.jpg", + h1:"Buffet", + p:"Vielfältige Auswahl an Speisen und Getränken", + button:{ + caption:"Speisekarte", + url:"/speisekarte" + } + } + ]) .row .col-md-6.graygray +img-responsive-center("/bin/image/rice-world.png","Welt voller Reis") diff --git a/views/menu.jade b/views/menu.jade index 239a3fe..6902aba 100644 --- a/views/menu.jade +++ b/views/menu.jade @@ -1,19 +1,19 @@ extends ./wrapper -mixin menu-item-adm(name, desc, price, prefix, newItem) +mixin menu-item-adm(name, anchor, desc, price, prefix, newItem) .row .col-md-1 if newItem button.btn.btn-sm.btn-success(type="submit",form="#{prefix}") +icon("ok") else - form(style="display:inline",method="post",action="") + form(style="display:inline",method="post",action="#{'/speisekarte#' + anchor}") input(type='hidden',name='nametag',value="#{prefix + '-del'}") button.btn.btn-sm.btn-danger(type="submit") +icon("remove") button.btn.btn-sm.btn-primary(type="submit",form="#{prefix}") +icon("edit") - form.col-md-11(id="#{prefix}",method="post",action="") + form.col-md-11(id="#{prefix}",method="post",action="#{'/speisekarte#' + anchor}") .col-md-9 input(type='hidden',name='nametag',value='#{prefix}') +input-val-simple("text",prefix + "-name","Name","name","name",name) @@ -32,16 +32,20 @@ mixin menu-item(name, desc, price) p.text-right #{price.toFixed(2).replace(/(\d)(?=(\d{3})+\,)/g, '$1,').replace(/^(.*)\.(.*)$/g, '$1,$2€')} -mixin menu-cat(name, anchor, content, adm, prefix) +mixin menu-cat(name, anchor, content, adm, prefix, message) a(name= anchor) h2= name .container .row .col-md-10.col-md-offset-1.stripe + if anchor + if message + if anchor==message.anchor + +status-message(message.type, message.message, message.reasons) if adm each val, index in content - +menu-item-adm(val.name, val.desc, val.price, prefix + "-" + index, "") - +menu-item-adm("", "", "", prefix + "-new", "x") + +menu-item-adm(val.name, anchor, val.desc, val.price, prefix + "-" + index, "") + +menu-item-adm("", anchor, "", "", prefix + "-new", "x") else each val, index in content +menu-item(val.name, val.desc, val.price) @@ -49,7 +53,7 @@ mixin menu-cat(name, anchor, content, adm, prefix) mixin menu-part(part, adm, prefix) each val, index in part - +menu-cat(val.name, val.anchor, val.items, adm, prefix + "-" + index) + +menu-cat(val.name, val.anchor, val.items, adm, prefix + "-" + index, statusMessage) block vars - var pageTitle = 'Speisekarte' diff --git a/views/wrapper.jade b/views/wrapper.jade index 1f25fb7..d27e926 100644 --- a/views/wrapper.jade +++ b/views/wrapper.jade @@ -3,6 +3,11 @@ doctype html extends ../node_modules/jade-bootstrap/_bootstrap +mixin textarea-val(id,placeholder,label,name,value,rows) + .form-group + label(for="#{id}") #{label} + textarea.form-control(id="#{id}",placeholder="#{placeholder}",name="#{name}",rows="#{rows}")= value + mixin input-val(type,id,placeholder,label,name,value) .form-group label(for="#{id}") #{label} @@ -13,6 +18,13 @@ mixin input-val-simple(type,id,placeholder,label,name,value) input.form-control(type="#{type}",id="#{id}",placeholder="#{placeholder}",name="#{name}",value="#{value}") +mixin status-message(type, message, reasons) + div(class="alert alert-#{type}" role="alert")= message + if reasons + ul + each val in reasons + li= val + mixin navbar-main(style) @@ -50,7 +62,8 @@ mixin loginBox .col-sm-7.text-center a.btn.btn-default(href="/login#admin") Admin-Panel - + + block styles //link(rel="stylesheet",type="text/css",href="/bin/bootstrap/css/bootstrap.min.css") link(rel="stylesheet",type="text/css",href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css")