diff --git a/Fitmo_backend/app/Http/Controllers/ProductController.php b/Fitmo_backend/app/Http/Controllers/ProductController.php index 0285dc0..0d2affb 100644 --- a/Fitmo_backend/app/Http/Controllers/ProductController.php +++ b/Fitmo_backend/app/Http/Controllers/ProductController.php @@ -22,27 +22,44 @@ class ProductController extends Controller * * @return \Illuminate\Http\Response */ - public function index() +public function index(Request $request) { $products = Product::select( - 'prices.*', - 'product_states.*', - 'categories.name as category_name', - 'colors.*', + 'prices.*', + 'product_states.*', + 'categories.name as category_name', + 'colors.*', 'products.*', 'product_categories.category_id as category_id' - )->with('categories') - ->selectRaw('(SELECT GROUP_CONCAT(image_path) FROM images WHERE images.product_id = products.id AND images.is_main = 1) AS image_urls') - ->leftJoin('prices', 'products.id', '=', 'prices.product_id') + )->with(['categories', 'children' => function($query) { + $query->select( + 'prices.*', + 'product_states.*', + 'categories.name as category_name', + 'colors.*', + 'products.*', + 'product_categories.category_id as category_id' + ) + ->leftJoin('prices', 'products.id', '=', 'prices.product_id') + ->join('product_states', 'products.id', '=', 'product_states.product_id') + ->join('product_categories', 'products.id', '=', 'product_categories.product_id') + ->join('categories', 'categories.id', '=', 'product_categories.category_id') + ->leftJoin('colors', 'products.color_id', '=', 'colors.id') + ->selectRaw('(SELECT GROUP_CONCAT(image_path) FROM images WHERE images.product_id = products.id AND images.is_main = 1) AS image_urls') + ->where('products.parent_id', '!=', 0); + }]) + ->leftJoin('prices', 'products.id', '=', 'prices.product_id') ->join('product_states', 'products.id', '=', 'product_states.product_id') ->join('product_categories', 'products.id', '=', 'product_categories.product_id') - ->join('categories', 'categories.id', '=', 'product_categories.category_id') + ->join('categories', 'categories.id', '=', 'product_categories.category_id') ->leftJoin('colors', 'products.color_id', '=', 'colors.id') + ->selectRaw('(SELECT GROUP_CONCAT(image_path) FROM images WHERE images.product_id = products.id AND images.is_main = 1) AS image_urls') ->orderBy('products.created_at', 'desc') - // ->paginated(20) - ->get(); + ->where('products.parent_id', 0) + ->paginate(20, ['*'], 'page', $request->page ?? 1); + - $products = $this->formatProducts($products); + $products = $this->formatProducts($products, true); return $products; } @@ -53,7 +70,7 @@ public function getSingleProduct($product_url_name) ->leftJoin('prices', 'products.id', '=', 'prices.product_id') ->join('product_states', 'products.id', '=', 'product_states.product_id') ->join('product_categories', 'products.id', '=', 'product_categories.product_id') - ->join('categories', 'categories.id', '=', 'product_categories.category_id') + ->join('categories', 'categories.id', '=', 'product_categories.category_id') ->join('map_table', 'product_categories.category_id', '=', 'map_table.category_id') ->leftJoin('colors', 'products.color_id', '=', 'colors.id') ->orderBy('products.created_at', "desc") @@ -111,12 +128,16 @@ public function getSearchedItems($query) public function getAllProducts() { - return Product::select('colors.*', 'prices.*', 'product_states.*', 'products.*') + $products = Product::select('colors.*', 'prices.*', 'product_states.*', 'products.*') ->selectRaw('(SELECT GROUP_CONCAT(image_path) FROM images WHERE images.product_id = products.id AND images.is_main = 1) AS image_urls') ->leftJoin('prices', 'products.id', '=', 'prices.product_id') ->leftJoin('colors', 'products.color_id', '=', 'colors.id') ->join('product_states', 'products.id', '=', 'product_states.product_id') ->get(); + foreach ($products as $product) { + $product->image_urls = explode(',', $product->image_urls); + } + return $products; } public function setUrlNames() @@ -134,28 +155,28 @@ public function getMainProducts() return Product::select('categories.*', 'products.*')->join('product_categories', 'products.id', '=', 'product_categories.product_id') ->join('categories', 'categories.id', '=', 'product_categories.category_id')->orderBy("products.name")->where("products.parent_id", 0)->with('categories')->get(); } - public function formatProducts($products) + public function formatProducts($products, $shouldWorkWithChildren = false) { + $newProducts = []; foreach ($products as $product) { - $product->image_urls = explode(',', $product->image_urls); - $product->categoryPath = str_replace([","], "", Str::ascii(Str::kebab(strtolower(($product["category_name"]))))); - } - $newProducts = []; - foreach ($products as $product) { - $temp = []; - $product->isActive = 0; - if ($product["parent_id"] === 0) { - $product->isActive = 1; - $temp[] = $product; - - foreach ($products as $tempProduct) { - if ($product["id"] === $tempProduct["parent_id"]) { - $temp[] = $tempProduct; - } - } - $newProducts[] = $temp; - } - } + $temp = []; + $product->isActive = 1; // Activate the product + $temp[] = $product; // Add the product to the array + // Format parent product + $product->image_urls = explode(',', $product->image_urls); + $product->categoryPath = str_replace([","], "", Str::ascii(Str::kebab(strtolower(($product["category_name"]))))); + + // Check if the product has children and format them + if ($shouldWorkWithChildren && $product->children->isNotEmpty()) { + foreach ($product->children as $child) { + $child->image_urls = explode(',', $child->image_urls); + $child->categoryPath = str_replace([","], "", Str::ascii(Str::kebab(strtolower(($child["category_name"]))))); + $child->isActive = 0; // Activate the product + $temp[] = $child; + } + } + $newProducts[] = $temp; + } return $newProducts; } @@ -216,7 +237,7 @@ public function getCategoryProducts($categoryName) ->selectRaw('(SELECT GROUP_CONCAT(image_path) FROM images WHERE images.product_id = products.id AND images.is_main = 1) AS image_urls') ->leftJoin('prices', 'products.id', '=', 'prices.product_id') ->join('product_categories', 'products.id', '=', 'product_categories.product_id') - ->join('categories', 'categories.id', '=', 'product_categories.category_id') + ->join('categories', 'categories.id', '=', 'product_categories.category_id') ->join('product_states', 'products.id', '=', 'product_states.product_id') ->leftJoin('colors', 'products.color_id', '=', 'colors.id') ->whereIn('category_id', $categoriesToSearch) @@ -331,7 +352,7 @@ public function store(Request $request) $newProduct->name = $request->name; $newProduct->description = $request->subName; $newProduct->variant = $request->variant; - + $newProduct->parent_id = $request->parent["id"]; $newProduct->url_name = str_replace(" ", "-", (strtolower(str_replace([","], "", Str::ascii($request["name"]))))); if ($request->colors["colorName"]) { @@ -347,13 +368,13 @@ public function store(Request $request) $newProductCategories->category_id = $category["categoryId"]; $newProductCategories->save(); } - + } else{ foreach ($request->parent["categories"] as $category) { $newProductCategories = new ProductCategory(); $newProductCategories->product_id = $newProduct->id; - $newProductCategories->category_id = $category["id"]; + $newProductCategories->category_id = $category["id"]; $newProductCategories->save(); } } diff --git a/Fitmo_backend/app/Models/Product.php b/Fitmo_backend/app/Models/Product.php index 8f1669d..e723478 100644 --- a/Fitmo_backend/app/Models/Product.php +++ b/Fitmo_backend/app/Models/Product.php @@ -8,7 +8,7 @@ class Product extends Model { use HasFactory; - + public function orders() { return $this->belongsToMany(Order::class)->withTimestamps(); @@ -24,4 +24,8 @@ public function color() { return $this->belongsTo(Color::class); } + public function children() + { + return $this->hasMany(Product::class, 'parent_id'); + } } diff --git a/Fitmo_frontend/package-lock.json b/Fitmo_frontend/package-lock.json index f8926c2..76a99fe 100644 --- a/Fitmo_frontend/package-lock.json +++ b/Fitmo_frontend/package-lock.json @@ -38,6 +38,7 @@ "vue3-quill": "^0.3.1", "vue3-snackbar": "^2.2.0", "vuedraggable": "^4.1.0", + "vuestic-ui": "^1.9.12", "vuex": "^4.1.0", "yup": "^1.3.2" }, @@ -369,6 +370,63 @@ "node": ">=12" } }, + "node_modules/@floating-ui/core": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.4.tgz", + "integrity": "sha512-a4IowK4QkXl4SCWTGUR0INAfEOX3wtsYw3rKK5InQEHMGObkR8Xk44qYQD9P4r6HHw0iIfK6GUKECmY8sTkqRA==", + "dependencies": { + "@floating-ui/utils": "^0.2.4" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.7.tgz", + "integrity": "sha512-wmVfPG5o2xnKDU4jx/m4w5qva9FWHcnZ8BvzEe90D/RpwsJaTAVYPEPdQ8sbr/N8zZTAHlZUTQdqg8ZUbzHmng==", + "dependencies": { + "@floating-ui/core": "^1.6.0", + "@floating-ui/utils": "^0.2.4" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.4.tgz", + "integrity": "sha512-dWO2pw8hhi+WrXq1YJy2yCuWoL20PddgGaqTgVe4cOS9Q6qklXCiA1tJEqX6BEwRNSCP84/afac9hd4MS+zEUA==" + }, + "node_modules/@floating-ui/vue": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@floating-ui/vue/-/vue-1.1.1.tgz", + "integrity": "sha512-cyawjk9etPZPl/RVtMRnWrwtAhWbPVSrRVYARgOzhLIqxr0k2up1APrrFjqP9QwRQ0AwjKSvbWg4YC6jESutow==", + "dependencies": { + "@floating-ui/dom": "^1.0.0", + "@floating-ui/utils": "^0.2.4", + "vue-demi": ">=0.13.0" + } + }, + "node_modules/@floating-ui/vue/node_modules/vue-demi": { + "version": "0.14.8", + "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.8.tgz", + "integrity": "sha512-Uuqnk9YE9SsWeReYqK2alDI5YzciATE0r2SkA6iMAtuXvNTMNACJLJEXNXaEy94ECuBe4Sk6RzRU80kjdbIo1Q==", + "hasInstallScript": true, + "bin": { + "vue-demi-fix": "bin/vue-demi-fix.js", + "vue-demi-switch": "bin/vue-demi-switch.js" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + }, + "peerDependencies": { + "@vue/composition-api": "^1.0.0-rc.1", + "vue": "^3.0.0-0 || ^2.6.0" + }, + "peerDependenciesMeta": { + "@vue/composition-api": { + "optional": true + } + } + }, "node_modules/@fortawesome/fontawesome-common-types": { "version": "6.4.2", "resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.4.2.tgz", @@ -703,6 +761,11 @@ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" }, + "node_modules/@types/lodash": { + "version": "4.17.6", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.6.tgz", + "integrity": "sha512-OpXEVoCKSS3lQqjx9GGGOapBeuW5eUboYHRlHP9urXPX25IKZ6AnP5ZRxtVf63iieUbsHxLn8NQ5Nlftc6yzAA==" + }, "node_modules/@types/mime": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", @@ -1992,6 +2055,11 @@ "node": ">= 10.0" } }, + "node_modules/cleave.js": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/cleave.js/-/cleave.js-1.6.0.tgz", + "integrity": "sha512-ivqesy3j5hQVG3gywPfwKPbi/7ZSftY/UNp5uphnqjr25yI2CP8FS2ODQPzuLXXnNLi29e2+PgPkkiKUXLs/Nw==" + }, "node_modules/cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", @@ -2130,6 +2198,11 @@ "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==" }, + "node_modules/colortranslator": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/colortranslator/-/colortranslator-1.10.2.tgz", + "integrity": "sha512-+vAnjWTdI0Vh6fT3I+dA6DAtnLfo91TzYKjewYeZ2dHdhF8Yo5OMo47inNJdJOXwjJ9lPD0LkNeQOLU/8yyfZA==" + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -8040,6 +8113,21 @@ "vue": "^3.0.1" } }, + "node_modules/vuestic-ui": { + "version": "1.9.12", + "resolved": "https://registry.npmjs.org/vuestic-ui/-/vuestic-ui-1.9.12.tgz", + "integrity": "sha512-YIcNBLbpryIQS8dGyNnxQ+1RgTPbm0rJ9biywei4+fVxMIcNGVs/qC2muAKwd9Od3GCJVbx/+uGxedjxXV1vLA==", + "dependencies": { + "@floating-ui/vue": "^1.0.1", + "@types/lodash": "^4.14.161", + "cleave.js": "^1.6.0", + "colortranslator": "^1.9.2", + "lodash": "^4.17.21" + }, + "peerDependencies": { + "vue": "^3.0.4" + } + }, "node_modules/vuex": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/vuex/-/vuex-4.1.0.tgz", diff --git a/Fitmo_frontend/package.json b/Fitmo_frontend/package.json index 9e4f0c6..7c72ed7 100644 --- a/Fitmo_frontend/package.json +++ b/Fitmo_frontend/package.json @@ -37,6 +37,7 @@ "vue3-quill": "^0.3.1", "vue3-snackbar": "^2.2.0", "vuedraggable": "^4.1.0", + "vuestic-ui": "^1.9.12", "vuex": "^4.1.0", "yup": "^1.3.2" }, diff --git a/Fitmo_frontend/src/components/Product.vue b/Fitmo_frontend/src/components/Product.vue index 64d6dbb..115cb80 100644 --- a/Fitmo_frontend/src/components/Product.vue +++ b/Fitmo_frontend/src/components/Product.vue @@ -40,7 +40,6 @@ " alt="" /> - - - - -
Akce | +Status | +Celková částka | +Delivery | +Delivery Price | +Payment | +Payment Price | +Fakturační adresa | +Dodací adresa | +Kdo objednal | +||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
+ + | +
+ |
+ {{ order.status }} | +{{ order.total_price }} | +{{ order.delivery_type.name }} | +{{ order.delivery_price }} | +{{ order.payment_type.name }} | +{{ order.payment_price }} | ++ {{ + order.billing_address.street + + ", " + + order.billing_address.city + + " " + + order.billing_address.zip + + " - " + + order.billing_address.country + }} + | ++ {{ + order.delivery_address.street + + ", " + + order.delivery_address.city + + " " + + order.delivery_address.zip + + " - " + + order.delivery_address.country + }} + | +
+ {{ order.user.name }} +{{ order.user.email }} +{{ order.user.prePhone }}{{ order.user.phone }} + |
+ |||||
Akce | -Status | -Celková částka | -Delivery | -Delivery Price | -Payment | -Payment Price | -Fakturační adresa | -Dodací adresa | -Kdo objednal | +Název | +Počet | +Cena | +Cena se slevou | +% sleva | +Barva/varianta |
- - | -
- |
- {{ order.status }} | -{{ order.total_price }} | -{{ order.delivery_type.name }} | -{{ order.delivery_price }} | -{{ order.payment_type.name }} | -{{ order.payment_price }} | -- {{ - order.billing_address.street + - ", " + - order.billing_address.city + - " " + - order.billing_address.zip + - " - " + - order.billing_address.country - }} - | -- {{ - order.delivery_address.street + - ", " + - order.delivery_address.city + - " " + - order.delivery_address.zip + - " - " + - order.delivery_address.country - }} - | -
- {{ order.user.name }} -{{ order.user.email }} -{{ order.user.prePhone }}{{ order.user.phone }} - |
- |||||
Název | -Počet | -Cena | -Cena se slevou | -% sleva | -Barva/varianta | -||||||||||
{{ product.name }} | -{{ product.pivot.product_count }} | -{{ product.price }} | -{{ product.discounted }} | -{{ product.discountedPercent }} | -- {{ - product.color - ? product.color.color_name - : product.variant - ? product.variant - : "" - }} - | -