diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 3d6e9a8c..4db55027 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -9,9 +9,12 @@ "version": "0.0.0", "dependencies": { "axios": "^1.6.1", + "normalize.css": "^8.0.1", "pinia": "^2.1.7", + "primeicons": "^7.0.0", + "primevue": "^3.53.1", "vue": "^3.3.8", - "vue-router": "^4.2.5" + "vue-router": "^4.5.0" }, "devDependencies": { "@pinia/testing": "^0.1.3", @@ -814,9 +817,10 @@ } }, "node_modules/@vue/devtools-api": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.5.1.tgz", - "integrity": "sha512-+KpckaAQyfbvshdDW5xQylLni1asvNSGme1JFs8I1+/H5pHEhqUKMEQD/qn3Nx5+/nycBq11qAEi8lk+LXI2dA==" + "version": "6.6.4", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz", + "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", + "license": "MIT" }, "node_modules/@vue/eslint-config-prettier": { "version": "8.0.0", @@ -2660,6 +2664,12 @@ "node": ">=0.10.0" } }, + "node_modules/normalize.css": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/normalize.css/-/normalize.css-8.0.1.tgz", + "integrity": "sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg==", + "license": "MIT" + }, "node_modules/npm-run-path": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz", @@ -3040,6 +3050,21 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/primeicons": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/primeicons/-/primeicons-7.0.0.tgz", + "integrity": "sha512-jK3Et9UzwzTsd6tzl2RmwrVY/b8raJ3QZLzoDACj+oTJ0oX7L9Hy+XnVwgo4QVKlKpnP/Ur13SXV/pVh4LzaDw==", + "license": "MIT" + }, + "node_modules/primevue": { + "version": "3.53.1", + "resolved": "https://registry.npmjs.org/primevue/-/primevue-3.53.1.tgz", + "integrity": "sha512-Bp4peZPdhfKYXwvtsOGGh5dfgmTelm+LZEZKGs/c5mOHhsUJ6xi3EcOZoQVI6oklS946ayMQvgD5L0S7itGO0g==", + "license": "MIT", + "peerDependencies": { + "vue": "^3.0.0" + } + }, "node_modules/proto-list": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", @@ -3885,11 +3910,12 @@ } }, "node_modules/vue-router": { - "version": "4.2.5", - "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.2.5.tgz", - "integrity": "sha512-DIUpKcyg4+PTQKfFPX88UWhlagBEBEfJ5A8XDXRJLUnZOvcpMF8o/dnL90vpVkGaPbjvXazV/rC1qBKrZlFugw==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.5.0.tgz", + "integrity": "sha512-HDuk+PuH5monfNuY+ct49mNmkCRK4xJAV9Ts4z9UFc4rzdDnxQLyCMGGc8pKhZhHTVzfanpNwB/lwqevcBwI4w==", + "license": "MIT", "dependencies": { - "@vue/devtools-api": "^6.5.0" + "@vue/devtools-api": "^6.6.4" }, "funding": { "url": "https://github.com/sponsors/posva" diff --git a/frontend/package.json b/frontend/package.json index d48471d7..e62db7d7 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -11,9 +11,12 @@ }, "dependencies": { "axios": "^1.6.1", + "normalize.css": "^8.0.1", "pinia": "^2.1.7", + "primeicons": "^7.0.0", + "primevue": "^3.53.1", "vue": "^3.3.8", - "vue-router": "^4.2.5" + "vue-router": "^4.5.0" }, "devDependencies": { "@pinia/testing": "^0.1.3", diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 2f88d12a..6a34e8d7 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -1,62 +1,48 @@ - + \ No newline at end of file diff --git a/frontend/src/assets/img/button-arrow.svg b/frontend/src/assets/img/button-arrow.svg deleted file mode 100644 index e6a1c1b6..00000000 --- a/frontend/src/assets/img/button-arrow.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/frontend/src/assets/img/cart.svg b/frontend/src/assets/img/cart.svg deleted file mode 100644 index 2209aa6d..00000000 --- a/frontend/src/assets/img/cart.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/frontend/src/assets/img/cola.svg b/frontend/src/assets/img/cola.svg deleted file mode 100644 index 62921a69..00000000 --- a/frontend/src/assets/img/cola.svg +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - diff --git a/frontend/src/assets/img/diameter.svg b/frontend/src/assets/img/diameter.svg deleted file mode 100644 index fb3da986..00000000 --- a/frontend/src/assets/img/diameter.svg +++ /dev/null @@ -1,151 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/frontend/src/assets/img/dough-large.svg b/frontend/src/assets/img/dough-large.svg deleted file mode 100644 index d00c96db..00000000 --- a/frontend/src/assets/img/dough-large.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/frontend/src/assets/img/dough-light.svg b/frontend/src/assets/img/dough-light.svg deleted file mode 100644 index e4251d24..00000000 --- a/frontend/src/assets/img/dough-light.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - diff --git a/frontend/src/assets/img/edit.svg b/frontend/src/assets/img/edit.svg deleted file mode 100644 index a246122f..00000000 --- a/frontend/src/assets/img/edit.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/frontend/src/assets/img/filling-big/ananas.svg b/frontend/src/assets/img/filling-big/ananas.svg deleted file mode 100644 index 846f8ed0..00000000 --- a/frontend/src/assets/img/filling-big/ananas.svg +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/frontend/src/assets/img/filling-big/bacon.svg b/frontend/src/assets/img/filling-big/bacon.svg deleted file mode 100644 index 963fb789..00000000 --- a/frontend/src/assets/img/filling-big/bacon.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/frontend/src/assets/img/filling-big/blue_cheese.svg b/frontend/src/assets/img/filling-big/blue_cheese.svg deleted file mode 100644 index 0964e370..00000000 --- a/frontend/src/assets/img/filling-big/blue_cheese.svg +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/frontend/src/assets/img/filling-big/cheddar.svg b/frontend/src/assets/img/filling-big/cheddar.svg deleted file mode 100644 index 6215a6ab..00000000 --- a/frontend/src/assets/img/filling-big/cheddar.svg +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/frontend/src/assets/img/filling-big/chile.svg b/frontend/src/assets/img/filling-big/chile.svg deleted file mode 100644 index e3b1d889..00000000 --- a/frontend/src/assets/img/filling-big/chile.svg +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/frontend/src/assets/img/filling-big/ham.svg b/frontend/src/assets/img/filling-big/ham.svg deleted file mode 100644 index 304bdb58..00000000 --- a/frontend/src/assets/img/filling-big/ham.svg +++ /dev/null @@ -1,79 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/frontend/src/assets/img/filling-big/jalapeno.svg b/frontend/src/assets/img/filling-big/jalapeno.svg deleted file mode 100644 index 9d32431a..00000000 --- a/frontend/src/assets/img/filling-big/jalapeno.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/frontend/src/assets/img/filling-big/mozzarella.svg b/frontend/src/assets/img/filling-big/mozzarella.svg deleted file mode 100644 index 16d68bff..00000000 --- a/frontend/src/assets/img/filling-big/mozzarella.svg +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/frontend/src/assets/img/filling-big/mushrooms.svg b/frontend/src/assets/img/filling-big/mushrooms.svg deleted file mode 100644 index 18635fc5..00000000 --- a/frontend/src/assets/img/filling-big/mushrooms.svg +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/frontend/src/assets/img/filling-big/olives.svg b/frontend/src/assets/img/filling-big/olives.svg deleted file mode 100644 index 6400dcda..00000000 --- a/frontend/src/assets/img/filling-big/olives.svg +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - - diff --git a/frontend/src/assets/img/filling-big/onion.svg b/frontend/src/assets/img/filling-big/onion.svg deleted file mode 100644 index f4e6e486..00000000 --- a/frontend/src/assets/img/filling-big/onion.svg +++ /dev/null @@ -1,62 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/frontend/src/assets/img/filling-big/parmesan.svg b/frontend/src/assets/img/filling-big/parmesan.svg deleted file mode 100644 index 0a0f9da0..00000000 --- a/frontend/src/assets/img/filling-big/parmesan.svg +++ /dev/null @@ -1,74 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/frontend/src/assets/img/filling-big/salami.svg b/frontend/src/assets/img/filling-big/salami.svg deleted file mode 100644 index baa6f925..00000000 --- a/frontend/src/assets/img/filling-big/salami.svg +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/frontend/src/assets/img/filling-big/salmon.svg b/frontend/src/assets/img/filling-big/salmon.svg deleted file mode 100644 index 29fdf136..00000000 --- a/frontend/src/assets/img/filling-big/salmon.svg +++ /dev/null @@ -1,40 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/frontend/src/assets/img/filling-big/tomatoes.svg b/frontend/src/assets/img/filling-big/tomatoes.svg deleted file mode 100644 index c7ad13e8..00000000 --- a/frontend/src/assets/img/filling-big/tomatoes.svg +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/frontend/src/assets/img/filling/ananas.svg b/frontend/src/assets/img/filling/ananas.svg deleted file mode 100644 index 537a8bec..00000000 --- a/frontend/src/assets/img/filling/ananas.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/frontend/src/assets/img/filling/bacon.svg b/frontend/src/assets/img/filling/bacon.svg deleted file mode 100644 index bb67928d..00000000 --- a/frontend/src/assets/img/filling/bacon.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/frontend/src/assets/img/filling/blue_cheese.svg b/frontend/src/assets/img/filling/blue_cheese.svg deleted file mode 100644 index f3363069..00000000 --- a/frontend/src/assets/img/filling/blue_cheese.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - diff --git a/frontend/src/assets/img/filling/cheddar.svg b/frontend/src/assets/img/filling/cheddar.svg deleted file mode 100644 index 3ae26e57..00000000 --- a/frontend/src/assets/img/filling/cheddar.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/frontend/src/assets/img/filling/chile.svg b/frontend/src/assets/img/filling/chile.svg deleted file mode 100644 index 24465838..00000000 --- a/frontend/src/assets/img/filling/chile.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/frontend/src/assets/img/filling/ham.svg b/frontend/src/assets/img/filling/ham.svg deleted file mode 100644 index 0e695448..00000000 --- a/frontend/src/assets/img/filling/ham.svg +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/frontend/src/assets/img/filling/jalapeno.svg b/frontend/src/assets/img/filling/jalapeno.svg deleted file mode 100644 index fa5f58c8..00000000 --- a/frontend/src/assets/img/filling/jalapeno.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/frontend/src/assets/img/filling/mozzarella.svg b/frontend/src/assets/img/filling/mozzarella.svg deleted file mode 100644 index 76ac9e07..00000000 --- a/frontend/src/assets/img/filling/mozzarella.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/frontend/src/assets/img/filling/mushrooms.svg b/frontend/src/assets/img/filling/mushrooms.svg deleted file mode 100644 index b382f3bb..00000000 --- a/frontend/src/assets/img/filling/mushrooms.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/frontend/src/assets/img/filling/olives.svg b/frontend/src/assets/img/filling/olives.svg deleted file mode 100644 index afea0ac1..00000000 --- a/frontend/src/assets/img/filling/olives.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/frontend/src/assets/img/filling/onion.svg b/frontend/src/assets/img/filling/onion.svg deleted file mode 100644 index 244cdb89..00000000 --- a/frontend/src/assets/img/filling/onion.svg +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/frontend/src/assets/img/filling/parmesan.svg b/frontend/src/assets/img/filling/parmesan.svg deleted file mode 100644 index d0890fea..00000000 --- a/frontend/src/assets/img/filling/parmesan.svg +++ /dev/null @@ -1,340 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/frontend/src/assets/img/filling/salami.svg b/frontend/src/assets/img/filling/salami.svg deleted file mode 100644 index 961628b2..00000000 --- a/frontend/src/assets/img/filling/salami.svg +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/frontend/src/assets/img/filling/salmon.svg b/frontend/src/assets/img/filling/salmon.svg deleted file mode 100644 index 6dbdfded..00000000 --- a/frontend/src/assets/img/filling/salmon.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - diff --git a/frontend/src/assets/img/filling/tomatoes.svg b/frontend/src/assets/img/filling/tomatoes.svg deleted file mode 100644 index e8203ff0..00000000 --- a/frontend/src/assets/img/filling/tomatoes.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/frontend/src/assets/img/foundation/big-creamy.svg b/frontend/src/assets/img/foundation/big-creamy.svg deleted file mode 100644 index 5f861177..00000000 --- a/frontend/src/assets/img/foundation/big-creamy.svg +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/frontend/src/assets/img/foundation/big-tomato.svg b/frontend/src/assets/img/foundation/big-tomato.svg deleted file mode 100644 index 4186acef..00000000 --- a/frontend/src/assets/img/foundation/big-tomato.svg +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/frontend/src/assets/img/foundation/small-creamy.svg b/frontend/src/assets/img/foundation/small-creamy.svg deleted file mode 100644 index 51c5a3df..00000000 --- a/frontend/src/assets/img/foundation/small-creamy.svg +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/frontend/src/assets/img/foundation/small-tomato.svg b/frontend/src/assets/img/foundation/small-tomato.svg deleted file mode 100644 index 29e257af..00000000 --- a/frontend/src/assets/img/foundation/small-tomato.svg +++ /dev/null @@ -1,35 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/frontend/src/assets/img/login.svg b/frontend/src/assets/img/login.svg deleted file mode 100644 index aa2dec7a..00000000 --- a/frontend/src/assets/img/login.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/frontend/src/assets/img/logo.svg b/frontend/src/assets/img/logo.svg deleted file mode 100644 index d289a389..00000000 --- a/frontend/src/assets/img/logo.svg +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - diff --git a/frontend/src/assets/img/popup.svg b/frontend/src/assets/img/popup.svg deleted file mode 100644 index 3643667a..00000000 --- a/frontend/src/assets/img/popup.svg +++ /dev/null @@ -1,53 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/frontend/src/assets/img/potato.svg b/frontend/src/assets/img/potato.svg deleted file mode 100644 index d938c64d..00000000 --- a/frontend/src/assets/img/potato.svg +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/frontend/src/assets/img/product.svg b/frontend/src/assets/img/product.svg deleted file mode 100644 index aac455d5..00000000 --- a/frontend/src/assets/img/product.svg +++ /dev/null @@ -1,249 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/frontend/src/assets/img/sauce.svg b/frontend/src/assets/img/sauce.svg deleted file mode 100644 index 9b2cb194..00000000 --- a/frontend/src/assets/img/sauce.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/frontend/src/assets/img/select.svg b/frontend/src/assets/img/select.svg deleted file mode 100644 index 7ef2b359..00000000 --- a/frontend/src/assets/img/select.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/frontend/src/assets/img/users/user5.jpg b/frontend/src/assets/img/users/user5.jpg deleted file mode 100644 index c93cf6f2..00000000 Binary files a/frontend/src/assets/img/users/user5.jpg and /dev/null differ diff --git a/frontend/src/assets/img/users/user5.webp b/frontend/src/assets/img/users/user5.webp deleted file mode 100644 index ac1c83d6..00000000 Binary files a/frontend/src/assets/img/users/user5.webp and /dev/null differ diff --git a/frontend/src/assets/img/users/user5@2x.jpg b/frontend/src/assets/img/users/user5@2x.jpg deleted file mode 100644 index ef4aa7e3..00000000 Binary files a/frontend/src/assets/img/users/user5@2x.jpg and /dev/null differ diff --git a/frontend/src/assets/img/users/user5@2x.webp b/frontend/src/assets/img/users/user5@2x.webp deleted file mode 100644 index 608d908b..00000000 Binary files a/frontend/src/assets/img/users/user5@2x.webp and /dev/null differ diff --git a/frontend/src/assets/img/users/user5@4x.jpg b/frontend/src/assets/img/users/user5@4x.jpg deleted file mode 100644 index af10490a..00000000 Binary files a/frontend/src/assets/img/users/user5@4x.jpg and /dev/null differ diff --git a/frontend/src/assets/img/users/user5@4x.webp b/frontend/src/assets/img/users/user5@4x.webp deleted file mode 100644 index 22cd44be..00000000 Binary files a/frontend/src/assets/img/users/user5@4x.webp and /dev/null differ diff --git a/frontend/src/assets/scss/app.scss b/frontend/src/assets/scss/app.scss index 9d86ab36..bf577727 100644 --- a/frontend/src/assets/scss/app.scss +++ b/frontend/src/assets/scss/app.scss @@ -5,3 +5,317 @@ @import "visually-hidden.scss"; @import "scaffolding.scss"; @import "mixins/mixins"; + +.content { + padding-top: 20px; +} + +.content__wrapper { + display: flex; + align-items: flex-start; + flex-wrap: wrap; + + width: 920px; + margin: 0 auto; + padding-right: 2.12%; + padding-bottom: 30px; + padding-left: 2.12%; +} + +.content__ingredients { + width: 527px; + margin-top: 15px; + margin-right: auto; + margin-bottom: 15px; +} + +.content__pizza { + width: 373px; + margin-top: 15px; + margin-bottom: 15px; +} + +.content__result { + display: flex; + align-items: center; + justify-content: center; + + margin-top: 25px; + + p { + @include b-s24-h28; + + margin: 0; + } + + button { + margin-left: 12px; + padding: 16px 45px; + } + } + + .sheet { + padding-top: 15px; + + border-radius: 8px; + background-color: $white; + box-shadow: $shadow-light; + } + + .sheet__title { + padding-right: 18px; + padding-left: 18px; + } + + .sheet__content { + display: flex; + align-items: center; + flex-wrap: wrap; + + margin-top: 8px; + padding-top: 18px; + padding-right: 18px; + padding-left: 18px; + + border-top: 1px solid rgba($green-500, 0.1); + } + + .title { + box-sizing: border-box; + width: 100%; + margin: 0; + + color: $black; + + &--big { + @include b-s36-h42; + } + + &--small { + @include b-s18-h21; + } + } + + .radio { + cursor: pointer; + + span { + @include r-s16-h19; + + position: relative; + + padding-left: 28px; + + &:before { + @include p_center-v; + + display: block; + + box-sizing: border-box; + width: 20px; + height: 20px; + + content: ""; + transition: 0.3s; + + border: 1px solid $purple-400; + border-radius: 50%; + background-color: $white; + } + } + + &:hover { + input:not(:checked):not(:disabled) + span { + &:before { + border-color: $purple-800; + } + } + } + + input { + display: none; + + &:checked + span { + &:before { + border: 6px solid $green-500; + } + } + + &:disabled { + & + span { + &:before { + border-color: $purple-400; + background-color: $silver-200; + } + } + + &:checked + span { + &:before { + border: 6px solid $purple-400; + } + } + } + } + } + + .button { + $bl: &; + + @include b-s18-h21; + font-family: inherit; + display: block; + + box-sizing: border-box; + margin: 0; + padding: 0; + + cursor: pointer; + transition: 0.3s; + text-align: center; + + color: $white; + border: none; + border-radius: 8px; + outline: none; + box-shadow: $shadow-medium; + + background-color: $green-500; + + &:hover:not(:active):not(:disabled) { + background-color: $green-400; + } + + &:active:not(:disabled) { + background-color: $green-600; + } + + &:focus:not(:disabled) { + opacity: 0.5; + } + + &:disabled { + background-color: $green-300; + color: rgba($white, 0.2); + cursor: default; + } + + &--border { + background-color: transparent; + border: 1px solid $green-500; + color: $black; + box-shadow: none; + + &:hover:not(:active):not(:disabled) { + color: $green-500; + border-color: $green-500; + background-color: transparent; + } + + &:active:not(:disabled) { + color: $green-600; + border-color: $green-600; + background-color: transparent; + } + + &:disabled { + opacity: 0.5; + } + } + + &--transparent { + @include b-s14-h16; + background-color: transparent; + box-shadow: none; + color: $black; + + &:hover:not(:active):not(:disabled) { + color: $red-800; + background-color: transparent; + } + + &:active:not(:disabled) { + color: $red-900; + background-color: transparent; + } + + &:disabled { + opacity: 0.25; + } + } + + &--arrow { + &::before { + content: ""; + background-image: url("@/assets/img/button-arrow.svg"); + background-position: center; + background-repeat: no-repeat; + margin-right: 16px; + width: 18px; + height: 18px; + display: inline-block; + vertical-align: middle; + transform: translateY(-1px); + } + } + + &--white { + background-color: $white; + color: $green-500; + } + } + + .input { + display: block; + + span { + @include r-s14-h16; + + display: block; + + margin-bottom: 4px; + } + + input { + @include r-s16-h19; + + display: block; + + box-sizing: border-box; + width: 100%; + margin: 0; + padding: 8px 16px; + + transition: 0.3s; + + color: $black; + border: 1px solid $purple-400; + border-radius: 8px; + outline: none; + background-color: $white; + + font-family: inherit; + + &:focus { + border-color: $green-500; + } + } + + &:hover { + input { + border-color: $black; + } + } + + &--big-label { + display: flex; + align-items: center; + + span { + @include b-s16-h19; + + margin-right: 16px; + + white-space: nowrap; + } + } + } \ No newline at end of file diff --git a/frontend/src/assets/scss/card.scss b/frontend/src/assets/scss/card.scss new file mode 100644 index 00000000..b071af8b --- /dev/null +++ b/frontend/src/assets/scss/card.scss @@ -0,0 +1,288 @@ +.layout-form { + display: flex; + flex-direction: column; + flex-grow: 1; + } + + .cart__title { + margin-bottom: 15px; + } + + .cart__additional { + margin-top: 15px; + margin-bottom: 25px; + } + + .cart__empty { + padding: 20px 30px; + } + + .cart-form { + display: flex; + align-items: center; + flex-wrap: wrap; + } + + .cart-form__select { + display: flex; + align-items: center; + + margin-right: auto; + + span { + margin-right: 16px; + } + } + + .cart-form__label { + @include b-s16-h19; + + white-space: nowrap; + } + + .cart-form__address { + display: flex; + align-items: center; + + width: 100%; + margin-top: 20px; + } + + .cart-form__input { + flex-grow: 1; + + margin-bottom: 20px; + margin-left: 16px; + + &--small { + max-width: 120px; + } + } + + .cart-list { + @include clear-list; + + padding: 15px 0; + } + + .cart-list__item { + display: flex; + align-items: flex-start; + + margin-bottom: 15px; + padding-right: 15px; + padding-bottom: 15px; + padding-left: 15px; + + border-bottom: 1px solid rgba($green-500, 0.1); + + &:last-child { + margin-bottom: 0; + padding-bottom: 0; + + border-bottom: none; + } + } + + .cart-list__product { + flex-grow: 1; + + margin-right: auto; + } + + .cart-list__counter { + width: 54px; + margin-right: auto; + margin-left: 20px; + } + + .cart-list__price { + min-width: 100px; + margin-right: 36px; + margin-left: 10px; + + text-align: right; + + b { + @include b-s16-h19; + } + } + + .cart-list__edit { + @include l-s11-h13; + + cursor: pointer; + transition: 0.3s; + + border: none; + outline: none; + background-color: transparent; + + &:hover { + color: $green-500; + } + + &:active { + color: $green-600; + } + + &:focus { + color: $green-400; + } + } + + .product { + display: flex; + align-items: center; + } + + .product__text { + margin-left: 15px; + + h2 { + @include b-s18-h21; + + margin-top: 0; + margin-bottom: 10px; + } + + ul { + @include clear-list; + @include l-s11-h13; + } + } + + .footer { + display: flex; + align-items: center; + + margin-top: auto; + padding: 25px 2.12%; + + background-color: rgba($green-500, 0.1); + } + + .footer__more { + width: 220px; + margin-right: 16px; + + a { + padding-top: 16px; + padding-bottom: 16px; + } + } + + .footer__text { + @include l-s11-h13; + + color: rgba($black, 0.5); + } + + .footer__price { + @include b-s24-h28; + + margin-right: 12px; + margin-left: auto; + } + + .footer__submit { + button { + padding: 16px 14px; + } + } + + .additional-list { + @include clear-list; + + display: flex; + flex-wrap: wrap; + } + + .additional-list__description { + display: flex; + align-items: flex-start; + + margin: 0; + margin-bottom: 8px; + } + + .additional-list__item { + display: flex; + align-items: flex-start; + flex-direction: column; + + width: 200px; + margin-right: 15px; + margin-bottom: 15px; + padding-top: 15px; + padding-bottom: 15px; + + img { + margin-right: 10px; + margin-left: 15px; + } + + span { + @include b-s14-h16; + + display: inline; + + width: 100px; + margin-right: 15px; + } + } + + .additional-list__wrapper { + display: flex; + align-items: center; + + box-sizing: border-box; + width: 100%; + margin-top: auto; + padding-top: 18px; + padding-right: 15px; + padding-left: 15px; + + border-top: 1px solid rgba($green-500, 0.1); + } + + .additional-list__counter { + width: 54px; + margin-right: auto; + } + + .additional-list__price { + @include b-s16-h19; + } + + .select { + @include r-s16-h19; + + display: block; + + margin: 0; + padding: 8px 16px; + padding-right: 30px; + + cursor: pointer; + transition: 0.3s; + + color: $black; + border: 1px solid $purple-400; + border-radius: 8px; + outline: none; + background-color: $silver-100; + background-image: url("@/assets/img/select.svg"); + background-repeat: no-repeat; + background-position: right 8px center; + font-family: inherit; + appearance: none; + + &:hover { + border-color: $orange-100; + } + + &:focus { + border-color: $green-500; + } + } \ No newline at end of file diff --git a/frontend/src/assets/scss/sidebar.scss b/frontend/src/assets/scss/sidebar.scss new file mode 100644 index 00000000..812aa63c --- /dev/null +++ b/frontend/src/assets/scss/sidebar.scss @@ -0,0 +1,70 @@ +.layout__sidebar { + position: fixed; + z-index: 2; + top: 0; + left: 0; + + width: 180px; + height: 100%; + + background-color: rgba($green-500, 0.05); + } + + .layout__logo { + display: block; + + margin-bottom: 30px; + padding-top: 10px; + padding-bottom: 10px; + + background-color: $green-500; + + img { + display: block; + + margin: 0 auto; + } + } + + .layout__link { + @include b-s14-h16; + + display: block; + + padding: 8px 14px; + + transition: 0.3s; + + color: $black; + + &--active { + background-color: rgba($green-500, 0.1); + } + + &:hover { + background-color: rgba($green-500, 0.2); + } + + &:active { + color: rgba($black, 0.5); + } + } + + .layout__content { + padding-top: 22px; + padding-right: 2.12%; + padding-left: 200px; + } + + .layout__title { + margin-bottom: 27px; + } + + .layout__button { + $self: &; + margin-top: 40px; + + button[type="button"] { + padding: 12px 23px; + } + } \ No newline at end of file diff --git a/frontend/src/common/components/AppCounter.vue b/frontend/src/common/components/AppCounter.vue new file mode 100644 index 00000000..63785437 --- /dev/null +++ b/frontend/src/common/components/AppCounter.vue @@ -0,0 +1,179 @@ + + + + + + + \ No newline at end of file diff --git a/frontend/src/common/components/AppDrag.vue b/frontend/src/common/components/AppDrag.vue new file mode 100644 index 00000000..4e599913 --- /dev/null +++ b/frontend/src/common/components/AppDrag.vue @@ -0,0 +1,30 @@ + + + diff --git a/frontend/src/common/components/AppDrop.vue b/frontend/src/common/components/AppDrop.vue new file mode 100644 index 00000000..0371744e --- /dev/null +++ b/frontend/src/common/components/AppDrop.vue @@ -0,0 +1,27 @@ + + + + diff --git a/frontend/src/common/components/address/AddressCard.vue b/frontend/src/common/components/address/AddressCard.vue new file mode 100644 index 00000000..8157d149 --- /dev/null +++ b/frontend/src/common/components/address/AddressCard.vue @@ -0,0 +1,82 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/common/components/address/AddressEditForm.vue b/frontend/src/common/components/address/AddressEditForm.vue new file mode 100644 index 00000000..7d49582e --- /dev/null +++ b/frontend/src/common/components/address/AddressEditForm.vue @@ -0,0 +1,134 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/common/components/popup/AppPopup.vue b/frontend/src/common/components/popup/AppPopup.vue new file mode 100644 index 00000000..c1e98ae7 --- /dev/null +++ b/frontend/src/common/components/popup/AppPopup.vue @@ -0,0 +1,52 @@ + + + \ No newline at end of file diff --git a/frontend/src/common/components/popup/AppPopupButton.vue b/frontend/src/common/components/popup/AppPopupButton.vue new file mode 100644 index 00000000..82bba223 --- /dev/null +++ b/frontend/src/common/components/popup/AppPopupButton.vue @@ -0,0 +1,13 @@ + + + \ No newline at end of file diff --git a/frontend/src/common/components/popup/AppPopupTitle.vue b/frontend/src/common/components/popup/AppPopupTitle.vue new file mode 100644 index 00000000..feba70e6 --- /dev/null +++ b/frontend/src/common/components/popup/AppPopupTitle.vue @@ -0,0 +1,14 @@ + + + \ No newline at end of file diff --git a/frontend/src/common/constants/index.js b/frontend/src/common/constants/index.js new file mode 100644 index 00000000..e138c51a --- /dev/null +++ b/frontend/src/common/constants/index.js @@ -0,0 +1,2 @@ +export const MAX_INGREDIENT_COUNT = 3; +export const DATA_TRANSFER_PAYLOAD = "payload"; \ No newline at end of file diff --git a/frontend/src/common/data/doughSizes.js b/frontend/src/common/data/doughSizes.js index 2495107b..ae750a90 100644 --- a/frontend/src/common/data/doughSizes.js +++ b/frontend/src/common/data/doughSizes.js @@ -1,4 +1,4 @@ export default { - 1: "light", - 2: "large", -}; + 1: "light", + 2: "large", + }; \ No newline at end of file diff --git a/frontend/src/common/data/ingredients.js b/frontend/src/common/data/ingredients.js index b7b6960a..eafd8356 100644 --- a/frontend/src/common/data/ingredients.js +++ b/frontend/src/common/data/ingredients.js @@ -1,17 +1,17 @@ export default { - 1: "mushrooms", - 2: "cheddar", - 3: "salami", - 4: "ham", - 5: "ananas", - 6: "bacon", - 7: "onion", - 8: "chile", - 9: "jalapeno", - 10: "olives", - 11: "tomatoes", - 12: "salmon", - 13: "mozzarella", - 14: "parmesan", - 15: "blue_cheese", -}; + 1: "mushrooms", + 2: "cheddar", + 3: "salami", + 4: "ham", + 5: "ananas", + 6: "bacon", + 7: "onion", + 8: "chile", + 9: "jalapeno", + 10: "olives", + 11: "tomatoes", + 12: "salmon", + 13: "mozzarella", + 14: "parmesan", + 15: "blue_cheese", + }; \ No newline at end of file diff --git a/frontend/src/common/data/sauces.js b/frontend/src/common/data/sauces.js index 4d2c5c5a..b8a407eb 100644 --- a/frontend/src/common/data/sauces.js +++ b/frontend/src/common/data/sauces.js @@ -1,4 +1,5 @@ export default { - 1: "tomato", - 2: "creamy", -}; + 1: "tomato", + 2: "chees", + 3: "terijaki", + }; \ No newline at end of file diff --git a/frontend/src/common/data/sizes.js b/frontend/src/common/data/sizes.js index 70bf1042..c07216bd 100644 --- a/frontend/src/common/data/sizes.js +++ b/frontend/src/common/data/sizes.js @@ -1,5 +1,5 @@ -export default { - 1: "small", - 2: "normal", - 3: "big", -}; + export default { + 1: "small", + 2: "normal", + 3: "big", + }; \ No newline at end of file diff --git a/frontend/src/common/helpers/ingredients-quantity.js b/frontend/src/common/helpers/ingredients-quantity.js new file mode 100644 index 00000000..3b72c40d --- /dev/null +++ b/frontend/src/common/helpers/ingredients-quantity.js @@ -0,0 +1,12 @@ +import { useDataStore } from "@/stores/data"; + +/* Функция вернёт объект { ингредиент: количество } */ +export const ingredientsQuantity = (pizza) => { + const data = useDataStore(); + return data.ingredients.reduce((acc, val) => { + acc[val.id] = + pizza.ingredients.find((item) => item.ingredientId === val.id) + ?.quantity ?? 0; + return acc; + }, {}); +}; \ No newline at end of file diff --git a/frontend/src/common/helpers/normalize.js b/frontend/src/common/helpers/normalize.js new file mode 100644 index 00000000..aeaa79ac --- /dev/null +++ b/frontend/src/common/helpers/normalize.js @@ -0,0 +1,32 @@ +import doughSizes from "@/common/data/doughSizes"; +import ingredients from "@/common/data/ingredients"; +import sauces from "@/common/data/sauces"; +import sizes from "@/common/data/sizes"; + +export const normalizeDough = (dough) => { + return { + ...dough, + value: doughSizes[dough.id], + }; + }; + + export const normalizeSize = (size) => { + return { + ...size, + value: sizes[size.id], + }; + }; + + export const normalizeIngredients = (ingredient) => { + return { + ...ingredient, + value: ingredients[ingredient.id], + }; + }; + + export const normalizeSauces = (sauce) => { + return { + ...sauce, + value: sauces[sauce.id], + }; + }; \ No newline at end of file diff --git a/frontend/src/common/helpers/pizza-price.js b/frontend/src/common/helpers/pizza-price.js new file mode 100644 index 00000000..26cd3a0d --- /dev/null +++ b/frontend/src/common/helpers/pizza-price.js @@ -0,0 +1,27 @@ +import { useDataStore } from "@/stores/data"; +import { ingredientsQuantity } from "@/common/helpers/ingredients-quantity"; + +export const pizzaPrice = (pizza) => { + const data = useDataStore(); + const ingredients = ingredientsQuantity(pizza); + + const sizeMultiplier = + data.sizes.find((item) => item.id === pizza.sizeId)?.multiplier ?? 1; + + const doughPrice = + data.doughs.find((item) => item.id === pizza.doughId)?.price ?? 0; + + const saucePrice = + data.sauces.find((item) => item.id === pizza.sauceId)?.price ?? 0; + + /* + * Здесь мы при помощи метода map превращаем массив ингредиентов + * в массив значений, соответствующих итоговой стоимости каждого ингредиента — просто умножаем известную цену на количество. + * После чего методом reduce вычисляем сумму всех элементов массива, что даст нам общую стоимость всех ингредиентов. + */ + const ingredientsPrice = data.ingredients + .map((item) => ingredients[item.id] * item.price) + .reduce((acc, item) => acc + item, 0); + + return (doughPrice + saucePrice + ingredientsPrice) * sizeMultiplier; +}; \ No newline at end of file diff --git a/frontend/src/common/helpers/public-image.js b/frontend/src/common/helpers/public-image.js new file mode 100644 index 00000000..25e99928 --- /dev/null +++ b/frontend/src/common/helpers/public-image.js @@ -0,0 +1,5 @@ +export const getPublicImage = (path) => { + const publicUrl = "/api"; + const divider = path.startsWith("/") ? "" : "/"; + return [publicUrl, path].join(divider); + }; \ No newline at end of file diff --git a/frontend/src/common/validator/index.js b/frontend/src/common/validator/index.js new file mode 100644 index 00000000..8dcf18fd --- /dev/null +++ b/frontend/src/common/validator/index.js @@ -0,0 +1,54 @@ +import { isRef } from "vue"; +import { EMAIL_REGEX } from "@/common/constants"; + +const rules = { + required: { + rule: (value) => { + const data = isRef(value) ? value.value : value; + return !!data?.trim(); + }, + message: "Поле обязательно для заполнения", + }, + email: { + rule: (value) => { + const data = isRef(value) ? value.value : value; + const normalizedValue = String(data).toLowerCase(); + return data ? EMAIL_REGEX.test(normalizedValue) : true; + }, + message: "Электронная почта имеет неверный формат", + }, + }; + + const validate = (value, appliedRules) => { + let error = ""; + appliedRules.forEach((appliedRule) => { + if (!rules[appliedRule]) { + return; + } + const { rule, message } = rules[appliedRule]; + if (!rule(value)) { + error = message; + } + }); + return error; + }; + + export const validateFields = (fields, validations) => { + let isValid = true; + Object.keys(validations).forEach((key) => { + validations[key].error = validate(fields[key], validations[key].rules); + if (validations[key].error) { + isValid = false; + } + }); + return isValid; + }; + + export const clearValidationErrors = (validations) => { + if (!validations) { + return; + } + Object.keys(validations).forEach((key) => { + validations[key].error = ""; + }); + }; \ No newline at end of file diff --git a/frontend/src/components/layouts/index.js b/frontend/src/components/layouts/index.js new file mode 100644 index 00000000..48236f12 --- /dev/null +++ b/frontend/src/components/layouts/index.js @@ -0,0 +1 @@ +export { default as AppLayout } from "./AppLayout.vue"; \ No newline at end of file diff --git a/frontend/src/components/views/index.js b/frontend/src/components/views/index.js new file mode 100644 index 00000000..aebdd2c4 --- /dev/null +++ b/frontend/src/components/views/index.js @@ -0,0 +1 @@ +export { default as HomeView } from "./HomeView.vue"; \ No newline at end of file diff --git a/frontend/src/layouts/AppHeader.vue b/frontend/src/layouts/AppHeader.vue new file mode 100644 index 00000000..63a5eb68 --- /dev/null +++ b/frontend/src/layouts/AppHeader.vue @@ -0,0 +1,121 @@ + + + \ No newline at end of file diff --git a/frontend/src/layouts/AppLayout.vue b/frontend/src/layouts/AppLayout.vue new file mode 100644 index 00000000..0fb9cb49 --- /dev/null +++ b/frontend/src/layouts/AppLayout.vue @@ -0,0 +1,28 @@ + + + \ No newline at end of file diff --git a/frontend/src/layouts/DefaultHeader.vue b/frontend/src/layouts/DefaultHeader.vue new file mode 100644 index 00000000..0fd2bd96 --- /dev/null +++ b/frontend/src/layouts/DefaultHeader.vue @@ -0,0 +1,154 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/layouts/DefaultLayout.vue b/frontend/src/layouts/DefaultLayout.vue new file mode 100644 index 00000000..6b654a15 --- /dev/null +++ b/frontend/src/layouts/DefaultLayout.vue @@ -0,0 +1,19 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/layouts/SidebarLayout.vue b/frontend/src/layouts/SidebarLayout.vue new file mode 100644 index 00000000..e0c95856 --- /dev/null +++ b/frontend/src/layouts/SidebarLayout.vue @@ -0,0 +1,83 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/layouts/SimpleLayout.vue b/frontend/src/layouts/SimpleLayout.vue new file mode 100644 index 00000000..799fa6f9 --- /dev/null +++ b/frontend/src/layouts/SimpleLayout.vue @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/frontend/src/main.js b/frontend/src/main.js index 786af441..dcf30ede 100644 --- a/frontend/src/main.js +++ b/frontend/src/main.js @@ -1,12 +1,10 @@ import { createApp } from "vue"; import { createPinia } from "pinia"; - import App from "./App.vue"; -import router from "./router"; +import router from './router' const app = createApp(App); app.use(createPinia()); app.use(router); - -app.mount("#app"); +app.mount("#app"); \ No newline at end of file diff --git a/frontend/src/middlewares/isLoggedIn.js b/frontend/src/middlewares/isLoggedIn.js new file mode 100644 index 00000000..06d8d619 --- /dev/null +++ b/frontend/src/middlewares/isLoggedIn.js @@ -0,0 +1,11 @@ +import { useAuthStore } from "@/stores/auth"; + +export const isLoggedIn = ({ to }) => { + const authStore = useAuthStore(); + + if (!authStore.isAuthenticated) { + return { path: "/login", query: { redirect: to.fullPath } }; + } else { + return true; + } +}; \ No newline at end of file diff --git a/frontend/src/middlewares/middlewarePipeline.js b/frontend/src/middlewares/middlewarePipeline.js new file mode 100644 index 00000000..28dab22b --- /dev/null +++ b/frontend/src/middlewares/middlewarePipeline.js @@ -0,0 +1,19 @@ +export const middlewarePipeline = (router) => { + router.beforeEach(async (to, from, next) => { + const middlewares = to.meta.middlewares; + if (!middlewares) { + return true; + } + for (const middleware of middlewares) { + const result = await middleware({ to, from }); + if ( + typeof result === "object" || + typeof result === "string" || + result === false + ) { + return result; + } + } + return true; + }); + }; \ No newline at end of file diff --git a/frontend/src/mocks/addresses.json b/frontend/src/mocks/addresses.json new file mode 100644 index 00000000..454139ac --- /dev/null +++ b/frontend/src/mocks/addresses.json @@ -0,0 +1,11 @@ +[ + { + "id": 1, + "name": "Мой адрес", + "street": "Невский пр.", + "building": "22", + "flat": "46", + "comment": "Позвоните, пожалуйста, от проходной", + "userId": "1" + } + ] \ No newline at end of file diff --git a/frontend/src/mocks/dough.json b/frontend/src/mocks/dough.json index 70b0fa1a..9254c985 100644 --- a/frontend/src/mocks/dough.json +++ b/frontend/src/mocks/dough.json @@ -1,16 +1,16 @@ [ - { - "id": 1, - "name": "Тонкое", - "image": "dough-light.svg", - "description": "Из твердых сортов пшеницы", - "price": 300 - }, - { - "id": 2, - "name": "Толстое", - "image": "dough-large.svg", - "description": "Из твердых сортов пшеницы", - "price": 300 - } -] + { + "id": 1, + "name": "Тонкое", + "image": "dough-light.svg", + "description": "Из твердых сортов пшеницы", + "price": 300 + }, + { + "id": 2, + "name": "Толстое", + "image": "dough-large.svg", + "description": "Из твердых сортов пшеницы", + "price": 300 + } + ] \ No newline at end of file diff --git a/frontend/src/mocks/ingredients.json b/frontend/src/mocks/ingredients.json index c9cbf543..4e04991c 100644 --- a/frontend/src/mocks/ingredients.json +++ b/frontend/src/mocks/ingredients.json @@ -1,17 +1,17 @@ [ - { "id": 1, "name": "Грибы", "image": "filling/mushrooms.svg", "price": 33 }, - { "id": 2, "name": "Чеддер", "image": "filling/cheddar.svg", "price": 42 }, - { "id": 3, "name": "Салями", "image": "filling/salami.svg", "price": 42 }, - { "id": 4, "name": "Ветчина", "image": "filling/ham.svg", "price": 42 }, - { "id": 5, "name": "Ананас", "image": "filling/ananas.svg", "price": 25 }, - { "id": 6, "name": "Бекон", "image": "filling/bacon.svg", "price": 42 }, - { "id": 7, "name": "Лук", "image": "filling/onion.svg", "price": 21 }, - { "id": 8, "name": "Чили", "image": "filling/chile.svg", "price": 21 }, - { "id": 9, "name": "Халапеньо", "image": "filling/jalapeno.svg", "price": 25 }, - { "id": 10, "name": "Маслины", "image": "filling/olives.svg", "price": 25 }, - { "id": 11, "name": "Томаты", "image": "filling/tomatoes.svg", "price": 35 }, - { "id": 12, "name": "Лосось", "image": "filling/salmon.svg", "price": 50 }, - { "id": 13, "name": "Моцарелла", "image": "filling/mozzarella.svg", "price": 35 }, - { "id": 14, "name": "Пармезан", "image": "filling/parmesan.svg", "price": 35 }, - { "id": 15, "name": "Блю чиз", "image": "filling/blue_cheese.svg", "price": 50 } -] + { "id": 1, "name": "Грибы", "image": "filling/mushrooms.svg", "price": 33 }, + { "id": 2, "name": "Чеддер", "image": "filling/cheddar.svg", "price": 42 }, + { "id": 3, "name": "Салями", "image": "filling/salami.svg", "price": 42 }, + { "id": 4, "name": "Ветчина", "image": "filling/ham.svg", "price": 42 }, + { "id": 5, "name": "Ананас", "image": "filling/ananas.svg", "price": 25 }, + { "id": 6, "name": "Бекон", "image": "filling/bacon.svg", "price": 42 }, + { "id": 7, "name": "Лук", "image": "filling/onion.svg", "price": 21 }, + { "id": 8, "name": "Чили", "image": "filling/chile.svg", "price": 21 }, + { "id": 9, "name": "Халапеньо", "image": "filling/jalapeno.svg", "price": 25 }, + { "id": 10, "name": "Маслины", "image": "filling/olives.svg", "price": 25 }, + { "id": 11, "name": "Томаты", "image": "filling/tomatoes.svg", "price": 35 }, + { "id": 12, "name": "Лосось", "image": "filling/salmon.svg", "price": 50 }, + { "id": 13, "name": "Моцарелла", "image": "filling/mozzarella.svg", "price": 35 }, + { "id": 14, "name": "Пармезан", "image": "filling/parmesan.svg", "price": 35 }, + { "id": 15, "name": "Блю чиз", "image": "filling/blue_cheese.svg", "price": 50 } + ] \ No newline at end of file diff --git a/frontend/src/mocks/sauces.json b/frontend/src/mocks/sauces.json index 3a1a1b8a..32f31ecf 100644 --- a/frontend/src/mocks/sauces.json +++ b/frontend/src/mocks/sauces.json @@ -1,12 +1,18 @@ [ - { - "id": 1, - "name": "Томатный", - "price": 50 - }, - { - "id": 2, - "name": "Сливочный", - "price": 50 - } -] + { + "id": 1, + "name": "Томатный", + "price": 50 + }, + { + "id": 2, + "name": "Сырный", + "price": 50 + } + , + { + "id": 3, + "name": "Терияки", + "price": 50 + } + ] \ No newline at end of file diff --git a/frontend/src/mocks/sizes.json b/frontend/src/mocks/sizes.json index eb299dbe..5f506df9 100644 --- a/frontend/src/mocks/sizes.json +++ b/frontend/src/mocks/sizes.json @@ -1,20 +1,20 @@ [ - { - "id": 1, - "name": "23 см", - "image": "diameter.svg", - "multiplier": 1 - }, - { - "id": 2, - "name": "32 см", - "image": "diameter.svg", - "multiplier": 2 - }, - { - "id": 3, - "name": "45 см", - "image": "diameter.svg", - "multiplier": 3 - } -] + { + "id": 1, + "name": "23 см", + "image": "diameter.svg", + "multiplier": 1 + }, + { + "id": 2, + "name": "32 см", + "image": "diameter.svg", + "multiplier": 2 + }, + { + "id": 3, + "name": "45 см", + "image": "diameter.svg", + "multiplier": 3 + } + ] \ No newline at end of file diff --git a/frontend/src/modules/constructor/DiameterSelector.vue b/frontend/src/modules/constructor/DiameterSelector.vue new file mode 100644 index 00000000..c231fd30 --- /dev/null +++ b/frontend/src/modules/constructor/DiameterSelector.vue @@ -0,0 +1,103 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/modules/constructor/DoughSelector.vue b/frontend/src/modules/constructor/DoughSelector.vue new file mode 100644 index 00000000..39fbd86f --- /dev/null +++ b/frontend/src/modules/constructor/DoughSelector.vue @@ -0,0 +1,85 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/modules/constructor/IngredientsSelector.vue b/frontend/src/modules/constructor/IngredientsSelector.vue new file mode 100644 index 00000000..714147b5 --- /dev/null +++ b/frontend/src/modules/constructor/IngredientsSelector.vue @@ -0,0 +1,112 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/modules/constructor/PizzaConstructor.vue b/frontend/src/modules/constructor/PizzaConstructor.vue new file mode 100644 index 00000000..8fa80416 --- /dev/null +++ b/frontend/src/modules/constructor/PizzaConstructor.vue @@ -0,0 +1,213 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/modules/constructor/SauceSelector.vue b/frontend/src/modules/constructor/SauceSelector.vue new file mode 100644 index 00000000..9f86de9f --- /dev/null +++ b/frontend/src/modules/constructor/SauceSelector.vue @@ -0,0 +1,58 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/router/index.js b/frontend/src/router/index.js index 6c13dc55..0eed19ec 100644 --- a/frontend/src/router/index.js +++ b/frontend/src/router/index.js @@ -1,8 +1,67 @@ -import { createRouter, createWebHistory } from "vue-router"; +import { createRouter, createWebHistory } from 'vue-router' +import { middlewarePipeline } from "@/middlewares/middlewarePipeline"; + +import HomeView from '@/views/HomeView.vue' +import Login from '@/views/LoginView.vue' +import Cart from '@/views/CartView.vue' +import Users from '@/views/UserView.vue' +import Orders from '@/views/OrdersView.vue' +import ProfileView from '@/views/ProfileView.vue' +import Success from '@/views/SuccessView.vue' +import { isLoggedIn } from "@/middlewares/isLoggedIn"; const router = createRouter({ - history: createWebHistory(import.meta.env.BASE_URL), - routes: [], -}); + history: createWebHistory(), + routes: [ + { + path: "", + name: "home", + component: HomeView, + meta: { layout: "DefaultLayout" }, + }, + { + path: "/login", + name: "login", + component: Login, + meta: { layout: "SimpleLayout" }, + }, + { + path: "/cart", + name: "cart", + component: Cart, + meta: { layout: "DefaultLayout" }, + }, + { + path: "/success", + name: "success", + component: Success, + meta: { layout: "SimpleLayout" }, + }, + { + path: "/user", + name: "user", + component: Users, + meta: { + layout: "DefaultLayout", + middlewares: [isLoggedIn], + }, + children: [ + { + path: "orders", + name: "orders", + component: Orders, + }, + { + path: "profile", + name: "profile", + component: ProfileView, + }, + ], + }, + + ] +}) export default router; + +middlewarePipeline(router); \ No newline at end of file diff --git a/frontend/src/services/api/api.service.js b/frontend/src/services/api/api.service.js new file mode 100644 index 00000000..fced1d53 --- /dev/null +++ b/frontend/src/services/api/api.service.js @@ -0,0 +1,71 @@ +import axios, { AxiosError } from "axios"; + +class ApiError extends Error { + constructor(message, response) { + super(message); + this.response = response; + } +} + +export class ApiService { + _getError(e) { + if (e instanceof AxiosError) { + return new ApiError( + e.response.data?.error?.message ?? e.message, + e.response + ); + } else { + return new ApiError(e.message, e.response); + } + } + + _wrapper1(method, url) { + return async () => { + try { + const response = await method(url); + return { + __state: "success", + ...response, + }; + } catch (e) { + return { + __state: "error", + data: this._getError(e), + }; + } + }; + } + + _wrapper2(method, url, payload) { + return async () => { + try { + const response = await method(url, payload); + return { + __state: "success", + ...response, + }; + } catch (e) { + return { + __state: "error", + data: this._getError(e), + }; + } + }; + } + + $get(url) { + return this._wrapper1(axios.get, url)(); + } + + $post(url, payload) { + return this._wrapper2(axios.post, url, payload)(); + } + + $put(url, payload) { + return this._wrapper2(axios.put, url, payload)(); + } + + $delete(url) { + return this._wrapper1(axios.delete, url)(); + } +} diff --git a/frontend/src/services/api/auth.js b/frontend/src/services/api/auth.js new file mode 100644 index 00000000..0e0b70e7 --- /dev/null +++ b/frontend/src/services/api/auth.js @@ -0,0 +1,27 @@ +import axios from "axios"; +import { ApiService } from "@/services/api/api.service"; + +export class AuthService extends ApiService { + constructor(path) { + super(); + this.path = path; + } + + setAuthHeader(token) { + axios.defaults.headers.common["Authorization"] = token + ? `Bearer ${token}` + : ""; + } + + login(params) { + return this.$post(`${this.path}/login`, params); + } + + logout() { + return this.$delete(`${this.path}/logout`); + } + + whoami() { + return this.$get(`${this.path}/whoAmI`); + } +} diff --git a/frontend/src/services/api/crud.js b/frontend/src/services/api/crud.js new file mode 100644 index 00000000..12bbf80f --- /dev/null +++ b/frontend/src/services/api/crud.js @@ -0,0 +1,24 @@ +import { ApiService } from "@/services/api/api.services"; + +export class CrudService extends ApiService { + constructor(resource) { + super(); + this.resource = resource; + } + + get() { + return this.$get(this.resource); + } + + post(entity) { + return this.$post(this.resource, entity); + } + + put(entity) { + return this.$put(`${this.resource}/${entity.id}`, entity); + } + + delete(id) { + return this.$delete(`${this.resource}/${id}`); + } +} \ No newline at end of file diff --git a/frontend/src/services/jwt/jwt.js b/frontend/src/services/jwt/jwt.js new file mode 100644 index 00000000..4604be02 --- /dev/null +++ b/frontend/src/services/jwt/jwt.js @@ -0,0 +1,17 @@ +const ID_TOKEN_KEY = "token"; + +class JwtService { + getToken() { + return window.localStorage.getItem(ID_TOKEN_KEY); + } + + saveToken(token) { + window.localStorage.setItem(ID_TOKEN_KEY, token); + } + + destroyToken() { + window.localStorage.removeItem(ID_TOKEN_KEY); + } +} + +export default new JwtService(); \ No newline at end of file diff --git a/frontend/src/services/resources/address.resource.js b/frontend/src/services/resources/address.resource.js new file mode 100644 index 00000000..2548ac3a --- /dev/null +++ b/frontend/src/services/resources/address.resource.js @@ -0,0 +1,23 @@ +import { CrudService } from "@/services/api/crud"; + +export class AddressResource extends CrudService { + constructor() { + super("/api/addresses"); + } + + getAddresses() { + return this.get(); + } + + addAddress(address) { + return this.post(address); + } + + updateAddress(address) { + return this.put(address); + } + + removeAddress(addressId) { + return this.delete(addressId); + } +} \ No newline at end of file diff --git a/frontend/src/services/resources/auth.resource.js b/frontend/src/services/resources/auth.resource.js new file mode 100644 index 00000000..149c93d0 --- /dev/null +++ b/frontend/src/services/resources/auth.resource.js @@ -0,0 +1,7 @@ +import { AuthService } from "@/services/api/auth.service"; + +export class AuthResource extends AuthService { + constructor() { + super("/api"); + } +} \ No newline at end of file diff --git a/frontend/src/services/resources/dough.resource.js b/frontend/src/services/resources/dough.resource.js new file mode 100644 index 00000000..d58df9ba --- /dev/null +++ b/frontend/src/services/resources/dough.resource.js @@ -0,0 +1,11 @@ +import { CrudService } from "@/services/api/crud"; + +export class DoughResource extends CrudService { + constructor() { + super("/api/dough"); + } + + getDoughs() { + return this.get(); + } +} diff --git a/frontend/src/services/resources/index.js b/frontend/src/services/resources/index.js new file mode 100644 index 00000000..38f0f524 --- /dev/null +++ b/frontend/src/services/resources/index.js @@ -0,0 +1,19 @@ +import { AuthResource } from "@/services/resources/auth.resource"; +import { AddressResource } from "@/services/resources/address.resource"; +import { DoughResource } from "@/services/resources/dough.resource"; +import { IngredientResource } from "@/services/resources/ingredient.resource"; +import { MiscResource } from "@/services/resources/misc.resource"; +import { OrderResource } from "@/services/resources/order.resource"; +import { SauceResource } from "@/services/resources/sauce.resource"; +import { SizeResource } from "@/services/resources/size.resource"; + +export default { + address: new AddressResource(), + auth: new AuthResource(), + dough: new DoughResource(), + ingredient: new IngredientResource(), + misc: new MiscResource(), + order: new OrderResource(), + sauce: new SauceResource(), + size: new SizeResource(), +}; \ No newline at end of file diff --git a/frontend/src/services/resources/ingredient.resource.js b/frontend/src/services/resources/ingredient.resource.js new file mode 100644 index 00000000..3000ab25 --- /dev/null +++ b/frontend/src/services/resources/ingredient.resource.js @@ -0,0 +1,12 @@ +import { CrudService } from "@/services/api/crud"; + +export class AddressResource extends CrudService { + constructor() { + super("/api/ingredients"); + } + + getIngredients() { + return this.get(); + } + +} \ No newline at end of file diff --git a/frontend/src/services/resources/misc.resource.js b/frontend/src/services/resources/misc.resource.js new file mode 100644 index 00000000..0df89d57 --- /dev/null +++ b/frontend/src/services/resources/misc.resource.js @@ -0,0 +1,13 @@ +import { CrudService } from "@/services/api/crud"; + + +export class MiscResource extends CrudService { + constructor() { + super("/api/misc"); + } + + getMisc() { + return this.get(); + } + +} \ No newline at end of file diff --git a/frontend/src/services/resources/order.resource.js b/frontend/src/services/resources/order.resource.js new file mode 100644 index 00000000..2dc256cf --- /dev/null +++ b/frontend/src/services/resources/order.resource.js @@ -0,0 +1,21 @@ +import { CrudService } from "@/services/api/crud"; + + +export class OrderResource extends CrudService { + constructor() { + super("/api/orders"); + } + + getOrders() { + return this.get(); + } + + createOrder(order) { + return this.post(order); + } + + removeOrder(id) { + return this.delete(id); + } + +} \ No newline at end of file diff --git a/frontend/src/services/resources/sauce.resource.js b/frontend/src/services/resources/sauce.resource.js new file mode 100644 index 00000000..7ec33905 --- /dev/null +++ b/frontend/src/services/resources/sauce.resource.js @@ -0,0 +1,11 @@ +import { CrudService } from "@/services/api/crud"; + +export class SauceResource extends CrudService { + constructor() { + super("/api/sauces"); + } + + getSauces() { + return this.get(); + } +} \ No newline at end of file diff --git a/frontend/src/services/resources/size.resource.js b/frontend/src/services/resources/size.resource.js new file mode 100644 index 00000000..59cc7c90 --- /dev/null +++ b/frontend/src/services/resources/size.resource.js @@ -0,0 +1,11 @@ +import { CrudService } from "@/services/api/crud"; + +export class SizeResource extends CrudService { + constructor() { + super("/api/sizes"); + } + + getSizes() { + return this.get(); + } +} \ No newline at end of file diff --git a/frontend/src/stores/auth.js b/frontend/src/stores/auth.js new file mode 100644 index 00000000..325bef2d --- /dev/null +++ b/frontend/src/stores/auth.js @@ -0,0 +1,65 @@ +import { defineStore } from "pinia"; +import resources from "@/services/resources"; +import { useProfileStore } from "@/stores/profile"; +import JwtService from "@/services/jwt/jwt"; + +export const useAuthStore = defineStore("auth", { + state: () => ({ + user: null, + }), + getters: { + isAuthenticated() { + return !!this.user; + }, + }, + actions: { + setUser(user) { + this.user = user; + }, + + async login(credentials) { + const res = await resources.auth.login(credentials); + if (res.__state === "success") { + jwtService.saveToken(res.data.token); + return "success"; + } else { + return res.data.message; + } + }, + + async logout() { + await resources.auth.logout(); + jwtService.destroyToken(); + resources.auth.setAuthHeader(""); + this.user = null; + }, + + async whoami() { + resources.auth.setAuthHeader(JwtService.getToken()); + const profileStore = useProfileStore(); + + const res1 = await resources.auth.whoami(); + if (res1.__state !== "success") { + await this.logout(); + return; + } else { + this.setUser(res1.data); + } + + const res2 = await resources.address.getAddresses(); + if (res2.__state !== "success") { + await this.logout(); + return; + } else { + profileStore.setAddresses(res2.data); + } + + const res3 = await resources.order.getOrders(); + if (res3.__state !== "success") { + await this.logout(); + } else { + profileStore.setOrders(res3.data); + } + }, + }, +}); \ No newline at end of file diff --git a/frontend/src/stores/cart.js b/frontend/src/stores/cart.js new file mode 100644 index 00000000..abdccbf3 --- /dev/null +++ b/frontend/src/stores/cart.js @@ -0,0 +1,135 @@ +import { defineStore } from "pinia"; +import { pizzaPrice } from "@/common/helpers/pizza-price"; +import { useDataStore } from "@/stores/data"; +import resources from "@/services/resources"; +import { useAuthStore } from "@/stores/auth"; + +export const useCartStore = defineStore("cart", { + state: () => ({ + phone: "", + address: { + street: "", + building: "", + flat: "", + comment: "", + }, + pizzas: [], + misc: [], + }), + getters: { + pizzasExtended: (state) => { + const data = useDataStore(); + + return state.pizzas.map((pizza) => { + const pizzaIngredientsIds = pizza.ingredients.map( + (i) => i.ingredientId + ); + + return { + name: pizza.name, + quantity: pizza.quantity, + dough: data.doughs.find((i) => i.id === pizza.doughId), + size: data.sizes.find((i) => i.id === pizza.sizeId), + sauce: data.sauces.find((i) => i.id === pizza.sauceId), + ingredients: data.ingredients.filter((i) => + pizzaIngredientsIds.includes(i.id) + ), + price: pizzaPrice(pizza), + }; + }); + }, + miscExtended: (state) => { + const data = useDataStore(); + + return data.misc.map((misc) => { + return { + ...misc, + quantity: state.misc.find((i) => i.miscId === misc.id)?.quantity ?? 0, + }; + }); + }, + total: (state) => { + const pizzaPrices = state.pizzasExtended + .map((item) => item.quantity * item.price) + .reduce((acc, val) => acc + val, 0); + + const miscPrices = state.miscExtended + .map((item) => item.quantity * item.price) + .reduce((acc, val) => acc + val, 0); + + return pizzaPrices + miscPrices; + }, + }, + actions: { + savePizza(pizza) { + const { index, ...pizzaData } = pizza; + + if (index !== null) { + this.pizzas[index] = { + quantity: this.pizzas[index].quantity, + ...pizzaData, + }; + } else { + this.pizzas.push({ + quantity: 1, + ...pizzaData, + }); + } + }, + setPizzaQuantity(index, count) { + if (this.pizzas[index]) { + this.pizzas[index].quantity = count; + } + }, + setMiscQuantity(miscId, count) { + const miscIdx = this.misc.findIndex((item) => item.miscId === miscId); + if (miscIdx === -1 && count > 0) { + this.misc.push({ + miscId, + quantity: 1, + }); + return; + } else if (miscIdx === -1) { + return; + } + + /* Удаляем ингредиент, если количество 0 */ + if (count === 0) { + this.misc.splice(miscIdx, 1); + return; + } + + this.misc[miscIdx].quantity = count; + }, + setPhone(phone) { + this.phone = phone; + }, + setAddress(address) { + const { street, building, flat, comment } = address; + this.address = { street, building, flat, comment }; + }, + setStreet(street) { + this.address.street = street; + }, + setBuilding(building) { + this.address.building = building; + }, + setFlat(flat) { + this.address.flat = flat; + }, + setComment(comment) { + this.address.street = comment; + }, + async publishOrder() { + const authStore = useAuthStore(); + + return await resources.order.createOrder({ + userId: authStore.user?.id ?? null, + phone: this.phone, + address: this.address, + pizzas: this.pizzas, + misc: this.misc, + }); + }, + }, +}); \ No newline at end of file diff --git a/frontend/src/stores/data.js b/frontend/src/stores/data.js new file mode 100644 index 00000000..4bce9566 --- /dev/null +++ b/frontend/src/stores/data.js @@ -0,0 +1,75 @@ +import { defineStore } from "pinia"; +import { + normalizeDough, + normalizeIngredients, + normalizeSauces, + normalizeSize, +} from "@/common/helpers/normalize"; +import resources from "@/services/resources"; + +export const useDataStore = defineStore("data", { + state: () => ({ + doughs: [], + ingredients: [], + sauces: [], + sizes: [], + misc: [], + }), + getters: { + isDataLoaded() { + return ( + this.doughs.length > 0 && + this.ingredients.length > 0 && + this.sauces.length > 0 && + this.sizes.length > 0 && + this.misc.length > 0 + ); + }, + }, + actions: { + async loadData() { + await Promise.all([ + this.loadDoughs(), + this.loadIngredients(), + this.loadSauces(), + this.loadSizes(), + this.loadMisc(), + ]); + }, + + async loadDoughs() { + const res = await resources.dough.getDoughs(); + if (res.__state === "success") { + this.doughs = res.data.map(normalizeDough); + } + }, + + async loadIngredients() { + const res = await resources.ingredient.getIngredients(); + if (res.__state === "success") { + this.ingredients = res.data.map(normalizeIngredients); + } + }, + + async loadSauces() { + const res = await resources.sauce.getSauces(); + if (res.__state === "success") { + this.sauces = res.data.map(normalizeSauces); + } + }, + + async loadSizes() { + const res = await resources.size.getSizes(); + if (res.__state === "success") { + this.sizes = res.data.map(normalizeSize); + } + }, + + async loadMisc() { + const res = await resources.misc.getMisc(); + if (res.__state === "success") { + this.misc = res.data; + } + }, + }, +}); \ No newline at end of file diff --git a/frontend/src/stores/pizza.js b/frontend/src/stores/pizza.js new file mode 100644 index 00000000..7b2b2fab --- /dev/null +++ b/frontend/src/stores/pizza.js @@ -0,0 +1,114 @@ +import { defineStore } from 'pinia' +import { ingredientsQuantity } from "@/common/helpers/ingredients-quantity"; +import { pizzaPrice } from "@/common/helpers/pizza-price"; +import { useDataStore } from "@/stores/data"; + +export const usePizzaStore = defineStore('pizzaStore', { + state: () => ({ + index: null, + name: '', + sauceId: 0, + doughId: 0, + sizeId: 0, + ingredients: [], + }), + getters: { + sauce: (state) => { + const data = useDataStore(); + return data.sauces.find((i) => i.id === state.sauceId) ?? data.sauces[0]; + }, + dough: (state) => { + const data = useDataStore(); + return data.doughs.find((i) => i.id === state.doughId) ?? data.doughs[0]; + }, + size: (state) => { + const data = useDataStore(); + return data.sizes.find((i) => i.id === state.sizeId) ?? data.sizes[0]; + }, + ingredientsExtended: (state) => { + const data = useDataStore(); + const pizzaIngredientsIds = state.ingredients.map((i) => i.ingredientId); + return data.ingredients + .filter((i) => pizzaIngredientsIds.includes(i.id)) + .map((i) => { + return { + ...i, + quantity: + state.ingredients.find((item) => item.ingredientId === i.id) + ?.quantity ?? 0, + }; + }); + }, + price: (state) => { + return pizzaPrice(state); + }, + ingredientQuantities: (state) => { + return ingredientsQuantity(state); + }, + }, + actions: { + setIndex(index) { + this.index = index; + }, + setName(name) { + this.name = name; + }, + setSauce(sauceId) { + this.sauceId = sauceId; + }, + setDough(doughId) { + this.doughId = doughId; + }, + setSize(sizeId) { + this.sizeId = sizeId; + }, + setIngredients(ingredients) { + this.ingredients = ingredients; + }, + addIngredient(ingredientId) { + this.ingredients.push({ + ingredientId, + quantity: 1, + }); + }, + incrementIngredientQuantity(ingredientId) { + const ingredientIdx = this.ingredients.findIndex( + (item) => item.ingredientId === ingredientId + ); + + if (ingredientIdx === -1) { + this.addIngredient(ingredientId); + return; + } + + this.ingredients[ingredientIdx].quantity++; + }, + setIngredientQuantity(ingredientId, count) { + const ingredientIdx = this.ingredients.findIndex( + (item) => item.ingredientId === ingredientId + ); + + if (ingredientIdx === -1 && count > 0) { + this.addIngredient(ingredientId); + return; + } else if (ingredientIdx === -1) { + return; + } + + if (count === 0) { + this.ingredients.splice(ingredientIdx, 1); + return; + } + + this.ingredients[ingredientIdx].quantity = count; + }, + loadPizza(pizza) { + this.index = pizza.index; + this.name = pizza.name; + this.sauceId = pizza.sauceId; + this.doughId = pizza.doughId; + this.sizeId = pizza.sizeId; + this.ingredients = pizza.ingredients; + }, + } +}) \ No newline at end of file diff --git a/frontend/src/stores/profile.js b/frontend/src/stores/profile.js new file mode 100644 index 00000000..76778cfc --- /dev/null +++ b/frontend/src/stores/profile.js @@ -0,0 +1,110 @@ +import { defineStore } from "pinia"; +import { pizzaPrice } from "@/common/helpers/pizza-price"; +import { useDataStore } from "@/stores/data"; +import resources from "@/services/resources"; +import { useAuthStore } from "@/stores/auth"; + +export const useProfileStore = defineStore("profileStore", { + state: () => ({ + addresses: addressesJSON, + }), + getters: { + ordersExtended: (state) => { + const data = useDataStore(); + + return state.orders.map((order) => { + const orderPizzas = order.orderPizzas?.map((pizza) => { + return { + name: pizza.name, + quantity: pizza.quantity, + dough: data.doughs.find((i) => i.id === pizza.doughId), + size: data.sizes.find((i) => i.id === pizza.sizeId), + sauce: data.sauces.find((i) => i.id === pizza.sauceId), + ingredients: pizza.ingredients.map((ingredient) => { + return { + ...data.ingredients.find( + (i) => i.id === ingredient.ingredientId + ), + quantity: ingredient.quantity, + }; + }), + price: pizzaPrice(pizza), + }; + }); + + const orderMisc = + order.orderMisc?.map((misc) => { + return { + ...data.misc.find((item) => item.id === misc.miscId), + quantity: misc.quantity, + }; + }) ?? []; + + const pizzaPrices = + orderPizzas + ?.map((item) => item.quantity * item.price) + .reduce((acc, val) => acc + val, 0) ?? 0; + + const miscPrices = + orderMisc + ?.map((item) => item.quantity * item.price) + .reduce((acc, val) => acc + val, 0) ?? 0; + + return { + ...order, + orderPizzas, + orderMisc, + total: pizzaPrices + miscPrices, + }; + }); + }, + }, + actions: { + setAddresses(addresses) { + this.addresses = addresses; + }, + setOrders(orders) { + this.orders = orders; + }, + async loadOrders() { + const res = await resources.order.getOrders(); + if (res.__state === "success") { + this.setOrders(res.data); + } + }, + async removeOrder(orderId) { + const res = await resources.order.removeOrder(orderId); + if (res.__state === "success") { + this.orders = this.orders.filter((i) => i.id !== orderId); + } + }, + async addAddress(address) { + const authStore = useAuthStore(); + if (!authStore.isAuthenticated) { + return; + } + + const res = await resources.address.addAddress({ + ...address, + userId: authStore.user.id, + }); + if (res.__state === "success") { + this.addresses.push(res.data); + } + }, + async removeAddress(addressId) { + const res = await resources.address.removeAddress(addressId); + if (res.__state === "success") { + this.addresses = this.addresses.filter((i) => i.id !== addressId); + } + }, + async updateAddress(address) { + const res = await resources.address.updateAddress(address); + if (res.__state === "success") { + this.addresses = this.addresses.map((i) => + i.id === address.id ? address : i + ); + } + }, + }, +}); diff --git a/frontend/src/style.css b/frontend/src/style.css new file mode 100644 index 00000000..f6913154 --- /dev/null +++ b/frontend/src/style.css @@ -0,0 +1,79 @@ +:root { + font-family: system-ui, Avenir, Helvetica, Arial, sans-serif; + line-height: 1.5; + font-weight: 400; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} +a:hover { + color: #535bf2; +} + +body { + margin: 0; + display: flex; + place-items: center; + min-width: 320px; + min-height: 100vh; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +button { + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: #1a1a1a; + cursor: pointer; + transition: border-color 0.25s; +} +button:hover { + border-color: #646cff; +} +button:focus, +button:focus-visible { + outline: 4px auto -webkit-focus-ring-color; +} + +.card { + padding: 2em; +} + +#app { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + a:hover { + color: #747bff; + } + button { + background-color: #f9f9f9; + } +} diff --git a/frontend/src/views/CartView.vue b/frontend/src/views/CartView.vue new file mode 100644 index 00000000..e2d59d95 --- /dev/null +++ b/frontend/src/views/CartView.vue @@ -0,0 +1,537 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/views/HomeView.vue b/frontend/src/views/HomeView.vue new file mode 100644 index 00000000..cf078a33 --- /dev/null +++ b/frontend/src/views/HomeView.vue @@ -0,0 +1,170 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/views/LoginView.vue b/frontend/src/views/LoginView.vue new file mode 100644 index 00000000..60b8058d --- /dev/null +++ b/frontend/src/views/LoginView.vue @@ -0,0 +1,223 @@ + + + + + diff --git a/frontend/src/views/OrdersView.vue b/frontend/src/views/OrdersView.vue new file mode 100644 index 00000000..e05f4673 --- /dev/null +++ b/frontend/src/views/OrdersView.vue @@ -0,0 +1,234 @@ + + + + + \ No newline at end of file diff --git a/frontend/src/views/ProfileView.vue b/frontend/src/views/ProfileView.vue new file mode 100644 index 00000000..6b201a52 --- /dev/null +++ b/frontend/src/views/ProfileView.vue @@ -0,0 +1,150 @@ + + + + + Копировать + + + +Стили также нужно изменить. Добавим :deep к некоторым правилам для корректного отображения компонентов адреса. + + \ No newline at end of file diff --git a/frontend/src/views/SuccessView.vue b/frontend/src/views/SuccessView.vue new file mode 100644 index 00000000..b9bbcbcd --- /dev/null +++ b/frontend/src/views/SuccessView.vue @@ -0,0 +1,28 @@ + + + \ No newline at end of file diff --git a/frontend/src/views/UserView.vue b/frontend/src/views/UserView.vue new file mode 100644 index 00000000..766e5b22 --- /dev/null +++ b/frontend/src/views/UserView.vue @@ -0,0 +1,25 @@ + + + \ No newline at end of file diff --git a/frontend/vite.config.js b/frontend/vite.config.js index be796387..2b82c4e1 100644 --- a/frontend/vite.config.js +++ b/frontend/vite.config.js @@ -1,7 +1,7 @@ -import { fileURLToPath, URL } from "url"; +import { fileURLToPath, URL } from "url" -import { defineConfig } from "vite"; -import vue from "@vitejs/plugin-vue"; +import { defineConfig } from "vite" +import vue from "@vitejs/plugin-vue" // https://vitejs.dev/config/ export default defineConfig({ @@ -19,9 +19,10 @@ export default defineConfig({ port: 8080, proxy: { "/api": { - target: "http://backend:3000/", + target: "https://pizza.vue.htmlacademy.pro/", + changeOrigin: true, rewrite: (path) => path.replace(/^\/api/, ""), }, }, - }, -}); + } +}) \ No newline at end of file