10 Commits
0.1 ... 1.2.1

Author SHA1 Message Date
Sebastian Seedorf
bbe1fb59df README.md login-Requests hinzugefügt 2016-03-30 15:31:36 +02:00
Sebastian Seedorf
486b7b6ae9 README.md auf den aktuellen Stand gebracht 2016-03-30 15:13:03 +02:00
Sebastian Seedorf
c00ad762ad reservierungen werden jetzt oben angefügt, Neue sind so leichter einsehbar 2016-03-30 12:56:56 +02:00
Sebastian Seedorf
ff0b1dcb7a Reservierungsformular unter Chrome und Safari gefixt 2016-03-30 12:29:27 +02:00
Sebastian Seedorf
411562c7ec Formulierungen ausgebessert und umgeschrieben (index/menu) 2016-03-30 11:40:35 +02:00
Sebastian Seedorf
c521f2670b AngularJS-Service implementiert; Reissorte des Tages wird per http über angular nachgeladen 2016-03-29 17:51:00 +02:00
Sebastian Seedorf
cbadac55c5 README aktualisiert; auf kleinen Geräten Reihenfolge der Kästchen auf der Startseite verändert 2016-03-29 16:17:10 +02:00
Sebastian Seedorf
82b0604247 Rechtschreibfehler verbessert 2016-03-29 14:49:46 +02:00
Sebastian Seedorf
0c6564db3b Readme aktualisiert 2016-03-28 19:45:56 +02:00
Sebastian Seedorf
62ff08624d Statusmeldungen hinzugefügt; Reservierungen funktionabel; kleine Bugfixes 2016-03-28 17:04:55 +02:00
17 changed files with 1187 additions and 401 deletions

110
README.md
View File

