Compare commits
13 Commits
v.0.1.0
...
d622fda7a9
| Author | SHA1 | Date | |
|---|---|---|---|
| d622fda7a9 | |||
| 831a667a27 | |||
| 947ed225b6 | |||
| b74da2dc3b | |||
| da484b08a9 | |||
| 22d7b2a11d | |||
| ac21e8d607 | |||
| 7c78226864 | |||
| fa2c7f2e8b | |||
| 07f486c72e | |||
| b5364639a5 | |||
| 2e15d4a960 | |||
| 1b0f48d374 |
@@ -4,29 +4,19 @@
|
|||||||
"id": 0,
|
"id": 0,
|
||||||
"name": "Unregistered",
|
"name": "Unregistered",
|
||||||
"privilegeBuy": false,
|
"privilegeBuy": false,
|
||||||
"privilegeAdminPanel": false,
|
"privilegeAdminPanel": false
|
||||||
"privilegeFileAccess": false
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 1,
|
"id": 1,
|
||||||
"name": "User",
|
"name": "User",
|
||||||
"privilegeBuy": true,
|
"privilegeBuy": true,
|
||||||
"privilegeAdminPanel": false,
|
"privilegeAdminPanel": false
|
||||||
"privilegeFileAccess": false
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": 2,
|
"id": 2,
|
||||||
"name": "Admin",
|
"name": "Admin",
|
||||||
"privilegeBuy": true,
|
"privilegeBuy": true,
|
||||||
"privilegeAdminPanel": true,
|
"privilegeAdminPanel": true
|
||||||
"privilegeFileAccess": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 3,
|
|
||||||
"name": "Super-Admin",
|
|
||||||
"privilegeBuy": true,
|
|
||||||
"privilegeAdminPanel": true,
|
|
||||||
"privilegeFileAccess": true
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
"iban": "DE92500105175721645777"
|
"iban": "DE92500105175721645777"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"accountRoleId": 2
|
"accountRoleId": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"username": "katjaStoiber",
|
"username": "katjaStoiber",
|
||||||
@@ -94,7 +94,7 @@
|
|||||||
"iban": "DE41500105172184936679"
|
"iban": "DE41500105172184936679"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"accountRoleId": 3
|
"accountRoleId": 2
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"username": "guitarhero",
|
"username": "guitarhero",
|
||||||
|
|||||||
@@ -78,25 +78,25 @@
|
|||||||
"location": "Swiss Life Hall"
|
"location": "Swiss Life Hall"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"date": "4",
|
"date": "8",
|
||||||
"price": 92,
|
"price": 92,
|
||||||
"inStock": 170,
|
"inStock": 170,
|
||||||
"location": "Swiss Life Hall"
|
"location": "Swiss Life Hall"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"date": "8",
|
"date": "12",
|
||||||
"price": 119.90,
|
"price": 119.90,
|
||||||
"inStock": 8736,
|
"inStock": 8736,
|
||||||
"location": "Olympiahalle München"
|
"location": "Olympiahalle München"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"date": "12",
|
"date": "19",
|
||||||
"price": 114.90,
|
"price": 114.90,
|
||||||
"inStock": 2793,
|
"inStock": 2793,
|
||||||
"location": "Barclays Arena"
|
"location": "Barclays Arena"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"date": "13",
|
"date": "31",
|
||||||
"price": 124.90,
|
"price": 124.90,
|
||||||
"inStock": 3079,
|
"inStock": 3079,
|
||||||
"location": "Uber Arena Berlin"
|
"location": "Uber Arena Berlin"
|
||||||
@@ -468,7 +468,7 @@
|
|||||||
"location": "ZAG Arena"
|
"location": "ZAG Arena"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"date": "6",
|
"date": "15",
|
||||||
"price": 84.90,
|
"price": 84.90,
|
||||||
"inStock": 192,
|
"inStock": 192,
|
||||||
"location": "Muffatwerk"
|
"location": "Muffatwerk"
|
||||||
@@ -613,25 +613,25 @@
|
|||||||
"image": "concerts/will-of-the-people-tour.jpg",
|
"image": "concerts/will-of-the-people-tour.jpg",
|
||||||
"concerts": [
|
"concerts": [
|
||||||
{
|
{
|
||||||
"date": "32",
|
"date": "2",
|
||||||
"price": 67.90,
|
"price": 67.90,
|
||||||
"inStock": 847,
|
"inStock": 847,
|
||||||
"location": "ZAG Arena"
|
"location": "ZAG Arena"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"date": "39",
|
"date": "17",
|
||||||
"price": 67.90,
|
"price": 67.90,
|
||||||
"inStock": 847,
|
"inStock": 847,
|
||||||
"location": "ZAG Arena"
|
"location": "ZAG Arena"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"date": "41",
|
"date": "31",
|
||||||
"price": 64.90,
|
"price": 64.90,
|
||||||
"inStock": 245,
|
"inStock": 245,
|
||||||
"location": "Olympiastadion Berlin"
|
"location": "Olympiastadion Berlin"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"date": "45",
|
"date": "43",
|
||||||
"price": 64.90,
|
"price": 64.90,
|
||||||
"inStock": 245,
|
"inStock": 245,
|
||||||
"location": "Astra Kulturhaus"
|
"location": "Astra Kulturhaus"
|
||||||
|
|||||||
@@ -60,39 +60,46 @@
|
|||||||
"descriptionDe": "Eine Datenbank arbeitet mit SQL Befehlen um Datensätze anzulegen, abzurufen, zu verändern und löschen. Ein Server wird über API-Schnittstellen angesprochen, führt die Befehle in der Datenbank aus und liefert das Ergebnis zurück. Der Client darf keinen direkten Zugriff auf die Datenbank haben. Bei SQL Injections wird versucht, diesen Sicherheitsmechanismus zu umgehen und über die API-Schnittstellen direkte SQL Befehle auszuführen.",
|
"descriptionDe": "Eine Datenbank arbeitet mit SQL Befehlen um Datensätze anzulegen, abzurufen, zu verändern und löschen. Ein Server wird über API-Schnittstellen angesprochen, führt die Befehle in der Datenbank aus und liefert das Ergebnis zurück. Der Client darf keinen direkten Zugriff auf die Datenbank haben. Bei SQL Injections wird versucht, diesen Sicherheitsmechanismus zu umgehen und über die API-Schnittstellen direkte SQL Befehle auszuführen.",
|
||||||
"descriptionEn": "todo",
|
"descriptionEn": "todo",
|
||||||
"exercises": [
|
"exercises": [
|
||||||
|
{
|
||||||
|
"nameDe": "Wie sieht die Datenbank aus?",
|
||||||
|
"nameEn": "How does the database look like?",
|
||||||
|
"exerciseNr": 1,
|
||||||
|
"descriptionDe": "Wir versuchen nun die Datenbank im Hintergrund anzugreifen. Aktuell wissen wir aber noch nicht wie die Datenbank aussieht, also welche Tabellen sie beinhaltet. Wir können uns aber mit einem SQL-Befehl ausgeben. Gehe zur globalen Suchseite. Öffne mit der Tastenkombination >>Strg<< + >>D<< die >>Developer Tools<<. Klicke auf den Reiter >>Network<<. Hier siehst du, wie das Frontend mit dem Server kommuniziert. Schreibe nun eine SQL-Injection, welche den Suchbegriff ignoriert und dir stattdessen alle Datensätze der Tabelle >>sqlite_master<< zurück gibt, sofern die Bedingung >>type='table'<< erfüllt ist. Kopiere dir bei erfolgreicher Rückmeldung des Backends die Namen der Tabellen in eine Text-Datei, damit wir für die kommenden Aufgaben die richtigen Namen der Tabellen angeben können.",
|
||||||
|
"descriptionEn": "todo"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"nameDe": "Alle Accounts ausspähen",
|
"nameDe": "Alle Accounts ausspähen",
|
||||||
"nameEn": "Get all accounts",
|
"nameEn": "Get all accounts",
|
||||||
"exerciseNr": 1,
|
"exerciseNr": 2,
|
||||||
"descriptionDe": "Wir versuchen nun die Datenbank im Hintergrund anzugreifen. Gehe zur globalen Suchseite. Öffne mit der Tastenkombination >>Strg + D<< die >>Developer Tools<<. Klicke auf den Reiter >>Network<<. Hier siehst du, wie das Frontend mit dem Server kommuniziert. Schreibe nun eine SQL-Injection, welche den Suchbegriff ignoriert und dir stattdessen alle Datensätze der Tabelle >>Accounts<< zurück liefert. Führe den Angriff über das Suchfeld aus. Sieh dir die Rückmeldung des Servers an, indem du im >>Network<<-Tab auf die zurück gegebenen Objekte klickst (z.B. >>locations<<).",
|
"descriptionDe": "Schreibe nun eine SQL-Injection, welche den Suchbegriff ignoriert und dir stattdessen alle Datensätze der Account-Tabelle zurück liefert. Führe den Angriff über das Suchfeld aus. Sieh dir die Rückmeldung des Servers an.",
|
||||||
"descriptionEn": "Execute an SQL-Injection on the Search page to get all datasets from >>Accounts<< table."
|
"descriptionEn": "Execute an SQL-Injection on the Search page to get all datasets from >>Accounts<< table."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"nameDe": "Alle Berechtigungsgruppen ausspähen",
|
"nameDe": "Alle Berechtigungsgruppen ausspähen",
|
||||||
"nameEn": "Get all account roles",
|
"nameEn": "Get all account roles",
|
||||||
"exerciseNr": 2,
|
"exerciseNr": 3,
|
||||||
"descriptionDe": "Wir sehen nun alle Accounts. Jeder hat eine Berechtigungs-ID (accountRoleId) mit der Berechtigungen wie der Zugriff aufs Admin-Panel geregelt werden. Wir wissen aber nicht, was die ID's bedeuten. Schreibe darum eine SQL-Injection, welche den Suchbegriff ignoriert und dir stattdessen alle Datensätze der Tabelle >>AccountRoles<< zurück liefert. Führe den Angriff über das Suchfeld aus. Beobachte die Rückmeldung des Servers über den >>Network<<-Tab.",
|
"descriptionDe": "Wir sehen nun alle Accounts. Jeder hat eine Berechtigungs-ID (accountRoleId) mit der Berechtigungen wie der Zugriff aufs Admin-Panel geregelt werden. Wir wissen aber nicht, was die ID's bedeuten. Schreibe darum eine SQL-Injection, welche den Suchbegriff ignoriert und dir stattdessen alle Datensätze der Tabelle >>AccountRoles<< zurück liefert. Führe den Angriff über das Suchfeld aus. Beobachte die Rückmeldung des Servers über den >>Network<<-Tab.",
|
||||||
"descriptionEn": "Execute an SQL-Injection on the Search page to get all datasets from >>AccountRoles<< table."
|
"descriptionEn": "Execute an SQL-Injection on the Search page to get all datasets from >>AccountRoles<< table."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"nameDe": "Eigene Berechtigungen erhöhen",
|
"nameDe": "Eigene Berechtigungen erhöhen",
|
||||||
"nameEn": "Upgrade your privileges",
|
"nameEn": "Upgrade your privileges",
|
||||||
"exerciseNr": 3,
|
"exerciseNr": 4,
|
||||||
"descriptionDe": "Jetzt bearbeiten wir unseren eigenen Account. Schreibe hierfür einen >>UPDATE<<-SQL-Befehl, welcher die >>accountRoleId<< auf das Niveau eines >>Admin<< erhöht für deinen Account-Namen.",
|
"descriptionDe": "Jetzt bearbeiten wir unseren eigenen Account. Schreibe hierfür einen >>UPDATE<<-SQL-Befehl, welcher die >>accountRoleId<< auf das Niveau eines >>Admin<< erhöht für deinen Account-Namen.",
|
||||||
"descriptionEn": "Change the privileges of your account"
|
"descriptionEn": "Change the privileges of your account"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"nameDe": "Einen fremden Account übernehmen",
|
"nameDe": "Einen fremden Account übernehmen",
|
||||||
"nameEn": "Capture another account",
|
"nameEn": "Capture another account",
|
||||||
"exerciseNr": 4,
|
"exerciseNr": 5,
|
||||||
"descriptionDe": "Statt unsere eigenen Berechtigungen zu erhöhen, können wir auch einen Account übernehmen, welcher bereits ein >>Super-Admin<< ist. Suche dir dafür aus der Liste der in Aufgabe 2.1 erhaltenen Accounts einen aus, welcher die Rolle >>Super-Admin<< inne hat. Nur damit lässt sich die Dateiverwaltung öffnen, welche wir später brauchen. Hast du den Account-Namen gefunden, gehe ins Login-Menü (logge dich aus, falls du noch angemeldet bist). Führe nun einen SQL-Injektion durch um diesen Account zu übernehmen.",
|
"descriptionDe": "Statt unsere eigenen Berechtigungen zu erhöhen, können wir auch einen Account übernehmen, welcher bereits ein >>Super-Admin<< ist. Suche dir dafür aus der Liste der in Aufgabe 2.1 erhaltenen Accounts einen aus, welcher die Rolle >>Super-Admin<< inne hat. Nur damit lässt sich die Dateiverwaltung öffnen, welche wir später brauchen. Hast du den Account-Namen gefunden, gehe ins Login-Menü (logge dich aus, falls du noch angemeldet bist). Führe nun einen SQL-Injektion durch um diesen Account zu übernehmen.",
|
||||||
"descriptionEn": "todo"
|
"descriptionEn": "todo"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"nameDe": "Bewertungen löschen",
|
"nameDe": "Bewertungen löschen",
|
||||||
"nameEn": "Delete ratings",
|
"nameEn": "Delete ratings",
|
||||||
"exerciseNr": 5,
|
"exerciseNr": 6,
|
||||||
"descriptionDe": "Jede Band hat Bewertungen auf einer Skala von eins bis fünf Sternen erhalten. Wir wollen alle Fünf-Sterne Bewertungen aus der Datenbank löschen. Schreibe eine SQL Injection, welche in der Tabelle >>Ratings<< alle Einträge mit der Bedingung >>rating = 5<< entfernt. Führe die Injection über die globale Suche aus.",
|
"descriptionDe": "Jede Band hat Bewertungen auf einer Skala von eins bis fünf Sternen erhalten. Wir wollen alle Fünf-Sterne Bewertungen aus der Datenbank löschen. Schreibe eine SQL Injection, welche in der Bewertungs-Tabelle alle Einträge mit der Bedingung >>rating = 5<< entfernt. Führe die Injection über die globale Suche aus.",
|
||||||
"descriptionEn": "todo"
|
"descriptionEn": "todo"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -108,14 +115,14 @@
|
|||||||
"nameDe": "Hallo Welt!",
|
"nameDe": "Hallo Welt!",
|
||||||
"nameEn": "Hello World!",
|
"nameEn": "Hello World!",
|
||||||
"exerciseNr": 1,
|
"exerciseNr": 1,
|
||||||
"descriptionDe": "Nimm dir eine URL des Shops und erweitere sie mit JavaScript Code so, dass beim Öffnen des Links eine 'Hallo Welt' Nachricht erscheint",
|
"descriptionDe": "Als nächstes wollen wir Schadcode in die Web-Applikation einschleusen. Zunächst testen wir, ob die Webseite hierfür anfällig ist. Gehe hierzu auf die Seite >>Alle Bands<< und filtere die Einträge nach einem beliebigen Genre deiner Wahl. In der URL-Leiste siehst du nun, dass hinter der URL und dem Ressourcen-Ziel ein Parameter angegeben ist (der Part hinter dem Fragezeichen). Wir tauschen diesen Parameter gegen einen HTML Tag aus. Der Trick hierbei: Als Quelle geben wir den Zahlenwert >>1<< an, wodurch automatisch das ausgeführt wird, was im >>onerror<<-Tag drinnen steht. Genau hier soll eine Alert-Meldung mit >>Hello World!<< als JavaScript Code eingefügt werden. Verändere die URL so, dass sie die Meldung ausgibt. Falls du nicht mit JavaScript vertraut bist, sieh dir die letzte Seite mit nützlichen Befehlen an.",
|
||||||
"descriptionEn": "Take an URL of the shop and extend it with JavaScript code so that a 'Hello World' message appears whent the link is opened"
|
"descriptionEn": "Take an URL of the shop and extend it with JavaScript code so that a 'Hello World' message appears whent the link is opened"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"nameDe": "Ein externes Script aufrufen",
|
"nameDe": "Ein externes Script aufrufen",
|
||||||
"nameEn": "Run an external script",
|
"nameEn": "Run an external script",
|
||||||
"exerciseNr": 2,
|
"exerciseNr": 2,
|
||||||
"descriptionDe": "Wir haben festgestellt, dass die Seite für Cross-Site-Scripting angreifbar ist! Im zweiten Schritt binden wir nun das Script ein. Es wurde bereits auf den Server hochgeladen. Suche über die Dateiverwaltung im Admin-Panel nach dem Skript. Kopiere dir die URL der Ressource heraus. Gehe zum Login und log dich aus. Füge einen beliebig benannten Query Parameter zur URL hinzu um das Skript zu laden. Nutze ein >>import<<-Statement um das Skript einzubinden. Logge dich nun ganz normal ein und sieh dir über die Konsole (Strg + D) an, wie deine Login-Daten abgegriffen werden. Hinweis: Wenn das Skript richtig eingebunden wurde, erscheint in der Konsole eine >>Active<<-Benachrichtigung. Da das >>import<<-Statement nur beim erstmaligen Laden der Seite genutzt wird, musst nach Eingabe der URL eventuell den Neu-Laden-Button neben der URL-Leiste betätigen.",
|
"descriptionDe": "Wir haben festgestellt, dass die Seite für Cross-Site-Scripting durch Reflected XSS angreifbar ist! Im zweiten Schritt binden wir nun das Script ein. Es wurde bereits auf den Server hochgeladen. Logge dich wahlweise mit einem Admin-Account (Aufgabe 2.5) oder deinem eigenen nun berechtigten Account (Aufgabe 2.4) ein. Öffne nun das Admin-Panel über den Button rechts oben. Suche über die Dateiverwaltung im Admin-Panel nach dem Skript und notiere dir die darunter angezeigte Adresse auf dem Backend-Server. Logge dich aus. Wir wollen das Skript auf der nun sichtbaren Login-Seite über eine veränderte URL einbinden. Nutze hierfür das gleiche Prinzip wie in Aufgabe 3.1. Statt >>genreName<< kannst du einen beliebigen anderen Parameter-Namen verwenden. Nutze die Konsole mit der Tastenkombination Strg + D vor dem Abschicken der URL.Logge dich nach erfolgreicher Aufgabenlösung ein und sieh in der Konsole, wie deine Login-Daten abgegriffen werden.",
|
||||||
"descriptionEn": "Create an URL of the shop, which calls the script"
|
"descriptionEn": "Create an URL of the shop, which calls the script"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
200
backend/data/licenses.json
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"image": "alex-turner.jpg",
|
||||||
|
"license": "CC BY 2.0",
|
||||||
|
"creator": "Raph_PH",
|
||||||
|
"url": "https://upload.wikimedia.org/wikipedia/commons/9/95/Alex_Turner%2C_Way_Out_West_2018.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"image": "andy-nicholson.jpg",
|
||||||
|
"license": "CC BY 2.0",
|
||||||
|
"creator": "Lola's Big Adventure!",
|
||||||
|
"url": "https://upload.wikimedia.org/wikipedia/commons/6/6c/Andy_Nicholson_%28cropped%29.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"image": "anthony-kiedis.jpg",
|
||||||
|
"license": "CC BY 2.0",
|
||||||
|
"creator": "Hel Davies",
|
||||||
|
"url": "https://upload.wikimedia.org/wikipedia/commons/c/ca/Anthony_Kiedis_2022.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"image": "chris-martin.jpg",
|
||||||
|
"license": "CC BY 2.0",
|
||||||
|
"creator": "Raph_PH",
|
||||||
|
"url": "https://upload.wikimedia.org/wikipedia/commons/6/68/ChrisMartinManch030623_%28cropped%29.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"image": "chris-wolstenholme.jpg",
|
||||||
|
"license": "CC BY-SA 4.0",
|
||||||
|
"creator": "Markus Felix",
|
||||||
|
"url": "https://upload.wikimedia.org/wikipedia/commons/e/eb/2018_Chris_Wolstenholme_%28cropped%29.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"image": "flea.jpg",
|
||||||
|
"license": "CC BY 2.0",
|
||||||
|
"creator": "Piyush Kumar",
|
||||||
|
"url": "https://upload.wikimedia.org/wikipedia/commons/8/8e/Flea_1012_%282%29.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"image": "chad-smith.jpg",
|
||||||
|
"license": "Gemeinfrei",
|
||||||
|
"creator": "Bojosoto",
|
||||||
|
"url": "https://upload.wikimedia.org/wikipedia/commons/4/42/Chadsmithclinic.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"image": "john-frusciante.jpg",
|
||||||
|
"license": "CC BY-SA 2.0",
|
||||||
|
"creator": "Hel Davies",
|
||||||
|
"url": "https://upload.wikimedia.org/wikipedia/commons/1/1f/John_Frusciante_%2852279466415%29.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"image": "logo.png",
|
||||||
|
"license": "MIT",
|
||||||
|
"creator": "Tobias Zoghaib",
|
||||||
|
"url": ""
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"image": "lanxess-arena-indoor.jpg",
|
||||||
|
"license": "CC BY-SA 3.0",
|
||||||
|
"creator": "Admin Kübelbeck",
|
||||||
|
"url": "https://upload.wikimedia.org/wikipedia/commons/f/f3/Koelnarena_inside.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"image": "lanxess-arena-outdoor.jpg",
|
||||||
|
"license": "CC BY-SA 2.0",
|
||||||
|
"creator": "Rolf H.",
|
||||||
|
"url": "https://upload.wikimedia.org/wikipedia/commons/1/18/Lanxess_Arena_Flight_over_Cologne.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"image": "red-hot-chili-peppers-1.jpg",
|
||||||
|
"license": "CC BY-SA 4.0",
|
||||||
|
"creator": "Kreepin Deth",
|
||||||
|
"url": "https://upload.wikimedia.org/wikipedia/commons/1/14/RHCP_Live_in_London_26_June_2022.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"image": "swiss-life-hall-indoor.jpg",
|
||||||
|
"license": "CC BY-SA 3.0",
|
||||||
|
"creator": "Bernd Schwabe in Hannover",
|
||||||
|
"url": "https://upload.wikimedia.org/wikipedia/commons/8/89/2013-09-18_Besuch_14._Dalai_Lama_Tendzin_Gyatsho_in_Hannover%2C_future4children%2C_Swiss_Life_Hall%2C_%2876%29.JPG"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"image": "swiss-life-hall-outdoor.jpg",
|
||||||
|
"license": "Public Domain",
|
||||||
|
"creator": "AxelHH",
|
||||||
|
"url": "https://upload.wikimedia.org/wikipedia/commons/6/66/AWD_Hall_Seite.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"image": "astra-kulturhaus-outdoor.jpg",
|
||||||
|
"license": "CC BY 2.0",
|
||||||
|
"creator": "Marcus Grbac",
|
||||||
|
"url": "https://upload.wikimedia.org/wikipedia/commons/f/fd/Astra_Kulturhaus_Biergarten_RAW_Berlin_July_2017.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"image": "thom-yorke.jpg",
|
||||||
|
"license": "CC BY 2.0",
|
||||||
|
"creator": "Raph_PH",
|
||||||
|
"url": "https://upload.wikimedia.org/wikipedia/commons/2/25/RadioheadMontreal170718-70_%2843600493681%29_%28cropped%29.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"image": "rami-jaffee.jpg",
|
||||||
|
"license": "CC BY 2.0",
|
||||||
|
"creator": "Raph_PH",
|
||||||
|
"url": "https://upload.wikimedia.org/wikipedia/commons/f/fa/Rami_Jaffee_1.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"image": "philip-selway.jpg",
|
||||||
|
"license": "CC BY-SA 2.0",
|
||||||
|
"creator": "Michell Zappa",
|
||||||
|
"url": "https://upload.wikimedia.org/wikipedia/commons/1/1f/Phil_Selway.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"image": "phil-harvey.jpg",
|
||||||
|
"license": "CC BY-SA 3.0",
|
||||||
|
"creator": "Hayley St. James",
|
||||||
|
"url": "https://upload.wikimedia.org/wikipedia/commons/3/36/PhilHarveyNewYork17062021.png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"image": "pat-smear.jpg",
|
||||||
|
"license": "GNU v.1.2",
|
||||||
|
"creator": "Andrew Burns",
|
||||||
|
"url": "https://upload.wikimedia.org/wikipedia/commons/6/66/Patsmear.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"image": "mike-kerr.jpg",
|
||||||
|
"license": "CC BY 4.0",
|
||||||
|
"creator": "Dena Flows",
|
||||||
|
"url": "https://upload.wikimedia.org/wikipedia/commons/8/85/017-BIME-2017-Royal-Blood-27X17-por-Dena-Flows.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"image": "matthew-bellamy.jpg",
|
||||||
|
"license": "CC BY 3.0",
|
||||||
|
"creator": "Minerva97",
|
||||||
|
"url": "https://upload.wikimedia.org/wikipedia/commons/d/d0/2009_Matthew_Bellamy_%28cropped%29.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"image": "capitol-outside.jpg",
|
||||||
|
"license": "",
|
||||||
|
"creator": "AxelHH",
|
||||||
|
"url": "https://upload.wikimedia.org/wikipedia/commons/6/6c/Hannover_Capitol_ganz.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"image": "red-hot-chili-peppers-logo.png",
|
||||||
|
"license": "",
|
||||||
|
"creator": "Viiticus",
|
||||||
|
"url": "https://upload.wikimedia.org/wikipedia/commons/3/31/Red_Hot_Chili_Peppers_logo.svg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"image": "red-hot-chili-peppers-2.jpg",
|
||||||
|
"license": "CC BY-SA 4.0",
|
||||||
|
"creator": "Roberto Gianardi",
|
||||||
|
"url": "https://upload.wikimedia.org/wikipedia/commons/6/64/Red_Hot_Chili_Peppers_Bologna_2016.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"image": "arctic-monkeys-1.jpg",
|
||||||
|
"license": "CC BY 3.0",
|
||||||
|
"creator": "Bill Ebbesen",
|
||||||
|
"url": "https://upload.wikimedia.org/wikipedia/commons/0/04/Arctic_Monkeys_-_Orange_Stage_-_Roskilde_Festival_2014.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"image": "arctic-monkeys-2.jpg",
|
||||||
|
"license": "CC BY-SA 3.0",
|
||||||
|
"creator": "Kennysun",
|
||||||
|
"url": "https://upload.wikimedia.org/wikipedia/commons/6/65/Arctic_Monkeys_Playing_at_MSG.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"image": "arctic-monkeys-3.jpg",
|
||||||
|
"license": "CC BY-SA 2.0",
|
||||||
|
"creator": "Aurelien Guichard",
|
||||||
|
"url": "https://upload.wikimedia.org/wikipedia/commons/f/f8/Arctic_Monkeys_%40_Shepherds_Bush_Empire.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"image": "european-tour-arctic-monkeys.jpg",
|
||||||
|
"license": "Gemeinfrei",
|
||||||
|
"creator": "Matthew Cooper",
|
||||||
|
"url": "https://upload.wikimedia.org/wikipedia/commons/e/e7/%22AM%22_%28Arctic_Monkeys%29.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"image": "billy-talent-1.jpg",
|
||||||
|
"license": "CC BY-SA 4.0",
|
||||||
|
"creator": "Biha",
|
||||||
|
"url": "https://upload.wikimedia.org/wikipedia/commons/2/2b/Billy_Talent_-_Frequency_Festival_-_2017-08-15-21-51-04.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"image": "billy-talent-2.jpg",
|
||||||
|
"license": "CC BY-SA 4.0",
|
||||||
|
"creator": "Markus Maier",
|
||||||
|
"url": "https://upload.wikimedia.org/wikipedia/commons/3/3f/Southside_Festival_-_Billy_Talent_-_DSC05306.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"image": "billy-talent-3.jpg",
|
||||||
|
"license": "CC BY-SA 2.0",
|
||||||
|
"creator": "sebi ryffel",
|
||||||
|
"url": "https://upload.wikimedia.org/wikipedia/commons/e/ec/Billy_Talent_at_Rock_Am_See_2007.jpg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"image": "coldplay-members.jpg",
|
||||||
|
"license": "CC BY 2.0",
|
||||||
|
"creator": "Raph_PH",
|
||||||
|
"url": "https://upload.wikimedia.org/wikipedia/commons/2/2e/ColdplayBBC071221_%28cropped%29.jpg"
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
"username": "duranduran",
|
"username": "duranduran",
|
||||||
"tickets": [
|
"tickets": [
|
||||||
{
|
{
|
||||||
"date": "4",
|
"date": "8",
|
||||||
"concertGroupName": "Unlimited Love",
|
"concertGroupName": "Unlimited Love",
|
||||||
"orderPrice": 184,
|
"orderPrice": 184,
|
||||||
"seatGroup": "A",
|
"seatGroup": "A",
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
"seat": 2
|
"seat": 2
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"date": "4",
|
"date": "8",
|
||||||
"concertGroupName": "Unlimited Love",
|
"concertGroupName": "Unlimited Love",
|
||||||
"orderPrice": 184,
|
"orderPrice": 184,
|
||||||
"seatGroup": "A",
|
"seatGroup": "A",
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 55 KiB After Width: | Height: | Size: 539 KiB |
|
Before Width: | Height: | Size: 67 KiB After Width: | Height: | Size: 92 KiB |
|
Before Width: | Height: | Size: 101 KiB After Width: | Height: | Size: 953 KiB |
|
Before Width: | Height: | Size: 1.4 MiB After Width: | Height: | Size: 3.9 MiB |
|
Before Width: | Height: | Size: 155 KiB After Width: | Height: | Size: 6.0 MiB |
|
Before Width: | Height: | Size: 2.1 MiB After Width: | Height: | Size: 193 KiB |
|
Before Width: | Height: | Size: 426 KiB After Width: | Height: | Size: 88 KiB |
|
Before Width: | Height: | Size: 143 KiB After Width: | Height: | Size: 1.5 MiB |
|
Before Width: | Height: | Size: 5.2 MiB After Width: | Height: | Size: 1.7 MiB |
|
Before Width: | Height: | Size: 360 KiB After Width: | Height: | Size: 11 MiB |
|
Before Width: | Height: | Size: 245 KiB After Width: | Height: | Size: 274 KiB |
|
Before Width: | Height: | Size: 356 KiB After Width: | Height: | Size: 5.2 MiB |
|
Before Width: | Height: | Size: 150 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 447 KiB After Width: | Height: | Size: 2.3 MiB |
|
Before Width: | Height: | Size: 648 KiB After Width: | Height: | Size: 339 KiB |
|
Before Width: | Height: | Size: 677 KiB After Width: | Height: | Size: 599 KiB |
|
Before Width: | Height: | Size: 197 KiB After Width: | Height: | Size: 111 KiB |
|
Before Width: | Height: | Size: 126 KiB After Width: | Height: | Size: 276 KiB |
@@ -4,7 +4,6 @@ import { validateString } from "../scripts/validateHelper";
|
|||||||
import { Address } from "../models/user/address.model";
|
import { Address } from "../models/user/address.model";
|
||||||
import { Payment } from "../models/user/payment.model";
|
import { Payment } from "../models/user/payment.model";
|
||||||
import { AccountRole } from "../models/user/accountRole.model";
|
import { AccountRole } from "../models/user/accountRole.model";
|
||||||
import { Exercise } from "../models/exercises/exercise.model";
|
|
||||||
import { sequelize } from "../database";
|
import { sequelize } from "../database";
|
||||||
import jwt from "jsonwebtoken"
|
import jwt from "jsonwebtoken"
|
||||||
import { verifyToken } from "../middlewares/auth.middleware";
|
import { verifyToken } from "../middlewares/auth.middleware";
|
||||||
@@ -12,17 +11,20 @@ import { encryptString } from "../scripts/encryptScripts";
|
|||||||
|
|
||||||
export const account = Router()
|
export const account = Router()
|
||||||
|
|
||||||
account.get("/", (req: Request, res: Response) => {
|
account.get("/", verifyToken, (req: Request, res: Response) => {
|
||||||
Account.findAll({
|
Account.findAll({
|
||||||
include: [ AccountRole ]
|
include: [ AccountRole ]
|
||||||
})
|
})
|
||||||
.then(accounts => {
|
.then(accounts => {
|
||||||
res.status(200).json(accounts)
|
res.status(200).json(accounts)
|
||||||
})
|
})
|
||||||
|
.catch(error => {
|
||||||
|
res.status(500).send()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
// Login user
|
// Login user
|
||||||
account.get("/login", async (req: Request, res: Response) => {
|
account.get("/account/login", async (req: Request, res: Response) => {
|
||||||
const encryptedPassword = encryptString(String(req.query.password))
|
const encryptedPassword = encryptString(String(req.query.password))
|
||||||
|
|
||||||
// Using raw SQL code for SQL injections!
|
// Using raw SQL code for SQL injections!
|
||||||
@@ -52,7 +54,7 @@ account.get("/login", async (req: Request, res: Response) => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
account.get("/account", verifyToken, async(req: Request, res: Response) => {
|
account.get("/account/data", verifyToken, async(req: Request, res: Response) => {
|
||||||
Account.findOne({
|
Account.findOne({
|
||||||
where: {
|
where: {
|
||||||
id: req["id"]
|
id: req["id"]
|
||||||
@@ -62,11 +64,14 @@ account.get("/account", verifyToken, async(req: Request, res: Response) => {
|
|||||||
.then(account => {
|
.then(account => {
|
||||||
res.status(200).json(account)
|
res.status(200).json(account)
|
||||||
})
|
})
|
||||||
|
.catch(error => {
|
||||||
|
res.status(500).send()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
// Creating a new user
|
// Creating a new user
|
||||||
account.post("/", async (req: Request, res: Response) => {
|
account.post("/account", async (req: Request, res: Response) => {
|
||||||
// Check if username is valid
|
// Check if username is valid
|
||||||
if (!validateString(req.body.username, 4))
|
if (!validateString(req.body.username, 4))
|
||||||
{
|
{
|
||||||
@@ -85,9 +90,10 @@ account.post("/", async (req: Request, res: Response) => {
|
|||||||
code: 400,
|
code: 400,
|
||||||
message: "Password too short!"
|
message: "Password too short!"
|
||||||
})
|
})
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create account
|
// User on creation gets User role
|
||||||
await AccountRole.findOne({
|
await AccountRole.findOne({
|
||||||
where: {
|
where: {
|
||||||
name: "User"
|
name: "User"
|
||||||
@@ -97,11 +103,12 @@ account.post("/", async (req: Request, res: Response) => {
|
|||||||
req.body["accountRoleId"] = role.id
|
req.body["accountRoleId"] = role.id
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Create account
|
||||||
Account.create(req.body)
|
Account.create(req.body)
|
||||||
.then(account => {
|
.then(account => {
|
||||||
// Status: 201 Created
|
// Status: 201 Created
|
||||||
res.status(201).json(account)
|
res.status(201).json(account)
|
||||||
}).catch(reason => {
|
}).catch(error => {
|
||||||
// Status: 409 Conflict
|
// Status: 409 Conflict
|
||||||
res.status(409).json({
|
res.status(409).json({
|
||||||
code: 409,
|
code: 409,
|
||||||
@@ -110,7 +117,7 @@ account.post("/", async (req: Request, res: Response) => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
account.patch("/", verifyToken, (req: Request, res: Response) => {
|
account.patch("/account", verifyToken, (req: Request, res: Response) => {
|
||||||
Account.update(req.body,
|
Account.update(req.body,
|
||||||
{
|
{
|
||||||
where: { id: req.body.id }
|
where: { id: req.body.id }
|
||||||
@@ -157,7 +164,7 @@ account.patch("/", verifyToken, (req: Request, res: Response) => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
account.delete("/:id", (req: Request, res: Response) => {
|
account.delete("/account/:id", (req: Request, res: Response) => {
|
||||||
Account.destroy({
|
Account.destroy({
|
||||||
where: {
|
where: {
|
||||||
id: req.params.id
|
id: req.params.id
|
||||||
@@ -166,4 +173,7 @@ account.delete("/:id", (req: Request, res: Response) => {
|
|||||||
.then(account => {
|
.then(account => {
|
||||||
res.status(200).send()
|
res.status(200).send()
|
||||||
})
|
})
|
||||||
|
.catch(error => {
|
||||||
|
res.status(500).send()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
@@ -65,6 +65,9 @@ band.get("/", (req: Request, res: Response) => {
|
|||||||
|
|
||||||
res.status(200).json(bands)
|
res.status(200).json(bands)
|
||||||
})
|
})
|
||||||
|
.catch(error => {
|
||||||
|
res.status(500).send()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -122,7 +125,7 @@ band.get("/band/:name", (req: Request, res: Response) => {
|
|||||||
|
|
||||||
res.status(200).json(band)
|
res.status(200).json(band)
|
||||||
})
|
})
|
||||||
.catch(e => {
|
.catch(error => {
|
||||||
res.status(404).send()
|
res.status(404).send()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -137,11 +140,13 @@ band.get("/search", async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
// On stacked prompts, execute last prompt
|
// On stacked prompts, execute last prompt
|
||||||
if (prompts.length > 1) {
|
if (prompts.length > 1) {
|
||||||
console.log(prompts[prompts.length - 2])
|
try {
|
||||||
const [results, metadata] =
|
const [results, metadata] =
|
||||||
await sequelize.query(prompts[prompts.length - 2])
|
await sequelize.query(prompts[prompts.length - 2])
|
||||||
|
res.status(200).json(results)
|
||||||
res.status(200).json(results)
|
} catch (e) {
|
||||||
|
res.status(400).send()
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Band.findAll({
|
Band.findAll({
|
||||||
where: {
|
where: {
|
||||||
@@ -154,6 +159,9 @@ band.get("/search", async (req: Request, res: Response) => {
|
|||||||
.then(bands => {
|
.then(bands => {
|
||||||
res.status(200).json(bands)
|
res.status(200).json(bands)
|
||||||
})
|
})
|
||||||
|
.catch(error => {
|
||||||
|
res.status(200).send()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -170,6 +178,9 @@ band.patch("/", (req: Request, res: Response) => {
|
|||||||
.then(result => {
|
.then(result => {
|
||||||
res.status(200).json(result)
|
res.status(200).json(result)
|
||||||
})
|
})
|
||||||
|
.catch(error => {
|
||||||
|
res.status(500).send()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
@@ -181,6 +192,9 @@ band.post("/", (req: Request, res: Response) => {
|
|||||||
.then(result => {
|
.then(result => {
|
||||||
res.status(200).json(result)
|
res.status(200).json(result)
|
||||||
})
|
})
|
||||||
|
.catch(error => {
|
||||||
|
res.status(500).send()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -8,4 +8,7 @@ city.get("/", (req: Request, res: Response) => {
|
|||||||
.then(cities => {
|
.then(cities => {
|
||||||
res.status(200).json(cities)
|
res.status(200).json(cities)
|
||||||
})
|
})
|
||||||
|
.catch(error => {
|
||||||
|
res.status(500).send()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
@@ -35,6 +35,9 @@ concert.get("/", (req: Request, res: Response) => {
|
|||||||
|
|
||||||
res.status(200).json(concerts)
|
res.status(200).json(concerts)
|
||||||
})
|
})
|
||||||
|
.catch(error => {
|
||||||
|
res.status(500).send()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
@@ -154,4 +157,7 @@ concert.get("/search", (req: Request, res: Response) => {
|
|||||||
.then(concerts => {
|
.then(concerts => {
|
||||||
res.status(200).json(concerts)
|
res.status(200).json(concerts)
|
||||||
})
|
})
|
||||||
|
.catch(error => {
|
||||||
|
res.status(500).send()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
@@ -11,13 +11,17 @@ export const exercises = Router()
|
|||||||
exercises.get("/", (req: Request, res: Response) => {
|
exercises.get("/", (req: Request, res: Response) => {
|
||||||
Exercise.findAll({
|
Exercise.findAll({
|
||||||
include: [ ExerciseGroup ]
|
include: [ ExerciseGroup ]
|
||||||
}).then(result => {
|
|
||||||
result.sort((a, b) => {
|
|
||||||
return (a.dataValues.exerciseGroup.dataValues.groupNr * 10 + a.dataValues.exerciseNr) > (b.dataValues.exerciseGroup.dataValues.groupNr * 10 + b.dataValues.exerciseNr) ? 1 : -1
|
|
||||||
})
|
|
||||||
|
|
||||||
res.status(200).json(result)
|
|
||||||
})
|
})
|
||||||
|
.then(result => {
|
||||||
|
result.sort((a, b) => {
|
||||||
|
return (a.dataValues.exerciseGroup.dataValues.groupNr * 10 + a.dataValues.exerciseNr) > (b.dataValues.exerciseGroup.dataValues.groupNr * 10 + b.dataValues.exerciseNr) ? 1 : -1
|
||||||
|
})
|
||||||
|
|
||||||
|
res.status(200).json(result)
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
res.status(500).send()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -54,21 +58,7 @@ exercises.post("/:groupNr/:exerciseNr/:state", (req: Request, res: Response) =>
|
|||||||
changed: changed
|
changed: changed
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
.catch(error => {
|
||||||
|
res.status(500).send()
|
||||||
// ExerciseGroup.findOne({
|
})
|
||||||
// where: { groupNr: req.params.groupNr }
|
|
||||||
// })
|
|
||||||
// .then(group => {
|
|
||||||
// Exercise.findOne({
|
|
||||||
// where: {
|
|
||||||
// exerciseNr: req.params.exerciseNr,
|
|
||||||
// exerciseGroupId: group.id
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
// .then(exercise => {
|
|
||||||
// exercise.update({ solved: req.params.state == "1"})
|
|
||||||
// res.status(200).send()
|
|
||||||
// })
|
|
||||||
// })
|
|
||||||
})
|
})
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
import { Request, Response, NextFunction, Router } from 'express'
|
import { Request, Response, NextFunction, Router } from 'express'
|
||||||
import fs from "fs"
|
import fs, { createReadStream } from "fs"
|
||||||
import multer from "multer"
|
import multer from "multer"
|
||||||
const upload = multer({ dest: './backend/images/' })
|
const upload = multer({ dest: './backend/images/' })
|
||||||
|
import licenses from "../data/licenses.json"
|
||||||
|
|
||||||
export const files = Router()
|
export const files = Router()
|
||||||
|
|
||||||
@@ -24,21 +25,28 @@ files.get("/folders", async (req: Request, res: Response) => {
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get all uploaded file names by file name
|
* Get all uploaded file names by folder name
|
||||||
|
*
|
||||||
|
* @param folder Name of folder on server
|
||||||
*/
|
*/
|
||||||
files.get("/:folder", async (req: Request, res: Response) => {
|
files.get("/:folder", async (req: Request, res: Response) => {
|
||||||
let result = []
|
let result = []
|
||||||
let fileNames = fs.readdirSync("./backend/images/" + req.params.folder + "/")
|
let fileNames = fs.readdirSync("./backend/images/" + req.params.folder + "/")
|
||||||
|
|
||||||
|
|
||||||
fileNames.forEach(file => {
|
fileNames.forEach(file => {
|
||||||
let resData = fs.readFileSync("./backend/images/" + req.params.folder + "/" + file, "utf8")
|
let resData = ""
|
||||||
|
let url = "http://localhost:3000/static/" + req.params.folder + "/" + file
|
||||||
|
|
||||||
|
if (file.endsWith("html") || file.endsWith("js")) {
|
||||||
|
resData = fs.readFileSync("./backend/images/" + req.params.folder + "/" + file, "utf8")
|
||||||
|
}
|
||||||
|
|
||||||
result.push({
|
result.push({
|
||||||
name: file,
|
name: file,
|
||||||
size: fs.statSync("./backend/images/" + req.params.folder + "/" + file).size,
|
size: fs.statSync("./backend/images/" + req.params.folder + "/" + file).size,
|
||||||
content: resData,
|
content: resData,
|
||||||
url: "http://localhost:3000/static/" + req.params.folder + "/" + file
|
url: url,
|
||||||
|
copyright: licenses.find(data => data.image == file)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -50,7 +58,5 @@ files.get("/:folder", async (req: Request, res: Response) => {
|
|||||||
* Upload a file
|
* Upload a file
|
||||||
*/
|
*/
|
||||||
files.post("/", upload.single("file"), function (req: Request, res: Response, next: NextFunction) {
|
files.post("/", upload.single("file"), function (req: Request, res: Response, next: NextFunction) {
|
||||||
console.log(req.file)
|
|
||||||
|
|
||||||
res.status(200).send()
|
res.status(200).send()
|
||||||
})
|
})
|
||||||
@@ -98,7 +98,7 @@ location.get("/location/:urlName", (req: Request, res: Response) => {
|
|||||||
|
|
||||||
res.status(200).json(location)
|
res.status(200).json(location)
|
||||||
})
|
})
|
||||||
.catch(e => {
|
.catch(error => {
|
||||||
res.status(404).send()
|
res.status(404).send()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -133,4 +133,7 @@ location.get("/search", (req: Request, res: Response) => {
|
|||||||
.then(locations => {
|
.then(locations => {
|
||||||
res.status(200).json(locations)
|
res.status(200).json(locations)
|
||||||
})
|
})
|
||||||
|
.catch(error => {
|
||||||
|
res.status(500).send()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
@@ -10,17 +10,15 @@ import { City } from "../models/locations/city.model";
|
|||||||
import { Seat } from "../models/locations/seat.model";
|
import { Seat } from "../models/locations/seat.model";
|
||||||
import { SeatRow } from "../models/locations/seatRow.model";
|
import { SeatRow } from "../models/locations/seatRow.model";
|
||||||
import { SeatGroup } from "../models/locations/seatGroup.model";
|
import { SeatGroup } from "../models/locations/seatGroup.model";
|
||||||
|
import { verifyToken } from "../middlewares/auth.middleware";
|
||||||
import { Account } from "../models/user/account.model";
|
import { Account } from "../models/user/account.model";
|
||||||
import { Exercise } from "backend/models/exercises/exercise.model";
|
|
||||||
|
|
||||||
export const order = Router()
|
export const order = Router()
|
||||||
|
|
||||||
// Get all orders
|
// Get all orders
|
||||||
order.get("/", (req: Request, res: Response) => {
|
order.get("/", verifyToken, (req: Request, res: Response) => {
|
||||||
Order.findAll({
|
Order.findAll({
|
||||||
include: [
|
include: [
|
||||||
Account,
|
|
||||||
Address,
|
|
||||||
{
|
{
|
||||||
model: Ticket,
|
model: Ticket,
|
||||||
include: [
|
include: [
|
||||||
@@ -35,14 +33,29 @@ order.get("/", (req: Request, res: Response) => {
|
|||||||
include: [ City ]
|
include: [ City ]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
model: Seat,
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
model: SeatRow,
|
||||||
|
include: [ SeatGroup ]
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
},
|
||||||
|
Address,
|
||||||
|
Payment,
|
||||||
|
Account
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
.then(orders => {
|
.then(orders => {
|
||||||
res.status(200).json(orders)
|
res.status(200).json(orders)
|
||||||
})
|
})
|
||||||
|
.catch(error => {
|
||||||
|
res.status(500).send()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
@@ -90,6 +103,9 @@ order.get("/:id", (req: Request, res: Response) => {
|
|||||||
.then(orders => {
|
.then(orders => {
|
||||||
res.status(200).json(orders)
|
res.status(200).json(orders)
|
||||||
})
|
})
|
||||||
|
.catch(error => {
|
||||||
|
res.status(500).send()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
// Place a new order
|
// Place a new order
|
||||||
@@ -116,4 +132,21 @@ order.post("/", (req: Request, res: Response) => {
|
|||||||
// Created
|
// Created
|
||||||
res.status(201).json(order)
|
res.status(201).json(order)
|
||||||
})
|
})
|
||||||
|
.catch(error => {
|
||||||
|
res.status(500).send()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
order.patch("/", (req: Request, res: Response) => {
|
||||||
|
Order.update(req.body, {
|
||||||
|
where: {
|
||||||
|
id: req.body.id
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(affectedCount => {
|
||||||
|
res.status(200).send()
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
res.status(500).send()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
@@ -29,6 +29,7 @@ startDatabase()
|
|||||||
const path = require('path')
|
const path = require('path')
|
||||||
app.use('/static', express.static(path.join(__dirname, 'images')))
|
app.use('/static', express.static(path.join(__dirname, 'images')))
|
||||||
app.use("/exercises", exercises)
|
app.use("/exercises", exercises)
|
||||||
|
app.use("/files", files)
|
||||||
|
|
||||||
// Add delay for more realistic response times
|
// Add delay for more realistic response times
|
||||||
app.use((req, res, next) => {
|
app.use((req, res, next) => {
|
||||||
@@ -44,7 +45,6 @@ app.use("/orders", order)
|
|||||||
app.use("/accounts", account)
|
app.use("/accounts", account)
|
||||||
app.use("/cities", city)
|
app.use("/cities", city)
|
||||||
app.use("/concerts", concert)
|
app.use("/concerts", concert)
|
||||||
app.use("/files", files)
|
|
||||||
|
|
||||||
// Start server
|
// Start server
|
||||||
const server = app.listen(port, () => {
|
const server = app.listen(port, () => {
|
||||||
|
|||||||
78
package-lock.json
generated
@@ -14,6 +14,10 @@
|
|||||||
"axios": "^1.7.7",
|
"axios": "^1.7.7",
|
||||||
"body-parser": "^1.20.2",
|
"body-parser": "^1.20.2",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
|
"csv": "^6.3.11",
|
||||||
|
"csv-reader": "^1.0.12",
|
||||||
|
"exif-js": "^2.3.0",
|
||||||
|
"exifreader": "^4.25.0",
|
||||||
"express": "^4.21.1",
|
"express": "^4.21.1",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"jspdf": "^2.5.2",
|
"jspdf": "^2.5.2",
|
||||||
@@ -4284,6 +4288,48 @@
|
|||||||
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/csv": {
|
||||||
|
"version": "6.3.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/csv/-/csv-6.3.11.tgz",
|
||||||
|
"integrity": "sha512-a8bhT76Q546jOElHcTrkzWY7Py925mfLO/jqquseH61ThOebYwOjLbWHBqdRB4K1VpU36sTyIei6Jwj7QdEZ7g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"csv-generate": "^4.4.2",
|
||||||
|
"csv-parse": "^5.6.0",
|
||||||
|
"csv-stringify": "^6.5.2",
|
||||||
|
"stream-transform": "^3.3.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.1.90"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/csv-generate": {
|
||||||
|
"version": "4.4.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/csv-generate/-/csv-generate-4.4.2.tgz",
|
||||||
|
"integrity": "sha512-W6nVsf+rz0J3yo9FOjeer7tmzBJKaTTxf7K0uw6GZgRocZYPVpuSWWa5/aoWWrjQZj4/oNIKTYapOM7hiNjVMA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/csv-parse": {
|
||||||
|
"version": "5.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/csv-parse/-/csv-parse-5.6.0.tgz",
|
||||||
|
"integrity": "sha512-l3nz3euub2QMg5ouu5U09Ew9Wf6/wQ8I++ch1loQ0ljmzhmfZYrH9fflS22i/PQEvsPvxCwxgz5q7UB8K1JO4Q==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/csv-reader": {
|
||||||
|
"version": "1.0.12",
|
||||||
|
"resolved": "https://registry.npmjs.org/csv-reader/-/csv-reader-1.0.12.tgz",
|
||||||
|
"integrity": "sha512-0AAgazKJUywtjvZbclNuovIiQY/WyvojWw15Y2k3kPixE+pDiOFnfg5FcH3CfDqqnrB2f3p5oPAc446EXD01Tw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/csv-stringify": {
|
||||||
|
"version": "6.5.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/csv-stringify/-/csv-stringify-6.5.2.tgz",
|
||||||
|
"integrity": "sha512-RFPahj0sXcmUyjrObAK+DOWtMvMIFV328n4qZJhgX3x2RqkQgOTU2mCUmiFR0CzM6AzChlRSUErjiJeEt8BaQA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/de-indent": {
|
"node_modules/de-indent": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz",
|
||||||
@@ -5023,6 +5069,32 @@
|
|||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/exif-js": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/exif-js/-/exif-js-2.3.0.tgz",
|
||||||
|
"integrity": "sha512-1Og9pAzG2FZRVlaavH8bB8BTeHcjMdJhKmeQITkX+uLRCD0xPtKAdZ2clZmQdJ56p9adXtJ8+jwrGp/4505lYg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/exifreader": {
|
||||||
|
"version": "4.25.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/exifreader/-/exifreader-4.25.0.tgz",
|
||||||
|
"integrity": "sha512-lPyPXWTUuYgoKdKf3rw2EDoE9Zl7xHoy/ehPNeQ4gFVNLzfLyNMP4oEI+sP0/Czp5r/2i7cFhqg5MHsl4FYtyw==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"license": "MPL-2.0",
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@xmldom/xmldom": "^0.9.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/exifreader/node_modules/@xmldom/xmldom": {
|
||||||
|
"version": "0.9.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.9.5.tgz",
|
||||||
|
"integrity": "sha512-6g1EwSs8cr8JhP1iBxzyVAWM6BIDvx9Y3FZRIQiMDzgG43Pxi8YkWOZ0nQj2NHgNzgXDZbJewFx/n+YAvMZrfg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"optional": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">=14.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/expand-template": {
|
"node_modules/expand-template": {
|
||||||
"version": "2.0.3",
|
"version": "2.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz",
|
||||||
@@ -8590,6 +8662,12 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/stream-transform": {
|
||||||
|
"version": "3.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/stream-transform/-/stream-transform-3.3.3.tgz",
|
||||||
|
"integrity": "sha512-dALXrXe+uq4aO5oStdHKlfCM/b3NBdouigvxVPxCdrMRAU6oHh3KNss20VbTPQNQmjAHzZGKGe66vgwegFEIog==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/streamsearch": {
|
"node_modules/streamsearch": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
|
||||||
|
|||||||
@@ -38,6 +38,10 @@
|
|||||||
"axios": "^1.7.7",
|
"axios": "^1.7.7",
|
||||||
"body-parser": "^1.20.2",
|
"body-parser": "^1.20.2",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
|
"csv": "^6.3.11",
|
||||||
|
"csv-reader": "^1.0.12",
|
||||||
|
"exif-js": "^2.3.0",
|
||||||
|
"exifreader": "^4.25.0",
|
||||||
"express": "^4.21.1",
|
"express": "^4.21.1",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"jspdf": "^2.5.2",
|
"jspdf": "^2.5.2",
|
||||||
|
|||||||
25
src/App.vue
@@ -26,7 +26,6 @@ watch(() => preferencesStore.language, () => {
|
|||||||
// Watch for theme change
|
// Watch for theme change
|
||||||
watch(() => preferencesStore.theme, () => {
|
watch(() => preferencesStore.theme, () => {
|
||||||
theme.global.name.value = preferencesStore.theme
|
theme.global.name.value = preferencesStore.theme
|
||||||
feedbackStore.addSnackbar(BannerStateEnum.ERROR)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// Watch for 404 page directions
|
// Watch for 404 page directions
|
||||||
@@ -80,9 +79,33 @@ function calcMargin(i) {
|
|||||||
<v-sheet color="sheet" height="100%">
|
<v-sheet color="sheet" height="100%">
|
||||||
<router-view></router-view>
|
<router-view></router-view>
|
||||||
</v-sheet>
|
</v-sheet>
|
||||||
|
|
||||||
</v-container>
|
</v-container>
|
||||||
|
|
||||||
|
<v-btn
|
||||||
|
fab
|
||||||
|
dark
|
||||||
|
fixed
|
||||||
|
bottom
|
||||||
|
right
|
||||||
|
color="primary"
|
||||||
|
>
|
||||||
|
<v-icon>keyboard_arrow_up</v-icon>
|
||||||
|
</v-btn>
|
||||||
|
|
||||||
<company-footer />
|
<company-footer />
|
||||||
</v-main>
|
</v-main>
|
||||||
</v-app>
|
</v-app>
|
||||||
|
|
||||||
|
<!-- Test Environment sticker in bottom right corner -->
|
||||||
|
<v-sheet
|
||||||
|
color="error"
|
||||||
|
position="fixed"
|
||||||
|
location="bottom right"
|
||||||
|
class="pa-3 mb-12 mr-n16 text-center text-h5"
|
||||||
|
width="300"
|
||||||
|
style="rotate: 315deg; z-index: 1008;"
|
||||||
|
>
|
||||||
|
{{ $t('misc.testEnvironment') }}
|
||||||
|
</v-sheet>
|
||||||
</template>
|
</template>
|
||||||
@@ -40,12 +40,16 @@ defineProps({
|
|||||||
type="image"
|
type="image"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
>
|
>
|
||||||
<v-img
|
<v-sheet
|
||||||
:src="image"
|
color="sheet"
|
||||||
:height="height"
|
>
|
||||||
:width="height"
|
<v-img
|
||||||
cover
|
:src="image"
|
||||||
/>
|
:height="height"
|
||||||
|
:width="height"
|
||||||
|
cover
|
||||||
|
/>
|
||||||
|
</v-sheet>
|
||||||
</v-skeleton-loader>
|
</v-skeleton-loader>
|
||||||
|
|
||||||
<v-skeleton-loader
|
<v-skeleton-loader
|
||||||
@@ -55,6 +59,7 @@ defineProps({
|
|||||||
>
|
>
|
||||||
<v-sheet
|
<v-sheet
|
||||||
:height="height"
|
:height="height"
|
||||||
|
color="sheet"
|
||||||
width="100%"
|
width="100%"
|
||||||
class="text-center d-flex justify-center align-center"
|
class="text-center d-flex justify-center align-center"
|
||||||
>
|
>
|
||||||
@@ -75,6 +80,8 @@ defineProps({
|
|||||||
>
|
>
|
||||||
<v-sheet
|
<v-sheet
|
||||||
:height="height"
|
:height="height"
|
||||||
|
color="sheet"
|
||||||
|
class="w-100"
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<div class="text-h4 font-weight-black pt-2 h-100">
|
<div class="text-h4 font-weight-black pt-2 h-100">
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
defineProps({
|
import { loadLicense } from '@/scripts/imageScripts';
|
||||||
|
import { ref, watch } from 'vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
image: String,
|
image: String,
|
||||||
errorImage: {
|
errorImage: {
|
||||||
type: String,
|
type: String,
|
||||||
@@ -16,6 +19,14 @@ defineProps({
|
|||||||
},
|
},
|
||||||
loading: Boolean
|
loading: Boolean
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const license = ref("")
|
||||||
|
|
||||||
|
loadLicense(props.image)
|
||||||
|
.then(result => {
|
||||||
|
console.log(result)
|
||||||
|
license.value = result
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ function confirmPressed() {
|
|||||||
<template>
|
<template>
|
||||||
<action-dialog
|
<action-dialog
|
||||||
:title="title"
|
:title="title"
|
||||||
max-width="400"
|
max-width="500"
|
||||||
v-model="showDialog"
|
v-model="showDialog"
|
||||||
persistent
|
persistent
|
||||||
>
|
>
|
||||||
@@ -36,7 +36,7 @@ function confirmPressed() {
|
|||||||
<outlined-button
|
<outlined-button
|
||||||
@click="showDialog = false"
|
@click="showDialog = false"
|
||||||
prepend-icon="mdi-close"
|
prepend-icon="mdi-close"
|
||||||
color="orange"
|
color="warning"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
>
|
>
|
||||||
{{ $t("misc.actions.cancel") }}
|
{{ $t("misc.actions.cancel") }}
|
||||||
@@ -45,7 +45,7 @@ function confirmPressed() {
|
|||||||
<outlined-button
|
<outlined-button
|
||||||
@click="confirmPressed"
|
@click="confirmPressed"
|
||||||
prepend-icon="mdi-check"
|
prepend-icon="mdi-check"
|
||||||
color="red"
|
color="error"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
>
|
>
|
||||||
{{ $t("misc.actions.confirm") }}
|
{{ $t("misc.actions.confirm") }}
|
||||||
|
|||||||
@@ -8,20 +8,28 @@ defineProps({
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<v-row class="pt-3 d-none d-md-flex">
|
<v-row class="pt-3 d-none d-md-flex">
|
||||||
|
<!-- Left line -->
|
||||||
<v-col class="d-flex justify-center align-center">
|
<v-col class="d-flex justify-center align-center">
|
||||||
<v-sheet height="12" width="100%" color="primary" class="rounded-s-lg" />
|
<v-sheet height="12" width="100%" color="primary" class="rounded-s-lg" />
|
||||||
</v-col>
|
</v-col>
|
||||||
|
|
||||||
|
<!-- Title -->
|
||||||
<v-col class="v-col-auto">
|
<v-col class="v-col-auto">
|
||||||
<v-skeleton-loader
|
<v-skeleton-loader
|
||||||
type="heading"
|
type="heading"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
width="300"
|
width="300"
|
||||||
>
|
>
|
||||||
<span class="text-h4">{{ title }}</span>
|
<v-sheet
|
||||||
|
class="text-h4"
|
||||||
|
color="sheet"
|
||||||
|
>
|
||||||
|
{{ title }}
|
||||||
|
</v-sheet>
|
||||||
</v-skeleton-loader>
|
</v-skeleton-loader>
|
||||||
</v-col>
|
</v-col>
|
||||||
|
|
||||||
|
<!-- Right line -->
|
||||||
<v-col class="d-flex justify-center align-center">
|
<v-col class="d-flex justify-center align-center">
|
||||||
<v-sheet height="12" width="100%" color="primary" class="rounded-e-lg" />
|
<v-sheet height="12" width="100%" color="primary" class="rounded-e-lg" />
|
||||||
</v-col>
|
</v-col>
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ exerciseStore.getAllExercises()
|
|||||||
:content="basketStore.itemsInBasket.reduce((tot, item) => {
|
:content="basketStore.itemsInBasket.reduce((tot, item) => {
|
||||||
return tot + item.seats.length
|
return tot + item.seats.length
|
||||||
}, 0)"
|
}, 0)"
|
||||||
color="red" offset-x="8" offset-y="8">
|
color="error" offset-x="8" offset-y="8">
|
||||||
<v-btn variant="plain" icon="mdi-cart" to="/basket" />
|
<v-btn variant="plain" icon="mdi-cart" to="/basket" />
|
||||||
</v-badge>
|
</v-badge>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ defineProps({
|
|||||||
type="heading"
|
type="heading"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
width="500"
|
width="500"
|
||||||
|
class="text-white"
|
||||||
>
|
>
|
||||||
<span class="text-h3 font-weight-bold">
|
<span class="text-h3 font-weight-bold">
|
||||||
{{ title }}
|
{{ title }}
|
||||||
@@ -77,11 +78,18 @@ defineProps({
|
|||||||
|
|
||||||
|
|
||||||
<!-- Description -->
|
<!-- Description -->
|
||||||
<p class="text-h6 text-medium-emphasis" v-if="!$slots.description">
|
<p
|
||||||
|
class="text-h6 text-white"
|
||||||
|
style="opacity: 0.7;"
|
||||||
|
v-if="!$slots.description"
|
||||||
|
>
|
||||||
{{ description }}
|
{{ description }}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p class="text-h6 text-medium-emphasis">
|
<p
|
||||||
|
class="text-h6 text-white"
|
||||||
|
style="opacity: 0.7;"
|
||||||
|
>
|
||||||
<slot name="description"></slot>
|
<slot name="description"></slot>
|
||||||
</p>
|
</p>
|
||||||
</v-skeleton-loader>
|
</v-skeleton-loader>
|
||||||
|
|||||||
@@ -3,34 +3,83 @@ import { AccountModel } from "../models/user/accountModel"
|
|||||||
|
|
||||||
const BASE_URL = "http://localhost:3000/accounts"
|
const BASE_URL = "http://localhost:3000/accounts"
|
||||||
|
|
||||||
export async function fetchAllAccounts() {
|
/**
|
||||||
return await axios.get(BASE_URL)
|
* Fetch all accounts from server
|
||||||
|
*
|
||||||
|
* @param token Validation token of current logged in user. User needs to have the right privileges
|
||||||
|
*
|
||||||
|
* @returns Response from server with list of all account body
|
||||||
|
*/
|
||||||
|
export async function fetchAllAccounts(token: string) {
|
||||||
|
return await axios.get(BASE_URL, {
|
||||||
|
headers: {
|
||||||
|
"Authorization": token
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function login(username: string, password: string) {
|
/**
|
||||||
return await axios.get(BASE_URL + "/login?username=" + username + "&password=" + password)
|
* Start the login process
|
||||||
|
*
|
||||||
|
* @param username Username of the account
|
||||||
|
* @param password Password of the account
|
||||||
|
*
|
||||||
|
* @returns Response from server with token body
|
||||||
|
*/
|
||||||
|
export async function getLogin(username: string, password: string) {
|
||||||
|
return await axios.get(BASE_URL + "/account/login?username=" + username + "&password=" + password)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all data about a single account
|
||||||
|
*
|
||||||
|
* @param token Validation token
|
||||||
|
*
|
||||||
|
* @returns Response from server with account body
|
||||||
|
*/
|
||||||
export async function getAccount(token: string) {
|
export async function getAccount(token: string) {
|
||||||
return await axios.get(BASE_URL + "/account", {
|
return await axios.get(BASE_URL + "/account/data", {
|
||||||
headers: {
|
headers: {
|
||||||
"Authorization": token
|
"Authorization": token
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Register a new account in servers database
|
||||||
|
*
|
||||||
|
* @param account Account data for new dataset
|
||||||
|
*
|
||||||
|
* @returns Response from server
|
||||||
|
*/
|
||||||
export async function registerAccount(account: AccountModel) {
|
export async function registerAccount(account: AccountModel) {
|
||||||
return await axios.post(BASE_URL, account)
|
return await axios.post(BASE_URL + "/account", account)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update data of an account
|
||||||
|
*
|
||||||
|
* @param account Account data to update
|
||||||
|
* @param token Validation token
|
||||||
|
*
|
||||||
|
* @returns Response from server
|
||||||
|
*/
|
||||||
export async function updateAccount(account: AccountModel, token: string) {
|
export async function updateAccount(account: AccountModel, token: string) {
|
||||||
return await axios.patch(BASE_URL, account, {
|
return await axios.patch(BASE_URL + "/account", account, {
|
||||||
headers: {
|
headers: {
|
||||||
"Authorization": token
|
"Authorization": token
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete an account in servers database
|
||||||
|
*
|
||||||
|
* @param account Account to delete
|
||||||
|
*
|
||||||
|
* @returns Response from server
|
||||||
|
*/
|
||||||
export async function deleteAccount(account: AccountModel) {
|
export async function deleteAccount(account: AccountModel) {
|
||||||
return await axios.delete(BASE_URL + "/" + account.id)
|
return await axios.delete(BASE_URL + "/account/" + account.id)
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import axios from "axios"
|
import axios from "axios"
|
||||||
import { BasketItemModel } from "../models/ordering/basketItemModel"
|
import { BasketItemModel } from "../models/ordering/basketItemModel"
|
||||||
|
import { OrderApiModel } from "../models/apiEndpoints/orderApiModel"
|
||||||
|
|
||||||
const BASE_URL = "http://localhost:3000/orders"
|
const BASE_URL = "http://localhost:3000/orders"
|
||||||
|
|
||||||
@@ -25,13 +26,6 @@ export async function createOrder(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log({
|
|
||||||
accountId: accountId,
|
|
||||||
tickets: tickets,
|
|
||||||
paymentId: paymentId,
|
|
||||||
addressId: addressId
|
|
||||||
})
|
|
||||||
|
|
||||||
return axios.post(BASE_URL, {
|
return axios.post(BASE_URL, {
|
||||||
accountId: accountId,
|
accountId: accountId,
|
||||||
tickets: tickets,
|
tickets: tickets,
|
||||||
@@ -40,6 +34,14 @@ export async function createOrder(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function fetchAllOrders() {
|
export async function fetchAllOrders(token: string) {
|
||||||
return axios.get(BASE_URL)
|
return axios.get(BASE_URL, {
|
||||||
|
headers: {
|
||||||
|
"Authorization": token
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function patchOrder(order: OrderApiModel) {
|
||||||
|
return axios.patch(BASE_URL, order)
|
||||||
}
|
}
|
||||||
@@ -1,12 +1,14 @@
|
|||||||
export enum BannerStateEnum {
|
export enum BannerStateEnum {
|
||||||
////////// System feedback //////////
|
////////// System feedback //////////
|
||||||
|
|
||||||
// Some error
|
// Unknown error
|
||||||
ERROR,
|
ERROR,
|
||||||
|
|
||||||
BASKETPRODUCTADDED,
|
// Ticket added to basket
|
||||||
|
BASKETTICKETADDED,
|
||||||
|
|
||||||
BASKETPRODUCTREMOVED,
|
// Ticket removed from basket
|
||||||
|
BASKETTICKETREMOVED,
|
||||||
|
|
||||||
|
|
||||||
////////// Exercise feedback //////////
|
////////// Exercise feedback //////////
|
||||||
@@ -30,6 +32,8 @@ export enum BannerStateEnum {
|
|||||||
EXERCISESOLVED24,
|
EXERCISESOLVED24,
|
||||||
|
|
||||||
EXERCISESOLVED25,
|
EXERCISESOLVED25,
|
||||||
|
|
||||||
|
EXERCISESOLVED26,
|
||||||
|
|
||||||
EXERCISESOLVED31,
|
EXERCISESOLVED31,
|
||||||
|
|
||||||
@@ -63,11 +67,20 @@ export enum BannerStateEnum {
|
|||||||
ACCOUNTREGISTERERROR,
|
ACCOUNTREGISTERERROR,
|
||||||
|
|
||||||
// Status: 409 Conflict
|
// Status: 409 Conflict
|
||||||
ACCOUNTREGISTERUSERNAMEINUSE,
|
ACCOUNTREGISTERUSERNAMEORMAILINUSE,
|
||||||
|
|
||||||
// Status: 200 OK
|
// Status: 200 OK
|
||||||
ACCOUNTUPDATESUCCESSFUL,
|
ACCOUNTUPDATESUCCESSFUL,
|
||||||
|
|
||||||
|
// Local check on unvalid username
|
||||||
|
ACCOUNTUSERNAMETOOSHORT,
|
||||||
|
|
||||||
|
// Local check on unvalid password
|
||||||
|
ACCOUNTPASSWORDTOOSHORT,
|
||||||
|
|
||||||
|
// Local check on unvalid mail address
|
||||||
|
ACCOUNTMAILADDRESSUNVALID,
|
||||||
|
|
||||||
// No status code, runs in local cache
|
// No status code, runs in local cache
|
||||||
ACCOUNTLOGOUTSUCCESSFUL,
|
ACCOUNTLOGOUTSUCCESSFUL,
|
||||||
|
|
||||||
|
|||||||
13
src/data/models/files/filesApiModel.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
export class FilesApiModel {
|
||||||
|
name: string
|
||||||
|
size: number
|
||||||
|
content: string
|
||||||
|
url: string
|
||||||
|
copyright: CopyRightModel = new CopyRightModel()
|
||||||
|
}
|
||||||
|
|
||||||
|
class CopyRightModel {
|
||||||
|
license: string = ""
|
||||||
|
creator: string = ""
|
||||||
|
url: string = ""
|
||||||
|
}
|
||||||
@@ -34,7 +34,7 @@ defineProps({
|
|||||||
<outlined-button
|
<outlined-button
|
||||||
v-if="!hideAddButton"
|
v-if="!hideAddButton"
|
||||||
prepend-icon="mdi-plus"
|
prepend-icon="mdi-plus"
|
||||||
color="green"
|
color="success"
|
||||||
:disabled="fetchInProgress"
|
:disabled="fetchInProgress"
|
||||||
@click="onAddClick()"
|
@click="onAddClick()"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -154,7 +154,7 @@
|
|||||||
"factoryReset": {
|
"factoryReset": {
|
||||||
"factoryReset": "Zurücksetzen auf Werkseinstellungen",
|
"factoryReset": "Zurücksetzen auf Werkseinstellungen",
|
||||||
"dialog": {
|
"dialog": {
|
||||||
"title": "Aus Werkseinstellungen zurücksetzen?",
|
"title": "Auf Werkseinstellungen zurücksetzen?",
|
||||||
"description": "Sollen alle Einstellungen und Daten auf Werkseinstellungen zurückgesetzt werden? Alle Änderungen und Fortschritte gehen verloren!"
|
"description": "Sollen alle Einstellungen und Daten auf Werkseinstellungen zurückgesetzt werden? Alle Änderungen und Fortschritte gehen verloren!"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -173,7 +173,6 @@
|
|||||||
"exerciseProgressResetSuccessful": "Aufgabenfortschritt erfolgreich zurück gesetzt!",
|
"exerciseProgressResetSuccessful": "Aufgabenfortschritt erfolgreich zurück gesetzt!",
|
||||||
"registerSuccessful": "Account erfolgreich erstellt!",
|
"registerSuccessful": "Account erfolgreich erstellt!",
|
||||||
"registerError": "Fehler beim Erstellen des Accounts",
|
"registerError": "Fehler beim Erstellen des Accounts",
|
||||||
"usernameInUse": "Der Accountname ist bereits vergeben!",
|
|
||||||
"accountUpdated": "Account erfolgreich aktualisiert",
|
"accountUpdated": "Account erfolgreich aktualisiert",
|
||||||
"logoutSuccessful": "Logout erfolgreich",
|
"logoutSuccessful": "Logout erfolgreich",
|
||||||
"orderPlaceSuccessfull": "Bestellung erfolgreich aufgegeben",
|
"orderPlaceSuccessfull": "Bestellung erfolgreich aufgegeben",
|
||||||
@@ -187,7 +186,11 @@
|
|||||||
"genreDeleteError": "Fehler beim Löschen des Genres",
|
"genreDeleteError": "Fehler beim Löschen des Genres",
|
||||||
"genreDeleteSuccessful": "Genre erfolgreich gelöscht",
|
"genreDeleteSuccessful": "Genre erfolgreich gelöscht",
|
||||||
"genreSavedError": "Fehler beim Speichern des Genres",
|
"genreSavedError": "Fehler beim Speichern des Genres",
|
||||||
"genreSavedSuccessful": "Genre erfolgreich gespeichert"
|
"genreSavedSuccessful": "Genre erfolgreich gespeichert",
|
||||||
|
"accountPasswordTooShort": "Passwort ist zu kurz",
|
||||||
|
"accountUsernameTooShort": "Username ist zu kurz",
|
||||||
|
"accountMailAddressUnvalid": "Mail-Adresse ungültig",
|
||||||
|
"usernameOrMailInUse": "Der Accountname und/oder die Mail-Adresse sind bereits vergeben!"
|
||||||
},
|
},
|
||||||
"misc": {
|
"misc": {
|
||||||
"404": {
|
"404": {
|
||||||
@@ -218,7 +221,7 @@
|
|||||||
"validation": {
|
"validation": {
|
||||||
"required": "Darf nicht leer bleiben",
|
"required": "Darf nicht leer bleiben",
|
||||||
"noDigitsAllowed": "Zahlen sind nicht erlaubt",
|
"noDigitsAllowed": "Zahlen sind nicht erlaubt",
|
||||||
"notEnoughChars": "Nicht wenige Zeichen",
|
"notEnoughChars": "Nicht genug Zeichen",
|
||||||
"tooMuchChars": "Zu viele Zeichen",
|
"tooMuchChars": "Zu viele Zeichen",
|
||||||
"onlyDigitsAllowed": "Nur Zahlen erlaubt",
|
"onlyDigitsAllowed": "Nur Zahlen erlaubt",
|
||||||
"digitsAtStartNeeded": "Muss mit einer Zahl beginnen"
|
"digitsAtStartNeeded": "Muss mit einer Zahl beginnen"
|
||||||
@@ -236,7 +239,8 @@
|
|||||||
"connectToServer": "Server",
|
"connectToServer": "Server",
|
||||||
"database": "Datenbank",
|
"database": "Datenbank",
|
||||||
"exercises": "Aufgaben",
|
"exercises": "Aufgaben",
|
||||||
"userData": "Persönliche Daten"
|
"userData": "Persönliche Daten",
|
||||||
|
"enterYourPersonalData": "Bitte gebe nun deinen Namen und deine Matrikelnummer von der Universität ein. Überprüfe die Angaben vor dem Absenden genau! Die Angaben können später nicht ohne Verlust des Bearbeitungsfortschrittes geändert werden!"
|
||||||
},
|
},
|
||||||
"user": "Angaben zur Person",
|
"user": "Angaben zur Person",
|
||||||
"registrationNumber": "Matrikelnummer",
|
"registrationNumber": "Matrikelnummer",
|
||||||
@@ -244,7 +248,14 @@
|
|||||||
"chooseFile": "Datei auswählen",
|
"chooseFile": "Datei auswählen",
|
||||||
"chooseDestinationFolder": "Zielordner auswählen",
|
"chooseDestinationFolder": "Zielordner auswählen",
|
||||||
"upload": "Hochladen",
|
"upload": "Hochladen",
|
||||||
"fulfillYourPersonalDataFirst": "Gehe zu den Einstellungen und fülle deinen Namen und deine Matrikelnummer aus"
|
"fulfillYourPersonalDataFirst": "Gehe zu den Einstellungen und fülle deinen Namen und deine Matrikelnummer aus",
|
||||||
|
"testEnvironment": "Testumgebung",
|
||||||
|
"search": {
|
||||||
|
"empty": {
|
||||||
|
"headline": "So leer hier..."
|
||||||
|
},
|
||||||
|
"searchterm": "Suchbegriff"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"genre": {
|
"genre": {
|
||||||
"withoutBand": "ohne Band"
|
"withoutBand": "ohne Band"
|
||||||
|
|||||||
@@ -173,7 +173,6 @@
|
|||||||
"exerciseProgressResetSuccessful": "Exercise progress successfully resetted!",
|
"exerciseProgressResetSuccessful": "Exercise progress successfully resetted!",
|
||||||
"registerSuccessful": "Account successfully created!",
|
"registerSuccessful": "Account successfully created!",
|
||||||
"registerError": "Error on register account",
|
"registerError": "Error on register account",
|
||||||
"usernameInUse": "The username is already in use!",
|
|
||||||
"accountUpdated": "Account successfully updated",
|
"accountUpdated": "Account successfully updated",
|
||||||
"logoutSuccessful": "Logout successfull",
|
"logoutSuccessful": "Logout successfull",
|
||||||
"orderPlaceSuccessfull": "Order successfully placed",
|
"orderPlaceSuccessfull": "Order successfully placed",
|
||||||
@@ -187,7 +186,11 @@
|
|||||||
"genreDeleteError": "Error on deleting Genre",
|
"genreDeleteError": "Error on deleting Genre",
|
||||||
"genreDeleteSuccessful": "Genre successfully deleted",
|
"genreDeleteSuccessful": "Genre successfully deleted",
|
||||||
"genreSavedError": "Error on saving genre",
|
"genreSavedError": "Error on saving genre",
|
||||||
"genreSavedSuccessful": "Genre successfully saved"
|
"genreSavedSuccessful": "Genre successfully saved",
|
||||||
|
"accountPasswordTooShort": "Password too short",
|
||||||
|
"accountUsernameTooShort": "Username too short",
|
||||||
|
"accountMailAddressUnvalid": "Mail-Address unvalid",
|
||||||
|
"usernameOrMailInUse": "The username and/or the mail address are already in use!"
|
||||||
},
|
},
|
||||||
"misc": {
|
"misc": {
|
||||||
"404": {
|
"404": {
|
||||||
@@ -236,7 +239,8 @@
|
|||||||
"connectToServer": "Server",
|
"connectToServer": "Server",
|
||||||
"database": "Database",
|
"database": "Database",
|
||||||
"exercises": "Exercises",
|
"exercises": "Exercises",
|
||||||
"userData": "User data"
|
"userData": "User data",
|
||||||
|
"enterYourPersonalData": "Please enter your name and your Matrikel number from your university. Check it twice! You can't change it later without loosing your exercise progress!"
|
||||||
},
|
},
|
||||||
"user": "About person",
|
"user": "About person",
|
||||||
"registrationNumber": "Matrikel number",
|
"registrationNumber": "Matrikel number",
|
||||||
@@ -244,7 +248,14 @@
|
|||||||
"chooseFile": "Choose file",
|
"chooseFile": "Choose file",
|
||||||
"chooseDestinationFolder": "Choose destination folder",
|
"chooseDestinationFolder": "Choose destination folder",
|
||||||
"upload": "Upload",
|
"upload": "Upload",
|
||||||
"fulfillYourPersonalDataFirst": "Go to settings and enter your name and the right register number"
|
"fulfillYourPersonalDataFirst": "Go to settings and enter your name and the right register number",
|
||||||
|
"testEnvironment": "Test Environment",
|
||||||
|
"search": {
|
||||||
|
"empty": {
|
||||||
|
"headline": "So empty here..."
|
||||||
|
},
|
||||||
|
"searchterm": "Search term"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"genre": {
|
"genre": {
|
||||||
"withoutBand": "without Band"
|
"withoutBand": "without Band"
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ const accountStore = useAccountStore()
|
|||||||
<v-col class="d-flex justify-center align-center">
|
<v-col class="d-flex justify-center align-center">
|
||||||
<outlined-button
|
<outlined-button
|
||||||
prepend-icon="mdi-delete"
|
prepend-icon="mdi-delete"
|
||||||
color="red"
|
color="error"
|
||||||
:loading="accountStore.fetchInProgress"
|
:loading="accountStore.fetchInProgress"
|
||||||
@click="showConfirmDialog = true"
|
@click="showConfirmDialog = true"
|
||||||
>
|
>
|
||||||
@@ -29,7 +29,7 @@ const accountStore = useAccountStore()
|
|||||||
<v-col class="d-flex justify-center align-center">
|
<v-col class="d-flex justify-center align-center">
|
||||||
<outlined-button
|
<outlined-button
|
||||||
prepend-icon="mdi-content-save"
|
prepend-icon="mdi-content-save"
|
||||||
color="green"
|
color="success"
|
||||||
:loading="accountStore.fetchInProgress"
|
:loading="accountStore.fetchInProgress"
|
||||||
@click="accountStore.updateAccount()"
|
@click="accountStore.updateAccount()"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ const accountStore = useAccountStore()
|
|||||||
<v-col class="d-flex justify-center align-center">
|
<v-col class="d-flex justify-center align-center">
|
||||||
<outlined-button
|
<outlined-button
|
||||||
@click="accountStore.removeAddress(address)"
|
@click="accountStore.removeAddress(address)"
|
||||||
color="red"
|
color="error"
|
||||||
prepend-icon="mdi-delete"
|
prepend-icon="mdi-delete"
|
||||||
>
|
>
|
||||||
{{ $t('misc.actions.remove') }}
|
{{ $t('misc.actions.remove') }}
|
||||||
@@ -102,7 +102,7 @@ const accountStore = useAccountStore()
|
|||||||
<outlined-button
|
<outlined-button
|
||||||
@click="accountStore.userAccount.addresses.push(new AddressModel())"
|
@click="accountStore.userAccount.addresses.push(new AddressModel())"
|
||||||
prepend-icon="mdi-plus"
|
prepend-icon="mdi-plus"
|
||||||
color="green"
|
color="success"
|
||||||
>
|
>
|
||||||
{{ $t('misc.actions.add') }}
|
{{ $t('misc.actions.add') }}
|
||||||
</outlined-button>
|
</outlined-button>
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ const accountStore = useAccountStore()
|
|||||||
<v-row>
|
<v-row>
|
||||||
<v-col>
|
<v-col>
|
||||||
<card-view-one-line
|
<card-view-one-line
|
||||||
color="amber"
|
color="warning"
|
||||||
prepend-icon="mdi-alert"
|
prepend-icon="mdi-alert"
|
||||||
:title="$t('account.noRealPaymentsNeeded')"
|
:title="$t('account.noRealPaymentsNeeded')"
|
||||||
/>
|
/>
|
||||||
@@ -60,7 +60,7 @@ const accountStore = useAccountStore()
|
|||||||
<v-col class="d-flex justify-center align-center">
|
<v-col class="d-flex justify-center align-center">
|
||||||
<outlined-button
|
<outlined-button
|
||||||
@click="accountStore.removePayment(payment)"
|
@click="accountStore.removePayment(payment)"
|
||||||
color="red"
|
color="error"
|
||||||
prepend-icon="mdi-delete"
|
prepend-icon="mdi-delete"
|
||||||
>
|
>
|
||||||
{{ $t('misc.actions.remove') }}
|
{{ $t('misc.actions.remove') }}
|
||||||
@@ -88,7 +88,7 @@ const accountStore = useAccountStore()
|
|||||||
<outlined-button
|
<outlined-button
|
||||||
@click="accountStore.userAccount.payments.push(new PaymentModel())"
|
@click="accountStore.userAccount.payments.push(new PaymentModel())"
|
||||||
prepend-icon="mdi-plus"
|
prepend-icon="mdi-plus"
|
||||||
color="green"
|
color="success"
|
||||||
>
|
>
|
||||||
{{ $t('misc.actions.add') }}
|
{{ $t('misc.actions.add') }}
|
||||||
</outlined-button>
|
</outlined-button>
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ async function startLogin() {
|
|||||||
v-model="accountStore.loginData.username"
|
v-model="accountStore.loginData.username"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
clearable
|
clearable
|
||||||
|
hide-details
|
||||||
@keyup.enter="startLogin"
|
@keyup.enter="startLogin"
|
||||||
id="txt-username"
|
id="txt-username"
|
||||||
/>
|
/>
|
||||||
@@ -48,6 +49,7 @@ async function startLogin() {
|
|||||||
variant="outlined"
|
variant="outlined"
|
||||||
v-model="accountStore.loginData.password"
|
v-model="accountStore.loginData.password"
|
||||||
clearable
|
clearable
|
||||||
|
hide-details
|
||||||
@keyup.enter="startLogin"
|
@keyup.enter="startLogin"
|
||||||
id="txt-password"
|
id="txt-password"
|
||||||
/>
|
/>
|
||||||
@@ -67,7 +69,7 @@ async function startLogin() {
|
|||||||
append-icon="mdi-arrow-right"
|
append-icon="mdi-arrow-right"
|
||||||
@click="startLogin"
|
@click="startLogin"
|
||||||
:loading="accountStore.fetchInProgress"
|
:loading="accountStore.fetchInProgress"
|
||||||
color="green"
|
color="success"
|
||||||
id="btn-login"
|
id="btn-login"
|
||||||
>
|
>
|
||||||
{{ $t('account.login.login') }}
|
{{ $t('account.login.login') }}
|
||||||
|
|||||||
@@ -3,11 +3,9 @@ import cardView from '@/components/basics/cardView.vue';
|
|||||||
import outlinedButton from '@/components/basics/outlinedButton.vue';
|
import outlinedButton from '@/components/basics/outlinedButton.vue';
|
||||||
import { useAccountStore } from '@/stores/account.store';
|
import { useAccountStore } from '@/stores/account.store';
|
||||||
import { getEmailRules, getPasswordRules, getStringRules } from '@/scripts/validationRules';
|
import { getEmailRules, getPasswordRules, getStringRules } from '@/scripts/validationRules';
|
||||||
import { useRouter } from 'vue-router';
|
|
||||||
|
|
||||||
const showRegisterCard = defineModel("showRegisterCard", { type: Boolean, default: false })
|
const showRegisterCard = defineModel("showRegisterCard", { type: Boolean, default: false })
|
||||||
const accountStore = useAccountStore()
|
const accountStore = useAccountStore()
|
||||||
const router = useRouter()
|
|
||||||
|
|
||||||
async function registerAccount() {
|
async function registerAccount() {
|
||||||
accountStore.registerAccount()
|
accountStore.registerAccount()
|
||||||
@@ -31,7 +29,6 @@ async function registerAccount() {
|
|||||||
prepend-icon="mdi-account"
|
prepend-icon="mdi-account"
|
||||||
v-model="accountStore.registerData.username"
|
v-model="accountStore.registerData.username"
|
||||||
clearable
|
clearable
|
||||||
hide-details
|
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
:rules="getStringRules()"
|
:rules="getStringRules()"
|
||||||
/>
|
/>
|
||||||
@@ -46,7 +43,6 @@ async function registerAccount() {
|
|||||||
type="password"
|
type="password"
|
||||||
v-model="accountStore.registerData.password"
|
v-model="accountStore.registerData.password"
|
||||||
clearable
|
clearable
|
||||||
hide-details
|
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
:rules="getPasswordRules()"
|
:rules="getPasswordRules()"
|
||||||
/>
|
/>
|
||||||
@@ -61,7 +57,6 @@ async function registerAccount() {
|
|||||||
v-model="accountStore.registerData.email"
|
v-model="accountStore.registerData.email"
|
||||||
:rules="getEmailRules()"
|
:rules="getEmailRules()"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
hide-details
|
|
||||||
clearable
|
clearable
|
||||||
/>
|
/>
|
||||||
</v-col>
|
</v-col>
|
||||||
@@ -80,6 +75,7 @@ async function registerAccount() {
|
|||||||
prepend-icon="mdi-account-plus"
|
prepend-icon="mdi-account-plus"
|
||||||
@click="registerAccount"
|
@click="registerAccount"
|
||||||
:loading="accountStore.fetchInProgress"
|
:loading="accountStore.fetchInProgress"
|
||||||
|
color="success"
|
||||||
>
|
>
|
||||||
{{ $t('account.register') }}
|
{{ $t('account.register') }}
|
||||||
</outlined-button>
|
</outlined-button>
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ function itemProps(item: GenreModel) {
|
|||||||
|
|
||||||
<template #actions>
|
<template #actions>
|
||||||
<outlined-button
|
<outlined-button
|
||||||
color="green"
|
color="success"
|
||||||
@click="bandStore.saveBand"
|
@click="bandStore.saveBand"
|
||||||
:loading="bandStore.fetchInProgress"
|
:loading="bandStore.fetchInProgress"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ const test = ref()
|
|||||||
<outlined-button
|
<outlined-button
|
||||||
@click="filesStore.uploadFile"
|
@click="filesStore.uploadFile"
|
||||||
prepend-icon="mdi-file-upload"
|
prepend-icon="mdi-file-upload"
|
||||||
color="green"
|
color="success"
|
||||||
:disabled="filesStore.fileUploadDir.length == 0 || filesStore.fileUpload == undefined"
|
:disabled="filesStore.fileUploadDir.length == 0 || filesStore.fileUpload == undefined"
|
||||||
:loading="filesStore.fetchInProgress"
|
:loading="filesStore.fetchInProgress"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ filesStore.getStaticFolders()
|
|||||||
:hide-add-button="true"
|
:hide-add-button="true"
|
||||||
>
|
>
|
||||||
<v-row >
|
<v-row >
|
||||||
|
<!-- Column folder -->
|
||||||
<v-col cols="2" class="border">
|
<v-col cols="2" class="border">
|
||||||
<v-list>
|
<v-list>
|
||||||
<v-list-item
|
<v-list-item
|
||||||
@@ -27,10 +28,13 @@ filesStore.getStaticFolders()
|
|||||||
:value="folder"
|
:value="folder"
|
||||||
:title="folder.name + '/'"
|
:title="folder.name + '/'"
|
||||||
@click="filesStore.selectedFolder = folder; filesStore.getStaticFiles()"
|
@click="filesStore.selectedFolder = folder; filesStore.getStaticFiles()"
|
||||||
|
prepend-icon="mdi-folder"
|
||||||
/>
|
/>
|
||||||
</v-list>
|
</v-list>
|
||||||
</v-col>
|
</v-col>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Column files in folder -->
|
||||||
<v-col cols="4" class="border">
|
<v-col cols="4" class="border">
|
||||||
<v-skeleton-loader
|
<v-skeleton-loader
|
||||||
:loading="filesStore.fetchInProgress"
|
:loading="filesStore.fetchInProgress"
|
||||||
@@ -41,36 +45,71 @@ filesStore.getStaticFolders()
|
|||||||
v-for="file of filesStore.staticFiles"
|
v-for="file of filesStore.staticFiles"
|
||||||
:title="file.name"
|
:title="file.name"
|
||||||
:value="file.name"
|
:value="file.name"
|
||||||
:subtitle="Math.round(file.size / 1024) + ' KB'"
|
|
||||||
@click="() => { filesStore.selectedFile = file }"
|
@click="() => { filesStore.selectedFile = file }"
|
||||||
/>
|
>
|
||||||
|
<template #prepend>
|
||||||
|
<v-icon
|
||||||
|
:icon="file.name.endsWith('js') ? 'mdi-file' : 'mdi-image'"
|
||||||
|
:color="file.copyright != undefined ? 'grey' : 'red'"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</v-list-item>
|
||||||
</v-list>
|
</v-list>
|
||||||
</v-skeleton-loader>
|
</v-skeleton-loader>
|
||||||
</v-col>
|
</v-col>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- File detail viewer -->
|
||||||
<v-col class="border">
|
<v-col class="border">
|
||||||
<v-row>
|
<v-row>
|
||||||
<v-col>
|
<v-col v-if="filesStore.selectedFile != undefined">
|
||||||
{{ filesStore.selectedFile.url }}
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
|
|
||||||
<v-row>
|
|
||||||
<v-col>
|
|
||||||
<v-textarea
|
<v-textarea
|
||||||
v-if="filesStore.selectedFile != undefined && filesStore.selectedFile.name.endsWith('html')"
|
v-if="filesStore.selectedFile != undefined && filesStore.selectedFile.name.endsWith('js')"
|
||||||
:model-value="filesStore.selectedFile.content"
|
:model-value="filesStore.selectedFile.content"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
label="Content"
|
label="Content"
|
||||||
height="300"
|
height="300"
|
||||||
rows="30"
|
rows="30"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<v-img
|
<v-img
|
||||||
v-else-if="filesStore.selectedFile != undefined"
|
v-else-if="filesStore.selectedFile != undefined"
|
||||||
:src="filesStore.selectedFile.url" max-height="400"
|
:src="filesStore.selectedFile.url" max-height="400"
|
||||||
/>
|
/>
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
|
|
||||||
|
<!-- File details -->
|
||||||
|
<v-row>
|
||||||
|
<v-col v-if="filesStore.selectedFile != undefined">
|
||||||
|
<v-list>
|
||||||
|
<v-list-item prepend-icon="mdi-server">
|
||||||
|
{{ filesStore.selectedFile.url }}
|
||||||
|
</v-list-item>
|
||||||
|
|
||||||
|
<v-list-item prepend-icon="mdi-package">
|
||||||
|
{{ Math.round(filesStore.selectedFile.size / 1024) + ' KB' }}
|
||||||
|
</v-list-item>
|
||||||
|
|
||||||
|
<template v-if="filesStore.selectedFile['copyright'] != undefined">
|
||||||
|
<v-list-item prepend-icon="mdi-copyright">
|
||||||
|
{{ filesStore.selectedFile.copyright.license }}
|
||||||
|
</v-list-item>
|
||||||
|
|
||||||
|
<v-list-item prepend-icon="mdi-account">
|
||||||
|
{{ filesStore.selectedFile.copyright.creator }}
|
||||||
|
</v-list-item>
|
||||||
|
|
||||||
|
<v-list-item
|
||||||
|
prepend-icon="mdi-web"
|
||||||
|
v-if="filesStore.selectedFile.copyright.url.length > 0"
|
||||||
|
>
|
||||||
|
<a :href="filesStore.selectedFile.copyright.url" target="_blank" >Quelle</a>
|
||||||
|
</v-list-item>
|
||||||
|
</template>
|
||||||
|
</v-list>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
</admin-data-layout>
|
</admin-data-layout>
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ const genreStore = useGenreStore()
|
|||||||
|
|
||||||
<template #actions>
|
<template #actions>
|
||||||
<outlined-button
|
<outlined-button
|
||||||
color="green"
|
color="success"
|
||||||
@click="genreStore.saveGenre"
|
@click="genreStore.saveGenre"
|
||||||
:disabled="!valid"
|
:disabled="!valid"
|
||||||
:loading="genreStore.fetchInProgress"
|
:loading="genreStore.fetchInProgress"
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ const headers = [
|
|||||||
{ title: "Adresse", value: "street" },
|
{ title: "Adresse", value: "street" },
|
||||||
{ title: "Stadt", value: "city" },
|
{ title: "Stadt", value: "city" },
|
||||||
{ title: "Versendet", value: "shipped" },
|
{ title: "Versendet", value: "shipped" },
|
||||||
{ title: "", value: "edit", width: 130 }
|
{ title: "Aktionen", value: "edit", width: 130 }
|
||||||
]
|
]
|
||||||
|
|
||||||
orderStore.getAllOrders()
|
orderStore.getAllOrders()
|
||||||
@@ -26,6 +26,8 @@ orderStore.getAllOrders()
|
|||||||
<v-data-table
|
<v-data-table
|
||||||
:headers="headers"
|
:headers="headers"
|
||||||
:items="orderStore.orders"
|
:items="orderStore.orders"
|
||||||
|
:loading="orderStore.fetchInProgress"
|
||||||
|
:items-per-page="100"
|
||||||
>
|
>
|
||||||
<template #item.account="{ item }">
|
<template #item.account="{ item }">
|
||||||
{{ item.account.firstName }} {{ item.account.lastName }}
|
{{ item.account.firstName }} {{ item.account.lastName }}
|
||||||
@@ -46,23 +48,23 @@ orderStore.getAllOrders()
|
|||||||
<template #item.shipped="{ item }">
|
<template #item.shipped="{ item }">
|
||||||
<v-icon
|
<v-icon
|
||||||
:icon="item.shipped ? 'mdi-check' : 'mdi-close'"
|
:icon="item.shipped ? 'mdi-check' : 'mdi-close'"
|
||||||
:color="item.shipped ? 'green' : 'red'"
|
:color="item.shipped ? 'success' : 'error'"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #item.edit="{ item }">
|
<template #item.edit="{ item }">
|
||||||
<!-- todo <v-btn
|
<v-btn
|
||||||
icon="mdi-eye"
|
icon="mdi-eye"
|
||||||
variant="plain"
|
variant="plain"
|
||||||
@click="orderStore.openDetails(item)"
|
@click="orderStore.openDetails(item)"
|
||||||
/> -->
|
/>
|
||||||
|
|
||||||
<!-- todo <v-btn
|
<v-btn
|
||||||
icon="mdi-delete"
|
:icon="item.shipped ? 'mdi-close-circle-outline' : 'mdi-check-circle-outline'"
|
||||||
variant="plain"
|
variant="plain"
|
||||||
color="red"
|
:color="item.shipped ? 'error' : 'success'"
|
||||||
@click="orderStore.deleteOrder(item)"
|
@click="orderStore.changeOrderShippedState(item, !item.shipped)"
|
||||||
/> -->
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
</v-data-table>
|
</v-data-table>
|
||||||
|
|||||||
@@ -12,15 +12,67 @@ const orderStore = useOrderStore()
|
|||||||
v-model="orderStore.showDetailDialog"
|
v-model="orderStore.showDetailDialog"
|
||||||
:title="$t('order.order')"
|
:title="$t('order.order')"
|
||||||
icon="mdi-basket"
|
icon="mdi-basket"
|
||||||
|
max-width="800"
|
||||||
>
|
>
|
||||||
<v-list>
|
<v-list>
|
||||||
<v-list-subheader>
|
<v-list-subheader>
|
||||||
{{ $t('ticket.ticket', 2) }}
|
{{ $t('account.account') }}
|
||||||
</v-list-subheader>
|
</v-list-subheader>
|
||||||
|
|
||||||
<v-list-item v-for="ticket of orderStore.order.tickets">
|
<v-list-item prepend-icon="mdi-account">
|
||||||
{{ moment(ticket.concert.date).format("DD.MM.YYYY") }} -
|
{{ orderStore.order.account.username }}
|
||||||
{{ ticket.concert.band.name }} - {{ ticket.concert.name }}
|
</v-list-item>
|
||||||
|
|
||||||
|
<v-list-item prepend-icon="mdi-card-account-details">
|
||||||
|
{{ orderStore.order.account.firstName }} {{ orderStore.order.account.lastName }}
|
||||||
|
</v-list-item>
|
||||||
|
|
||||||
|
<v-list-item prepend-icon="mdi-home">
|
||||||
|
{{ orderStore.order.address.street }} {{ orderStore.order.address.houseNumber }}
|
||||||
|
</v-list-item>
|
||||||
|
|
||||||
|
<v-list-item prepend-icon="mdi-city">
|
||||||
|
{{ orderStore.order.address.postalCode }} {{ orderStore.order.address.city }}
|
||||||
|
</v-list-item>
|
||||||
|
|
||||||
|
<v-list-subheader>
|
||||||
|
{{ $t('order.order') }}
|
||||||
|
</v-list-subheader>
|
||||||
|
|
||||||
|
<v-list-item prepend-icon="mdi-calendar">
|
||||||
|
{{ moment(orderStore.order.orderedAt).format("DD.MM.YYYY, HH:mm:ss") }}
|
||||||
|
</v-list-item>
|
||||||
|
|
||||||
|
<v-list-item prepend-icon="mdi-truck">
|
||||||
|
{{ orderStore.order.shipped ? 'Versendet' : 'Nicht versendet' }}
|
||||||
|
</v-list-item>
|
||||||
|
|
||||||
|
<v-list-item>
|
||||||
|
<v-table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>{{ $t('concert.date') }}</th>
|
||||||
|
<th>{{ $t('concert.name') }}</th>
|
||||||
|
<th>{{ $t('band.name') }}</th>
|
||||||
|
<th>{{ $t('location.name') }}</th>
|
||||||
|
<th>{{ $t('location.seat.seatGroup') }}</th>
|
||||||
|
<th>{{ $t('location.seat.seatRow') }}</th>
|
||||||
|
<th>{{ $t('location.seat.seat') }}</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="ticket of orderStore.order.tickets">
|
||||||
|
<td>{{ moment(ticket.concert.date).format("DD.MM.YYYY") }}</td>
|
||||||
|
<td>{{ ticket.concert.name }}</td>
|
||||||
|
<td>{{ ticket.concert.band.name }}</td>
|
||||||
|
<td>{{ ticket.concert.location.name }}</td>
|
||||||
|
<td>{{ ticket.seat.seatRow.seatGroup.name }}</td>
|
||||||
|
<td>{{ ticket.seat.seatRow.row }}</td>
|
||||||
|
<td>{{ ticket.seat.seatNr }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</v-table>
|
||||||
</v-list-item>
|
</v-list-item>
|
||||||
</v-list>
|
</v-list>
|
||||||
</action-dialog>
|
</action-dialog>
|
||||||
|
|||||||
@@ -46,8 +46,10 @@ defineProps({
|
|||||||
</v-col>
|
</v-col>
|
||||||
|
|
||||||
<v-col>
|
<v-col>
|
||||||
<v-list>
|
<v-list style="background-color: transparent;">
|
||||||
<v-list-item v-for="ratingValue in ratings">
|
<v-list-item
|
||||||
|
v-for="ratingValue in ratings"
|
||||||
|
>
|
||||||
<template v-slot:prepend>
|
<template v-slot:prepend>
|
||||||
<span>{{ ratingValue.value }}</span>
|
<span>{{ ratingValue.value }}</span>
|
||||||
<v-icon class="ml-3 mr-n3" icon="mdi-star" />
|
<v-icon class="ml-3 mr-n3" icon="mdi-star" />
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ watch(() => router.currentRoute.value.params.date, () => {
|
|||||||
|
|
||||||
<v-row>
|
<v-row>
|
||||||
<v-col>
|
<v-col>
|
||||||
<v-list >
|
<v-list style="background-color: transparent;">
|
||||||
<v-list-item v-for="seat in basketStore.selectedSeats" >
|
<v-list-item v-for="seat in basketStore.selectedSeats" >
|
||||||
<ticket-list-item
|
<ticket-list-item
|
||||||
:concert="concertStore.concert"
|
:concert="concertStore.concert"
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ const showOrderingDialog = ref()
|
|||||||
<v-col>
|
<v-col>
|
||||||
<v-alert
|
<v-alert
|
||||||
color="info"
|
color="info"
|
||||||
closable
|
|
||||||
>
|
>
|
||||||
{{ $t('account.login.pleaseLoginToOrder') }}
|
{{ $t('account.login.pleaseLoginToOrder') }}
|
||||||
</v-alert>
|
</v-alert>
|
||||||
@@ -53,7 +52,7 @@ const showOrderingDialog = ref()
|
|||||||
prepend-icon="mdi-basket-check"
|
prepend-icon="mdi-basket-check"
|
||||||
:disabled="basketStore.itemsInBasket.length == 0 || accountStore.userAccount.id == null"
|
:disabled="basketStore.itemsInBasket.length == 0 || accountStore.userAccount.id == null"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
color="green"
|
color="success"
|
||||||
@click="showOrderingDialog = true"
|
@click="showOrderingDialog = true"
|
||||||
>
|
>
|
||||||
{{ $t('order.takeOrder') }}
|
{{ $t('order.takeOrder') }}
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ function paymentItemProps(item: PaymentModel) {
|
|||||||
@click="doOrder"
|
@click="doOrder"
|
||||||
:loading="orderingInProgress"
|
:loading="orderingInProgress"
|
||||||
prepend-icon="mdi-send"
|
prepend-icon="mdi-send"
|
||||||
color="green"
|
color="success"
|
||||||
>
|
>
|
||||||
{{ $t('order.takeOrder') }}
|
{{ $t('order.takeOrder') }}
|
||||||
</outlined-button>
|
</outlined-button>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
import { useBasketStore } from '@/stores/basket.store';
|
import { useBasketStore } from '@/stores/basket.store';
|
||||||
import { BasketItemModel } from '@/data/models/ordering/basketItemModel';
|
import { BasketItemModel } from '@/data/models/ordering/basketItemModel';
|
||||||
import { calcPrice } from '@/scripts/concertScripts';
|
import { calcPrice } from '@/scripts/concertScripts';
|
||||||
|
import moment from 'moment';
|
||||||
|
|
||||||
const basketStore = useBasketStore()
|
const basketStore = useBasketStore()
|
||||||
|
|
||||||
@@ -11,9 +12,10 @@ function removeFromBasket(basketItem: BasketItemModel) {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<v-table>
|
<v-table style="background-color: transparent;">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
|
<th>{{ $t('concert.date') }}</th>
|
||||||
<th>{{ $t('band.band') }}</th>
|
<th>{{ $t('band.band') }}</th>
|
||||||
<th>{{ $t('concert.concert') }}</th>
|
<th>{{ $t('concert.concert') }}</th>
|
||||||
<th class="text-center">{{ $t('misc.quantity') }}</th>
|
<th class="text-center">{{ $t('misc.quantity') }}</th>
|
||||||
@@ -25,6 +27,11 @@ function removeFromBasket(basketItem: BasketItemModel) {
|
|||||||
|
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="basketItem in basketStore.itemsInBasket">
|
<tr v-for="basketItem in basketStore.itemsInBasket">
|
||||||
|
<!-- Concert date -->
|
||||||
|
<td>
|
||||||
|
{{ moment(basketItem.concert.date).format("DD.MM.YYYY") }}
|
||||||
|
</td>
|
||||||
|
|
||||||
<!-- Band name -->
|
<!-- Band name -->
|
||||||
<td>
|
<td>
|
||||||
{{ basketItem.band.name }}
|
{{ basketItem.band.name }}
|
||||||
@@ -43,7 +50,7 @@ function removeFromBasket(basketItem: BasketItemModel) {
|
|||||||
<!-- Price per event -->
|
<!-- Price per event -->
|
||||||
<td class="text-right">
|
<td class="text-right">
|
||||||
<div v-if="basketItem.seats">
|
<div v-if="basketItem.seats">
|
||||||
{{ basketItem.price }} €
|
{{ basketItem.price.toFixed(2) }} €
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
@@ -56,7 +63,7 @@ function removeFromBasket(basketItem: BasketItemModel) {
|
|||||||
<v-btn
|
<v-btn
|
||||||
icon="mdi-delete"
|
icon="mdi-delete"
|
||||||
@click="removeFromBasket(basketItem)"
|
@click="removeFromBasket(basketItem)"
|
||||||
color="red"
|
color="error"
|
||||||
variant="text"
|
variant="text"
|
||||||
flat
|
flat
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -19,6 +19,25 @@ function getDotColor(exerciseGroupNr: number) {
|
|||||||
case 3: return "pink"
|
case 3: return "pink"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function checksum(num: number) {
|
||||||
|
let cs = 0
|
||||||
|
|
||||||
|
for (; num > 0; num = Math.trunc(num / 10)) {
|
||||||
|
cs += num % 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cs
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateExerciseKey(exerciseGroup: number, exerciseNr: number) {
|
||||||
|
try {
|
||||||
|
let matrikelNr = Number(preferencesStore.registrationNumber)
|
||||||
|
let a = matrikelNr + exerciseGroup * 100 + exerciseNr * 12345678 +
|
||||||
|
checksum(Number(preferencesStore.registrationNumber)) * 123
|
||||||
|
return a.toString(16).toUpperCase()
|
||||||
|
} catch(e) {}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -97,6 +116,9 @@ function getDotColor(exerciseGroupNr: number) {
|
|||||||
:color="exercise.solved ? 'green' : 'primary'"
|
:color="exercise.solved ? 'green' : 'primary'"
|
||||||
>
|
>
|
||||||
{{ preferencesStore.language == LanguageEnum.GERMAN ? exercise.descriptionDe : exercise.descriptionEn }}
|
{{ preferencesStore.language == LanguageEnum.GERMAN ? exercise.descriptionDe : exercise.descriptionEn }}
|
||||||
|
<div class="pt-2 text-h6">
|
||||||
|
Solution Code: 0x{{ generateExerciseKey(exercise.exerciseGroup.groupNr, exercise.exerciseNr) }}
|
||||||
|
</div>
|
||||||
</card-view>
|
</card-view>
|
||||||
</v-timeline-item>
|
</v-timeline-item>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ watch(() => currentStep.value, () => {
|
|||||||
:title="step"
|
:title="step"
|
||||||
:value="n + 1"
|
:value="n + 1"
|
||||||
complete-icon="mdi-check"
|
complete-icon="mdi-check"
|
||||||
color="green"
|
color="success"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<v-divider v-if="n < steps.length - 1" />
|
<v-divider v-if="n < steps.length - 1" />
|
||||||
@@ -73,6 +73,7 @@ watch(() => currentStep.value, () => {
|
|||||||
|
|
||||||
<!-- Content -->
|
<!-- Content -->
|
||||||
<v-stepper-window>
|
<v-stepper-window>
|
||||||
|
<!-- Step 1: Check connection to backend server -->
|
||||||
<v-stepper-window-item
|
<v-stepper-window-item
|
||||||
:value="1"
|
:value="1"
|
||||||
class="text-h4 text-center"
|
class="text-h4 text-center"
|
||||||
@@ -84,6 +85,7 @@ watch(() => currentStep.value, () => {
|
|||||||
<server-state-text />
|
<server-state-text />
|
||||||
</v-stepper-window-item>
|
</v-stepper-window-item>
|
||||||
|
|
||||||
|
<!-- Step 2: Reset the database -->
|
||||||
<v-stepper-window-item
|
<v-stepper-window-item
|
||||||
:value="2"
|
:value="2"
|
||||||
>
|
>
|
||||||
@@ -100,7 +102,7 @@ watch(() => currentStep.value, () => {
|
|||||||
</div>
|
</div>
|
||||||
</v-stepper-window-item>
|
</v-stepper-window-item>
|
||||||
|
|
||||||
|
<!-- Step 3: Create exercises -->
|
||||||
<v-stepper-window-item
|
<v-stepper-window-item
|
||||||
:value="3"
|
:value="3"
|
||||||
>
|
>
|
||||||
@@ -117,11 +119,18 @@ watch(() => currentStep.value, () => {
|
|||||||
</div>
|
</div>
|
||||||
</v-stepper-window-item>
|
</v-stepper-window-item>
|
||||||
|
|
||||||
|
<!-- Step 4: Personal data -->
|
||||||
<v-stepper-window-item
|
<v-stepper-window-item
|
||||||
:value="4"
|
:value="4"
|
||||||
>
|
>
|
||||||
<v-container class="px-0 py-2">
|
<v-container class="px-0 py-2">
|
||||||
|
<v-row>
|
||||||
|
<v-col>
|
||||||
|
<v-alert color="warning" icon="mdi-alert">
|
||||||
|
{{ $t('misc.firstStartup.enterYourPersonalData') }}
|
||||||
|
</v-alert>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
<v-row>
|
<v-row>
|
||||||
<v-col>
|
<v-col>
|
||||||
<v-text-field
|
<v-text-field
|
||||||
@@ -171,7 +180,7 @@ watch(() => currentStep.value, () => {
|
|||||||
:disabled="preferencesStore.studentName.length == 0 ||
|
:disabled="preferencesStore.studentName.length == 0 ||
|
||||||
preferencesStore.registrationNumber.length == 0"
|
preferencesStore.registrationNumber.length == 0"
|
||||||
prepend-icon="mdi-check"
|
prepend-icon="mdi-check"
|
||||||
color="green"
|
color="success"
|
||||||
>
|
>
|
||||||
{{ $t('misc.firstStartup.complete') }}
|
{{ $t('misc.firstStartup.complete') }}
|
||||||
</outlined-button>
|
</outlined-button>
|
||||||
|
|||||||
12
src/pages/misc/imageLicensePage/index.vue
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { useFilesStore } from '@/stores/files.store';
|
||||||
|
|
||||||
|
|
||||||
|
const filesStore = useFilesStore()
|
||||||
|
|
||||||
|
filesStore.getStaticFiles()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<v-img v-for="image of filesStore.staticFiles" :src="image.url"/>
|
||||||
|
</template>
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
import pageSetup from './pageSetupSection.vue';
|
import pageSetup from './pageSetupSection.vue';
|
||||||
import systemSetup from './systemSetupSection.vue';
|
import systemSetup from './systemSetupSection.vue';
|
||||||
import aboutSection from './aboutSection.vue';
|
import aboutSection from './aboutSection.vue';
|
||||||
import userSection from './userSection.vue';
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -13,12 +12,6 @@ import userSection from './userSection.vue';
|
|||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
|
|
||||||
<v-row>
|
|
||||||
<v-col>
|
|
||||||
<user-section />
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
|
|
||||||
<v-row>
|
<v-row>
|
||||||
<v-col>
|
<v-col>
|
||||||
<system-setup />
|
<system-setup />
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ preferenceStore.getServerState()
|
|||||||
<outlined-button
|
<outlined-button
|
||||||
@click="preferenceStore.showDeleteDbDialog = true"
|
@click="preferenceStore.showDeleteDbDialog = true"
|
||||||
prepend-icon="mdi-database-refresh"
|
prepend-icon="mdi-database-refresh"
|
||||||
color="red"
|
color="warning"
|
||||||
:disabled="preferenceStore.serverState != ServerStateEnum.ONLINE || preferenceStore.fetchInProgress"
|
:disabled="preferenceStore.serverState != ServerStateEnum.ONLINE || preferenceStore.fetchInProgress"
|
||||||
>
|
>
|
||||||
{{ $t('preferences.resetDatabase.resetDatabase') }}
|
{{ $t('preferences.resetDatabase.resetDatabase') }}
|
||||||
@@ -39,7 +39,7 @@ preferenceStore.getServerState()
|
|||||||
<outlined-button
|
<outlined-button
|
||||||
@click="preferenceStore.showDeleteExerciseDialog = true"
|
@click="preferenceStore.showDeleteExerciseDialog = true"
|
||||||
prepend-icon="mdi-progress-close"
|
prepend-icon="mdi-progress-close"
|
||||||
color="red"
|
color="warning"
|
||||||
:disabled="preferenceStore.serverState != ServerStateEnum.ONLINE || preferenceStore.fetchInProgress"
|
:disabled="preferenceStore.serverState != ServerStateEnum.ONLINE || preferenceStore.fetchInProgress"
|
||||||
>
|
>
|
||||||
{{ $t('preferences.resetExerciseProgress.resetExerciseProgress') }}
|
{{ $t('preferences.resetExerciseProgress.resetExerciseProgress') }}
|
||||||
@@ -50,7 +50,7 @@ preferenceStore.getServerState()
|
|||||||
<outlined-button
|
<outlined-button
|
||||||
@click="preferenceStore.showFactoryResetDialog = true"
|
@click="preferenceStore.showFactoryResetDialog = true"
|
||||||
prepend-icon="mdi-factory"
|
prepend-icon="mdi-factory"
|
||||||
color="red"
|
color="warning"
|
||||||
:disabled="preferenceStore.serverState != ServerStateEnum.ONLINE || preferenceStore.fetchInProgress"
|
:disabled="preferenceStore.serverState != ServerStateEnum.ONLINE || preferenceStore.fetchInProgress"
|
||||||
>
|
>
|
||||||
{{ $t('preferences.factoryReset.factoryReset') }}
|
{{ $t('preferences.factoryReset.factoryReset') }}
|
||||||
@@ -67,6 +67,7 @@ preferenceStore.getServerState()
|
|||||||
v-model="preferenceStore.showDeleteDbDialog"
|
v-model="preferenceStore.showDeleteDbDialog"
|
||||||
:onConfirm="preferenceStore.resetDb"
|
:onConfirm="preferenceStore.resetDb"
|
||||||
:loading="preferenceStore.fetchInProgress"
|
:loading="preferenceStore.fetchInProgress"
|
||||||
|
icon="mdi-database-refresh"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- Confirm delete exercise progress -->
|
<!-- Confirm delete exercise progress -->
|
||||||
@@ -76,12 +77,14 @@ preferenceStore.getServerState()
|
|||||||
v-model="preferenceStore.showDeleteExerciseDialog"
|
v-model="preferenceStore.showDeleteExerciseDialog"
|
||||||
:onConfirm="preferenceStore.resetExerciseProg"
|
:onConfirm="preferenceStore.resetExerciseProg"
|
||||||
:loading="preferenceStore.fetchInProgress"
|
:loading="preferenceStore.fetchInProgress"
|
||||||
|
icon="mdi-progress-close"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<confirm-dialog
|
<confirm-dialog
|
||||||
:title="$t('preferences.factoryReset.dialog.title')"
|
:title="$t('preferences.factoryReset.dialog.title')"
|
||||||
:description="$t('preferences.factoryReset.dialog.description')"
|
:description="$t('preferences.factoryReset.dialog.description')"
|
||||||
v-model="preferenceStore.showFactoryResetDialog"
|
v-model="preferenceStore.showFactoryResetDialog"
|
||||||
|
icon="mdi-factory"
|
||||||
:onConfirm="() => {
|
:onConfirm="() => {
|
||||||
preferenceStore.resetToFactorySettings()
|
preferenceStore.resetToFactorySettings()
|
||||||
router.push('/')
|
router.push('/')
|
||||||
|
|||||||
@@ -1,32 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import cardView from '@/components/basics/cardView.vue';
|
|
||||||
import { usePreferencesStore } from '@/stores/preferences.store';
|
|
||||||
|
|
||||||
const preferencesStore = usePreferencesStore()
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<card-view icon="mdi-account-school" :title="$t('misc.user')">
|
|
||||||
<v-row>
|
|
||||||
<v-col>
|
|
||||||
<v-text-field
|
|
||||||
variant="outlined"
|
|
||||||
hide-details
|
|
||||||
:label="$t('misc.yourFullName')"
|
|
||||||
v-model="preferencesStore.studentName"
|
|
||||||
/>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
|
|
||||||
<v-row>
|
|
||||||
<v-col>
|
|
||||||
<v-text-field
|
|
||||||
variant="outlined"
|
|
||||||
hide-details
|
|
||||||
:label="$t('misc.registrationNumber')"
|
|
||||||
v-model="preferencesStore.registrationNumber"
|
|
||||||
/>
|
|
||||||
</v-col>
|
|
||||||
</v-row>
|
|
||||||
</card-view>
|
|
||||||
</template>
|
|
||||||
@@ -23,7 +23,17 @@ const searchStore = useSearchStore()
|
|||||||
</v-col>
|
</v-col>
|
||||||
</v-row>
|
</v-row>
|
||||||
|
|
||||||
<div v-if="searchStore.alreadySearched">
|
<v-row v-if="!searchStore.alreadySearched">
|
||||||
|
<v-col>
|
||||||
|
<v-empty-state
|
||||||
|
icon="mdi-magnify"
|
||||||
|
:headline="$t('misc.search.empty.headline')"
|
||||||
|
:title="$t('misc.enterSomeKeywords')"
|
||||||
|
/>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
|
||||||
|
<div v-else>
|
||||||
<v-row>
|
<v-row>
|
||||||
<v-col>
|
<v-col>
|
||||||
<section-divider :title="$t('band.band', 2)" />
|
<section-divider :title="$t('band.band', 2)" />
|
||||||
@@ -41,7 +51,8 @@ const searchStore = useSearchStore()
|
|||||||
|
|
||||||
<v-row
|
<v-row
|
||||||
v-else-if="searchStore.bands.length > 0"
|
v-else-if="searchStore.bands.length > 0"
|
||||||
v-for="band in searchStore.bands">
|
v-for="band in searchStore.bands"
|
||||||
|
>
|
||||||
<v-col>
|
<v-col>
|
||||||
<band-list-item
|
<band-list-item
|
||||||
:band="band"
|
:band="band"
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ const searchStore = useSearchStore()
|
|||||||
variant="outlined"
|
variant="outlined"
|
||||||
hide-details
|
hide-details
|
||||||
v-model="searchStore.searchTerm"
|
v-model="searchStore.searchTerm"
|
||||||
:placeholder="$t('misc.enterSomeKeywords')"
|
:label="$t('misc.search.searchterm')"
|
||||||
@keyup.enter="searchStore.startSearch"
|
@keyup.enter="searchStore.startSearch"
|
||||||
>
|
>
|
||||||
<template #append-inner>
|
<template #append-inner>
|
||||||
|
|||||||
@@ -18,7 +18,11 @@ const vuetify = createVuetify({
|
|||||||
colors: {
|
colors: {
|
||||||
primary: colors.blue.darken4,
|
primary: colors.blue.darken4,
|
||||||
secondary: colors.yellow.darken3,
|
secondary: colors.yellow.darken3,
|
||||||
sheet: colors.grey.darken4
|
sheet: colors.grey.darken4,
|
||||||
|
warning: colors.amber.darken3,
|
||||||
|
success: colors.green.darken2,
|
||||||
|
error: colors.red.darken2,
|
||||||
|
exerciseSolved: colors.purple.darken2
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
light: {
|
light: {
|
||||||
@@ -26,7 +30,11 @@ const vuetify = createVuetify({
|
|||||||
colors: {
|
colors: {
|
||||||
primary: colors.blue.darken4,
|
primary: colors.blue.darken4,
|
||||||
secondary: colors.yellow.darken3,
|
secondary: colors.yellow.darken3,
|
||||||
sheet: colors.grey.lighten3
|
sheet: colors.grey.lighten4,
|
||||||
|
warning: colors.amber.darken4,
|
||||||
|
success: colors.green.darken2,
|
||||||
|
error: colors.red.darken4,
|
||||||
|
exerciseSolved: colors.purple.darken2
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import LoginPage from "@/pages/account/loginPage/index.vue"
|
|||||||
import PreferencesPage from "@/pages/misc/preferencesPage/index.vue";
|
import PreferencesPage from "@/pages/misc/preferencesPage/index.vue";
|
||||||
import HelpPage from "@/pages/misc/helpPage/index.vue"
|
import HelpPage from "@/pages/misc/helpPage/index.vue"
|
||||||
import ErrorPage from "@/pages/misc/errorPage/index.vue"
|
import ErrorPage from "@/pages/misc/errorPage/index.vue"
|
||||||
|
import ImageLicensePage from "@/pages/misc/imageLicensePage/index.vue"
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
// Main page
|
// Main page
|
||||||
@@ -52,6 +53,7 @@ const routes = [
|
|||||||
{ path: '/basket', component: BasketPage },
|
{ path: '/basket', component: BasketPage },
|
||||||
{ path: '/preferences', component: PreferencesPage },
|
{ path: '/preferences', component: PreferencesPage },
|
||||||
{ path: '/help', component: HelpPage },
|
{ path: '/help', component: HelpPage },
|
||||||
|
{ path: '/license', component: ImageLicensePage },
|
||||||
|
|
||||||
// Error Page
|
// Error Page
|
||||||
{ path: "/404", component: ErrorPage }
|
{ path: "/404", component: ErrorPage }
|
||||||
|
|||||||
@@ -53,9 +53,12 @@ export function createDateRangeString(concerts: Array<ConcertModel>) {
|
|||||||
export function lowestTicketPrice(concerts: Array<ConcertModel>): string {
|
export function lowestTicketPrice(concerts: Array<ConcertModel>): string {
|
||||||
const priceArray : Array<number> = []
|
const priceArray : Array<number> = []
|
||||||
|
|
||||||
for (let concert of concerts) {
|
try {
|
||||||
priceArray.push(concert.price)
|
for (let concert of concerts) {
|
||||||
}
|
priceArray.push(concert.price)
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
|
||||||
|
|
||||||
priceArray.sort()
|
priceArray.sort()
|
||||||
|
|
||||||
|
|||||||
14
src/scripts/imageScripts.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { load } from "exifreader"
|
||||||
|
|
||||||
|
export async function loadLicense(url: string){
|
||||||
|
let result = ""
|
||||||
|
|
||||||
|
try {
|
||||||
|
await load(url)
|
||||||
|
.then(tags => {
|
||||||
|
result = tags["Copyright"]["description"] + " by " + tags["Artist"]["description"]
|
||||||
|
})
|
||||||
|
} catch {}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useLocalStorage } from "@vueuse/core";
|
import { useLocalStorage } from "@vueuse/core";
|
||||||
import { AccountModel } from "../data/models/user/accountModel";
|
import { AccountModel } from "../data/models/user/accountModel";
|
||||||
import { useFeedbackStore } from "./feedback.store";
|
import { useFeedbackStore } from "./feedback.store";
|
||||||
import { deleteAccount, fetchAllAccounts, getAccount, login, registerAccount, updateAccount } from "../data/api/accountApi";
|
import { deleteAccount, fetchAllAccounts, getAccount, getLogin, registerAccount, updateAccount } from "../data/api/accountApi";
|
||||||
import { fetchUserOrders } from "../data/api/orderApi";
|
import { fetchUserOrders } from "../data/api/orderApi";
|
||||||
import { BannerStateEnum } from "../data/enums/bannerStateEnum";
|
import { BannerStateEnum } from "../data/enums/bannerStateEnum";
|
||||||
import { AddressModel } from "../data/models/user/addressModel";
|
import { AddressModel } from "../data/models/user/addressModel";
|
||||||
@@ -16,9 +16,10 @@ export const useAccountStore = defineStore("accountStore", {
|
|||||||
/** All accounts */
|
/** All accounts */
|
||||||
accounts: ref<Array<AccountApiModel>>([]),
|
accounts: ref<Array<AccountApiModel>>([]),
|
||||||
|
|
||||||
/** Useraccount which is currently logged in */
|
/** Server token of currently logged in account */
|
||||||
userAccountToken: useLocalStorage("hackmycart/accountStore/userAccountToken", ""),
|
userAccountToken: useLocalStorage("hackmycart/accountStore/userAccountToken", ""),
|
||||||
|
|
||||||
|
/** Useraccount which is currently logged in */
|
||||||
userAccount: useLocalStorage("hackmycart/accountStore/userAccount", new AccountApiModel()),
|
userAccount: useLocalStorage("hackmycart/accountStore/userAccount", new AccountApiModel()),
|
||||||
|
|
||||||
/** User input on login screen */
|
/** User input on login screen */
|
||||||
@@ -32,20 +33,28 @@ export const useAccountStore = defineStore("accountStore", {
|
|||||||
/** Request to server sent, waiting for data response */
|
/** Request to server sent, waiting for data response */
|
||||||
fetchInProgress: ref(false),
|
fetchInProgress: ref(false),
|
||||||
|
|
||||||
|
/** Flag to show or hide admin icon in navigation bar */
|
||||||
adminPanelVisible: ref(false),
|
adminPanelVisible: ref(false),
|
||||||
|
|
||||||
|
/** Flag to activate buy option on basket page */
|
||||||
privilegeBuy: ref(false)
|
privilegeBuy: ref(false)
|
||||||
}),
|
}),
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
|
/**
|
||||||
|
* Fetch all accounts on the database
|
||||||
|
*/
|
||||||
async getAllAccounts() {
|
async getAllAccounts() {
|
||||||
this.fetchInProgress = true
|
this.fetchInProgress = true
|
||||||
|
|
||||||
fetchAllAccounts()
|
fetchAllAccounts(this.userAccountToken)
|
||||||
.then(response => {
|
.then(response => {
|
||||||
this.accounts = response.data
|
this.accounts = response.data
|
||||||
this.fetchInProgress = false
|
this.fetchInProgress = false
|
||||||
})
|
})
|
||||||
|
.catch(onrejected => {
|
||||||
|
this.fetchInProgress = false
|
||||||
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -68,7 +77,7 @@ export const useAccountStore = defineStore("accountStore", {
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await login(this.loginData.username, this.loginData.password)
|
await getLogin(this.loginData.username, this.loginData.password)
|
||||||
.then(async result => {
|
.then(async result => {
|
||||||
this.userAccountToken = result.data.token
|
this.userAccountToken = result.data.token
|
||||||
|
|
||||||
@@ -82,8 +91,8 @@ export const useAccountStore = defineStore("accountStore", {
|
|||||||
this.privilegeBuy = true
|
this.privilegeBuy = true
|
||||||
this.adminPanelVisible = response.data.accountRole.privilegeAdminPanel
|
this.adminPanelVisible = response.data.accountRole.privilegeAdminPanel
|
||||||
|
|
||||||
if (response.data.accountRoleId == 3) {
|
if (response.data.accountRoleId == 2) {
|
||||||
exerciseStore.solveExercise(2, 4)
|
exerciseStore.solveExercise(2, 5)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@@ -100,6 +109,21 @@ export const useAccountStore = defineStore("accountStore", {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reload account information about current logged in user
|
||||||
|
*/
|
||||||
|
async refreshAccount() {
|
||||||
|
getAccount(this.userAccountToken)
|
||||||
|
.then(response => {
|
||||||
|
this.userAccount = response.data
|
||||||
|
|
||||||
|
this.fetchInProgress = false
|
||||||
|
|
||||||
|
this.privilegeBuy = true
|
||||||
|
this.adminPanelVisible = response.data.accountRole.privilegeAdminPanel
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register a new account to the database
|
* Register a new account to the database
|
||||||
* Log in on success
|
* Log in on success
|
||||||
@@ -111,32 +135,42 @@ export const useAccountStore = defineStore("accountStore", {
|
|||||||
const exerciseStore = useExerciseStore()
|
const exerciseStore = useExerciseStore()
|
||||||
this.fetchInProgress = true
|
this.fetchInProgress = true
|
||||||
|
|
||||||
await registerAccount(this.registerData)
|
if (this.registerData.username == null || this.registerData.username.length < 4) {
|
||||||
.then(async res => {
|
feedbackStore.addSnackbar(BannerStateEnum.ACCOUNTUSERNAMETOOSHORT)
|
||||||
if (res.status == 201) {
|
} else if (this.registerData.password == null || this.registerData.password.length < 8) {
|
||||||
feedbackStore.addSnackbar(BannerStateEnum.ACCOUNTREGISTERSUCCESSFUL)
|
feedbackStore.addSnackbar(BannerStateEnum.ACCOUNTPASSWORDTOOSHORT)
|
||||||
exerciseStore.solveExercise(0, 1)
|
} else if (!this.registerData.email.match(/^(([^<>()[\]\\.,;:\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,}))$/)) {
|
||||||
}
|
feedbackStore.addSnackbar(BannerStateEnum.ACCOUNTMAILADDRESSUNVALID)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
await registerAccount(this.registerData)
|
||||||
|
.then(async res => {
|
||||||
|
if (res.status == 201) {
|
||||||
|
feedbackStore.addSnackbar(BannerStateEnum.ACCOUNTREGISTERSUCCESSFUL)
|
||||||
|
exerciseStore.solveExercise(0, 1)
|
||||||
|
}
|
||||||
|
|
||||||
this.loginData = {
|
this.loginData = {
|
||||||
username: this.registerData.username,
|
username: this.registerData.username,
|
||||||
password: this.registerData.password
|
password: this.registerData.password
|
||||||
}
|
}
|
||||||
|
|
||||||
this.fetchInProgress = false
|
this.fetchInProgress = false
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
if (error.status == 400) {
|
if (error.status == 400) {
|
||||||
feedbackStore.addSnackbar(BannerStateEnum.ACCOUNTREGISTERERROR)
|
feedbackStore.addSnackbar(BannerStateEnum.ACCOUNTREGISTERERROR)
|
||||||
} else if (error.status == 409) {
|
} else if (error.status == 409) {
|
||||||
feedbackStore.addSnackbar(BannerStateEnum.ACCOUNTREGISTERUSERNAMEINUSE)
|
feedbackStore.addSnackbar(BannerStateEnum.ACCOUNTREGISTERUSERNAMEORMAILINUSE)
|
||||||
}
|
}
|
||||||
|
|
||||||
this.fetchInProgress = false
|
this.fetchInProgress = false
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return false
|
this.fetchInProgress = false
|
||||||
|
return false
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -217,6 +251,11 @@ export const useAccountStore = defineStore("accountStore", {
|
|||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delete user account
|
||||||
|
*
|
||||||
|
* @param account Account which should be deleted
|
||||||
|
*/
|
||||||
async deleteAccount(account: AccountModel) {
|
async deleteAccount(account: AccountModel) {
|
||||||
this.fetchInProgress = true
|
this.fetchInProgress = true
|
||||||
|
|
||||||
|
|||||||
@@ -19,10 +19,10 @@ export const useBasketStore = defineStore('basketStore', {
|
|||||||
itemsInBasket: useLocalStorage<Array<BasketItemModel>>("hackmycart/basketStore/itemsInBasket", []),
|
itemsInBasket: useLocalStorage<Array<BasketItemModel>>("hackmycart/basketStore/itemsInBasket", []),
|
||||||
|
|
||||||
/** Address used in the order dialog */
|
/** Address used in the order dialog */
|
||||||
usedAddress: ref(new AddressModel()),
|
usedAddress: ref<AddressModel>(null),
|
||||||
|
|
||||||
/** Payment method used in the order dialog */
|
/** Payment method used in the order dialog */
|
||||||
usedPayment: ref(new PaymentModel()),
|
usedPayment: ref<PaymentModel>(null),
|
||||||
|
|
||||||
/** Selected seats in the booking page */
|
/** Selected seats in the booking page */
|
||||||
selectedSeats: ref<Array<SelectedSeatModel>>([])
|
selectedSeats: ref<Array<SelectedSeatModel>>([])
|
||||||
@@ -53,7 +53,7 @@ export const useBasketStore = defineStore('basketStore', {
|
|||||||
*/
|
*/
|
||||||
removeItemFromBasket(item: BasketItemModel) {
|
removeItemFromBasket(item: BasketItemModel) {
|
||||||
const feedbackStore = useFeedbackStore()
|
const feedbackStore = useFeedbackStore()
|
||||||
feedbackStore.addSnackbar(BannerStateEnum.BASKETPRODUCTREMOVED)
|
feedbackStore.addSnackbar(BannerStateEnum.BASKETTICKETREMOVED)
|
||||||
|
|
||||||
this.itemsInBasket = this.itemsInBasket.filter((basketItemModel: BasketItemModel) =>
|
this.itemsInBasket = this.itemsInBasket.filter((basketItemModel: BasketItemModel) =>
|
||||||
basketItemModel.concert.id != item.concert.id
|
basketItemModel.concert.id != item.concert.id
|
||||||
@@ -119,7 +119,6 @@ export const useBasketStore = defineStore('basketStore', {
|
|||||||
for (let item of this.itemsInBasket) {
|
for (let item of this.itemsInBasket) {
|
||||||
if (!item.concert.offered) {
|
if (!item.concert.offered) {
|
||||||
exerciseStore.solveExercise(1, 2)
|
exerciseStore.solveExercise(1, 2)
|
||||||
feedbackStore.addSnackbar(BannerStateEnum.EXERCISESOLVED12)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ export const useExerciseStore = defineStore("exerciseStore", {
|
|||||||
case 3: bannerState = BannerStateEnum.EXERCISESOLVED23; break;
|
case 3: bannerState = BannerStateEnum.EXERCISESOLVED23; break;
|
||||||
case 4: bannerState = BannerStateEnum.EXERCISESOLVED24; break;
|
case 4: bannerState = BannerStateEnum.EXERCISESOLVED24; break;
|
||||||
case 5: bannerState = BannerStateEnum.EXERCISESOLVED25; break;
|
case 5: bannerState = BannerStateEnum.EXERCISESOLVED25; break;
|
||||||
|
case 6: bannerState = BannerStateEnum.EXERCISESOLVED26; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -46,10 +46,10 @@ export const useFeedbackStore = defineStore("feedbackStore", {
|
|||||||
case BannerStateEnum.ERROR:
|
case BannerStateEnum.ERROR:
|
||||||
return this.i18n.t('bannerMessages.error')
|
return this.i18n.t('bannerMessages.error')
|
||||||
|
|
||||||
case BannerStateEnum.BASKETPRODUCTADDED:
|
case BannerStateEnum.BASKETTICKETADDED:
|
||||||
return this.i18n.t('bannerMessages.basketTicketAdded')
|
return this.i18n.t('bannerMessages.basketTicketAdded')
|
||||||
|
|
||||||
case BannerStateEnum.BASKETPRODUCTREMOVED:
|
case BannerStateEnum.BASKETTICKETREMOVED:
|
||||||
return this.i18n.t("bannerMessages.basketTicketRemoved")
|
return this.i18n.t("bannerMessages.basketTicketRemoved")
|
||||||
|
|
||||||
|
|
||||||
@@ -90,6 +90,9 @@ export const useFeedbackStore = defineStore("feedbackStore", {
|
|||||||
|
|
||||||
case BannerStateEnum.EXERCISESOLVED25:
|
case BannerStateEnum.EXERCISESOLVED25:
|
||||||
return this.i18n.t("bannerMessages.exerciseSolvedNr", [2, 5])
|
return this.i18n.t("bannerMessages.exerciseSolvedNr", [2, 5])
|
||||||
|
|
||||||
|
case BannerStateEnum.EXERCISESOLVED26:
|
||||||
|
return this.i18n.t('bannerMessages.exerciseSolvedNr', [2, 6])
|
||||||
|
|
||||||
|
|
||||||
case BannerStateEnum.EXERCISESOLVED31:
|
case BannerStateEnum.EXERCISESOLVED31:
|
||||||
@@ -127,8 +130,8 @@ export const useFeedbackStore = defineStore("feedbackStore", {
|
|||||||
case BannerStateEnum.ACCOUNTREGISTERERROR:
|
case BannerStateEnum.ACCOUNTREGISTERERROR:
|
||||||
return this.i18n.t("bannerMessages.registerSuccessful")
|
return this.i18n.t("bannerMessages.registerSuccessful")
|
||||||
|
|
||||||
case BannerStateEnum.ACCOUNTREGISTERUSERNAMEINUSE:
|
case BannerStateEnum.ACCOUNTREGISTERUSERNAMEORMAILINUSE:
|
||||||
return this.i18n.t("bannerMessages.usernameInUse")
|
return this.i18n.t("bannerMessages.usernameOrMailInUse")
|
||||||
|
|
||||||
case BannerStateEnum.ACCOUNTUPDATESUCCESSFUL:
|
case BannerStateEnum.ACCOUNTUPDATESUCCESSFUL:
|
||||||
return this.i18n.t("bannerMessages.accountUpdated")
|
return this.i18n.t("bannerMessages.accountUpdated")
|
||||||
@@ -136,6 +139,15 @@ export const useFeedbackStore = defineStore("feedbackStore", {
|
|||||||
case BannerStateEnum.ACCOUNTLOGOUTSUCCESSFUL:
|
case BannerStateEnum.ACCOUNTLOGOUTSUCCESSFUL:
|
||||||
return this.i18n.t('bannerMessages.logoutSuccessful')
|
return this.i18n.t('bannerMessages.logoutSuccessful')
|
||||||
|
|
||||||
|
case BannerStateEnum.ACCOUNTPASSWORDTOOSHORT:
|
||||||
|
return this.i18n.t('bannerMessages.accountPasswordTooShort')
|
||||||
|
|
||||||
|
case BannerStateEnum.ACCOUNTUSERNAMETOOSHORT:
|
||||||
|
return this.i18n.t('bannerMessages.accountUsernameTooShort')
|
||||||
|
|
||||||
|
case BannerStateEnum.ACCOUNTMAILADDRESSUNVALID:
|
||||||
|
return this.i18n.t('bannerMessages.accountMailAddressUnvalid')
|
||||||
|
|
||||||
|
|
||||||
////////// API Endpoint /orders //////////
|
////////// API Endpoint /orders //////////
|
||||||
|
|
||||||
@@ -180,14 +192,17 @@ export const useFeedbackStore = defineStore("feedbackStore", {
|
|||||||
case BannerStateEnum.ACCOUNTLOGINERROR:
|
case BannerStateEnum.ACCOUNTLOGINERROR:
|
||||||
case BannerStateEnum.ACCOUNTLOGINWRONGLOGIN:
|
case BannerStateEnum.ACCOUNTLOGINWRONGLOGIN:
|
||||||
case BannerStateEnum.ACCOUNTREGISTERERROR:
|
case BannerStateEnum.ACCOUNTREGISTERERROR:
|
||||||
case BannerStateEnum.ACCOUNTREGISTERUSERNAMEINUSE:
|
case BannerStateEnum.ACCOUNTREGISTERUSERNAMEORMAILINUSE:
|
||||||
|
case BannerStateEnum.ACCOUNTPASSWORDTOOSHORT:
|
||||||
|
case BannerStateEnum.ACCOUNTUSERNAMETOOSHORT:
|
||||||
|
case BannerStateEnum.ACCOUNTMAILADDRESSUNVALID:
|
||||||
case BannerStateEnum.BANDDELETEERROR:
|
case BannerStateEnum.BANDDELETEERROR:
|
||||||
case BannerStateEnum.BANDSAVEDERROR:
|
case BannerStateEnum.BANDSAVEDERROR:
|
||||||
case BannerStateEnum.GENREDELETEERROR:
|
case BannerStateEnum.GENREDELETEERROR:
|
||||||
case BannerStateEnum.GENRESAVEDERROR:
|
case BannerStateEnum.GENRESAVEDERROR:
|
||||||
return "red"
|
return "warning"
|
||||||
|
|
||||||
case BannerStateEnum.BASKETPRODUCTADDED:
|
case BannerStateEnum.BASKETTICKETADDED:
|
||||||
case BannerStateEnum.DATABASERESETSUCCESSFUL:
|
case BannerStateEnum.DATABASERESETSUCCESSFUL:
|
||||||
case BannerStateEnum.ACCOUNTLOGINSUCCESSFUL:
|
case BannerStateEnum.ACCOUNTLOGINSUCCESSFUL:
|
||||||
case BannerStateEnum.ACCOUNTREGISTERSUCCESSFUL:
|
case BannerStateEnum.ACCOUNTREGISTERSUCCESSFUL:
|
||||||
@@ -199,7 +214,8 @@ export const useFeedbackStore = defineStore("feedbackStore", {
|
|||||||
case BannerStateEnum.EXERCISEPROGRESSRESETSUCCESSFUL:
|
case BannerStateEnum.EXERCISEPROGRESSRESETSUCCESSFUL:
|
||||||
case BannerStateEnum.GENREDELETESUCCESSFUL:
|
case BannerStateEnum.GENREDELETESUCCESSFUL:
|
||||||
case BannerStateEnum.GENRESAVEDSUCCESSFUL:
|
case BannerStateEnum.GENRESAVEDSUCCESSFUL:
|
||||||
return "green"
|
case BannerStateEnum.BASKETTICKETREMOVED:
|
||||||
|
return "success"
|
||||||
|
|
||||||
case BannerStateEnum.EXERCISESOLVED01:
|
case BannerStateEnum.EXERCISESOLVED01:
|
||||||
case BannerStateEnum.EXERCISESOLVED02:
|
case BannerStateEnum.EXERCISESOLVED02:
|
||||||
@@ -211,12 +227,10 @@ export const useFeedbackStore = defineStore("feedbackStore", {
|
|||||||
case BannerStateEnum.EXERCISESOLVED23:
|
case BannerStateEnum.EXERCISESOLVED23:
|
||||||
case BannerStateEnum.EXERCISESOLVED24:
|
case BannerStateEnum.EXERCISESOLVED24:
|
||||||
case BannerStateEnum.EXERCISESOLVED25:
|
case BannerStateEnum.EXERCISESOLVED25:
|
||||||
|
case BannerStateEnum.EXERCISESOLVED26:
|
||||||
case BannerStateEnum.EXERCISESOLVED31:
|
case BannerStateEnum.EXERCISESOLVED31:
|
||||||
case BannerStateEnum.EXERCISESOLVED32:
|
case BannerStateEnum.EXERCISESOLVED32:
|
||||||
return "purple"
|
return "exerciseSolved"
|
||||||
|
|
||||||
case BannerStateEnum.BASKETPRODUCTREMOVED:
|
|
||||||
return "blue"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -229,7 +243,10 @@ export const useFeedbackStore = defineStore("feedbackStore", {
|
|||||||
case BannerStateEnum.ACCOUNTLOGINERROR:
|
case BannerStateEnum.ACCOUNTLOGINERROR:
|
||||||
case BannerStateEnum.ACCOUNTLOGINWRONGLOGIN:
|
case BannerStateEnum.ACCOUNTLOGINWRONGLOGIN:
|
||||||
case BannerStateEnum.ACCOUNTREGISTERERROR:
|
case BannerStateEnum.ACCOUNTREGISTERERROR:
|
||||||
case BannerStateEnum.ACCOUNTREGISTERUSERNAMEINUSE:
|
case BannerStateEnum.ACCOUNTPASSWORDTOOSHORT:
|
||||||
|
case BannerStateEnum.ACCOUNTUSERNAMETOOSHORT:
|
||||||
|
case BannerStateEnum.ACCOUNTMAILADDRESSUNVALID:
|
||||||
|
case BannerStateEnum.ACCOUNTREGISTERUSERNAMEORMAILINUSE:
|
||||||
return "mdi-account"
|
return "mdi-account"
|
||||||
|
|
||||||
case BannerStateEnum.EXERCISESOLVED01:
|
case BannerStateEnum.EXERCISESOLVED01:
|
||||||
@@ -242,6 +259,7 @@ export const useFeedbackStore = defineStore("feedbackStore", {
|
|||||||
case BannerStateEnum.EXERCISESOLVED23:
|
case BannerStateEnum.EXERCISESOLVED23:
|
||||||
case BannerStateEnum.EXERCISESOLVED24:
|
case BannerStateEnum.EXERCISESOLVED24:
|
||||||
case BannerStateEnum.EXERCISESOLVED25:
|
case BannerStateEnum.EXERCISESOLVED25:
|
||||||
|
case BannerStateEnum.EXERCISESOLVED26:
|
||||||
case BannerStateEnum.EXERCISESOLVED31:
|
case BannerStateEnum.EXERCISESOLVED31:
|
||||||
case BannerStateEnum.EXERCISESOLVED32:
|
case BannerStateEnum.EXERCISESOLVED32:
|
||||||
return "mdi-check-circle-outline"
|
return "mdi-check-circle-outline"
|
||||||
@@ -250,8 +268,8 @@ export const useFeedbackStore = defineStore("feedbackStore", {
|
|||||||
case BannerStateEnum.EXERCISEPROGRESSRESETSUCCESSFUL:
|
case BannerStateEnum.EXERCISEPROGRESSRESETSUCCESSFUL:
|
||||||
return "mdi-database-refresh"
|
return "mdi-database-refresh"
|
||||||
|
|
||||||
case BannerStateEnum.BASKETPRODUCTADDED:
|
case BannerStateEnum.BASKETTICKETADDED:
|
||||||
case BannerStateEnum.BASKETPRODUCTREMOVED:
|
case BannerStateEnum.BASKETTICKETREMOVED:
|
||||||
return "mdi-basket"
|
return "mdi-basket"
|
||||||
|
|
||||||
case BannerStateEnum.ORDERPLACESUCCESSFUL:
|
case BannerStateEnum.ORDERPLACESUCCESSFUL:
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { fetchFileNames, fetchFolderNames, postFile } from "@/data/api/files.api";
|
import { fetchFileNames, fetchFolderNames, postFile } from "@/data/api/files.api";
|
||||||
|
import { FilesApiModel } from "@/data/models/files/filesApiModel";
|
||||||
import { defineStore } from "pinia";
|
import { defineStore } from "pinia";
|
||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
|
|
||||||
@@ -7,14 +8,17 @@ export const useFilesStore = defineStore('filesStore', {
|
|||||||
/** Request to server sent, waiting for data response */
|
/** Request to server sent, waiting for data response */
|
||||||
fetchInProgress: ref(false),
|
fetchInProgress: ref(false),
|
||||||
|
|
||||||
|
/** List of all folders on the server */
|
||||||
staticFolders: ref<Array<{name: string, nrOfItems: number}>>([]),
|
staticFolders: ref<Array<{name: string, nrOfItems: number}>>([]),
|
||||||
|
|
||||||
|
/** Current selected folder in file browsre */
|
||||||
selectedFolder: ref<{name: string, nrOfItems: number}>(),
|
selectedFolder: ref<{name: string, nrOfItems: number}>(),
|
||||||
|
|
||||||
/** List of files on the server */
|
/** List of files on the server */
|
||||||
staticFiles: ref<Array<{name: string, size: number, content: string, url: string}>>([]),
|
staticFiles: ref<Array<FilesApiModel>>([]),
|
||||||
|
|
||||||
selectedFile: ref<{name: string, size: number, content: string, url: string}>(),
|
/** Current selected file in file browser */
|
||||||
|
selectedFile: ref<FilesApiModel>(),
|
||||||
|
|
||||||
showFileUploadDialog: ref(false),
|
showFileUploadDialog: ref(false),
|
||||||
|
|
||||||
@@ -24,6 +28,9 @@ export const useFilesStore = defineStore('filesStore', {
|
|||||||
}),
|
}),
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
|
/**
|
||||||
|
* Fetch all static folders on the server
|
||||||
|
*/
|
||||||
async getStaticFolders() {
|
async getStaticFolders() {
|
||||||
this.fetchInProgress = true
|
this.fetchInProgress = true
|
||||||
|
|
||||||
@@ -35,7 +42,7 @@ export const useFilesStore = defineStore('filesStore', {
|
|||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Request all available static files on server
|
* Request static files on server by selected folder
|
||||||
*/
|
*/
|
||||||
async getStaticFiles() {
|
async getStaticFiles() {
|
||||||
this.fetchInProgress = true
|
this.fetchInProgress = true
|
||||||
@@ -47,6 +54,7 @@ export const useFilesStore = defineStore('filesStore', {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
async uploadFile() {
|
async uploadFile() {
|
||||||
this.fetchInProgress = true
|
this.fetchInProgress = true
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import { CityModel } from "../data/models/locations/cityModel";
|
|||||||
import { fetchAllCities } from "../data/api/cityApi";
|
import { fetchAllCities } from "../data/api/cityApi";
|
||||||
import { LocationDetailsApiModel } from "@/data/models/locations/locationDetailsApiModel";
|
import { LocationDetailsApiModel } from "@/data/models/locations/locationDetailsApiModel";
|
||||||
import { useFeedbackStore } from "./feedback.store";
|
import { useFeedbackStore } from "./feedback.store";
|
||||||
|
import { getData, getTag } from "exif-js";
|
||||||
|
|
||||||
export const useLocationStore = defineStore("locationStore", {
|
export const useLocationStore = defineStore("locationStore", {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
|
|||||||
@@ -1,16 +1,19 @@
|
|||||||
import { fetchAllOrders, fetchUserOrders } from "@/data/api/orderApi";
|
import { fetchAllOrders, fetchUserOrders, patchOrder } from "@/data/api/orderApi";
|
||||||
import { OrderApiModel } from "@/data/models/apiEndpoints/orderApiModel";
|
import { OrderApiModel } from "@/data/models/apiEndpoints/orderApiModel";
|
||||||
import { AccountModel } from "@/data/models/user/accountModel";
|
import { AccountModel } from "@/data/models/user/accountModel";
|
||||||
import { defineStore } from "pinia";
|
import { defineStore } from "pinia";
|
||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
|
import { useAccountStore } from "./account.store";
|
||||||
|
|
||||||
export const useOrderStore = defineStore("orderStore", {
|
export const useOrderStore = defineStore("orderStore", {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
/** All orders of one/all users */
|
/** All orders of one/all users */
|
||||||
orders: ref<Array<OrderApiModel>>([]),
|
orders: ref<Array<OrderApiModel>>([]),
|
||||||
|
|
||||||
|
/** Current selected order */
|
||||||
order: ref<OrderApiModel>(new OrderApiModel),
|
order: ref<OrderApiModel>(new OrderApiModel),
|
||||||
|
|
||||||
|
/** Show detail dialog on admin page */
|
||||||
showDetailDialog: ref<boolean>(false),
|
showDetailDialog: ref<boolean>(false),
|
||||||
|
|
||||||
/** Request to server sent, waiting for data response */
|
/** Request to server sent, waiting for data response */
|
||||||
@@ -22,9 +25,10 @@ export const useOrderStore = defineStore("orderStore", {
|
|||||||
* Get all orders from all accounts from server
|
* Get all orders from all accounts from server
|
||||||
*/
|
*/
|
||||||
async getAllOrders() {
|
async getAllOrders() {
|
||||||
|
const accountStore = useAccountStore()
|
||||||
this.fetchInProgress = true
|
this.fetchInProgress = true
|
||||||
|
|
||||||
fetchAllOrders()
|
fetchAllOrders(accountStore.userAccountToken)
|
||||||
.then(res => {
|
.then(res => {
|
||||||
this.orders = res.data
|
this.orders = res.data
|
||||||
this.fetchInProgress = false
|
this.fetchInProgress = false
|
||||||
@@ -46,13 +50,31 @@ export const useOrderStore = defineStore("orderStore", {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open detail dialog
|
||||||
|
*
|
||||||
|
* @param order Order to view
|
||||||
|
*/
|
||||||
openDetails(order: OrderApiModel) {
|
openDetails(order: OrderApiModel) {
|
||||||
this.order = order
|
this.order = order
|
||||||
this.showDetailDialog = true
|
this.showDetailDialog = true
|
||||||
},
|
},
|
||||||
|
|
||||||
async deleteOrder(order: OrderApiModel) {
|
|
||||||
// todo
|
/**
|
||||||
|
*
|
||||||
|
* @param order
|
||||||
|
* @param shipped
|
||||||
|
*/
|
||||||
|
async changeOrderShippedState(order: OrderApiModel, shipped: boolean) {
|
||||||
|
this.fetchInProgress = true
|
||||||
|
|
||||||
|
order.shipped = shipped
|
||||||
|
|
||||||
|
patchOrder(order)
|
||||||
|
.then(res => {
|
||||||
|
this.getAllOrders()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -5,17 +5,21 @@ import { fetchLocationsBySearchTerm } from "../data/api/locationApi";
|
|||||||
import { fetchConcertsBySearchTerm } from "../data/api/concertApi";
|
import { fetchConcertsBySearchTerm } from "../data/api/concertApi";
|
||||||
import { ConcertApiModel } from "@/data/models/acts/concertApiModel";
|
import { ConcertApiModel } from "@/data/models/acts/concertApiModel";
|
||||||
import { useExerciseStore } from "./exercise.store";
|
import { useExerciseStore } from "./exercise.store";
|
||||||
|
import { BandApiModel } from "@/data/models/acts/bandApiModel";
|
||||||
|
import { useBandStore } from "./band.store";
|
||||||
|
import { useAccountStore } from "./account.store";
|
||||||
|
import { LocationDetailsApiModel } from "@/data/models/locations/locationDetailsApiModel";
|
||||||
|
|
||||||
export const useSearchStore = defineStore("searchStore", {
|
export const useSearchStore = defineStore("searchStore", {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
/** Search term */
|
/** Search term */
|
||||||
searchTerm: ref(""),
|
searchTerm: ref<string>(""),
|
||||||
|
|
||||||
/** Band results */
|
/** Band results */
|
||||||
bands: ref(),
|
bands: ref<Array<BandApiModel>>([]),
|
||||||
|
|
||||||
/** Location results */
|
/** Location results */
|
||||||
locations: ref(),
|
locations: ref<Array<LocationDetailsApiModel>>([]),
|
||||||
|
|
||||||
/** Concert results */
|
/** Concert results */
|
||||||
concerts: ref<Array<ConcertApiModel>>([]),
|
concerts: ref<Array<ConcertApiModel>>([]),
|
||||||
@@ -37,28 +41,74 @@ export const useSearchStore = defineStore("searchStore", {
|
|||||||
this.alreadySearched = true
|
this.alreadySearched = true
|
||||||
this.fetchInProgress = true
|
this.fetchInProgress = true
|
||||||
|
|
||||||
// Exercise solutions
|
/**
|
||||||
// todo: Rewrite to avoid easy exercise solution
|
* Fetch all bands by this.searchTerm
|
||||||
if (this.searchTerm.endsWith("'); SELECT * FROM Accounts; --")) {
|
*/
|
||||||
exerciseStore.solveExercise(2, 1)
|
|
||||||
} else if (this.searchTerm.endsWith("'); SELECT * FROM AccountRoles; --")) {
|
|
||||||
exerciseStore.solveExercise(2, 2)
|
|
||||||
} else if (this.searchTerm.includes("'); UPDATE Accounts SET accountRoleId = 2 WHERE username = ")) {
|
|
||||||
exerciseStore.solveExercise(2, 3)
|
|
||||||
} else if (this.searchTerm.includes("'); DELETE FROM Ratings WHERE rating = 5; --")) {
|
|
||||||
exerciseStore.solveExercise(2, 5)
|
|
||||||
}
|
|
||||||
|
|
||||||
await fetchBandsBySearchTerm(this.searchTerm)
|
await fetchBandsBySearchTerm(this.searchTerm)
|
||||||
.then(result => {
|
.then(async result => {
|
||||||
this.bands = result.data
|
|
||||||
|
|
||||||
|
// Check for exercise solution
|
||||||
|
if (result.data.length != 0) {
|
||||||
|
// Exercise 2.1
|
||||||
|
if (result.data[0].type != undefined && result.data[0].type == "table") {
|
||||||
|
exerciseStore.solveExercise(2, 1)
|
||||||
|
console.log("Exercise 2.1 solved")
|
||||||
|
}
|
||||||
|
// Exercise 2.2
|
||||||
|
else if (result.data[0].username != undefined) {
|
||||||
|
exerciseStore.solveExercise(2, 2)
|
||||||
|
console.log("Exercise 2.2 solved")
|
||||||
|
}
|
||||||
|
// Exercise 2.3
|
||||||
|
else if (result.data[0].privilegeAdminPanel != undefined) {
|
||||||
|
exerciseStore.solveExercise(2, 3)
|
||||||
|
console.log("Exercise 2.3 solved")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exercise 2.4
|
||||||
|
else if (this.searchTerm.includes("UPDATE")) {
|
||||||
|
const accountStore = useAccountStore()
|
||||||
|
await accountStore.refreshAccount()
|
||||||
|
|
||||||
|
if (accountStore.userAccount.accountRole.privilegeAdminPanel == true) {
|
||||||
|
exerciseStore.solveExercise(2, 4)
|
||||||
|
console.log("Exercise 2.4 solved")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exercise 2.6
|
||||||
|
else if (this.searchTerm.includes("DELETE")) {
|
||||||
|
const bandStore = useBandStore()
|
||||||
|
await bandStore.getBand("muse")
|
||||||
|
|
||||||
|
if (bandStore.band.ratingValues.find(
|
||||||
|
rating => rating.value == 5).count == 0
|
||||||
|
) {
|
||||||
|
exerciseStore.solveExercise(2, 6)
|
||||||
|
console.log("Exercise 2.6 solved")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else {
|
||||||
|
this.bands = result.data
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch all locations by this.searchTerm
|
||||||
|
*/
|
||||||
await fetchLocationsBySearchTerm(this.searchTerm)
|
await fetchLocationsBySearchTerm(this.searchTerm)
|
||||||
.then(result => {
|
.then(result => {
|
||||||
this.locations = result.data
|
this.locations = result.data
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch all concerts by this.searchTerm
|
||||||
|
*/
|
||||||
await fetchConcertsBySearchTerm(this.searchTerm)
|
await fetchConcertsBySearchTerm(this.searchTerm)
|
||||||
.then(result => {
|
.then(result => {
|
||||||
this.concerts = result.data
|
this.concerts = result.data
|
||||||
|
|||||||