@@ -0,0 +1,110 @@
# Restaurant Reiskorn
## Installation
1. Herunterladen des aktuellen [Release](https://gogs.sebse.de/Caesar2011/html5-restaurant/releases).
2. Starten der `main.js` im Hauptprojekt-Ordner.
- `node path/to/main.js`
- Relative und absolute Pfade erlaubt
3. Aufrufen [der lokalen Node-Anwendung](http://localhost:3000) oder [dem gehosteten Service](http://restaurant.sebse.de)
## Login
Anmeldedaten finden sich in der `data/users.json`, die, falls Änderungen vorgenommen werden sollen, manuell vor dem Start der nodejs-Anwendung durchgeführt werden müssen.
Standard-Login-Daten
```js
{
"Richard Reis": {
"pwd": "secret",
"rights": [
null,
true,
true,
true
]
},
"Ursula Ulstein": {
"pwd": "ultimativ",
"rights": [
null,
true,
true,
false
]
}
}
```
## Funktionen
- Unterschiedliche CRUD-Routinen für Reservierung und Speisekarte
- Verwendung von Bootstrap mit minimalistischem, eigenen CSS
- Einbindung einer eigenen Schriftart
- Implementierung einer eigenen, komplexen Angular-Direktive zur Anzeige einer Google Map
- Responsive Design für alle Bildschirmgrößen und nicht JS-Benutzer
- Serverseitige Implementierung mit Node.js
- Persistierung des Speiseplans und der Reservierungen nach Serverneustart
- Login/Speicherung der Nutzer in externer Datei
- Eigenen AngularJS-Service und Controller implementiert
- Verwendung des AngularJS-Service $http
- Name der Tagesreisempfehlung per Agular nachgeladen
- Persönliche Formulierungen
- Realitätsnahe Sprache
## Dateistruktur
### data
- Daten zur Speicherung der
- Reservierungen
- Speisekarte
- Benutzerliste
- Mit jeweiligen Beispielwerten zum manuellen Einladen
### modules
- eigene nodejs-Module
- entspricht im MVC-Modell einem Model
- CRUD-Routinen
- Reservierungen
- Speisekarte
- Benutzerliste
### node_modules
- von npm installierte Pakete
### public
- Client-Assets
- CSS
- Client-JS
- Bilder
- Bootstrap/AngularJS
- werden über HTML nachgeladen
### views
- JADE-Views
- HTML-Template-Engine
## Requests
| Min. Vers. | Methode | URI | Parameter | Beschreibung | Benötige Rechte |
|--------|--------|--------|--------|--------|--------|
| 0.1 | GET | / | keine | Zeigt die Startseite an | keine |
| 0.1 | GET | /login | keine | Zeigt die Loginseite an | keine |
| 0.1 | POST | /login | username:<br> `[Benutzername]`<br>password:<br> `[Passwort des Benutzers]` | Loginversuch | keine |
| 0.1 | POST | /login | targetLogout:<br> `logout` | Logout | keine |
| 0.1 | GET | /speisekarte | keine | Zeigt die Speisekarte an | keine (Bearbeitungsfunktionen mit MENU) |
| 0.1 | POST | /speisekarte | nametag:<br> `[f\|d]-[Index in der Liste "food"/"drinks"]-new`<br>name:<br> `[Name des Gerichts]`<br>desc:<br> `[Beschreibung des Menüeintrags]` | Hinzufügen eines Menüeintrags | MENU |
| 0.1 | POST | /speisekarte | nametag:<br> `[f\|d]-[Index in der Liste "food"/"drinks"]-[Index in der Liste "items"]`<br>name:<br> `[Name des Gerichts]`<br>desc:<br> `[Beschreibung des Menüeintrags]` | Aktualisieren der Informationen eines Eintrages | MENU |
| 0.1 | POST | /speisekarte | nametag:<br> `[f\|d]-[Index in der Liste "food"/"drinks"]-[Index in der Liste "items"]-del` | Löschen eines Eintages aus der Speisekarte | MENU |
| 0.1 | GET | /kontakt | keine | Zeigt die Kontaktseite an | keine:<br> Reservierungsformular<br>RESERVATION:<br>Liste aller eingegangenen Reservierungen |
| 1.0 | GET | /kontakt | nametag:<br> `[Index der Reservierung]` | Öffnet das Fenster zum Bearbeiten eines Eintages | RESERVATION |
| 1.0 | POST | /kontakt | nametag:<br> `new`<br>name:<br> `[Name des Reservierenden]`<br>desc:<br> `[Kommentarfeld]`<br>email:<br> `[gülige E-Mail-Adresse]`<br>person:<br> `[positive, natürliche Zahl]`<br>time:<br> `^(([01][0-9]\|2[0-4]):[0-5][0-9])$`<br>date:<br> `^(([0-2][0-9]\|3[01]).(0[0-9]\|1[012]).[0-9]{4})$` | Hinzufügen einer Reservierung | keine |
| 1.0 | POST | /kontakt | nametag:<br> `[Index der Reservierung]`<br>name:<br> `[Name des Reservierenden]`<br>desc:<br> `[Kommentarfeld]`<br>email:<br> `[gülige E-Mail-Adresse]`<br>person:<br> `[positive, natürliche Zahl]`<br>time:<br> `^(([01][0-9]\|2[0-4]):[0-5][0-9])$`<br>date:<br> `^(([0-2][0-9]\|3[01]).(0[0-9]\|1[012]).[0-9]{4})$` | Aktualisieren der Informationen einer Reservierung | RESERVATION |
| 1.0 | POST | /kontakt | nametag:<br> `[Index der Reservierung]-del` | Löschen einer Reservierung aus der Liste | RESERVATION |
| 1.1.1 | GET | /api/specialOffer.json | keine | JSON, der aktuell gültigen Angebote | keine |

View File

@@ -1,286 +1,286 @@
{
"food": [
{
"name": "Vorspeisen",
"anchor": "vorspeise",
"items": [
{
"name": "Reis-alat",
"price": 5.5,
"desc": "Frischer Blattsalat mit Basmati-Reis und würzigem Balsamico"
},
{
"name": "Chinesisches Bauernfrühstück",
"price": 6.4,
"desc": "Feuriges Chili in gedämpftem, kaltem Reis mit heißem Gemüse"
},
{
"name": "Thailändisches Arbeiterfrühstück",
"price": 7.4,
"desc": "Feuriges Chili in gedämpftem, kaltem Reis mit heißem Gemüse und Hähnchen"
},
{
"name": "Viatnamesisches Denkerfrühstück",
"price": 7.9,
"desc": "Feuriges Chili in gedämpftem, kaltem Reis mit heißem Gemüse und Ente"
},
{
"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"
}
]
},
{
"name": "Suppen",
"anchor": "suppe",
"items": [
{
"name": "Reis-uppe",
"price": 3.9,
"desc": "Köstliche, klare Hühnerbrühe mit einem Schuss Soja und Reis"
},
{
"name": "Tomatensuppe",
"price": 4.2,
"desc": "Cremige Tomatensuppe mit frischen Tomaten, Rahm und Reis sowie einer leckeren Petersilie als Dekör"
},
{
"name": "Üppige Suppe",
"price": 2.2,
"desc": "Üppige Suppe mit üppigem Gemüse gewuppt mit zerruppten Gestrüpp"
},
{
"name": "Henker's Mahlzeit",
"price": 6.8,
"desc": "Roter Reis mit scharfem Chili mit tomaten und roter Paprika"
},
{
"name": "Nudelsuppe",
"price": 3.3,
"desc": "Leissuppe mit liesigel Poltion klingliger Knödel"
}
]
},
{
"name": "Hauptspeisen",
"anchor": "hauptspeise",
"items": [
{
"name": "Grillplatte \"China\"",
"price": 11.2,
"desc": "Ein großes, paniertes Schnitzel in Form des Landes china dazu Tibet-formender Reis, Ente im Pazifik und Kähnchen als Peking geformt"
},
{
"name": "Reis mit Reis",
"price": 14.8,
"desc": "Reis mit Reis dazu Reis als Beilage in Kombination mit Reis und Reis"
},
{
"name": "O'jeh-gerl-schnitzel",
"price": 13.5,
"desc": "Deutsches Jägerschnitzel nach Art des Hauses mit Reis als Beilage"
},
{
"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"
},
{
"name": "Burger Duck",
"price": 14.2,
"desc": "Saftiger Burger mit aufregenden Gewürzen, Gewürzgurken, Gurken, Tomaten, BBQ-Sauce und knusprig überbackenem Entenfleisch, dazu Reis"
},
{
"name": "Money Boy",
"price": 95.9,
"desc": "Goldfarbener Reis mit mit in Gold überzogener Hähnchenbrust in Champagner-Soße dazu goldfarbenes Brot aus Provìnce de uberteuereé"
},
{
"name": "Reis-otto",
"price": 11.6,
"desc": "Risotto mit Brokkoli und Schinken, dazu etwas Parmesan und Thymian"
}
]
},
{
"name": "Junior-Reisesser",
"anchor": "junior",
"items": [
{
"name": "\"Chinesischer Seeräuber\"-Teller",
"price": 0,
"desc": "Ein Messer, eine Gabel, ein Teller und nur eine Aufgabe: Das Essen des Nachbars! (auf Wunsch mit Seeräuber-Hut)"
},
{
"name": "Käpt'n Reisbart",
"price": 7.7,
"desc": "Süß-Sauer-Soße mit Hähnchenschiffchen in Reis gebadet"
},
{
"name": "Spagetti Reisonese",
"price": 5.6,
"desc": "Spaghetti mit Tomatensoße und Reis"
}
]
},
{
"name": "Desserts",
"anchor": "dessert",
"items": [
{
"name": "Milchreis",
"price": 6.3,
"desc": "Leckerer Milchreis aus der Tüte vom Großhändler mit heißen Sauerkirschen aus der Konservenbüchse"
},
{
"name": "R-Eis",
"price": 4.1,
"desc": "Gemischtes Eis mit Kokos-reis-Streumaterial; 3 Kugeln nach Wahl"
},
{
"name": "Eierreiskuchen",
"price": 5,
"desc": "2 köstliche Palatschinken (regional auch Eierkuchen genannt) mit im Teig eingearbeitetetem Reis, dazu feinster Powidl (regional auch Pflaumenmuß) genannt"
},
{
"name": "Süßes Reisotto",
"price": 8.7,
"desc": "Reis mit bunten Früchten und viel Joghurt, angerichtet mit etweas Zimt und einem Schuss Honig"
},
{
"name": "Reisstrudel",
"price": 5.6,
"desc": "In Blätterteil gewickelter Reis mit Milchcreme und Minze, dazu Vanillesoße und eine Kugel Vanilleeis"
}
]
}
],
"drinks": [
{
"name": "Auf Art des Hauses",
"anchor": "haus",
"items": [
{
"name": "Reiswein",
"price": 3.9,
"desc": "Exquisiter Reiswein mit leiter gelblicher Färbung aus eigener Herstellung mit über 1337 Jahren Erfahrung (Alkoholgehalt 13,37%)"
},
{
"name": "Reisschnaps",
"price": 2.1,
"desc": "Starker Reisschnaps als Absacker geeignet mit feiner Süße im Abgang (Alkoholgehalt 69%)"
},
{
"name": "Reisbier",
"price": 3.5,
"desc": "Aus der Reispflanze gewonnenes Bier mit Hopfen nach chinesischem Reinheitsgebot (Alkoholgehalt 4,2%)"
},
{
"name": "Reissud",
"price": 1.2,
"desc": "Heißer beim Reis kochen entstandener Sud; alkoholfrei, warm und jedes Mal je nach Reissorte ein individueller, neuer Geschmack"
}
]
},
{
"name": "Alkoholfrei",
"anchor": "alkoholfrei",
"items": [
{
"name": "Softdrinks (0,5l)",
"price": 3.4,
"desc": "Verschidene Sorten nur diesen Framstag im Angebot!"
},
{
"name": "Softdrinks (0,3l)",
"price": 2.9,
"desc": "Verschidene Sorten nur diesen Framstag im Angebot!"
},
{
"name": "Wasser (1l)",
"price": 4.1,
"desc": "Das Wasser trüb, die Luft ist rein, Franz-Josef muss ertrunken sein."
}
]
},
{
"name": "Alkohol",
"anchor": "alkohol",
"items": [
{
"name": "Bier vom Fass (0,5l)",
"price": 3.5,
"desc": "Wählen sie zwischen Becks, Sternburg und Berliner Kindl"
},
{
"name": "Bier vom Fass (0,3l)",
"price": 3,
"desc": "Wählen sie zwischen Becks, Sternburg und Berliner Kindl"
},
{
"name": "Chinesisches Bier (0,69l)",
"price": 2.4,
"desc": "Tsingtao-Exportbier"
},
{
"name": "Chinesisches Bier (0,42l)",
"price": 1.9,
"desc": "Tsingtao-Exportbier"
},
{
"name": "Rislinger Wein (0,2l)",
"price": 4.2,
"desc": "Asiatischer Wein mit unbekanntem Ursprung aus gezüchteten, von Monsanto genmanipulierten Weintrauben"
},
{
"name": "Radler (0,5l)",
"price": 13.2,
"desc": "Für echte Vampire"
}
]
},
{
"name": "Spriritus",
"anchor": "spirituose",
"items": [
{
"name": "Asiatische Freude",
"price": 1.5,
"desc": "Auf 45 Grad erwärmter Reisschnaps mit Raucharoma für natürliches Pekinger Stadtklima"
},
{
"name": "Sprit-Tussi",
"price": 1.6,
"desc": "Diesel (Cola-Bier-Gemisch) in einem 0,1cl-Glas, um es teurer verkaufen zu können"
}
]
},
{
"name": "Cocktails",
"anchor": "cocktails",
"items": [
{
"name": "Beste Freundschaft",
"price": 9.3,
"desc": "Lila Livo-Liva mit weißem Rohrzucker und Limettensaft mit frischer, grüner Limette"
},
{
"name": "Schwanz-Schwanz",
"price": 5.6,
"desc": "Ausgefuchste Kombination von Schwanz und Schwanz des Hans und Franz"
},
{
"name": "Der Reisende",
"price": 8.9,
"desc": "Du schmeckst eh nichts anderes mehr, so viel Alk ist hier drin, und nach einem davon reist du durch die ganze Welt (zumindest im Kopf)"
},
{
"name": "Pina Reisolada",
"price": 6.1,
"desc": "Mit Ananas und Kokos aufgemixte Mixtur in Reisschaum, mit Reisschnaps und fast dem ganzen Glas voller Eis"
}
]
}
]
"food": [
{
"name": "Vorspeisen",
"anchor": "vorspeise",
"items": [
{
"name": "Reis-alat",
"price": 5.50,
"desc": "Frischer Blattsalat mit Basmati-Reis und würzigem Balsamico"
},
{
"name": "Chinesisches Bauernfrühstück",
"price": 6.40,
"desc": "Feuriges Chili in gedämpften, kalten Reis mit heißem Gemüse"
},
{
"name": "Thailändisches Arbeiterfrühstück",
"price": 7.40,
"desc": "Feuriges Chili in gedämpften, kalten Reis mit heißem Gemüse und Hähnchen"
},
{
"name": "Viatnamesisches Denkerfrühstück",
"price": 7.90,
"desc": "Feuriges Chili in gedämpften, kalten Reis mit heißem Gemüse und Ente"
},
{
"name": "90er-Reis-Plattenscheibe",
"price": 51.90,
"desc": "Die seltene Platte \"Blue Eyes\" von \"Brooky Ryes\" für Retrofreunde, bestückt mit 5 verschiedenen Reissorten, dazu etwas Gemüse in Vinyl-Optik"
}
]
},
{
"name": "Suppen",
"anchor": "suppe",
"items": [
{
"name": "Reis-uppe",
"price": 3.90,
"desc": "Köstliche, klare Hühnerbrühe mit einem Schuss Soja und Reis"
},
{
"name": "Tomatensuppe",
"price": 4.20,
"desc": "Cremige Tomatensuppe mit frischen Tomaten, Rahm und Reis sowie einer leckeren Petersilie als Dekör"
},
{
"name": "Üppige Suppe",
"price": 2.20,
"desc": "Üppige Suppe mit üppigem Gemüse gewuppt mit zerruppten Gestrüpp"
},
{
"name": "Henker's Mahlzeit",
"price": 6.80,
"desc": "Roter Reis mit scharfen Chili mit Tomaten und roter Paprika"
},
{
"name": "Nudelsuppe",
"price": 3.30,
"desc": "Leissuppe mit liesigel Poltion klingliger Knödel"
}
]
},
{
"name": "Hauptspeisen",
"anchor": "hauptspeise",
"items": [
{
"name": "Grillplatte \"China\"",
"price": 11.20,
"desc": "Ein großes, paniertes Schnitzel in Form des Landes China dazu Tibet-formender Reis, im Pazifik ausgelegte Ente und ein als Peking geformtes Hähnchen"
},
{
"name": "Reis mit Reis",
"price": 14.80,
"desc": "Reis mit Reis dazu Reis als Beilage in Kombination mit Reis und Reis"
},
{
"name": "O'jeh-gerl-schnitzel",
"price": 13.50,
"desc": "Deutsches Jägerschnitzel nach Art des Hauses mit Reisbeilage"
},
{
"name": "RisiBisi",
"price": 17.30,
"desc": "Lila Reis aus kontrolliertem Bio-Anbau mit knallgrünen Erbsen, orangener Paprika und gelben Mais als Beilage mit sauerstoffarmen, blauen Schweinefleisch von glücklichen Tieren"
},
{
"name": "Burger Duck",
"price": 14.20,
"desc": "Saftiger Burger mit aufregenden Gewürzen, Gewürzgurken, Salatgurken, Tomaten, BBQ-Sauce und knusprig überbackenem Entenfleisch, dazu Reis"
},
{
"name": "Money Boy",
"price": 95.90,
"desc": "Goldfarbener Reis mit in Gold überzogener Hähnchenbrust in Champagner-Soße, dazu goldfarbenes Brot aus Provìnce de uberteuereé"
},
{
"name": "Reis-otto",
"price": 11.60,
"desc": "Risotto mit Brokkoli und Schinken, dazu etwas Parmesan und Thymian"
}
]
},
{
"name": "Junior-Reisesser",
"anchor": "junior",
"items": [
{
"name": "\"Chinesischer Seeräuber\"-Teller",
"price": 0,
"desc": "Ein Messer, eine Gabel, ein Teller und nur eine Aufgabe: Das Essen des Nachbars! (auf Wunsch mit Seeräuber-Hut)"
},
{
"name": "Käpt'n Reisbart",
"price": 7.70,
"desc": "Süß-Sauer-Soße mit Hähnchenschiffchen in Reis gebadet"
},
{
"name": "Spagetti Reisonese",
"price": 5.60,
"desc": "Spaghetti mit Tomatensoße und Reis"
}
]
},
{
"name": "Desserts",
"anchor": "dessert",
"items": [
{
"name": "Milchreis",
"price": 6.30,
"desc": "Leckerer Milchreis aus der Tüte vom Großhändler mit heißen Sauerkirschen aus der Konservenbüchse"
},
{
"name": "R-Eis",
"price": 4.10,
"desc": "Gemischtes Eis mit Kokos-reis-Streumaterial; 3 Kugeln nach Wahl"
},
{
"name": "Eierreiskuchen",
"price": 5.00,
"desc": "2 köstliche Palatschinken (regional auch Eierkuchen genannt) mit im Teig eingearbeitetem Reis, dazu feinster Powidl (regional auch Pflaumenmuß) genannt"
},
{
"name": "Süßes Reisotto",
"price": 8.70,
"desc": "Reis mit bunten Früchten und viel Joghurt, angerichtet mit etwas Zimt und einem Schuss Honig"
},
{
"name": "Reisstrudel",
"price": 5.60,
"desc": "In Blätterteig gewickelter Reis mit Milchcreme und Minze, dazu Vanillesoße und eine Kugel Vanilleeis"
}
]
}
],
"drinks": [
{
"name": "Auf Art des Hauses",
"anchor": "haus",
"items": [
{
"name": "Reiswein",
"price": 3.90,
"desc": "Exquisiter Reiswein mit leicht gelblicher Färbung aus eigener Herstellung mit über 1337 Jahren Erfahrung (Alkoholgehalt 13,37%)"
},
{
"name": "Reisschnaps",
"price": 2.10,
"desc": "Starker Reisschnaps als Absacker geeignet mit feiner Süße im Abgang (Alkoholgehalt 69%)"
},
{
"name": "Reisbier",
"price": 3.50,
"desc": "Aus der Reispflanze gewonnenes Bier mit Hopfen nach chinesischem Reinheitsgebot (Alkoholgehalt 4,2%)"
},
{
"name": "Reissud",
"price": 1.20,
"desc": "Heißer, beim Reiskochen entstandener Sud, jedes Mal, je nach Reissorte, ein individueller Geschmack, warm & alkoholfrei"
}
]
},
{
"name": "Alkoholfrei",
"anchor": "alkoholfrei",
"items": [
{
"name": "Softdrinks (0,5l)",
"price": 3.40,
"desc": "Verschiedene Sorten nur diesen Framstag im Angebot!"
},
{
"name": "Softdrinks (0,3l)",
"price": 2.90,
"desc": "Verschidene Sorten nur diesen Framstag im Angebot!"
},
{
"name": "Wasser (1l)",
"price": 4.10,
"desc": "Das Wasser trüb, die Luft ist rein, Franz-Josef muss ertrunken sein."
}
]
},
{
"name": "Alkohol",
"anchor": "alkohol",
"items": [
{
"name": "Bier vom Fass (0,5l)",
"price": 3.50,
"desc": "Wählen sie zwischen Becks, Sternburg und Berliner Kindl"
},
{
"name": "Bier vom Fass (0,3l)",
"price": 3.00,
"desc": "Wählen sie zwischen Becks, Sternburg und Berliner Kindl"
},
{
"name": "Chinesisches Bier (0,69l)",
"price": 2.40,
"desc": "Tsingtao-Exportbier"
},
{
"name": "Chinesisches Bier (0,42l)",
"price": 1.90,
"desc": "Tsingtao-Exportbier"
},
{
"name": "Rislinger Wein (0,2l)",
"price": 4.20,
"desc": "Asiatischer Wein mit unbekanntem Ursprung aus gezüchteten, von Monsanto genmanipulierten Weintrauben"
},
{
"name": "Radler (0,5l)",
"price": 13.20,
"desc": "Für echte Vampire"
}
]
},
{
"name": "Spriritus",
"anchor": "spirituose",
"items": [
{
"name": "Asiatische Freude",
"price": 1.50,
"desc": "Auf 45 Grad erwärmter Reisschnaps mit Raucharoma für natürliches Pekinger Stadtklima"
},
{
"name": "Sprit-Tussi",
"price": 1.60,
"desc": "Diesel (Cola-Bier-Gemisch) in einem 0,1cl-Glas, um es teurer verkaufen zu können"
}
]
},
{
"name": "Cocktails",
"anchor": "cocktails",
"items": [
{
"name": "Beste Freundschaft",
"price": 9.30,
"desc": "Lila Livo-Liva mit weißem Rohrzucker und Limettensaft mit frischer, grüner Limette"
},
{
"name": "Schwanz-Schwanz",
"price": 5.60,
"desc": "Ausgefuchste Kombination vom Schwanz und Schwanz des Hans und Franz"
},
{
"name": "Der Reisende",
"price": 8.90,
"desc": "Du schmeckst eh nichts anderes mehr, so viel Alk ist hier drin, und nach einem davon reist du durch die ganze Welt (zumindest im Kopf)"
},
{
"name": "Piña Reisolada",
"price": 6.10,
"desc": "Mit Ananas und Kokos aufgemixte Mixtur in Reisschaum, mit Reisschnaps und fast dem ganzen Glas voller Eis"
}
]
}
]
}

View File

@@ -12,22 +12,22 @@
{
"name": "Chinesisches Bauernfrühstück",
"price": 6.40,
"desc": "Feuriges Chili in gedämpftem, kaltem Reis mit heißem Gemüse"
"desc": "Feuriges Chili in gedämpften, kalten Reis mit heißem Gemüse"
},
{
"name": "Thailändisches Arbeiterfrühstück",
"price": 7.40,
"desc": "Feuriges Chili in gedämpftem, kaltem Reis mit heißem Gemüse und Hähnchen"
"desc": "Feuriges Chili in gedämpften, kalten Reis mit heißem Gemüse und Hähnchen"
},
{
"name": "Viatnamesisches Denkerfrühstück",
"price": 7.90,
"desc": "Feuriges Chili in gedämpftem, kaltem Reis mit heißem Gemüse und Ente"
"desc": "Feuriges Chili in gedämpften, kalten Reis mit heißem Gemüse und Ente"
},
{
"name": "90er-Reis-Plattenscheibe",
"price": 51.90,
"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": "Die seltene Platte \"Blue Eyes\" von \"Brooky Ryes\" für Retrofreunde, bestückt mit 5 verschiedenen Reissorten, dazu etwas Gemüse in Vinyl-Optik"
}
]
},
@@ -53,7 +53,7 @@
{
"name": "Henker's Mahlzeit",
"price": 6.80,
"desc": "Roter Reis mit scharfem Chili mit tomaten und roter Paprika"
"desc": "Roter Reis mit scharfen Chili mit Tomaten und roter Paprika"
},
{
"name": "Nudelsuppe",
@@ -69,7 +69,7 @@
{
"name": "Grillplatte \"China\"",
"price": 11.20,
"desc": "Ein großes, paniertes Schnitzel in Form des Landes china dazu Tibet-formender Reis, Ente im Pazifik und Kähnchen als Peking geformt"
"desc": "Ein großes, paniertes Schnitzel in Form des Landes China dazu Tibet-formender Reis, im Pazifik ausgelegte Ente und ein als Peking geformtes Hähnchen"
},
{
"name": "Reis mit Reis",
@@ -79,22 +79,22 @@
{
"name": "O'jeh-gerl-schnitzel",
"price": 13.50,
"desc": "Deutsches Jägerschnitzel nach Art des Hauses mit Reis als Beilage"
"desc": "Deutsches Jägerschnitzel nach Art des Hauses mit Reisbeilage"
},
{
"name": "RisiBisi",
"price": 17.30,
"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, orangener Paprika und gelben Mais als Beilage mit sauerstoffarmen, blauen Schweinefleisch von glücklichen Tieren"
},
{
"name": "Burger Duck",
"price": 14.20,
"desc": "Saftiger Burger mit aufregenden Gewürzen, Gewürzgurken, Gurken, Tomaten, BBQ-Sauce und knusprig überbackenem Entenfleisch, dazu Reis"
"desc": "Saftiger Burger mit aufregenden Gewürzen, Gewürzgurken, Salatgurken, Tomaten, BBQ-Sauce und knusprig überbackenem Entenfleisch, dazu Reis"
},
{
"name": "Money Boy",
"price": 95.90,
"desc": "Goldfarbener Reis mit mit in Gold überzogener Hähnchenbrust in Champagner-Soße dazu goldfarbenes Brot aus Provìnce de uberteuereé"
"desc": "Goldfarbener Reis mit in Gold überzogener Hähnchenbrust in Champagner-Soße, dazu goldfarbenes Brot aus Provìnce de uberteuereé"
},
{
"name": "Reis-otto",
@@ -141,17 +141,17 @@
{
"name": "Eierreiskuchen",
"price": 5.00,
"desc": "2 köstliche Palatschinken (regional auch Eierkuchen genannt) mit im Teig eingearbeitetetem Reis, dazu feinster Powidl (regional auch Pflaumenmuß) genannt"
"desc": "2 köstliche Palatschinken (regional auch Eierkuchen genannt) mit im Teig eingearbeitetem Reis, dazu feinster Powidl (regional auch Pflaumenmuß) genannt"
},
{
"name": "Süßes Reisotto",
"price": 8.70,
"desc": "Reis mit bunten Früchten und viel Joghurt, angerichtet mit etweas Zimt und einem Schuss Honig"
"desc": "Reis mit bunten Früchten und viel Joghurt, angerichtet mit etwas Zimt und einem Schuss Honig"
},
{
"name": "Reisstrudel",
"price": 5.60,
"desc": "In Blätterteil gewickelter Reis mit Milchcreme und Minze, dazu Vanillesoße und eine Kugel Vanilleeis"
"desc": "In Blätterteig gewickelter Reis mit Milchcreme und Minze, dazu Vanillesoße und eine Kugel Vanilleeis"
}
]
}
@@ -164,7 +164,7 @@
{
"name": "Reiswein",
"price": 3.90,
"desc": "Exquisiter Reiswein mit leiter gelblicher Färbung aus eigener Herstellung mit über 1337 Jahren Erfahrung (Alkoholgehalt 13,37%)"
"desc": "Exquisiter Reiswein mit leicht gelblicher Färbung aus eigener Herstellung mit über 1337 Jahren Erfahrung (Alkoholgehalt 13,37%)"
},
{
"name": "Reisschnaps",
@@ -179,7 +179,7 @@
{
"name": "Reissud",
"price": 1.20,
"desc": "Heißer beim Reis kochen entstandener Sud; alkoholfrei, warm und jedes Mal je nach Reissorte ein individueller, neuer Geschmack"
"desc": "Heißer, beim Reiskochen entstandener Sud, jedes Mal, je nach Reissorte, ein individueller Geschmack, warm & alkoholfrei"
}
]
},
@@ -190,7 +190,7 @@
{
"name": "Softdrinks (0,5l)",
"price": 3.40,
"desc": "Verschidene Sorten nur diesen Framstag im Angebot!"
"desc": "Verschiedene Sorten nur diesen Framstag im Angebot!"
},
{
"name": "Softdrinks (0,3l)",
@@ -268,7 +268,7 @@
{
"name": "Schwanz-Schwanz",
"price": 5.60,
"desc": "Ausgefuchste Kombination von Schwanz und Schwanz des Hans und Franz"
"desc": "Ausgefuchste Kombination vom Schwanz und Schwanz des Hans und Franz"
},
{
"name": "Der Reisende",
@@ -276,7 +276,7 @@
"desc": "Du schmeckst eh nichts anderes mehr, so viel Alk ist hier drin, und nach einem davon reist du durch die ganze Welt (zumindest im Kopf)"
},
{
"name": "Pina Reisolada",
"name": "Piña Reisolada",
"price": 6.10,
"desc": "Mit Ananas und Kokos aufgemixte Mixtur in Reisschaum, mit Reisschnaps und fast dem ganzen Glas voller Eis"
}

49
data/reservation.json Normal file
View File

@@ -0,0 +1,49 @@
{
"items": [
{
"name": "Theresa Tarantel",
"desc": "Ich komme alleine, möchte aber 11 Plätze reservieren, um genug Abstand von allen zu haben! Ich hasse Menschen!",
"email": "sweetmoon@web.de",
"person": 11,
"time": "22:00",
"date": "2016-12-07",
"read": false
},
{
"name": "Hans Bauer",
"desc": "Kleine Familienfeier zu meinem 80. Geburtstag",
"email": "hans@gmx.de",
"person": 20,
"time": "20:15",
"date": "2016-05-16",
"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": "2016-03-14",
"read": true
},
{
"name": "Spa Mer",
"desc": "Das ist eine <b>versuchte</b> Injection\r\nmit mehreren Zeilen.",
"email": "ano.ny@mus.com",
"person": 1337,
"time": "00:00",
"date": "2016-01-01",
"read": true
},
{
"name": "Agathe Andolerian",
"desc": "keine",
"email": "agathe.ando123@t-online.de",
"person": 8,
"time": "20:15",
"date": "2016-06-17",
"read": false
}
]
}

View File

@@ -0,0 +1,49 @@
{
"items": [
{
"name": "Theresa Tarantel",
"desc": "Ich komme alleine, möchte aber 11 Plätze reservieren, um genug Abstand von allen zu haben! Ich hasse Menschen!",
"email": "sweetmoon@web.de",
"person": 11,
"time": "22:00",
"date": "2016-12-07",
"read": false
},
{
"name": "Hans Bauer",
"desc": "Kleine Familienfeier zu meinem 80. Geburtstag",
"email": "hans@gmx.de",
"person": 20,
"time": "20:15",
"date": "2016-05-16",
"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": "2016-03-14",
"read": true
},
{
"name": "Spa Mer",
"desc": "Das ist eine <b>versuchte</b> Injection\r\nmit mehreren Zeilen.",
"email": "ano.ny@mus.com",
"person": 1337,
"time": "00:00",
"date": "2016-01-01",
"read": true
},
{
"name": "Agathe Andolerian",
"desc": "keine",
"email": "agathe.ando123@t-online.de",
"person": 8,
"time": "20:15",
"date": "2016-06-17",
"read": false
}
]
}

20
data/users.json Normal file
View File

@@ -0,0 +1,20 @@
{
"Richard Reis": {
"pwd": "secret",
"rights": [
null,
true,
true,
true
]
},
"Ursula Ulstein": {
"pwd": "ultimativ",
"rights": [
null,
true,
true,
false
]
}
}

20
data/users.json.example Normal file
View File

@@ -0,0 +1,20 @@
{
"Richard Reis": {
"pwd": "secret",
"rights": [
null,
true,
true,
true
]
},
"Ursula Ulstein": {
"pwd": "ultimativ",
"rights": [
null,
true,
true,
false
]
},
}

87
main.js
View File

@@ -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)
});
});
@@ -143,9 +214,13 @@ app.post('/login', function(req, res) {
});
});
app.get('/api/specialOffer.json', function(req, res) {
res.json({ rice: "Neuer Waldreis" });
});
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);
});

View File

@@ -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 = {

View File

@@ -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("-");

272
modules/reservation.js Normal file
View File

@@ -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-9]{4})-(0[0-9]|1[012])-([0-2][0-9]|3[01])$/;
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 JJJJ-MM-TT 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"].unshift({
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;
}
}

View File

@@ -81,7 +81,6 @@ textarea {
#carousel-home_carousel {
max-width: 1170px;
margin: auto;
padding: 0 15px;
}
.btn {
@@ -89,6 +88,10 @@ textarea {
margin-right: auto;
}
[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
display: none !important;
}
/* Login menu */
#loginDropdown {
@@ -120,3 +123,20 @@ textarea {
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;
}

View File

@@ -1,4 +1,16 @@
var app = angular.module('myApp', []);
app.controller("indexController", function ($scope, specialService) {
$scope.rice = "Hello World";
specialService.success(function(data) {
$scope.rice = data.rice;
});
});
app.factory("specialService", function ($http) {
return $http.get('/api/specialOffer.json');
});
app.directive('rrMap', function() {
return {
restrict: 'A',

View File

@@ -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","JJJJ-MM-TT","Datum:","date",date)
.col-sm-6
+input-val("time","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, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/\n/g, '<br/>')}
.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
@@ -82,7 +157,7 @@ block content
h4 Haftung für Inhalte
| Die Inhalte unserer Seiten wurden mit größter Sorgfalt erstellt. Für die Richtigkeit, Vollständigkeit und Aktualität der Inhalte können wir jedoch keine Gewähr übernehmen. Als Diensteanbieter sind wir gemäß § 7 Abs.1 TMG für eigene Inhalte auf diesen Seiten nach den allgemeinen Gesetzen verantwortlich. Nach §§ 8 bis 10 TMG sind wir als Diensteanbieter jedoch nicht verpflichtet, übermittelte oder gespeicherte fremde Informationen zu überwachen oder nach Umständen zu forschen, die auf eine rechtswidrige Tätigkeit hinweisen. Verpflichtungen zur Entfernung oder Sperrung der Nutzung von Informationen nach den allgemeinen Gesetzen bleiben hiervon unberührt. Eine diesbezügliche Haftung ist jedoch erst ab dem Zeitpunkt der Kenntnis einer konkreten Rechtsverletzung möglich. Bei Bekanntwerden von entsprechenden Rechtsverletzungen werden wir diese Inhalte umgehend entfernen.
| Die Inhalte unserer Seiten wurden mit größter Sorgfalt erstellt. Für die Richtigkeit, Vollständigkeit und Aktualität der Inhalte können wir jedoch keine Gewähr übernehmen. Als Dienstanbieter sind wir gemäß § 7 Abs.1 TMG für eigene Inhalte auf diesen Seiten nach den allgemeinen Gesetzen verantwortlich. Nach §§ 8 bis 10 TMG sind wir als Dienstanbieter jedoch nicht verpflichtet, übermittelte oder gespeicherte fremde Informationen zu überwachen oder nach Umständen zu forschen, die auf eine rechtswidrige Tätigkeit hinweisen. Verpflichtungen zur Entfernung oder Sperrung der Nutzung von Informationen nach den allgemeinen Gesetzen bleiben hiervon unberührt. Eine diesbezügliche Haftung ist jedoch erst ab dem Zeitpunkt der Kenntnis einer konkreten Rechtsverletzung möglich. Bei Bekanntwerden von entsprechenden Rechtsverletzungen werden wir diese Inhalte umgehend entfernen.
h4 Urheberrecht
@@ -94,4 +169,5 @@ block content
| Wir weisen darauf hin, dass die Datenübertragung im Internet (z.B. bei der Kommunikation per E-Mail) Sicherheitslücken aufweisen kann. Ein lückenloser Schutz der Daten vor dem Zugriff durch Dritte ist nicht möglich.
| Der Nutzung von im Rahmen der Impressumspflicht veröffentlichten Kontaktdaten durch Dritte zur Übersendung von nicht ausdrücklich angeforderter Werbung und Informationsmaterialien wird hiermit ausdrücklich widersprochen. Die Betreiber der Seiten behalten sich ausdrücklich rechtliche Schritte im Falle der unverlangten Zusendung von Werbeinformationen, etwa durch Spam-Mails, vor.
| Das Impressum dieser Homepage wurde mit dem Impressum Generator der Kanzlei Hasselbach erstellt
| Das Impressum dieser Homepage wurde mit dem Impressum Generator der Kanzlei Hasselbach erstellt.
| Das Impressum dieser Homepage wurde mit dem Impressum Generator der Kanzlei Hasselbach erstellt.

View File

@@ -2,54 +2,59 @@ 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")
.col-md-6.orange Wir bieten Ihnen Reis aus aller Welt. Wählen Sie zwischen 52 verschiedenen Sorten oder lassen Sie sich durch die perfekte Wahl des Chefkochs überraschen.
.col-md-6.orange Wir bieten Ihnen Reis aus aller Welt. Wählen Sie zwischen 52 verschiedenen Sorten oder lassen Sie sich von unserem Chefkochs überraschen.
p.ng-cloak(data-ng-controller="indexController") Die heutige Empfehlung unseres Chefkochs lautet "{{rice}}". Alle Gerichte, die mit dieser Reissorte bestellt werden, sind um 1€ vergünstigt!
.row
.col-md-6.orange Zur Zubereitung wird regional angebautes Gemüse verwendet. Somit schmeckt das Essen noch frischer und die Umwelt wird geschont. Der Reis stammt aus einem fairen Handel mit Kleinbauern in ganz Asien. Nur so kann der originale Geschmack des Reis gewährt werden und er schmeckt besonders kräftig und intensiv!
.col-md-6.graygray
.col-md-6.graygray.hidden-md.hidden-lg
+img-responsive-center("/bin/image/vegetables.png","Gemüse")
.col-md-6.orange Wir nutzen ausschließlich regional angebautes Gemüse, denn nur so schmeckt das Essen noch frischer. Zugleich wird die Umwelt wird geschont. Unser Reis stammt aus einem fairen Handel mit Kleinbauern in ganz Asien. Nur so bekommen Sie den kräftigen und intensiven Originalgeschmack des Reis zu genießen!
.col-md-6.graygray.hidden-xs.hidden-sm
+img-responsive-center("/bin/image/vegetables.png","Gemüse")
.row
.col-md-6.graygray
+img-responsive-center("/bin/image/asia-chef-2.png","Asiatischer Koch")
.col-md-6.orange Unser erfahrener Koch überzeugt mit einer Vielzahl an asiatischen Gerichten. Wie verwenden origrinale Rezepte und exquisite Gewürzmischungen für einen unverwechselbaren Geschmack!
.col-md-6.orange Unser erfahrener Koch überzeugt mit einer Vielzahl an asiatischen Gerichten. Wir verwenden originale Rezepte und exquisite Gewürzmischungen für einen unverwechselbaren Geschmack!

View File

@@ -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'

View File

@@ -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)
@@ -51,6 +63,7 @@ mixin loginBox
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")