From fa53b7205883e174516141e91993b4ecdccd6115 Mon Sep 17 00:00:00 2001 From: Jitendra Singh Date: Fri, 14 Feb 2020 11:40:12 +0530 Subject: [PATCH 001/229] Booking product type added --- composer.json | 3 +- config/app.php | 1 + config/concord.php | 3 +- packages/Webkul/BookingProduct/.gitignore | 3 + packages/Webkul/BookingProduct/package.json | 22 ++ .../assets/css/booking-product.css | 1 + .../publishable/assets/images/arrow-down.svg | 10 + .../publishable/assets/images/arrow-up.svg | 10 + .../publishable/assets/images/location.svg | 11 + .../publishable/assets/images/phone.svg | 18 + .../publishable/assets/images/slot.svg | 15 + .../publishable/assets/mix-manifest.json | 3 + .../src/Config/product_types.php | 10 + .../src/Contracts/BookingProduct.php | 7 + .../BookingProductAppointmentSlot.php | 7 + .../Contracts/BookingProductDefaultSlot.php | 7 + .../src/Contracts/BookingProductEventSlot.php | 7 + .../Contracts/BookingProductRentalSlot.php | 7 + .../src/Contracts/BookingProductTableSlot.php | 7 + ...2_180307_create_booking_products_table.php | 37 +++ ...te_booking_product_default_slots_table.php | 39 +++ ...ooking_product_appointment_slots_table.php | 41 +++ ...eate_booking_product_event_slots_table.php | 36 ++ ...ate_booking_product_rental_slots_table.php | 42 +++ ...eate_booking_product_table_slots_table.php | 43 +++ .../src/Helpers/AppointmentSlot.php | 13 + .../BookingProduct/src/Helpers/Booking.php | 276 ++++++++++++++++ .../src/Helpers/DefaultSlot.php | 154 +++++++++ .../BookingProduct/src/Helpers/EventSlot.php | 60 ++++ .../BookingProduct/src/Helpers/RentalSlot.php | 94 ++++++ .../BookingProduct/src/Helpers/TableSlot.php | 13 + .../Shop/BookingProductController.php | 78 +++++ .../src/Http/Controllers/Shop/Controller.php | 12 + .../BookingProduct/src/Http/front-routes.php | 5 + .../BookingProduct/src/Listeners/Product.php | 53 +++ .../src/Models/BookingProduct.php | 54 +++ .../Models/BookingProductAppointmentSlot.php | 15 + .../BookingProductAppointmentSlotProxy.php | 10 + .../src/Models/BookingProductDefaultSlot.php | 25 ++ .../Models/BookingProductDefaultSlotProxy.php | 10 + .../src/Models/BookingProductEventSlot.php | 15 + .../Models/BookingProductEventSlotProxy.php | 10 + .../src/Models/BookingProductProxy.php | 10 + .../src/Models/BookingProductRentalSlot.php | 15 + .../Models/BookingProductRentalSlotProxy.php | 10 + .../src/Models/BookingProductTableSlot.php | 15 + .../Models/BookingProductTableSlotProxy.php | 10 + .../BookingProductServiceProvider.php | 42 +++ .../src/Providers/EventServiceProvider.php | 23 ++ .../src/Providers/ModuleServiceProvider.php | 17 + ...ookingProductAppointmentSlotRepository.php | 24 ++ .../BookingProductDefaultSlotRepository.php | 24 ++ .../BookingProductEventSlotRepository.php | 24 ++ .../BookingProductRentalSlotRepository.php | 24 ++ .../Repositories/BookingProductRepository.php | 135 ++++++++ .../BookingProductTableSlotRepository.php | 24 ++ .../Resources/assets/images/arrow-down.svg | 10 + .../src/Resources/assets/images/arrow-up.svg | 10 + .../src/Resources/assets/images/location.svg | 11 + .../src/Resources/assets/images/phone.svg | 18 + .../src/Resources/assets/images/slot.svg | 15 + .../src/Resources/assets/sass/app.scss | 213 ++++++++++++ .../src/Resources/assets/sass/icons.scss | 17 + .../src/Resources/lang/en/app.php | 109 ++++++ .../products/accordians/booking.blade.php | 84 +++++ .../accordians/booking/appointment.blade.php | 131 ++++++++ .../accordians/booking/default.blade.php | 311 ++++++++++++++++++ .../accordians/booking/event.blade.php | 186 +++++++++++ .../accordians/booking/rental.blade.php | 151 +++++++++ .../accordians/booking/slots.blade.php | 211 ++++++++++++ .../accordians/booking/table.blade.php | 150 +++++++++ .../shop/products/view/booking.blade.php | 60 ++++ .../view/booking/appointment.blade.php | 55 ++++ .../products/view/booking/default.blade.php | 10 + .../products/view/booking/event.blade.php | 80 +++++ .../products/view/booking/rental.blade.php | 153 +++++++++ .../products/view/booking/slots.blade.php | 58 ++++ .../products/view/booking/table.blade.php | 69 ++++ .../BookingProduct/src/Type/Booking.php | 169 ++++++++++ packages/Webkul/BookingProduct/webpack.mix.js | 31 ++ 80 files changed, 3994 insertions(+), 2 deletions(-) create mode 100755 packages/Webkul/BookingProduct/.gitignore create mode 100644 packages/Webkul/BookingProduct/package.json create mode 100644 packages/Webkul/BookingProduct/publishable/assets/css/booking-product.css create mode 100644 packages/Webkul/BookingProduct/publishable/assets/images/arrow-down.svg create mode 100644 packages/Webkul/BookingProduct/publishable/assets/images/arrow-up.svg create mode 100644 packages/Webkul/BookingProduct/publishable/assets/images/location.svg create mode 100644 packages/Webkul/BookingProduct/publishable/assets/images/phone.svg create mode 100644 packages/Webkul/BookingProduct/publishable/assets/images/slot.svg create mode 100644 packages/Webkul/BookingProduct/publishable/assets/mix-manifest.json create mode 100644 packages/Webkul/BookingProduct/src/Config/product_types.php create mode 100644 packages/Webkul/BookingProduct/src/Contracts/BookingProduct.php create mode 100644 packages/Webkul/BookingProduct/src/Contracts/BookingProductAppointmentSlot.php create mode 100644 packages/Webkul/BookingProduct/src/Contracts/BookingProductDefaultSlot.php create mode 100644 packages/Webkul/BookingProduct/src/Contracts/BookingProductEventSlot.php create mode 100644 packages/Webkul/BookingProduct/src/Contracts/BookingProductRentalSlot.php create mode 100644 packages/Webkul/BookingProduct/src/Contracts/BookingProductTableSlot.php create mode 100644 packages/Webkul/BookingProduct/src/Database/Migrations/2019_07_02_180307_create_booking_products_table.php create mode 100644 packages/Webkul/BookingProduct/src/Database/Migrations/2019_07_05_154415_create_booking_product_default_slots_table.php create mode 100644 packages/Webkul/BookingProduct/src/Database/Migrations/2019_07_05_154429_create_booking_product_appointment_slots_table.php create mode 100644 packages/Webkul/BookingProduct/src/Database/Migrations/2019_07_05_154440_create_booking_product_event_slots_table.php create mode 100644 packages/Webkul/BookingProduct/src/Database/Migrations/2019_07_05_154451_create_booking_product_rental_slots_table.php create mode 100644 packages/Webkul/BookingProduct/src/Database/Migrations/2019_07_05_154502_create_booking_product_table_slots_table.php create mode 100644 packages/Webkul/BookingProduct/src/Helpers/AppointmentSlot.php create mode 100644 packages/Webkul/BookingProduct/src/Helpers/Booking.php create mode 100644 packages/Webkul/BookingProduct/src/Helpers/DefaultSlot.php create mode 100644 packages/Webkul/BookingProduct/src/Helpers/EventSlot.php create mode 100644 packages/Webkul/BookingProduct/src/Helpers/RentalSlot.php create mode 100644 packages/Webkul/BookingProduct/src/Helpers/TableSlot.php create mode 100644 packages/Webkul/BookingProduct/src/Http/Controllers/Shop/BookingProductController.php create mode 100644 packages/Webkul/BookingProduct/src/Http/Controllers/Shop/Controller.php create mode 100644 packages/Webkul/BookingProduct/src/Http/front-routes.php create mode 100644 packages/Webkul/BookingProduct/src/Listeners/Product.php create mode 100644 packages/Webkul/BookingProduct/src/Models/BookingProduct.php create mode 100644 packages/Webkul/BookingProduct/src/Models/BookingProductAppointmentSlot.php create mode 100644 packages/Webkul/BookingProduct/src/Models/BookingProductAppointmentSlotProxy.php create mode 100644 packages/Webkul/BookingProduct/src/Models/BookingProductDefaultSlot.php create mode 100644 packages/Webkul/BookingProduct/src/Models/BookingProductDefaultSlotProxy.php create mode 100644 packages/Webkul/BookingProduct/src/Models/BookingProductEventSlot.php create mode 100644 packages/Webkul/BookingProduct/src/Models/BookingProductEventSlotProxy.php create mode 100644 packages/Webkul/BookingProduct/src/Models/BookingProductProxy.php create mode 100644 packages/Webkul/BookingProduct/src/Models/BookingProductRentalSlot.php create mode 100644 packages/Webkul/BookingProduct/src/Models/BookingProductRentalSlotProxy.php create mode 100644 packages/Webkul/BookingProduct/src/Models/BookingProductTableSlot.php create mode 100644 packages/Webkul/BookingProduct/src/Models/BookingProductTableSlotProxy.php create mode 100644 packages/Webkul/BookingProduct/src/Providers/BookingProductServiceProvider.php create mode 100644 packages/Webkul/BookingProduct/src/Providers/EventServiceProvider.php create mode 100644 packages/Webkul/BookingProduct/src/Providers/ModuleServiceProvider.php create mode 100644 packages/Webkul/BookingProduct/src/Repositories/BookingProductAppointmentSlotRepository.php create mode 100644 packages/Webkul/BookingProduct/src/Repositories/BookingProductDefaultSlotRepository.php create mode 100644 packages/Webkul/BookingProduct/src/Repositories/BookingProductEventSlotRepository.php create mode 100644 packages/Webkul/BookingProduct/src/Repositories/BookingProductRentalSlotRepository.php create mode 100644 packages/Webkul/BookingProduct/src/Repositories/BookingProductRepository.php create mode 100644 packages/Webkul/BookingProduct/src/Repositories/BookingProductTableSlotRepository.php create mode 100644 packages/Webkul/BookingProduct/src/Resources/assets/images/arrow-down.svg create mode 100644 packages/Webkul/BookingProduct/src/Resources/assets/images/arrow-up.svg create mode 100644 packages/Webkul/BookingProduct/src/Resources/assets/images/location.svg create mode 100644 packages/Webkul/BookingProduct/src/Resources/assets/images/phone.svg create mode 100644 packages/Webkul/BookingProduct/src/Resources/assets/images/slot.svg create mode 100644 packages/Webkul/BookingProduct/src/Resources/assets/sass/app.scss create mode 100644 packages/Webkul/BookingProduct/src/Resources/assets/sass/icons.scss create mode 100644 packages/Webkul/BookingProduct/src/Resources/lang/en/app.php create mode 100644 packages/Webkul/BookingProduct/src/Resources/views/admin/catalog/products/accordians/booking.blade.php create mode 100644 packages/Webkul/BookingProduct/src/Resources/views/admin/catalog/products/accordians/booking/appointment.blade.php create mode 100644 packages/Webkul/BookingProduct/src/Resources/views/admin/catalog/products/accordians/booking/default.blade.php create mode 100644 packages/Webkul/BookingProduct/src/Resources/views/admin/catalog/products/accordians/booking/event.blade.php create mode 100644 packages/Webkul/BookingProduct/src/Resources/views/admin/catalog/products/accordians/booking/rental.blade.php create mode 100644 packages/Webkul/BookingProduct/src/Resources/views/admin/catalog/products/accordians/booking/slots.blade.php create mode 100644 packages/Webkul/BookingProduct/src/Resources/views/admin/catalog/products/accordians/booking/table.blade.php create mode 100644 packages/Webkul/BookingProduct/src/Resources/views/shop/products/view/booking.blade.php create mode 100644 packages/Webkul/BookingProduct/src/Resources/views/shop/products/view/booking/appointment.blade.php create mode 100644 packages/Webkul/BookingProduct/src/Resources/views/shop/products/view/booking/default.blade.php create mode 100644 packages/Webkul/BookingProduct/src/Resources/views/shop/products/view/booking/event.blade.php create mode 100644 packages/Webkul/BookingProduct/src/Resources/views/shop/products/view/booking/rental.blade.php create mode 100644 packages/Webkul/BookingProduct/src/Resources/views/shop/products/view/booking/slots.blade.php create mode 100644 packages/Webkul/BookingProduct/src/Resources/views/shop/products/view/booking/table.blade.php create mode 100644 packages/Webkul/BookingProduct/src/Type/Booking.php create mode 100644 packages/Webkul/BookingProduct/webpack.mix.js diff --git a/composer.json b/composer.json index 74f093097..9675be01a 100755 --- a/composer.json +++ b/composer.json @@ -101,7 +101,8 @@ "Webkul\\CartRule\\": "packages/Webkul/CartRule/src", "Webkul\\Rule\\": "packages/Webkul/Rule/src", "Webkul\\CMS\\": "packages/Webkul/CMS/src", - "Webkul\\Velocity\\": "packages/Webkul/Velocity/src" + "Webkul\\Velocity\\": "packages/Webkul/Velocity/src", + "Webkul\\BookingProduct\\": "packages/Webkul/BookingProduct/src" } }, diff --git a/config/app.php b/config/app.php index 33129e719..e617fc79f 100755 --- a/config/app.php +++ b/config/app.php @@ -265,6 +265,7 @@ return [ Webkul\Rule\Providers\RuleServiceProvider::class, Webkul\CMS\Providers\CMSServiceProvider::class, Webkul\Velocity\Providers\VelocityServiceProvider::class, + Webkul\BookingProduct\Providers\BookingProductServiceProvider::class, ], /* diff --git a/config/concord.php b/config/concord.php index 24c40b47a..e7ec5845e 100755 --- a/config/concord.php +++ b/config/concord.php @@ -21,6 +21,7 @@ return [ \Webkul\User\Providers\ModuleServiceProvider::class, \Webkul\CatalogRule\Providers\ModuleServiceProvider::class, \Webkul\CartRule\Providers\ModuleServiceProvider::class, - \Webkul\CMS\Providers\ModuleServiceProvider::class + \Webkul\CMS\Providers\ModuleServiceProvider::class, + \Webkul\BookingProduct\Providers\ModuleServiceProvider::class ] ]; \ No newline at end of file diff --git a/packages/Webkul/BookingProduct/.gitignore b/packages/Webkul/BookingProduct/.gitignore new file mode 100755 index 000000000..6376db975 --- /dev/null +++ b/packages/Webkul/BookingProduct/.gitignore @@ -0,0 +1,3 @@ +/node_modules +/package-lock.json +npm-debug.log \ No newline at end of file diff --git a/packages/Webkul/BookingProduct/package.json b/packages/Webkul/BookingProduct/package.json new file mode 100644 index 000000000..16e824391 --- /dev/null +++ b/packages/Webkul/BookingProduct/package.json @@ -0,0 +1,22 @@ +{ + "private": true, + "scripts": { + "dev": "npm run development", + "development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js", + "watch": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js", + "watch-poll": "cross-env npm run watch -- --watch-poll --progress", + "hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js", + "prod": "npm run production", + "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js" + }, + "devDependencies": { + "axios": "^0.19.0", + "cross-env": "^6.0.3", + "jquery": "^3.4.1", + "laravel-mix": "^5.0.0", + "laravel-mix-merge-manifest": "^0.1.2", + "sass": "^1.25.0", + "sass-loader": "^8.0.2", + "vue": "^2.6.10" + } +} diff --git a/packages/Webkul/BookingProduct/publishable/assets/css/booking-product.css b/packages/Webkul/BookingProduct/publishable/assets/css/booking-product.css new file mode 100644 index 000000000..aea2997d8 --- /dev/null +++ b/packages/Webkul/BookingProduct/publishable/assets/css/booking-product.css @@ -0,0 +1 @@ +.bp-location-icon{background-image:url(../images/location.svg);width:32px;height:32px}.bp-slot-icon{background-image:url(../images/slot.svg);width:32px;height:32px}.bp-phone-icon{background-image:url(../images/phone.svg);width:32px;height:32px}.booking-information{margin-bottom:15px;border-top:1px solid #e8e8e8;padding-top:15px}.booking-information .booking-info-row{padding-left:32px;margin-bottom:20px;position:relative}.booking-information .booking-info-row .icon{position:absolute;left:0;top:-4px}.booking-information .booking-info-row .title{color:#5e5e5e;display:block;margin-bottom:5px}.booking-information .booking-info-row .value{display:block;margin-bottom:5px}.booking-information .booking-info-row .value .text-danger{color:#ff5656}.booking-information .booking-info-row .toggle{color:#0041ff;cursor:pointer;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.booking-information .booking-info-row .toggle .icon{position:relative;width:10px;height:10px;margin-left:5px;top:0}.booking-information .booking-info-row .toggle .icon.arrow-down-icon{background-image:url(../images/arrow-down.svg)!important}.booking-information .booking-info-row .toggle .icon.arrow-up-icon{background-image:url(../images/arrow-up.svg)!important}.booking-information .booking-info-row .days-availability table{margin-top:10px;border-collapse:collapse}.booking-information .booking-info-row .days-availability table tr td{padding:5px;vertical-align:top}.booking-information .booking-info-row .days-availability table tr td:first-child{padding-left:0}.booking-information .booking-info-row .days-availability table tr td:last-child{font-size:14px;padding-left:15px;padding-right:0;color:#5e5e5e}.booking-information .booking-info-row .days-availability table tr td .text-danger{color:#ff5656}.booking-information .book-slots{padding-top:25px;display:inline-block;width:100%;border-top:1px solid #e8e8e8}.booking-information .book-slots h3{font-weight:600;font-size:16px;color:#242424;margin-top:0}.booking-information .book-slots label{color:#3a3a3a}.booking-information .book-slots .control-group label{font-size:16px}.booking-information .book-slots .control-group .radio{display:inline-block}.booking-information .book-slots .control-group-container{width:100%;float:left}.booking-information .book-slots .control-group-container .control-group:not(.quantity){width:50%;float:left}.booking-information .book-slots .control-group-container .control-group:not(.quantity) .control{width:100%}.booking-information .book-slots .control-group-container .control-group:not(.quantity).date{padding-right:5px}.booking-information .book-slots .control-group-container .control-group:not(.quantity).date:after{position:absolute;top:14px;right:10px}.booking-information .book-slots .control-group-container .control-group:not(.quantity).slots:first-child{padding-right:5px}.booking-information .book-slots .control-group-container .control-group:not(.quantity).slots:last-child{padding-left:5px}.booking-information .book-slots .ticket-list .ticket-item{width:100%;display:inline-block;padding:16px 0;border-bottom:1px solid #e8e8e8}.booking-information .book-slots .ticket-list .ticket-item .ticket-info{width:50%;float:left}.booking-information .book-slots .ticket-list .ticket-item .ticket-info .ticket-name{color:#242424;margin-bottom:12px}.booking-information .book-slots .ticket-list .ticket-item .ticket-info .ticket-price{color:#5e5e5e}.booking-information .book-slots .ticket-list .ticket-item .ticket-quantity{width:50%;display:inline-block}.booking-information .book-slots .ticket-list .ticket-item .ticket-quantity .control-group{margin:0}.booking-information .book-slots .ticket-list .ticket-item p{color:#242424;margin-bottom:0;font-weight:400}.booking-information .book-slots .ticket-total{font-size:16px;font-weight:600;color:#242424;padding-top:16px}.booking-information .book-slots .ticket-total>div{margin-bottom:12px}.booking-information .book-slots .ticket-total>div:last-child{margin-bottom:0}.booking-information .book-slots .ticket-total>div p{color:#242424;font-weight:400;margin-top:4px} \ No newline at end of file diff --git a/packages/Webkul/BookingProduct/publishable/assets/images/arrow-down.svg b/packages/Webkul/BookingProduct/publishable/assets/images/arrow-down.svg new file mode 100644 index 000000000..dd459433e --- /dev/null +++ b/packages/Webkul/BookingProduct/publishable/assets/images/arrow-down.svg @@ -0,0 +1,10 @@ + + + + icon-dropdown + Created with Sketch. + + + + + \ No newline at end of file diff --git a/packages/Webkul/BookingProduct/publishable/assets/images/arrow-up.svg b/packages/Webkul/BookingProduct/publishable/assets/images/arrow-up.svg new file mode 100644 index 000000000..4ebba93f5 --- /dev/null +++ b/packages/Webkul/BookingProduct/publishable/assets/images/arrow-up.svg @@ -0,0 +1,10 @@ + + + + icon-dropdown-up + Created with Sketch. + + + + + \ No newline at end of file diff --git a/packages/Webkul/BookingProduct/publishable/assets/images/location.svg b/packages/Webkul/BookingProduct/publishable/assets/images/location.svg new file mode 100644 index 000000000..19fd55a0d --- /dev/null +++ b/packages/Webkul/BookingProduct/publishable/assets/images/location.svg @@ -0,0 +1,11 @@ + + + + tab/heading/icon/address + Created with Sketch. + + + + + + \ No newline at end of file diff --git a/packages/Webkul/BookingProduct/publishable/assets/images/phone.svg b/packages/Webkul/BookingProduct/publishable/assets/images/phone.svg new file mode 100644 index 000000000..e132e7b15 --- /dev/null +++ b/packages/Webkul/BookingProduct/publishable/assets/images/phone.svg @@ -0,0 +1,18 @@ + + + + icon-menu copy + Created with Sketch. + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/Webkul/BookingProduct/publishable/assets/images/slot.svg b/packages/Webkul/BookingProduct/publishable/assets/images/slot.svg new file mode 100644 index 000000000..18d00bd75 --- /dev/null +++ b/packages/Webkul/BookingProduct/publishable/assets/images/slot.svg @@ -0,0 +1,15 @@ + + + + tab/heading/icon/calender + Created with Sketch. + + + + + + + + + + \ No newline at end of file diff --git a/packages/Webkul/BookingProduct/publishable/assets/mix-manifest.json b/packages/Webkul/BookingProduct/publishable/assets/mix-manifest.json new file mode 100644 index 000000000..8fb05dd91 --- /dev/null +++ b/packages/Webkul/BookingProduct/publishable/assets/mix-manifest.json @@ -0,0 +1,3 @@ +{ + "/css/booking-product.css": "/css/booking-product.css?id=083d9215be8e5552a0e7" +} diff --git a/packages/Webkul/BookingProduct/src/Config/product_types.php b/packages/Webkul/BookingProduct/src/Config/product_types.php new file mode 100644 index 000000000..c7520c39e --- /dev/null +++ b/packages/Webkul/BookingProduct/src/Config/product_types.php @@ -0,0 +1,10 @@ + [ + 'key' => 'booking', + 'name' => 'Booking', + 'class' => 'Webkul\BookingProduct\Type\Booking', + 'sort' => 7 + ] +]; \ No newline at end of file diff --git a/packages/Webkul/BookingProduct/src/Contracts/BookingProduct.php b/packages/Webkul/BookingProduct/src/Contracts/BookingProduct.php new file mode 100644 index 000000000..73a55d322 --- /dev/null +++ b/packages/Webkul/BookingProduct/src/Contracts/BookingProduct.php @@ -0,0 +1,7 @@ +increments('id'); + $table->string('type'); + $table->string('location')->nullable(); + $table->boolean('show_location')->default(0); + + $table->integer('product_id')->unsigned(); + $table->foreign('product_id')->references('id')->on('products')->onDelete('cascade'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('booking_products'); + } +} diff --git a/packages/Webkul/BookingProduct/src/Database/Migrations/2019_07_05_154415_create_booking_product_default_slots_table.php b/packages/Webkul/BookingProduct/src/Database/Migrations/2019_07_05_154415_create_booking_product_default_slots_table.php new file mode 100644 index 000000000..77ac24993 --- /dev/null +++ b/packages/Webkul/BookingProduct/src/Database/Migrations/2019_07_05_154415_create_booking_product_default_slots_table.php @@ -0,0 +1,39 @@ +increments('id'); + $table->string('booking_type'); + $table->date('available_from')->nullable(); + $table->date('available_to')->nullable(); + $table->integer('duration')->nullable(); + $table->integer('break_time')->nullable(); + $table->json('slots')->nullable(); + + $table->integer('booking_product_id')->unsigned(); + $table->foreign('booking_product_id')->references('id')->on('booking_products')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('booking_product_default_slots'); + } +} diff --git a/packages/Webkul/BookingProduct/src/Database/Migrations/2019_07_05_154429_create_booking_product_appointment_slots_table.php b/packages/Webkul/BookingProduct/src/Database/Migrations/2019_07_05_154429_create_booking_product_appointment_slots_table.php new file mode 100644 index 000000000..3a3190f8a --- /dev/null +++ b/packages/Webkul/BookingProduct/src/Database/Migrations/2019_07_05_154429_create_booking_product_appointment_slots_table.php @@ -0,0 +1,41 @@ +increments('id'); + $table->integer('duration')->nullable(); + $table->integer('break_time')->nullable(); + $table->boolean('available_every_week')->nullable(); + $table->date('available_from')->nullable(); + $table->date('available_to')->nullable(); + $table->boolean('slot_has_quantity')->nullable(); + $table->boolean('same_slot_all_days')->nullable(); + $table->json('slots')->nullable(); + + $table->integer('booking_product_id')->unsigned(); + $table->foreign('booking_product_id')->references('id')->on('booking_products')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('booking_product_appointment_slots'); + } +} diff --git a/packages/Webkul/BookingProduct/src/Database/Migrations/2019_07_05_154440_create_booking_product_event_slots_table.php b/packages/Webkul/BookingProduct/src/Database/Migrations/2019_07_05_154440_create_booking_product_event_slots_table.php new file mode 100644 index 000000000..5857c5a89 --- /dev/null +++ b/packages/Webkul/BookingProduct/src/Database/Migrations/2019_07_05_154440_create_booking_product_event_slots_table.php @@ -0,0 +1,36 @@ +increments('id'); + $table->datetime('available_from')->nullable(); + $table->datetime('available_to')->nullable(); + $table->json('slots')->nullable(); + + $table->integer('booking_product_id')->unsigned(); + $table->foreign('booking_product_id')->references('id')->on('booking_products')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('booking_product_event_slots'); + } +} diff --git a/packages/Webkul/BookingProduct/src/Database/Migrations/2019_07_05_154451_create_booking_product_rental_slots_table.php b/packages/Webkul/BookingProduct/src/Database/Migrations/2019_07_05_154451_create_booking_product_rental_slots_table.php new file mode 100644 index 000000000..c48a332f4 --- /dev/null +++ b/packages/Webkul/BookingProduct/src/Database/Migrations/2019_07_05_154451_create_booking_product_rental_slots_table.php @@ -0,0 +1,42 @@ +increments('id'); + $table->string('renting_type'); + $table->decimal('daily_price', 12, 4)->default(0)->nullable();; + $table->decimal('hourly_price', 12, 4)->default(0)->nullable();; + $table->boolean('available_every_week')->nullable(); + $table->date('available_from')->nullable(); + $table->date('available_to')->nullable(); + $table->boolean('slot_has_quantity')->nullable(); + $table->boolean('same_slot_all_days')->nullable(); + $table->json('slots')->nullable(); + + $table->integer('booking_product_id')->unsigned(); + $table->foreign('booking_product_id')->references('id')->on('booking_products')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('booking_product_rental_slots'); + } +} diff --git a/packages/Webkul/BookingProduct/src/Database/Migrations/2019_07_05_154502_create_booking_product_table_slots_table.php b/packages/Webkul/BookingProduct/src/Database/Migrations/2019_07_05_154502_create_booking_product_table_slots_table.php new file mode 100644 index 000000000..2ca99a88e --- /dev/null +++ b/packages/Webkul/BookingProduct/src/Database/Migrations/2019_07_05_154502_create_booking_product_table_slots_table.php @@ -0,0 +1,43 @@ +increments('id'); + $table->string('price_type'); + $table->integer('guest_limit')->default(0); + $table->integer('duration'); + $table->integer('break_time'); + $table->integer('prevent_scheduling_before'); + $table->boolean('available_every_week')->nullable(); + $table->date('available_from')->nullable(); + $table->date('available_to')->nullable(); + $table->boolean('same_slot_all_days')->nullable(); + $table->json('slots')->nullable(); + + $table->integer('booking_product_id')->unsigned(); + $table->foreign('booking_product_id')->references('id')->on('booking_products')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::dropIfExists('booking_product_table_slots'); + } +} diff --git a/packages/Webkul/BookingProduct/src/Helpers/AppointmentSlot.php b/packages/Webkul/BookingProduct/src/Helpers/AppointmentSlot.php new file mode 100644 index 000000000..82a522c5e --- /dev/null +++ b/packages/Webkul/BookingProduct/src/Helpers/AppointmentSlot.php @@ -0,0 +1,13 @@ + + * @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com) + */ +class AppointmentSlot extends Booking +{ +} \ No newline at end of file diff --git a/packages/Webkul/BookingProduct/src/Helpers/Booking.php b/packages/Webkul/BookingProduct/src/Helpers/Booking.php new file mode 100644 index 000000000..0e52a2d9f --- /dev/null +++ b/packages/Webkul/BookingProduct/src/Helpers/Booking.php @@ -0,0 +1,276 @@ + + * @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com) + */ +class Booking +{ + /** + * @return array + */ + protected $typeRepositories = []; + + /** + * @return array + */ + protected $daysOfWeek = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; + + /** + * Create a new helper instance. + * + * @param Webkul\BookingProduct\Repositories\BookingProductDefaultSlotRepository $bookingProductDefaultSlotRepository + * @param Webkul\BookingProduct\Repositories\BookingProductAppointmentSlotRepository $bookingProductAppointmentSlotRepository + * @param Webkul\BookingProduct\Repositories\BookingProductEventSlotRepository $bookingProductEventSlotRepository + * @param Webkul\BookingProduct\Repositories\BookingProductRentalSlotRepository $bookingProductRentalSlotRepository + * @param Webkul\BookingProduct\Repositories\BookingProductTableSlotRepository $bookingProductTableSlotRepository + * @return void + */ + public function __construct( + BookingProductDefaultSlotRepository $bookingProductDefaultSlotRepository, + BookingProductAppointmentSlotRepository $bookingProductAppointmentSlotRepository, + BookingProductEventSlotRepository $bookingProductEventSlotRepository, + BookingProductRentalSlotRepository $bookingProductRentalSlotRepository, + BookingProductTableSlotRepository $bookingProductTableSlotRepository + ) + { + $this->typeRepositories['default'] = $bookingProductDefaultSlotRepository; + + $this->typeRepositories['appointment'] = $bookingProductAppointmentSlotRepository; + + $this->typeRepositories['event'] = $bookingProductEventSlotRepository; + + $this->typeRepositories['rental'] = $bookingProductRentalSlotRepository; + + $this->typeRepositories['table'] = $bookingProductTableSlotRepository; + } + + /** + * Returns the booking information + * + * @param BookingProduct $bookingProduct + * @return array + */ + public function getWeekSlotDurations($bookingProduct) + { + $slotsByDays = []; + + $bookingProductSlot = $this->typeRepositories[$bookingProduct->type]->findOneByField('booking_product_id', $bookingProduct->id); + + $availabileDays = $this->getAvailableWeekDays($bookingProductSlot); + + foreach ($this->daysOfWeek as $index => $isOpen) { + $slots = []; + + if ($isOpen) + $slots = $bookingProductSlot->same_slot_all_days ? $bookingProductSlot->slots : ($bookingProductSlot->slots[$index] ?? []); + + $slotsByDays[] = [ + 'name' => trans($this->daysOfWeek[$index]), + 'slots' => isset($availabileDays[$index]) ? $this->conver24To12Hours($slots) : [] + ]; + } + + return $slotsByDays; + } + + /** + * Returns html of slots for a current day + * + * @param BookingProduct $bookingProduct + * @return string + */ + public function getTodaySlotsHtml($bookingProduct) + { + $slots = []; + + foreach ($this->getTodaySlots($bookingProduct) as $slot) { + $slots[] = $slot['from'] . ' - ' . $slot['to']; + } + + return count($slots) + ? implode(' | ', $slots) + : '' . trans('bookingproduct::app.shop.products.closed') . ''; + } + + /** + * Returns slots for a current day + * + * @param BookingProduct $bookingProduct + * @return array + */ + public function getTodaySlots($bookingProduct) + { + $weekSlots = $this->getWeekSlotDurations($bookingProduct); + + return $weekSlots[Carbon::now()->format('w')]['slots']; + } + + /** + * Returns the available week days + * + * @param Object $bookingProductSlot + * @return array + */ + public function getAvailableWeekDays($bookingProductSlot) + { + if ($bookingProductSlot->available_every_week) + return $this->daysOfWeek; + + $days = []; + + $currentTime = Carbon::now(); + + $availableFrom = Carbon::createFromTimeString($bookingProductSlot->available_from . " 00:00:01"); + + $availableTo = Carbon::createFromTimeString($bookingProductSlot->available_to . " 23:59:59"); + + for ($i = 0; $i < 7; $i++) { + $date = clone $currentTime; + $date->addDays($i); + + if ($date >= $availableFrom && $date <= $availableTo) + $days[$i] = $date->format('l'); + } + + return $this->sortDaysOfWeek($days); + } + + /** + * Sort days + * + * @param array $days + * @return array + */ + public function sortDaysOfWeek($days) + { + $daysAux = []; + + foreach ($days as $day) { + $key = array_search($day, $this->daysOfWeek); + + if ($key !== FALSE) { + $daysAux[$key] = $day; + } + } + + ksort($daysAux); + + return $daysAux; + } + + /** + * Convert time from 24 to 12 hour format + * + * @param array $slots + * @return array + */ + public function conver24To12Hours($slots) + { + if (! $slots) + return []; + + foreach ($slots as $index => $slot) { + $slots[$index]['from'] = Carbon::createFromTimeString($slot['from'])->format("h:i a"); + $slots[$index]['to'] = Carbon::createFromTimeString($slot['to'])->format("h:i a"); + } + + return $slots; + } + + /** + * Returns slots for a perticular day + * + * @param BookingProduct $bookingProduct + * @param string $date + * @return array + */ + public function getSlotsByDate($bookingProduct, $date) + { + $bookingProductSlot = $this->typeRepositories[$bookingProduct->type]->findOneByField('booking_product_id', $bookingProduct->id); + + if (! is_array($bookingProductSlot->slots) || ! count($bookingProductSlot->slots)) + return []; + + $requestedDate = Carbon::createFromTimeString($date . " 00:00:00"); + + $currentTime = Carbon::now(); + + $availableFrom = ! $bookingProductSlot->available_every_week + ? Carbon::createFromTimeString($bookingProductSlot->available_from . " 00:00:00") + : Carbon::createFromTimeString($currentTime->format('Y-m-d') . ' 00:00:00'); + + $availableTo = ! $bookingProductSlot->available_every_week + ? Carbon::createFromTimeString($bookingProductSlot->available_to . ' 23:59:59') + : Carbon::createFromTimeString('2080-01-01 00:00:00'); + + $timeDurations = $bookingProductSlot->same_slot_all_days + ? $bookingProductSlot->slots + : ($bookingProductSlot->slots[$requestedDate->format('w')] ?? []); + + $slots = []; + + foreach ($timeDurations as $timeDuration) { + $fromChunks = explode(':', $timeDuration['from']); + $toChunks = explode(':', $timeDuration['to']); + + $startDayTime = Carbon::createFromTimeString($requestedDate->format('Y-m-d') . ' 00:00:00'); + $startDayTime->addMinutes(($fromChunks[0] * 60) + $fromChunks[1]); + $tempStartDayTime = clone $startDayTime; + + $endDayTime = Carbon::createFromTimeString($requestedDate->format('Y-m-d') . ' 00:00:00'); + $endDayTime->addMinutes(($toChunks[0] * 60) + $toChunks[1]); + + $isFirstIteration = true; + + while (1) { + $from = clone $tempStartDayTime; + $tempStartDayTime->addMinutes($bookingProductSlot->duration); + + if ($isFirstIteration) { + $isFirstIteration = false; + } else { + $from->modify('+' . $bookingProductSlot->break_time . ' minutes'); + $tempStartDayTime->modify('+' . $bookingProductSlot->break_time . ' minutes'); + } + + $to = clone $tempStartDayTime; + + if (($startDayTime <= $from && $from <= $availableTo) + && ($availableTo >= $to && $to >= $startDayTime) + && ($startDayTime <= $from && $from <= $endDayTime) + && ($endDayTime >= $to && $to >= $startDayTime)) { + + // Get already ordered qty for this slot + $orderedQty = 0; + + $qty = isset($timeDuration['qty']) ? ( $timeDuration['qty'] - $orderedQty ) : 1; + + if ($qty && $currentTime <= $from) { + $slots[] = [ + 'from' => $from->format('h:i A'), + 'to' => $to->format('h:i A'), + 'timestamp' => $from->getTimestamp() . '-' . $to->getTimestamp(), + 'qty' => $qty, + ]; + } + } else { + break; + } + } + } + + return $slots; + } +} \ No newline at end of file diff --git a/packages/Webkul/BookingProduct/src/Helpers/DefaultSlot.php b/packages/Webkul/BookingProduct/src/Helpers/DefaultSlot.php new file mode 100644 index 000000000..26f9f1253 --- /dev/null +++ b/packages/Webkul/BookingProduct/src/Helpers/DefaultSlot.php @@ -0,0 +1,154 @@ + + * @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com) + */ +class DefaultSlot extends Booking +{ + /** + * @return array + */ + protected $daysOfWeek = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']; + + /** + * Returns slots for a perticular day + * + * @param BookingProduct $bookingProduct + * @param string $date + * @return array + */ + public function getSlotsByDate($bookingProduct, $date) + { + $bookingProductSlot = $this->typeRepositories[$bookingProduct->type]->findOneByField('booking_product_id', $bookingProduct->id); + + if (! is_array($bookingProductSlot->slots) || ! count($bookingProductSlot->slots)) + return []; + + $requestedDate = Carbon::createFromTimeString($date . " 00:00:00"); + + $currentTime = Carbon::now(); + + $availableFrom = Carbon::createFromTimeString($bookingProductSlot->available_from . " 00:00:00"); + + $availableTo = Carbon::createFromTimeString($bookingProductSlot->available_to . ' 23:59:59'); + + if ($requestedDate < $currentTime + || $requestedDate < $availableFrom + || $requestedDate > $availableTo) + return []; + + $slots = []; + + return $bookingProductSlot->booking_type == 'one' + ? $this->getOneBookingForManyDaysSlots($bookingProductSlot, $requestedDate) + : $this->getManyBookingsforOneDaySlots($bookingProductSlot, $requestedDate); + } + + /** + * Returns slots for One Booking For Many Days + * + * @param BookingProductSlot $bookingProductSlot + * @param string $requestedDate + * @return array + */ + public function getOneBookingForManyDaysSlots($bookingProductSlot, $requestedDate) + { + $slots = []; + + foreach ($bookingProductSlot->slots as $timeDuration) { + if ($requestedDate->dayOfWeek != $timeDuration['from_day']) + continue; + + $startDate = clone $requestedDate->modify('this ' . $this->daysOfWeek[$timeDuration['from_day']]); + $endDate = clone $requestedDate->modify('this ' . $this->daysOfWeek[$timeDuration['to_day']]); + + $slots[] = [ + 'from' => $startDate->format('d F, Y h:i A'), + 'to' => $endDate->format('d F, Y h:i A'), + 'timestamp' => $startDate->getTimestamp() . '-' . $endDate->getTimestamp(), + ]; + } + + return $slots; + } + + /** + * Returns slots for Many Bookings for One Day + * + * @param BookingProductSlot $bookingProductSlot + * @param string $requestedDate + * @return array + */ + public function getManyBookingsforOneDaySlots($bookingProductSlot, $requestedDate) + { + $currentTime = Carbon::now(); + + $availableFrom = Carbon::createFromTimeString($bookingProductSlot->available_from . ' 00:00:00'); + + $availableTo = Carbon::createFromTimeString($bookingProductSlot->available_to . ' 23:59:59'); + + $timeDuration = $bookingProductSlot->slots[$requestedDate->format('w')] ?? []; + + if (! count($timeDuration) || ! $timeDuration['status']) + return []; + + $slots = []; + + $fromChunks = explode(':', $timeDuration['from']); + $toChunks = explode(':', $timeDuration['to']); + + $startDayTime = Carbon::createFromTimeString($requestedDate->format('Y-m-d') . ' 00:00:00'); + $startDayTime->addMinutes(($fromChunks[0] * 60) + $fromChunks[1]); + $tempStartDayTime = clone $startDayTime; + + $endDayTime = Carbon::createFromTimeString($requestedDate->format('Y-m-d') . ' 00:00:00'); + $endDayTime->addMinutes(($toChunks[0] * 60) + $toChunks[1]); + + $isFirstIteration = true; + + while (1) { + $from = clone $tempStartDayTime; + $tempStartDayTime->addMinutes($bookingProductSlot->duration); + + if ($isFirstIteration) { + $isFirstIteration = false; + } else { + $from->modify('+' . $bookingProductSlot->break_time . ' minutes'); + $tempStartDayTime->modify('+' . $bookingProductSlot->break_time . ' minutes'); + } + + $to = clone $tempStartDayTime; + + if (($startDayTime <= $from && $from <= $availableTo) + && ($availableTo >= $to && $to >= $startDayTime) + && ($startDayTime <= $from && $from <= $endDayTime) + && ($endDayTime >= $to && $to >= $startDayTime)) { + + // Get already ordered qty for this slot + $orderedQty = 0; + + $qty = isset($timeDuration['qty']) ? ( $timeDuration['qty'] - $orderedQty ) : 1; + + if ($qty && $currentTime <= $from) { + $slots[] = [ + 'from' => $from->format('h:i A'), + 'to' => $to->format('h:i A'), + 'timestamp' => $from->getTimestamp() . '-' . $to->getTimestamp(), + 'qty' => $qty, + ]; + } + } else { + break; + } + } + + return $slots; + } +} \ No newline at end of file diff --git a/packages/Webkul/BookingProduct/src/Helpers/EventSlot.php b/packages/Webkul/BookingProduct/src/Helpers/EventSlot.php new file mode 100644 index 000000000..064f9dc3d --- /dev/null +++ b/packages/Webkul/BookingProduct/src/Helpers/EventSlot.php @@ -0,0 +1,60 @@ + + * @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com) + */ +class EventSlot extends Booking +{ + /** + * Returns event date + * + * @param BookingProduct $bookingProduct + * @return string + */ + public function getEventDate($bookingProduct) + { + $from = Carbon::createFromTimeString($bookingProduct->event_slot->available_from)->format('d F, Y h:i A'); + + $to = Carbon::createFromTimeString($bookingProduct->event_slot->available_to)->format('d F, Y h:i A'); + + return $from . ' - ' . $to; + } + + /** + * Returns tickets + * + * @param BookingProduct $bookingProduct + * @return array + */ + public function getTickets($bookingProduct) + { + if (! $bookingProduct->event_slot) + return; + + return $this->formatPrice($bookingProduct->event_slot->slots); + } + + /** + * Format ticket price + * + * @param array $tickets + * @return array + */ + public function formatPrice($tickets) + { + foreach ($tickets as $index => $ticket) { + $tickets[$index]['converted_price'] = core()->convertPrice($ticket['price']); + $tickets[$index]['formated_price'] = $formatedPrice = core()->currency($ticket['price']); + $tickets[$index]['formated_price_text'] = trans('bookingproduct::app.shop.products.per-ticket-price', ['price' => $formatedPrice]); + } + + return $tickets; + } +} \ No newline at end of file diff --git a/packages/Webkul/BookingProduct/src/Helpers/RentalSlot.php b/packages/Webkul/BookingProduct/src/Helpers/RentalSlot.php new file mode 100644 index 000000000..2e04cfb90 --- /dev/null +++ b/packages/Webkul/BookingProduct/src/Helpers/RentalSlot.php @@ -0,0 +1,94 @@ + + * @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com) + */ +class RentalSlot extends Booking +{ + /** + * Returns slots for a perticular day + * + * @param BookingProduct $bookingProduct + * @param string $date + * @return array + */ + public function getSlotsByDate($bookingProduct, $date) + { + $bookingProductSlot = $this->typeRepositories[$bookingProduct->type]->findOneByField('booking_product_id', $bookingProduct->id); + + if (! is_array($bookingProductSlot->slots) || ! count($bookingProductSlot->slots)) + return []; + + $requestedDate = Carbon::createFromTimeString($date . " 00:00:00"); + + $currentTime = Carbon::now(); + + $availableFrom = ! $bookingProductSlot->available_every_week + ? Carbon::createFromTimeString($bookingProductSlot->available_from . ' 00:00:00') + : Carbon::createFromTimeString($currentTime->format('Y-m-d') . ' 00:00:00'); + + $availableTo = ! $bookingProductSlot->available_every_week + ? Carbon::createFromTimeString($bookingProductSlot->available_to . ' 23:59:59') + : Carbon::createFromTimeString('2080-01-01 00:00:00'); + + $timeDurations = $bookingProductSlot->same_slot_all_days + ? $bookingProductSlot->slots + : $bookingProductSlot->slots[$requestedDate->format('w')]; + + $slots = []; + + foreach ($timeDurations as $index => $timeDuration) { + $fromChunks = explode(':', $timeDuration['from']); + $toChunks = explode(':', $timeDuration['to']); + + $startDayTime = Carbon::createFromTimeString($requestedDate->format('Y-m-d') . ' 00:00:00'); + $startDayTime->addMinutes(($fromChunks[0] * 60) + $fromChunks[1]); + $tempStartDayTime = clone $startDayTime; + + $endDayTime = Carbon::createFromTimeString($requestedDate->format('Y-m-d') . ' 00:00:00'); + $endDayTime->addMinutes(($toChunks[0] * 60) + $toChunks[1]); + + while (1) { + $from = clone $tempStartDayTime; + $tempStartDayTime->addMinutes(60); + + $to = clone $tempStartDayTime; + + if (($startDayTime <= $from && $from <= $availableTo) + && ($availableTo >= $to && $to >= $startDayTime) + && ($startDayTime <= $from && $from <= $endDayTime) + && ($endDayTime >= $to && $to >= $startDayTime)) { + + // Get already ordered qty for this slot + $orderedQty = 0; + + $qty = isset($timeDuration['qty']) ? ( $timeDuration['qty'] - $orderedQty ) : 1; + + if ($qty && $currentTime <= $from) { + if (! isset($slots[$index])) + $slots[$index]['time'] = $startDayTime->format('h:i A') . ' - ' . $endDayTime->format('h:i A'); + + $slots[$index]['slots'][] = [ + 'from' => $from->format('h:i A'), + 'to' => $to->format('h:i A'), + 'from_timestamp' => $from->getTimestamp(), + 'to_timestamp' => $to->getTimestamp(), + 'qty' => $qty + ]; + } + } else { + break; + } + } + } + + return $slots; + } +} \ No newline at end of file diff --git a/packages/Webkul/BookingProduct/src/Helpers/TableSlot.php b/packages/Webkul/BookingProduct/src/Helpers/TableSlot.php new file mode 100644 index 000000000..3fc5ebb56 --- /dev/null +++ b/packages/Webkul/BookingProduct/src/Helpers/TableSlot.php @@ -0,0 +1,13 @@ + + * @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com) + */ +class TableSlot extends Booking +{ +} \ No newline at end of file diff --git a/packages/Webkul/BookingProduct/src/Http/Controllers/Shop/BookingProductController.php b/packages/Webkul/BookingProduct/src/Http/Controllers/Shop/BookingProductController.php new file mode 100644 index 000000000..15ef0c984 --- /dev/null +++ b/packages/Webkul/BookingProduct/src/Http/Controllers/Shop/BookingProductController.php @@ -0,0 +1,78 @@ + + * @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com) + */ +class BookingProductController extends Controller +{ + /** + * Booking object + * + * @var Object + */ + protected $bookingHelper; + + /** + * @return array + */ + protected $bookingHelpers = []; + + /** + * Create a new helper instance. + * + * @param Webkul\BookingProduct\Repositories\BookingProductRepository $bookingProductRepository + * @param Webkul\BookingProduct\Helpers\DefaultSlot $defaultSlotHelper + * @param Webkul\BookingProduct\Helpers\AppointmentSlot $appointmentSlotHelper + * @param Webkul\BookingProduct\Helpers\RentalSlot $rentalSlotHelper + * @param Webkul\BookingProduct\Helpers\EventSlot $eventSlotHelper + * @param Webkul\BookingProduct\Helpers\TableSlot $tableSlotHelper + * @return void + */ + public function __construct( + BookingProductRepository $bookingProductRepository, + DefaultSlotHelper $defaultSlotHelper, + AppointmentSlotHelper $appointmentSlotHelper, + RentalSlotHelper $rentalSlotHelper, + EventSlotHelper $eventSlotHelper, + TableSlotHelper $tableSlotHelper + ) + { + $this->bookingProductRepository = $bookingProductRepository; + + $this->bookingHelpers['default'] = $defaultSlotHelper; + + $this->bookingHelpers['appointment'] = $appointmentSlotHelper; + + $this->bookingHelpers['rental'] = $rentalSlotHelper; + + $this->bookingHelpers['event'] = $eventSlotHelper; + + $this->bookingHelpers['table'] = $tableSlotHelper; + } + + /** + * Display the specified resource. + * + * @return \Illuminate\Http\Response + */ + public function index() + { + $bookingProduct = $this->bookingProductRepository->find(request('id')); + + return response()->json([ + 'data' => $this->bookingHelpers[$bookingProduct->type]->getSlotsByDate($bookingProduct, request()->get('date')) + ]); + } +} \ No newline at end of file diff --git a/packages/Webkul/BookingProduct/src/Http/Controllers/Shop/Controller.php b/packages/Webkul/BookingProduct/src/Http/Controllers/Shop/Controller.php new file mode 100644 index 000000000..0209a71dc --- /dev/null +++ b/packages/Webkul/BookingProduct/src/Http/Controllers/Shop/Controller.php @@ -0,0 +1,12 @@ + ['web', 'theme', 'locale', 'currency']], function () { + Route::get('/booking-slots/{id}', 'Webkul\BookingProduct\Http\Controllers\Shop\BookingProductController@index')->name('booking_product.slots.index'); +}); \ No newline at end of file diff --git a/packages/Webkul/BookingProduct/src/Listeners/Product.php b/packages/Webkul/BookingProduct/src/Listeners/Product.php new file mode 100644 index 000000000..094280dd3 --- /dev/null +++ b/packages/Webkul/BookingProduct/src/Listeners/Product.php @@ -0,0 +1,53 @@ + + * @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com) + */ +class Product +{ + /** + * BookingProductRepository Object + * + * @var Object + */ + protected $bookingProductRepository; + + /** + * Create a new listener instance. + * + * @param Webkul\BookingProduct\Repositories\BookingProductRepository $bookingProductRepository + * @return void + */ + public function __construct(BookingProductRepository $bookingProductRepository) + { + $this->bookingProductRepository = $bookingProductRepository; + } + + /** + * After the product is updated + * + * @return void + */ + public function afterProductUpdated($product) + { + if ($product->type != 'booking' || ! request('booking')) + return; + + $bookingProduct = $this->bookingProductRepository->findOneByField('product_id', $product->id); + + if ($bookingProduct) { + $this->bookingProductRepository->update(request('booking'), $bookingProduct->id); + } else { + $this->bookingProductRepository->create(array_merge(request('booking'), [ + 'product_id' => $product->id + ])); + } + } +} \ No newline at end of file diff --git a/packages/Webkul/BookingProduct/src/Models/BookingProduct.php b/packages/Webkul/BookingProduct/src/Models/BookingProduct.php new file mode 100644 index 000000000..6c54592c3 --- /dev/null +++ b/packages/Webkul/BookingProduct/src/Models/BookingProduct.php @@ -0,0 +1,54 @@ +hasOne(BookingProductDefaultSlotProxy::modelClass()); + } + + /** + * The Product Appointment Booking that belong to the product booking. + */ + public function appointment_slot() + { + return $this->hasOne(BookingProductAppointmentSlotProxy::modelClass()); + } + + /** + * The Product Event Booking that belong to the product booking. + */ + public function event_slot() + { + return $this->hasOne(BookingProductEventSlotProxy::modelClass()); + } + + /** + * The Product Rental Booking that belong to the product booking. + */ + public function rental_slot() + { + return $this->hasOne(BookingProductRentalSlotProxy::modelClass()); + } + + /** + * The Product Table Booking that belong to the product booking. + */ + public function table_slot() + { + return $this->hasOne(BookingProductTableSlotProxy::modelClass()); + } +} \ No newline at end of file diff --git a/packages/Webkul/BookingProduct/src/Models/BookingProductAppointmentSlot.php b/packages/Webkul/BookingProduct/src/Models/BookingProductAppointmentSlot.php new file mode 100644 index 000000000..1766d3bbf --- /dev/null +++ b/packages/Webkul/BookingProduct/src/Models/BookingProductAppointmentSlot.php @@ -0,0 +1,15 @@ + 'array']; + + protected $fillable = ['duration', 'break_time', 'available_every_week', 'slot_has_quantity', 'same_slot_all_days', 'slots', 'booking_product_id']; +} \ No newline at end of file diff --git a/packages/Webkul/BookingProduct/src/Models/BookingProductAppointmentSlotProxy.php b/packages/Webkul/BookingProduct/src/Models/BookingProductAppointmentSlotProxy.php new file mode 100644 index 000000000..a33d0f368 --- /dev/null +++ b/packages/Webkul/BookingProduct/src/Models/BookingProductAppointmentSlotProxy.php @@ -0,0 +1,10 @@ + 'array']; + + protected $fillable = ['booking_type', 'duration', 'break_time', 'available_from', 'available_to', 'slots', 'booking_product_id']; + + /** + * Get the product that owns the attribute value. + */ + public function booking_product() + { + return $this->belongsTo(BookingProductProxy::modelClass()); + } +} \ No newline at end of file diff --git a/packages/Webkul/BookingProduct/src/Models/BookingProductDefaultSlotProxy.php b/packages/Webkul/BookingProduct/src/Models/BookingProductDefaultSlotProxy.php new file mode 100644 index 000000000..67ee768c8 --- /dev/null +++ b/packages/Webkul/BookingProduct/src/Models/BookingProductDefaultSlotProxy.php @@ -0,0 +1,10 @@ + 'array']; + + protected $fillable = ['available_from', 'available_to', 'slots', 'booking_product_id']; +} \ No newline at end of file diff --git a/packages/Webkul/BookingProduct/src/Models/BookingProductEventSlotProxy.php b/packages/Webkul/BookingProduct/src/Models/BookingProductEventSlotProxy.php new file mode 100644 index 000000000..ec05e5f54 --- /dev/null +++ b/packages/Webkul/BookingProduct/src/Models/BookingProductEventSlotProxy.php @@ -0,0 +1,10 @@ + 'array']; + + protected $fillable = ['renting_type', 'daily_price', 'hourly_price', 'available_every_week', 'slot_has_quantity', 'same_slot_all_days', 'slots', 'booking_product_id']; +} \ No newline at end of file diff --git a/packages/Webkul/BookingProduct/src/Models/BookingProductRentalSlotProxy.php b/packages/Webkul/BookingProduct/src/Models/BookingProductRentalSlotProxy.php new file mode 100644 index 000000000..1191761bf --- /dev/null +++ b/packages/Webkul/BookingProduct/src/Models/BookingProductRentalSlotProxy.php @@ -0,0 +1,10 @@ + 'array']; + + protected $fillable = ['price_type', 'guest_limit', 'duration', 'break_time', 'prevent_scheduling_before', 'available_every_week', 'available_from', 'available_to', 'same_slot_all_days', 'slots', 'booking_product_id']; +} \ No newline at end of file diff --git a/packages/Webkul/BookingProduct/src/Models/BookingProductTableSlotProxy.php b/packages/Webkul/BookingProduct/src/Models/BookingProductTableSlotProxy.php new file mode 100644 index 000000000..2d5acb217 --- /dev/null +++ b/packages/Webkul/BookingProduct/src/Models/BookingProductTableSlotProxy.php @@ -0,0 +1,10 @@ +loadRoutesFrom(__DIR__ . '/../Http/front-routes.php'); + + $this->loadMigrationsFrom(__DIR__ . '/../Database/Migrations'); + + $this->loadTranslationsFrom(__DIR__ . '/../Resources/lang', 'bookingproduct'); + + $this->loadViewsFrom(__DIR__ . '/../Resources/views', 'bookingproduct'); + + $this->publishes([ + __DIR__ . '/../../publishable/assets' => public_path('themes/default/assets'), + ], 'public'); + + $this->app->register(EventServiceProvider::class); + } + + /** + * Register services. + * + * @return void + */ + public function register() + { + $this->mergeConfigFrom( + dirname(__DIR__) . '/Config/product_types.php', 'product_types' + ); + } +} \ No newline at end of file diff --git a/packages/Webkul/BookingProduct/src/Providers/EventServiceProvider.php b/packages/Webkul/BookingProduct/src/Providers/EventServiceProvider.php new file mode 100644 index 000000000..98d66d424 --- /dev/null +++ b/packages/Webkul/BookingProduct/src/Providers/EventServiceProvider.php @@ -0,0 +1,23 @@ +addTemplate('bookingproduct::shop.products.view.booking'); + }); + + Event::listen('catalog.product.update.after', 'Webkul\BookingProduct\Listeners\Product@afterProductUpdated'); + } +} \ No newline at end of file diff --git a/packages/Webkul/BookingProduct/src/Providers/ModuleServiceProvider.php b/packages/Webkul/BookingProduct/src/Providers/ModuleServiceProvider.php new file mode 100644 index 000000000..8e0692a64 --- /dev/null +++ b/packages/Webkul/BookingProduct/src/Providers/ModuleServiceProvider.php @@ -0,0 +1,17 @@ + + * @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com) + */ +class BookingProductAppointmentSlotRepository extends Repository +{ + /** + * Specify Model class name + * + * @return mixed + */ + function model() + { + return 'Webkul\BookingProduct\Contracts\BookingProductAppointmentSlot'; + } +} \ No newline at end of file diff --git a/packages/Webkul/BookingProduct/src/Repositories/BookingProductDefaultSlotRepository.php b/packages/Webkul/BookingProduct/src/Repositories/BookingProductDefaultSlotRepository.php new file mode 100644 index 000000000..440803e13 --- /dev/null +++ b/packages/Webkul/BookingProduct/src/Repositories/BookingProductDefaultSlotRepository.php @@ -0,0 +1,24 @@ + + * @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com) + */ +class BookingProductDefaultSlotRepository extends Repository +{ + /** + * Specify Model class name + * + * @return mixed + */ + function model() + { + return 'Webkul\BookingProduct\Contracts\BookingProductDefaultSlot'; + } +} \ No newline at end of file diff --git a/packages/Webkul/BookingProduct/src/Repositories/BookingProductEventSlotRepository.php b/packages/Webkul/BookingProduct/src/Repositories/BookingProductEventSlotRepository.php new file mode 100644 index 000000000..b2e44336c --- /dev/null +++ b/packages/Webkul/BookingProduct/src/Repositories/BookingProductEventSlotRepository.php @@ -0,0 +1,24 @@ + + * @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com) + */ +class BookingProductEventSlotRepository extends Repository +{ + /** + * Specify Model class name + * + * @return mixed + */ + function model() + { + return 'Webkul\BookingProduct\Contracts\BookingProductEventSlot'; + } +} \ No newline at end of file diff --git a/packages/Webkul/BookingProduct/src/Repositories/BookingProductRentalSlotRepository.php b/packages/Webkul/BookingProduct/src/Repositories/BookingProductRentalSlotRepository.php new file mode 100644 index 000000000..e4c2185fa --- /dev/null +++ b/packages/Webkul/BookingProduct/src/Repositories/BookingProductRentalSlotRepository.php @@ -0,0 +1,24 @@ + + * @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com) + */ +class BookingProductRentalSlotRepository extends Repository +{ + /** + * Specify Model class name + * + * @return mixed + */ + function model() + { + return 'Webkul\BookingProduct\Contracts\BookingProductRentalSlot'; + } +} \ No newline at end of file diff --git a/packages/Webkul/BookingProduct/src/Repositories/BookingProductRepository.php b/packages/Webkul/BookingProduct/src/Repositories/BookingProductRepository.php new file mode 100644 index 000000000..3dfbe4e50 --- /dev/null +++ b/packages/Webkul/BookingProduct/src/Repositories/BookingProductRepository.php @@ -0,0 +1,135 @@ + + * @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com) + */ +class BookingProductRepository extends Repository +{ + /** + * @return array + */ + protected $typeRepositories = []; + + /** + * Create a new repository instance. + * + * @param Webkul\BookingProduct\Repositories\BookingProductDefaultSlotRepository $bookingProductDefaultSlotRepository + * @param Webkul\BookingProduct\Repositories\BookingProductAppointmentSlotRepository $bookingProductAppointmentSlotRepository + * @param Webkul\BookingProduct\Repositories\BookingProductEventSlotRepository $bookingProductEventSlotRepository + * @param Webkul\BookingProduct\Repositories\BookingProductRentalSlotRepository $bookingProductRentalSlotRepository + * @param Webkul\BookingProduct\Repositories\BookingProductTableSlotRepository $bookingProductTableSlotRepository + * @return void + */ + public function __construct( + BookingProductDefaultSlotRepository $bookingProductDefaultSlotRepository, + BookingProductAppointmentSlotRepository $bookingProductAppointmentSlotRepository, + BookingProductEventSlotRepository $bookingProductEventSlotRepository, + BookingProductRentalSlotRepository $bookingProductRentalSlotRepository, + BookingProductTableSlotRepository $bookingProductTableSlotRepository, + App $app + ) + { + parent::__construct($app); + + $this->typeRepositories['default'] = $bookingProductDefaultSlotRepository; + + $this->typeRepositories['appointment'] = $bookingProductAppointmentSlotRepository; + + $this->typeRepositories['event'] = $bookingProductEventSlotRepository; + + $this->typeRepositories['rental'] = $bookingProductRentalSlotRepository; + + $this->typeRepositories['table'] = $bookingProductTableSlotRepository; + } + + /** + * Specify Model class name + * + * @return mixed + */ + function model() + { + return 'Webkul\BookingProduct\Contracts\BookingProduct'; + } + + /** + * @param array $data + * @return mixed + */ + public function create(array $data) + { + $bookingProduct = parent::create($data); + + $this->typeRepositories[$data['type']]->create(array_merge($data, ['booking_product_id' => $bookingProduct->id])); + + return $bookingProduct; + } + + /** + * @param array $data + * @param integer $id + * @param string $attribute + * @return mixed + */ + public function update(array $data, $id, $attribute = "id") + { + parent::update($data, $id, $attribute); + + foreach ($this->typeRepositories as $type => $repository) { + if ($type == $data['type']) + continue; + + $repository->deleteWhere(['booking_product_id' => $id]); + } + + $bookingProductTypeSlot = $this->typeRepositories[$data['type']]->findOneByField('booking_product_id', $id); + + if (isset($data['slots'])) + $data['slots'] = $this->formatSlots($data); + + if (! $bookingProductTypeSlot) { + $this->typeRepositories[$data['type']]->create(array_merge($data, ['booking_product_id' => $id])); + } else { + $this->typeRepositories[$data['type']]->update($data, $bookingProductTypeSlot->id); + } + } + + /** + * @param array $data + * @return array + */ + public function formatSlots($data) + { + if (isset($data['slots']) && isset($data['same_slot_all_days']) && ! $data['same_slot_all_days']) { + for ($i = 0; $i < 7; $i++) { + if (! isset($data['slots'][$i])) { + $data['slots'][$i] = []; + } else { + $count = 0; + + $slots = []; + + foreach ($data['slots'][$i] as $slot) { + $slots[] = array_merge($slot, ['id' => $i . '_slot_' . $count]); + + $count++; + } + + $data['slots'][$i] = $slots; + } + } + + ksort($data['slots']); + } + + return $data['slots']; + } +} \ No newline at end of file diff --git a/packages/Webkul/BookingProduct/src/Repositories/BookingProductTableSlotRepository.php b/packages/Webkul/BookingProduct/src/Repositories/BookingProductTableSlotRepository.php new file mode 100644 index 000000000..634d264d1 --- /dev/null +++ b/packages/Webkul/BookingProduct/src/Repositories/BookingProductTableSlotRepository.php @@ -0,0 +1,24 @@ + + * @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com) + */ +class BookingProductTableSlotRepository extends Repository +{ + /** + * Specify Model class name + * + * @return mixed + */ + function model() + { + return 'Webkul\BookingProduct\Contracts\BookingProductTableSlot'; + } +} \ No newline at end of file diff --git a/packages/Webkul/BookingProduct/src/Resources/assets/images/arrow-down.svg b/packages/Webkul/BookingProduct/src/Resources/assets/images/arrow-down.svg new file mode 100644 index 000000000..dd459433e --- /dev/null +++ b/packages/Webkul/BookingProduct/src/Resources/assets/images/arrow-down.svg @@ -0,0 +1,10 @@ + + + + icon-dropdown + Created with Sketch. + + + + + \ No newline at end of file diff --git a/packages/Webkul/BookingProduct/src/Resources/assets/images/arrow-up.svg b/packages/Webkul/BookingProduct/src/Resources/assets/images/arrow-up.svg new file mode 100644 index 000000000..4ebba93f5 --- /dev/null +++ b/packages/Webkul/BookingProduct/src/Resources/assets/images/arrow-up.svg @@ -0,0 +1,10 @@ + + + + icon-dropdown-up + Created with Sketch. + + + + + \ No newline at end of file diff --git a/packages/Webkul/BookingProduct/src/Resources/assets/images/location.svg b/packages/Webkul/BookingProduct/src/Resources/assets/images/location.svg new file mode 100644 index 000000000..19fd55a0d --- /dev/null +++ b/packages/Webkul/BookingProduct/src/Resources/assets/images/location.svg @@ -0,0 +1,11 @@ + + + + tab/heading/icon/address + Created with Sketch. + + + + + + \ No newline at end of file diff --git a/packages/Webkul/BookingProduct/src/Resources/assets/images/phone.svg b/packages/Webkul/BookingProduct/src/Resources/assets/images/phone.svg new file mode 100644 index 000000000..e132e7b15 --- /dev/null +++ b/packages/Webkul/BookingProduct/src/Resources/assets/images/phone.svg @@ -0,0 +1,18 @@ + + + + icon-menu copy + Created with Sketch. + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/Webkul/BookingProduct/src/Resources/assets/images/slot.svg b/packages/Webkul/BookingProduct/src/Resources/assets/images/slot.svg new file mode 100644 index 000000000..18d00bd75 --- /dev/null +++ b/packages/Webkul/BookingProduct/src/Resources/assets/images/slot.svg @@ -0,0 +1,15 @@ + + + + tab/heading/icon/calender + Created with Sketch. + + + + + + + + + + \ No newline at end of file diff --git a/packages/Webkul/BookingProduct/src/Resources/assets/sass/app.scss b/packages/Webkul/BookingProduct/src/Resources/assets/sass/app.scss new file mode 100644 index 000000000..cee8ee29f --- /dev/null +++ b/packages/Webkul/BookingProduct/src/Resources/assets/sass/app.scss @@ -0,0 +1,213 @@ +@import "icons"; + +.booking-information { + margin-bottom: 15px; + border-top: 1px solid #E8E8E8; + padding-top: 15px; + + .booking-info-row { + padding-left: 32px; + margin-bottom: 20px; + position: relative; + + .icon { + position: absolute; + left: 0; + top: -4px; + } + + .title { + color: #5E5E5E; + display: block; + margin-bottom: 5px; + } + + .value { + display: block; + margin-bottom: 5px; + + .text-danger { + color: #ff5656; + } + } + + .toggle { + color: #0041FF; + cursor: pointer; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + + .icon { + position: relative; + width: 10px; + height: 10px; + margin-left: 5px; + top: 0; + + &.arrow-down-icon { + background-image: url(../images/arrow-down.svg) !important; + } + + &.arrow-up-icon { + background-image: url(../images/arrow-up.svg) !important; + } + } + } + + .days-availability { + table { + margin-top: 10px; + border-collapse: collapse; + + tr { + td { + padding: 5px; + vertical-align: top; + + &:first-child { + padding-left: 0; + } + + &:last-child { + font-size: 14px; + padding-left: 15px; + padding-right: 0; + color: #5E5E5E; + } + + .text-danger { + color: #ff5656; + } + } + } + } + } + } + + .book-slots { + padding-top: 25px; + display: inline-block; + width: 100%; + border-top: solid 1px #e8e8e8; + + h3 { + font-weight: 600; + font-size: 16px; + color: #242424; + margin-top: 0; + } + + label { + color: #3a3a3a; + } + + .control-group { + label { + font-size: 16px; + } + + .radio { + display: inline-block; + } + } + + .control-group-container { + width: 100%; + float: left; + + .control-group:not(.quantity) { + width: 50%; + float: left; + + .control { + width: 100%; + } + + &.date { + padding-right: 5px; + + &::after { + position: absolute; + top: 14px; + right: 10px; + } + } + + &.slots { + &:first-child { + padding-right: 5px; + } + + &:last-child { + padding-left: 5px; + } + } + } + } + + .ticket-list { + + .ticket-item { + width: 100%; + display: inline-block; + padding: 16px 0; + border-bottom: solid 1px #e8e8e8; + + .ticket-info { + width: 50%; + float: left; + + .ticket-name { + color: #242424; + margin-bottom: 12px; + } + + .ticket-price { + color: #5E5E5E; + } + } + + .ticket-quantity { + width: 50%; + display: inline-block; + + .control-group { + margin: 0; + } + } + + p { + color: #242424; + margin-bottom: 0; + font-weight: 400; + } + } + } + + .ticket-total { + font-size: 16px; + font-weight: 600; + color: #242424; + padding-top: 16px; + // border-top: solid 1px #e8e8e8; + + > div { + margin-bottom: 12px; + + &:last-child { + margin-bottom: 0; + } + + p { + color: #242424; + font-weight: 400; + margin-top: 4px; + } + } + } + } +} \ No newline at end of file diff --git a/packages/Webkul/BookingProduct/src/Resources/assets/sass/icons.scss b/packages/Webkul/BookingProduct/src/Resources/assets/sass/icons.scss new file mode 100644 index 000000000..77c4d1be1 --- /dev/null +++ b/packages/Webkul/BookingProduct/src/Resources/assets/sass/icons.scss @@ -0,0 +1,17 @@ +.bp-location-icon { + background-image: url("../images/location.svg"); + width: 32px; + height: 32px; +} + +.bp-slot-icon { + background-image: url("../images/slot.svg"); + width: 32px; + height: 32px; +} + +.bp-phone-icon { + background-image: url("../images/phone.svg"); + width: 32px; + height: 32px; +} \ No newline at end of file diff --git a/packages/Webkul/BookingProduct/src/Resources/lang/en/app.php b/packages/Webkul/BookingProduct/src/Resources/lang/en/app.php new file mode 100644 index 000000000..1d6d63b7d --- /dev/null +++ b/packages/Webkul/BookingProduct/src/Resources/lang/en/app.php @@ -0,0 +1,109 @@ + [ + 'catalog' => [ + 'products' => [ + 'booking' => 'Booking Information', + 'booking-type' => 'Booking Type', + 'default' => 'Default', + 'appointment-booking' => 'Appointment Booking', + 'event-booking' => 'Event Booking', + 'rental-booking' => 'Rental Booking', + 'table-booking' => 'Table Booking', + 'slot-duration' => 'Slot Duration (Mins)', + 'break-time' => 'Break Time b/w Slots (Mins)', + 'available-every-week' => 'Available Every Week', + 'yes' => 'Yes', + 'no' => 'No', + 'available-from' => 'Available From', + 'available-to' => 'Available To', + 'same-slot-all-days' => 'Same Slot All Days', + 'slot-has-quantity' => 'Slot has Quantity', + 'slots' => 'Slots', + 'from' => 'From', + 'to' => 'To', + 'qty' => 'Qty', + 'add-slot' => 'Add Slot', + 'sunday' => 'Sunday', + 'monday' => 'Monday', + 'tuesday' => 'Tuesday', + 'wednesday' => 'Wednesday', + 'thursday' => 'Thursday', + 'friday' => 'Friday', + 'saturday' => 'Saturday', + 'renting-type' => 'Renting Type', + 'daily' => 'Daily Basis', + 'hourly' => 'Hourly Basis', + 'daily-hourly' => 'Both (Daily and Hourly Basis)', + 'daily-price' => 'Daily Price', + 'hourly-price' => 'Hourly Price', + 'location' => 'Location', + 'show-location' => 'Show Location', + 'event-start-date' => 'Event Start Date', + 'event-end-date' => 'Event End Date', + 'tickets' => 'Tickets', + 'add-ticket' => 'Add Ticket', + 'name' => 'Name', + 'price' => 'Price', + 'quantity' => 'Quantity', + 'description' => 'Description', + 'charged-per' => 'Charged Per', + 'guest' => 'Guest', + 'table' => 'Table', + 'prevent-scheduling-before' => 'Prevent Scheduling Before', + 'guest-limit' => 'Guest Limit', + 'type' => 'Type', + 'many-bookings-for-one-day' => 'Many Bookings for One Day', + 'one-booking-for-many-days' => 'One Booking for Many Days', + 'day' => 'Day', + 'status' => 'Status', + 'open' => 'Open', + 'close' => 'Close' + ] + ] + ], + + 'shop' => [ + 'products' => [ + 'location' => 'Location', + 'contact' => 'Contact', + 'email' => 'Email', + 'slot-duration' => 'Slot Duration', + 'slot-duration-in-minutes' => ':minutes Minutes', + 'today-availability' => 'Today Availability', + 'sunday' => 'Sunday', + 'monday' => 'Monday', + 'tuesday' => 'Tuesday', + 'wednesday' => 'Wednesday', + 'thursday' => 'Thursday', + 'friday' => 'Friday', + 'saturday' => 'Saturday', + 'closed' => 'Closed', + 'book-an-appointment' => 'Book an Appointment', + 'date' => 'Date', + 'slot' => 'Slot', + 'rent-an-item' => 'Rent an Item', + 'choose-rent-option' => 'Choose Rent Option', + 'daily-basis' => 'Daily Basis', + 'hourly-basis' => 'Hourly Basis', + 'select-time-slot'=> 'Select time slot', + 'select-slot' => 'Select Slot', + 'select-date' => 'Select date', + 'select-rent-time' => 'Select Rent Time', + 'from' => 'From', + 'to' => 'To', + 'book-a-table' => 'Book a Table', + 'number-of-tables' => 'Number of Tables', + 'special-notes' => 'Special Request/Notes', + 'event-on' => 'Event On', + 'book-your-ticket' => 'Book Your Ticket', + 'per-ticket-price' => ':price Per Ticket', + 'number-of-tickets' => 'Number of Tickets', + 'total-tickets' => 'Total Tickets', + 'base-price' => 'Base Price', + 'total-price' => 'Total Price', + 'base-price-info' => '(This will be apply to each type of ticket for each quantity)' + ] + ] +]; \ No newline at end of file diff --git a/packages/Webkul/BookingProduct/src/Resources/views/admin/catalog/products/accordians/booking.blade.php b/packages/Webkul/BookingProduct/src/Resources/views/admin/catalog/products/accordians/booking.blade.php new file mode 100644 index 000000000..d667982b3 --- /dev/null +++ b/packages/Webkul/BookingProduct/src/Resources/views/admin/catalog/products/accordians/booking.blade.php @@ -0,0 +1,84 @@ +{!! view_render_event('bagisto.admin.catalog.product.edit_form_accordian.booking.before', ['product' => $product]) !!} + + +
+ + + +
+
+ +{!! view_render_event('bagisto.admin.catalog.product.edit_form_accordian.booking.after', ['product' => $product]) !!} + +@push('scripts') + findOneByField('product_id', $product->id) ?> + + @parent + + + + + + @include ('bookingproduct::admin.catalog.products.accordians.booking.slots') + +@endpush \ No newline at end of file diff --git a/packages/Webkul/BookingProduct/src/Resources/views/admin/catalog/products/accordians/booking/appointment.blade.php b/packages/Webkul/BookingProduct/src/Resources/views/admin/catalog/products/accordians/booking/appointment.blade.php new file mode 100644 index 000000000..5d885c644 --- /dev/null +++ b/packages/Webkul/BookingProduct/src/Resources/views/admin/catalog/products/accordians/booking/appointment.blade.php @@ -0,0 +1,131 @@ +{!! view_render_event('bagisto.admin.catalog.product.edit_form_accordian.booking.appointment.before', ['product' => $product]) !!} + + + +{!! view_render_event('bagisto.admin.catalog.product.edit_form_accordian.booking.appointment.after', ['product' => $product]) !!} + +@push('scripts') + @parent + + + + +@endpush \ No newline at end of file diff --git a/packages/Webkul/BookingProduct/src/Resources/views/admin/catalog/products/accordians/booking/default.blade.php b/packages/Webkul/BookingProduct/src/Resources/views/admin/catalog/products/accordians/booking/default.blade.php new file mode 100644 index 000000000..07e0610a9 --- /dev/null +++ b/packages/Webkul/BookingProduct/src/Resources/views/admin/catalog/products/accordians/booking/default.blade.php @@ -0,0 +1,311 @@ +{!! view_render_event('bagisto.admin.catalog.product.edit_form_accordian.booking.table.before', ['product' => $product]) !!} + + + +{!! view_render_event('bagisto.admin.catalog.product.edit_form_accordian.booking.table.after', ['product' => $product]) !!} + + +@section('css') + +@stop + +@push('scripts') + @parent + + + + + + +@endpush \ No newline at end of file diff --git a/packages/Webkul/BookingProduct/src/Resources/views/admin/catalog/products/accordians/booking/event.blade.php b/packages/Webkul/BookingProduct/src/Resources/views/admin/catalog/products/accordians/booking/event.blade.php new file mode 100644 index 000000000..99d9cc579 --- /dev/null +++ b/packages/Webkul/BookingProduct/src/Resources/views/admin/catalog/products/accordians/booking/event.blade.php @@ -0,0 +1,186 @@ +{!! view_render_event('bagisto.admin.catalog.product.edit_form_accordian.booking.event.before', ['product' => $product]) !!} + + + +{!! view_render_event('bagisto.admin.catalog.product.edit_form_accordian.booking.event.after', ['product' => $product]) !!} + +@push('scripts') + @parent + + + + + + + + + +@endpush \ No newline at end of file diff --git a/packages/Webkul/BookingProduct/src/Resources/views/admin/catalog/products/accordians/booking/rental.blade.php b/packages/Webkul/BookingProduct/src/Resources/views/admin/catalog/products/accordians/booking/rental.blade.php new file mode 100644 index 000000000..9abce359e --- /dev/null +++ b/packages/Webkul/BookingProduct/src/Resources/views/admin/catalog/products/accordians/booking/rental.blade.php @@ -0,0 +1,151 @@ +{!! view_render_event('bagisto.admin.catalog.product.edit_form_accordian.booking.rental.before', ['product' => $product]) !!} + + + +{!! view_render_event('bagisto.admin.catalog.product.edit_form_accordian.booking.rental.after', ['product' => $product]) !!} + +@push('scripts') + @parent + + + + +@endpush \ No newline at end of file diff --git a/packages/Webkul/BookingProduct/src/Resources/views/admin/catalog/products/accordians/booking/slots.blade.php b/packages/Webkul/BookingProduct/src/Resources/views/admin/catalog/products/accordians/booking/slots.blade.php new file mode 100644 index 000000000..2edfc8fe0 --- /dev/null +++ b/packages/Webkul/BookingProduct/src/Resources/views/admin/catalog/products/accordians/booking/slots.blade.php @@ -0,0 +1,211 @@ + + + + + \ No newline at end of file diff --git a/packages/Webkul/BookingProduct/src/Resources/views/admin/catalog/products/accordians/booking/table.blade.php b/packages/Webkul/BookingProduct/src/Resources/views/admin/catalog/products/accordians/booking/table.blade.php new file mode 100644 index 000000000..db57aa9d4 --- /dev/null +++ b/packages/Webkul/BookingProduct/src/Resources/views/admin/catalog/products/accordians/booking/table.blade.php @@ -0,0 +1,150 @@ +{!! view_render_event('bagisto.admin.catalog.product.edit_form_accordian.booking.table.before', ['product' => $product]) !!} + + + +{!! view_render_event('bagisto.admin.catalog.product.edit_form_accordian.booking.table.after', ['product' => $product]) !!} + +@push('scripts') + @parent + + + + +@endpush \ No newline at end of file diff --git a/packages/Webkul/BookingProduct/src/Resources/views/shop/products/view/booking.blade.php b/packages/Webkul/BookingProduct/src/Resources/views/shop/products/view/booking.blade.php new file mode 100644 index 000000000..77196fe48 --- /dev/null +++ b/packages/Webkul/BookingProduct/src/Resources/views/shop/products/view/booking.blade.php @@ -0,0 +1,60 @@ +@if ($product->type == 'booking') + + @if ($bookingProduct = app('\Webkul\BookingProduct\Repositories\BookingProductRepository')->findOneByField('product_id', $product->product_id)) + + @push('css') + + @endpush + + + + @push('scripts') + + + + + + @endpush + + @endif + +@endif \ No newline at end of file diff --git a/packages/Webkul/BookingProduct/src/Resources/views/shop/products/view/booking/appointment.blade.php b/packages/Webkul/BookingProduct/src/Resources/views/shop/products/view/booking/appointment.blade.php new file mode 100644 index 000000000..cc1e5ecd7 --- /dev/null +++ b/packages/Webkul/BookingProduct/src/Resources/views/shop/products/view/booking/appointment.blade.php @@ -0,0 +1,55 @@ +
+ + + {{ __('bookingproduct::app.shop.products.slot-duration') }} : + + {{ __('bookingproduct::app.shop.products.slot-duration-in-minutes', ['minutes' => 30]) }} + +
+ +@inject ('bookingSlotHelper', 'Webkul\BookingProduct\Helpers\AppointmentSlot') + +
+ + + {{ __('bookingproduct::app.shop.products.today-availability') }} + + + + + {!! $bookingSlotHelper->getTodaySlotsHtml($bookingProduct) !!} + + + +
+ Show for all days + + +
+ +
+ + + + @foreach ($bookingSlotHelper->getWeekSlotDurations($bookingProduct) as $day) + + + + + + @endforeach + +
{{ $day['name'] }} + @if ($day['slots'] && count($day['slots'])) + @foreach ($day['slots'] as $slot) + {{ $slot['from'] . ' - ' . $slot['to'] }}
+ @endforeach + @else + {{ __('bookingproduct::app.shop.products.closed') }} + @endif +
+ +
+
+ +@include ('bookingproduct::shop.products.view.booking.slots', ['bookingProduct' => $bookingProduct]) \ No newline at end of file diff --git a/packages/Webkul/BookingProduct/src/Resources/views/shop/products/view/booking/default.blade.php b/packages/Webkul/BookingProduct/src/Resources/views/shop/products/view/booking/default.blade.php new file mode 100644 index 000000000..992374a3c --- /dev/null +++ b/packages/Webkul/BookingProduct/src/Resources/views/shop/products/view/booking/default.blade.php @@ -0,0 +1,10 @@ +
+ + + {{ __('bookingproduct::app.shop.products.slot-duration') }} : + + {{ __('bookingproduct::app.shop.products.slot-duration-in-minutes', ['minutes' => 30]) }} + +
+ +@include ('bookingproduct::shop.products.view.booking.slots', ['bookingProduct' => $bookingProduct]) \ No newline at end of file diff --git a/packages/Webkul/BookingProduct/src/Resources/views/shop/products/view/booking/event.blade.php b/packages/Webkul/BookingProduct/src/Resources/views/shop/products/view/booking/event.blade.php new file mode 100644 index 000000000..d018d298a --- /dev/null +++ b/packages/Webkul/BookingProduct/src/Resources/views/shop/products/view/booking/event.blade.php @@ -0,0 +1,80 @@ +@inject ('bookingSlotHelper', 'Webkul\BookingProduct\Helpers\EventSlot') + +
+ + + {{ __('bookingproduct::app.shop.products.event-on') }} + + + {!! $bookingSlotHelper->getEventDate($bookingProduct) !!} + +
+ + + +@push('scripts') + + + + + +@endpush \ No newline at end of file diff --git a/packages/Webkul/BookingProduct/src/Resources/views/shop/products/view/booking/rental.blade.php b/packages/Webkul/BookingProduct/src/Resources/views/shop/products/view/booking/rental.blade.php new file mode 100644 index 000000000..8de8a1d53 --- /dev/null +++ b/packages/Webkul/BookingProduct/src/Resources/views/shop/products/view/booking/rental.blade.php @@ -0,0 +1,153 @@ + + +@push('scripts') + + + +@endpush \ No newline at end of file diff --git a/packages/Webkul/BookingProduct/src/Resources/views/shop/products/view/booking/slots.blade.php b/packages/Webkul/BookingProduct/src/Resources/views/shop/products/view/booking/slots.blade.php new file mode 100644 index 000000000..1be8e2e89 --- /dev/null +++ b/packages/Webkul/BookingProduct/src/Resources/views/shop/products/view/booking/slots.blade.php @@ -0,0 +1,58 @@ + + +@push('scripts') + + + +@endpush \ No newline at end of file diff --git a/packages/Webkul/BookingProduct/src/Resources/views/shop/products/view/booking/table.blade.php b/packages/Webkul/BookingProduct/src/Resources/views/shop/products/view/booking/table.blade.php new file mode 100644 index 000000000..2c1dd395d --- /dev/null +++ b/packages/Webkul/BookingProduct/src/Resources/views/shop/products/view/booking/table.blade.php @@ -0,0 +1,69 @@ +
+ + + {{ __('bookingproduct::app.shop.products.slot-duration') }} : + + {{ __('bookingproduct::app.shop.products.slot-duration-in-minutes', ['minutes' => 30]) }} + +
+ +@inject ('bookingSlotHelper', 'Webkul\BookingProduct\Helpers\TableSlot') + +
+ + + {{ __('bookingproduct::app.shop.products.today-availability') }} + + + + + {!! $bookingSlotHelper->getTodaySlotsHtml($bookingProduct) !!} + + + +
+ Show for all days + + +
+ +
+ + + + @foreach ($bookingSlotHelper->getWeekSlotDurations($bookingProduct) as $day) + + + + + + @endforeach + +
{{ $day['name'] }} + @if ($day['slots'] && count($day['slots'])) + @foreach ($day['slots'] as $slot) + {{ $slot['from'] . ' - ' . $slot['to'] }}
+ @endforeach + @else + {{ __('bookingproduct::app.shop.products.closed') }} + @endif +
+ +
+
+ +@include ('bookingproduct::shop.products.view.booking.slots', [ + 'bookingProduct' => $bookingProduct, + 'title' => __('bookingproduct::app.shop.products.book-a-table') + ]) + +
+ + + @{{ errors.first('booking[qty]') }} +
+ +
+ + ",g.noCloneChecked=!!xt.cloneNode(!0).lastChild.defaultValue;var St=/^key/,kt=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,$t=/^([^.]*)(?:\.(.+)|)/;function Ot(){return!0}function At(){return!1}function Et(t,e){return t===function(){try{return a.activeElement}catch(t){}}()==("focus"===e)}function Dt(t,e,n,r,i,o){var a,s;if("object"==typeof e){for(s in"string"!=typeof n&&(r=r||n,n=void 0),e)Dt(t,s,n,r,e[s],o);return t}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=At;else if(!i)return t;return 1===o&&(a=i,(i=function(t){return C().off(t),a.apply(this,arguments)}).guid=a.guid||(a.guid=C.guid++)),t.each(function(){C.event.add(this,e,i,r,n)})}function Pt(t,e,n){n?(J.set(t,e,!1),C.event.add(t,e,{namespace:!1,handler:function(t){var r,i,o=J.get(this,e);if(1&t.isTrigger&&this[e]){if(o.length)(C.event.special[e]||{}).delegateType&&t.stopPropagation();else if(o=u.call(arguments),J.set(this,e,o),r=n(this,e),this[e](),o!==(i=J.get(this,e))||r?J.set(this,e,!1):i={},o!==i)return t.stopImmediatePropagation(),t.preventDefault(),i.value}else o.length&&(J.set(this,e,{value:C.event.trigger(C.extend(o[0],C.Event.prototype),o.slice(1),this)}),t.stopImmediatePropagation())}})):void 0===J.get(t,e)&&C.event.add(t,e,Ot)}C.event={global:{},add:function(t,e,n,r,i){var o,a,s,u,l,c,f,d,p,h,v,m=J.get(t);if(m)for(n.handler&&(n=(o=n).handler,i=o.selector),i&&C.find.matchesSelector(at,i),n.guid||(n.guid=C.guid++),(u=m.events)||(u=m.events={}),(a=m.handle)||(a=m.handle=function(e){return void 0!==C&&C.event.triggered!==e.type?C.event.dispatch.apply(t,arguments):void 0}),l=(e=(e||"").match(I)||[""]).length;l--;)p=v=(s=$t.exec(e[l])||[])[1],h=(s[2]||"").split(".").sort(),p&&(f=C.event.special[p]||{},p=(i?f.delegateType:f.bindType)||p,f=C.event.special[p]||{},c=C.extend({type:p,origType:v,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&C.expr.match.needsContext.test(i),namespace:h.join(".")},o),(d=u[p])||((d=u[p]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(t,r,h,a)||t.addEventListener&&t.addEventListener(p,a)),f.add&&(f.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),i?d.splice(d.delegateCount++,0,c):d.push(c),C.event.global[p]=!0)},remove:function(t,e,n,r,i){var o,a,s,u,l,c,f,d,p,h,v,m=J.hasData(t)&&J.get(t);if(m&&(u=m.events)){for(l=(e=(e||"").match(I)||[""]).length;l--;)if(p=v=(s=$t.exec(e[l])||[])[1],h=(s[2]||"").split(".").sort(),p){for(f=C.event.special[p]||{},d=u[p=(r?f.delegateType:f.bindType)||p]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=d.length;o--;)c=d[o],!i&&v!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(d.splice(o,1),c.selector&&d.delegateCount--,f.remove&&f.remove.call(t,c));a&&!d.length&&(f.teardown&&!1!==f.teardown.call(t,h,m.handle)||C.removeEvent(t,p,m.handle),delete u[p])}else for(p in u)C.event.remove(t,p+e[l],n,r,!0);C.isEmptyObject(u)&&J.remove(t,"handle events")}},dispatch:function(t){var e,n,r,i,o,a,s=C.event.fix(t),u=new Array(arguments.length),l=(J.get(this,"events")||{})[s.type]||[],c=C.event.special[s.type]||{};for(u[0]=s,e=1;e=1))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==t.type||!0!==l.disabled)){for(o=[],a={},n=0;n-1:C.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}return l=this,u\x20\t\r\n\f]*)[^>]*)\/>/gi,Nt=/\s*$/g;function Rt(t,e){return E(t,"table")&&E(11!==e.nodeType?e:e.firstChild,"tr")&&C(t).children("tbody")[0]||t}function It(t){return t.type=(null!==t.getAttribute("type"))+"/"+t.type,t}function Ft(t){return"true/"===(t.type||"").slice(0,5)?t.type=t.type.slice(5):t.removeAttribute("type"),t}function Ht(t,e){var n,r,i,o,a,s,u,l;if(1===e.nodeType){if(J.hasData(t)&&(o=J.access(t),a=J.set(e,o),l=o.events))for(i in delete a.handle,a.events={},l)for(n=0,r=l[i].length;n1&&"string"==typeof h&&!g.checkClone&&Mt.test(h))return t.each(function(i){var o=t.eq(i);v&&(e[0]=h.call(this,i,o.html())),Ut(o,e,n,r)});if(d&&(o=(i=Tt(e,t[0].ownerDocument,!1,t,r)).firstChild,1===i.childNodes.length&&(i=o),o||r)){for(s=(a=C.map(bt(i,"script"),It)).length;f")},clone:function(t,e,n){var r,i,o,a,s,u,l,c=t.cloneNode(!0),f=st(t);if(!(g.noCloneChecked||1!==t.nodeType&&11!==t.nodeType||C.isXMLDoc(t)))for(a=bt(c),r=0,i=(o=bt(t)).length;r0&&wt(a,!f&&bt(t,"script")),c},cleanData:function(t){for(var e,n,r,i=C.event.special,o=0;void 0!==(n=t[o]);o++)if(X(n)){if(e=n[J.expando]){if(e.events)for(r in e.events)i[r]?C.event.remove(n,r):C.removeEvent(n,r,e.handle);n[J.expando]=void 0}n[Q.expando]&&(n[Q.expando]=void 0)}}}),C.fn.extend({detach:function(t){return Bt(this,t,!0)},remove:function(t){return Bt(this,t)},text:function(t){return z(this,function(t){return void 0===t?C.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=t)})},null,t,arguments.length)},append:function(){return Ut(this,arguments,function(t){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||Rt(this,t).appendChild(t)})},prepend:function(){return Ut(this,arguments,function(t){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var e=Rt(this,t);e.insertBefore(t,e.firstChild)}})},before:function(){return Ut(this,arguments,function(t){this.parentNode&&this.parentNode.insertBefore(t,this)})},after:function(){return Ut(this,arguments,function(t){this.parentNode&&this.parentNode.insertBefore(t,this.nextSibling)})},empty:function(){for(var t,e=0;null!=(t=this[e]);e++)1===t.nodeType&&(C.cleanData(bt(t,!1)),t.textContent="");return this},clone:function(t,e){return t=null!=t&&t,e=null==e?t:e,this.map(function(){return C.clone(this,t,e)})},html:function(t){return z(this,function(t){var e=this[0]||{},n=0,r=this.length;if(void 0===t&&1===e.nodeType)return e.innerHTML;if("string"==typeof t&&!Nt.test(t)&&!yt[(mt.exec(t)||["",""])[1].toLowerCase()]){t=C.htmlPrefilter(t);try{for(;n=0&&(u+=Math.max(0,Math.ceil(t["offset"+e[0].toUpperCase()+e.slice(1)]-o-u-s-.5))||0),u}function ie(t,e,n){var r=qt(t),i=(!g.boxSizingReliable()||n)&&"border-box"===C.css(t,"boxSizing",!1,r),o=i,a=Wt(t,e,r),s="offset"+e[0].toUpperCase()+e.slice(1);if(Vt.test(a)){if(!n)return a;a="auto"}return(!g.boxSizingReliable()&&i||"auto"===a||!parseFloat(a)&&"inline"===C.css(t,"display",!1,r))&&t.getClientRects().length&&(i="border-box"===C.css(t,"boxSizing",!1,r),(o=s in t)&&(a=t[s])),(a=parseFloat(a)||0)+re(t,e,n||(i?"border":"content"),o,r,a)+"px"}function oe(t,e,n,r,i){return new oe.prototype.init(t,e,n,r,i)}C.extend({cssHooks:{opacity:{get:function(t,e){if(e){var n=Wt(t,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,gridArea:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnStart:!0,gridRow:!0,gridRowEnd:!0,gridRowStart:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(t,e,n,r){if(t&&3!==t.nodeType&&8!==t.nodeType&&t.style){var i,o,a,s=Z(e),u=Qt.test(e),l=t.style;if(u||(e=Kt(s)),a=C.cssHooks[e]||C.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(t,!1,r))?i:l[e];"string"===(o=typeof n)&&(i=it.exec(n))&&i[1]&&(n=ft(t,e,i),o="number"),null!=n&&n==n&&("number"!==o||u||(n+=i&&i[3]||(C.cssNumber[s]?"":"px")),g.clearCloneStyle||""!==n||0!==e.indexOf("background")||(l[e]="inherit"),a&&"set"in a&&void 0===(n=a.set(t,n,r))||(u?l.setProperty(e,n):l[e]=n))}},css:function(t,e,n,r){var i,o,a,s=Z(e);return Qt.test(e)||(e=Kt(s)),(a=C.cssHooks[e]||C.cssHooks[s])&&"get"in a&&(i=a.get(t,!0,n)),void 0===i&&(i=Wt(t,e,r)),"normal"===i&&e in ee&&(i=ee[e]),""===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),C.each(["height","width"],function(t,e){C.cssHooks[e]={get:function(t,n,r){if(n)return!Jt.test(C.css(t,"display"))||t.getClientRects().length&&t.getBoundingClientRect().width?ie(t,e,r):ct(t,te,function(){return ie(t,e,r)})},set:function(t,n,r){var i,o=qt(t),a=!g.scrollboxSize()&&"absolute"===o.position,s=(a||r)&&"border-box"===C.css(t,"boxSizing",!1,o),u=r?re(t,e,r,s,o):0;return s&&a&&(u-=Math.ceil(t["offset"+e[0].toUpperCase()+e.slice(1)]-parseFloat(o[e])-re(t,e,"border",!1,o)-.5)),u&&(i=it.exec(n))&&"px"!==(i[3]||"px")&&(t.style[e]=n,n=C.css(t,e)),ne(0,n,u)}}}),C.cssHooks.marginLeft=Yt(g.reliableMarginLeft,function(t,e){if(e)return(parseFloat(Wt(t,"marginLeft"))||t.getBoundingClientRect().left-ct(t,{marginLeft:0},function(){return t.getBoundingClientRect().left}))+"px"}),C.each({margin:"",padding:"",border:"Width"},function(t,e){C.cssHooks[t+e]={expand:function(n){for(var r=0,i={},o="string"==typeof n?n.split(" "):[n];r<4;r++)i[t+ot[r]+e]=o[r]||o[r-2]||o[0];return i}},"margin"!==t&&(C.cssHooks[t+e].set=ne)}),C.fn.extend({css:function(t,e){return z(this,function(t,e,n){var r,i,o={},a=0;if(Array.isArray(e)){for(r=qt(t),i=e.length;a1)}}),C.Tween=oe,oe.prototype={constructor:oe,init:function(t,e,n,r,i,o){this.elem=t,this.prop=n,this.easing=i||C.easing._default,this.options=e,this.start=this.now=this.cur(),this.end=r,this.unit=o||(C.cssNumber[n]?"":"px")},cur:function(){var t=oe.propHooks[this.prop];return t&&t.get?t.get(this):oe.propHooks._default.get(this)},run:function(t){var e,n=oe.propHooks[this.prop];return this.options.duration?this.pos=e=C.easing[this.easing](t,this.options.duration*t,0,1,this.options.duration):this.pos=e=t,this.now=(this.end-this.start)*e+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):oe.propHooks._default.set(this),this}},oe.prototype.init.prototype=oe.prototype,oe.propHooks={_default:{get:function(t){var e;return 1!==t.elem.nodeType||null!=t.elem[t.prop]&&null==t.elem.style[t.prop]?t.elem[t.prop]:(e=C.css(t.elem,t.prop,""))&&"auto"!==e?e:0},set:function(t){C.fx.step[t.prop]?C.fx.step[t.prop](t):1!==t.elem.nodeType||!C.cssHooks[t.prop]&&null==t.elem.style[Kt(t.prop)]?t.elem[t.prop]=t.now:C.style(t.elem,t.prop,t.now+t.unit)}}},oe.propHooks.scrollTop=oe.propHooks.scrollLeft={set:function(t){t.elem.nodeType&&t.elem.parentNode&&(t.elem[t.prop]=t.now)}},C.easing={linear:function(t){return t},swing:function(t){return.5-Math.cos(t*Math.PI)/2},_default:"swing"},C.fx=oe.prototype.init,C.fx.step={};var ae,se,ue=/^(?:toggle|show|hide)$/,le=/queueHooks$/;function ce(){se&&(!1===a.hidden&&n.requestAnimationFrame?n.requestAnimationFrame(ce):n.setTimeout(ce,C.fx.interval),C.fx.tick())}function fe(){return n.setTimeout(function(){ae=void 0}),ae=Date.now()}function de(t,e){var n,r=0,i={height:t};for(e=e?1:0;r<4;r+=2-e)i["margin"+(n=ot[r])]=i["padding"+n]=t;return e&&(i.opacity=i.width=t),i}function pe(t,e,n){for(var r,i=(he.tweeners[e]||[]).concat(he.tweeners["*"]),o=0,a=i.length;o1)},removeAttr:function(t){return this.each(function(){C.removeAttr(this,t)})}}),C.extend({attr:function(t,e,n){var r,i,o=t.nodeType;if(3!==o&&8!==o&&2!==o)return void 0===t.getAttribute?C.prop(t,e,n):(1===o&&C.isXMLDoc(t)||(i=C.attrHooks[e.toLowerCase()]||(C.expr.match.bool.test(e)?ve:void 0)),void 0!==n?null===n?void C.removeAttr(t,e):i&&"set"in i&&void 0!==(r=i.set(t,n,e))?r:(t.setAttribute(e,n+""),n):i&&"get"in i&&null!==(r=i.get(t,e))?r:null==(r=C.find.attr(t,e))?void 0:r)},attrHooks:{type:{set:function(t,e){if(!g.radioValue&&"radio"===e&&E(t,"input")){var n=t.value;return t.setAttribute("type",e),n&&(t.value=n),e}}}},removeAttr:function(t,e){var n,r=0,i=e&&e.match(I);if(i&&1===t.nodeType)for(;n=i[r++];)t.removeAttribute(n)}}),ve={set:function(t,e,n){return!1===e?C.removeAttr(t,n):t.setAttribute(n,n),n}},C.each(C.expr.match.bool.source.match(/\w+/g),function(t,e){var n=me[e]||C.find.attr;me[e]=function(t,e,r){var i,o,a=e.toLowerCase();return r||(o=me[a],me[a]=i,i=null!=n(t,e,r)?a:null,me[a]=o),i}});var ge=/^(?:input|select|textarea|button)$/i,ye=/^(?:a|area)$/i;function be(t){return(t.match(I)||[]).join(" ")}function we(t){return t.getAttribute&&t.getAttribute("class")||""}function xe(t){return Array.isArray(t)?t:"string"==typeof t&&t.match(I)||[]}C.fn.extend({prop:function(t,e){return z(this,C.prop,t,e,arguments.length>1)},removeProp:function(t){return this.each(function(){delete this[C.propFix[t]||t]})}}),C.extend({prop:function(t,e,n){var r,i,o=t.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&C.isXMLDoc(t)||(e=C.propFix[e]||e,i=C.propHooks[e]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(t,n,e))?r:t[e]=n:i&&"get"in i&&null!==(r=i.get(t,e))?r:t[e]},propHooks:{tabIndex:{get:function(t){var e=C.find.attr(t,"tabindex");return e?parseInt(e,10):ge.test(t.nodeName)||ye.test(t.nodeName)&&t.href?0:-1}}},propFix:{for:"htmlFor",class:"className"}}),g.optSelected||(C.propHooks.selected={get:function(t){var e=t.parentNode;return e&&e.parentNode&&e.parentNode.selectedIndex,null},set:function(t){var e=t.parentNode;e&&(e.selectedIndex,e.parentNode&&e.parentNode.selectedIndex)}}),C.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){C.propFix[this.toLowerCase()]=this}),C.fn.extend({addClass:function(t){var e,n,r,i,o,a,s,u=0;if(y(t))return this.each(function(e){C(this).addClass(t.call(this,e,we(this)))});if((e=xe(t)).length)for(;n=this[u++];)if(i=we(n),r=1===n.nodeType&&" "+be(i)+" "){for(a=0;o=e[a++];)r.indexOf(" "+o+" ")<0&&(r+=o+" ");i!==(s=be(r))&&n.setAttribute("class",s)}return this},removeClass:function(t){var e,n,r,i,o,a,s,u=0;if(y(t))return this.each(function(e){C(this).removeClass(t.call(this,e,we(this)))});if(!arguments.length)return this.attr("class","");if((e=xe(t)).length)for(;n=this[u++];)if(i=we(n),r=1===n.nodeType&&" "+be(i)+" "){for(a=0;o=e[a++];)for(;r.indexOf(" "+o+" ")>-1;)r=r.replace(" "+o+" "," ");i!==(s=be(r))&&n.setAttribute("class",s)}return this},toggleClass:function(t,e){var n=typeof t,r="string"===n||Array.isArray(t);return"boolean"==typeof e&&r?e?this.addClass(t):this.removeClass(t):y(t)?this.each(function(n){C(this).toggleClass(t.call(this,n,we(this),e),e)}):this.each(function(){var e,i,o,a;if(r)for(i=0,o=C(this),a=xe(t);e=a[i++];)o.hasClass(e)?o.removeClass(e):o.addClass(e);else void 0!==t&&"boolean"!==n||((e=we(this))&&J.set(this,"__className__",e),this.setAttribute&&this.setAttribute("class",e||!1===t?"":J.get(this,"__className__")||""))})},hasClass:function(t){var e,n,r=0;for(e=" "+t+" ";n=this[r++];)if(1===n.nodeType&&(" "+be(we(n))+" ").indexOf(e)>-1)return!0;return!1}});var _e=/\r/g;C.fn.extend({val:function(t){var e,n,r,i=this[0];return arguments.length?(r=y(t),this.each(function(n){var i;1===this.nodeType&&(null==(i=r?t.call(this,n,C(this).val()):t)?i="":"number"==typeof i?i+="":Array.isArray(i)&&(i=C.map(i,function(t){return null==t?"":t+""})),(e=C.valHooks[this.type]||C.valHooks[this.nodeName.toLowerCase()])&&"set"in e&&void 0!==e.set(this,i,"value")||(this.value=i))})):i?(e=C.valHooks[i.type]||C.valHooks[i.nodeName.toLowerCase()])&&"get"in e&&void 0!==(n=e.get(i,"value"))?n:"string"==typeof(n=i.value)?n.replace(_e,""):null==n?"":n:void 0}}),C.extend({valHooks:{option:{get:function(t){var e=C.find.attr(t,"value");return null!=e?e:be(C.text(t))}},select:{get:function(t){var e,n,r,i=t.options,o=t.selectedIndex,a="select-one"===t.type,s=a?null:[],u=a?o+1:i.length;for(r=o<0?u:a?o:0;r-1)&&(n=!0);return n||(t.selectedIndex=-1),o}}}}),C.each(["radio","checkbox"],function(){C.valHooks[this]={set:function(t,e){if(Array.isArray(e))return t.checked=C.inArray(C(t).val(),e)>-1}},g.checkOn||(C.valHooks[this].get=function(t){return null===t.getAttribute("value")?"on":t.value})}),g.focusin="onfocusin"in n;var Ce=/^(?:focusinfocus|focusoutblur)$/,Te=function(t){t.stopPropagation()};C.extend(C.event,{trigger:function(t,e,r,i){var o,s,u,l,c,f,d,p,v=[r||a],m=h.call(t,"type")?t.type:t,g=h.call(t,"namespace")?t.namespace.split("."):[];if(s=p=u=r=r||a,3!==r.nodeType&&8!==r.nodeType&&!Ce.test(m+C.event.triggered)&&(m.indexOf(".")>-1&&(m=(g=m.split(".")).shift(),g.sort()),c=m.indexOf(":")<0&&"on"+m,(t=t[C.expando]?t:new C.Event(m,"object"==typeof t&&t)).isTrigger=i?2:3,t.namespace=g.join("."),t.rnamespace=t.namespace?new RegExp("(^|\\.)"+g.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=void 0,t.target||(t.target=r),e=null==e?[t]:C.makeArray(e,[t]),d=C.event.special[m]||{},i||!d.trigger||!1!==d.trigger.apply(r,e))){if(!i&&!d.noBubble&&!b(r)){for(l=d.delegateType||m,Ce.test(l+m)||(s=s.parentNode);s;s=s.parentNode)v.push(s),u=s;u===(r.ownerDocument||a)&&v.push(u.defaultView||u.parentWindow||n)}for(o=0;(s=v[o++])&&!t.isPropagationStopped();)p=s,t.type=o>1?l:d.bindType||m,(f=(J.get(s,"events")||{})[t.type]&&J.get(s,"handle"))&&f.apply(s,e),(f=c&&s[c])&&f.apply&&X(s)&&(t.result=f.apply(s,e),!1===t.result&&t.preventDefault());return t.type=m,i||t.isDefaultPrevented()||d._default&&!1!==d._default.apply(v.pop(),e)||!X(r)||c&&y(r[m])&&!b(r)&&((u=r[c])&&(r[c]=null),C.event.triggered=m,t.isPropagationStopped()&&p.addEventListener(m,Te),r[m](),t.isPropagationStopped()&&p.removeEventListener(m,Te),C.event.triggered=void 0,u&&(r[c]=u)),t.result}},simulate:function(t,e,n){var r=C.extend(new C.Event,n,{type:t,isSimulated:!0});C.event.trigger(r,null,e)}}),C.fn.extend({trigger:function(t,e){return this.each(function(){C.event.trigger(t,e,this)})},triggerHandler:function(t,e){var n=this[0];if(n)return C.event.trigger(t,e,n,!0)}}),g.focusin||C.each({focus:"focusin",blur:"focusout"},function(t,e){var n=function(t){C.event.simulate(e,t.target,C.event.fix(t))};C.event.special[e]={setup:function(){var r=this.ownerDocument||this,i=J.access(r,e);i||r.addEventListener(t,n,!0),J.access(r,e,(i||0)+1)},teardown:function(){var r=this.ownerDocument||this,i=J.access(r,e)-1;i?J.access(r,e,i):(r.removeEventListener(t,n,!0),J.remove(r,e))}}});var Se=n.location,ke=Date.now(),$e=/\?/;C.parseXML=function(t){var e;if(!t||"string"!=typeof t)return null;try{e=(new n.DOMParser).parseFromString(t,"text/xml")}catch(t){e=void 0}return e&&!e.getElementsByTagName("parsererror").length||C.error("Invalid XML: "+t),e};var Oe=/\[\]$/,Ae=/\r?\n/g,Ee=/^(?:submit|button|image|reset|file)$/i,De=/^(?:input|select|textarea|keygen)/i;function Pe(t,e,n,r){var i;if(Array.isArray(e))C.each(e,function(e,i){n||Oe.test(t)?r(t,i):Pe(t+"["+("object"==typeof i&&null!=i?e:"")+"]",i,n,r)});else if(n||"object"!==_(e))r(t,e);else for(i in e)Pe(t+"["+i+"]",e[i],n,r)}C.param=function(t,e){var n,r=[],i=function(t,e){var n=y(e)?e():e;r[r.length]=encodeURIComponent(t)+"="+encodeURIComponent(null==n?"":n)};if(null==t)return"";if(Array.isArray(t)||t.jquery&&!C.isPlainObject(t))C.each(t,function(){i(this.name,this.value)});else for(n in t)Pe(n,t[n],e,i);return r.join("&")},C.fn.extend({serialize:function(){return C.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var t=C.prop(this,"elements");return t?C.makeArray(t):this}).filter(function(){var t=this.type;return this.name&&!C(this).is(":disabled")&&De.test(this.nodeName)&&!Ee.test(t)&&(this.checked||!vt.test(t))}).map(function(t,e){var n=C(this).val();return null==n?null:Array.isArray(n)?C.map(n,function(t){return{name:e.name,value:t.replace(Ae,"\r\n")}}):{name:e.name,value:n.replace(Ae,"\r\n")}}).get()}});var je=/%20/g,Ne=/#.*$/,Me=/([?&])_=[^&]*/,Le=/^(.*?):[ \t]*([^\r\n]*)$/gm,Re=/^(?:GET|HEAD)$/,Ie=/^\/\//,Fe={},He={},Ue="*/".concat("*"),Be=a.createElement("a");function Ve(t){return function(e,n){"string"!=typeof e&&(n=e,e="*");var r,i=0,o=e.toLowerCase().match(I)||[];if(y(n))for(;r=o[i++];)"+"===r[0]?(r=r.slice(1)||"*",(t[r]=t[r]||[]).unshift(n)):(t[r]=t[r]||[]).push(n)}}function qe(t,e,n,r){var i={},o=t===He;function a(s){var u;return i[s]=!0,C.each(t[s]||[],function(t,s){var l=s(e,n,r);return"string"!=typeof l||o||i[l]?o?!(u=l):void 0:(e.dataTypes.unshift(l),a(l),!1)}),u}return a(e.dataTypes[0])||!i["*"]&&a("*")}function ze(t,e){var n,r,i=C.ajaxSettings.flatOptions||{};for(n in e)void 0!==e[n]&&((i[n]?t:r||(r={}))[n]=e[n]);return r&&C.extend(!0,t,r),t}Be.href=Se.href,C.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Se.href,type:"GET",isLocal:/^(?:about|app|app-storage|.+-extension|file|res|widget):$/.test(Se.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Ue,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":C.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(t,e){return e?ze(ze(t,C.ajaxSettings),e):ze(C.ajaxSettings,t)},ajaxPrefilter:Ve(Fe),ajaxTransport:Ve(He),ajax:function(t,e){"object"==typeof t&&(e=t,t=void 0),e=e||{};var r,i,o,s,u,l,c,f,d,p,h=C.ajaxSetup({},e),v=h.context||h,m=h.context&&(v.nodeType||v.jquery)?C(v):C.event,g=C.Deferred(),y=C.Callbacks("once memory"),b=h.statusCode||{},w={},x={},_="canceled",T={readyState:0,getResponseHeader:function(t){var e;if(c){if(!s)for(s={};e=Le.exec(o);)s[e[1].toLowerCase()+" "]=(s[e[1].toLowerCase()+" "]||[]).concat(e[2]);e=s[t.toLowerCase()+" "]}return null==e?null:e.join(", ")},getAllResponseHeaders:function(){return c?o:null},setRequestHeader:function(t,e){return null==c&&(t=x[t.toLowerCase()]=x[t.toLowerCase()]||t,w[t]=e),this},overrideMimeType:function(t){return null==c&&(h.mimeType=t),this},statusCode:function(t){var e;if(t)if(c)T.always(t[T.status]);else for(e in t)b[e]=[b[e],t[e]];return this},abort:function(t){var e=t||_;return r&&r.abort(e),S(0,e),this}};if(g.promise(T),h.url=((t||h.url||Se.href)+"").replace(Ie,Se.protocol+"//"),h.type=e.method||e.type||h.method||h.type,h.dataTypes=(h.dataType||"*").toLowerCase().match(I)||[""],null==h.crossDomain){l=a.createElement("a");try{l.href=h.url,l.href=l.href,h.crossDomain=Be.protocol+"//"+Be.host!=l.protocol+"//"+l.host}catch(t){h.crossDomain=!0}}if(h.data&&h.processData&&"string"!=typeof h.data&&(h.data=C.param(h.data,h.traditional)),qe(Fe,h,e,T),c)return T;for(d in(f=C.event&&h.global)&&0==C.active++&&C.event.trigger("ajaxStart"),h.type=h.type.toUpperCase(),h.hasContent=!Re.test(h.type),i=h.url.replace(Ne,""),h.hasContent?h.data&&h.processData&&0===(h.contentType||"").indexOf("application/x-www-form-urlencoded")&&(h.data=h.data.replace(je,"+")):(p=h.url.slice(i.length),h.data&&(h.processData||"string"==typeof h.data)&&(i+=($e.test(i)?"&":"?")+h.data,delete h.data),!1===h.cache&&(i=i.replace(Me,"$1"),p=($e.test(i)?"&":"?")+"_="+ke+++p),h.url=i+p),h.ifModified&&(C.lastModified[i]&&T.setRequestHeader("If-Modified-Since",C.lastModified[i]),C.etag[i]&&T.setRequestHeader("If-None-Match",C.etag[i])),(h.data&&h.hasContent&&!1!==h.contentType||e.contentType)&&T.setRequestHeader("Content-Type",h.contentType),T.setRequestHeader("Accept",h.dataTypes[0]&&h.accepts[h.dataTypes[0]]?h.accepts[h.dataTypes[0]]+("*"!==h.dataTypes[0]?", "+Ue+"; q=0.01":""):h.accepts["*"]),h.headers)T.setRequestHeader(d,h.headers[d]);if(h.beforeSend&&(!1===h.beforeSend.call(v,T,h)||c))return T.abort();if(_="abort",y.add(h.complete),T.done(h.success),T.fail(h.error),r=qe(He,h,e,T)){if(T.readyState=1,f&&m.trigger("ajaxSend",[T,h]),c)return T;h.async&&h.timeout>0&&(u=n.setTimeout(function(){T.abort("timeout")},h.timeout));try{c=!1,r.send(w,S)}catch(t){if(c)throw t;S(-1,t)}}else S(-1,"No Transport");function S(t,e,a,s){var l,d,p,w,x,_=e;c||(c=!0,u&&n.clearTimeout(u),r=void 0,o=s||"",T.readyState=t>0?4:0,l=t>=200&&t<300||304===t,a&&(w=function(t,e,n){for(var r,i,o,a,s=t.contents,u=t.dataTypes;"*"===u[0];)u.shift(),void 0===r&&(r=t.mimeType||e.getResponseHeader("Content-Type"));if(r)for(i in s)if(s[i]&&s[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||t.converters[i+" "+u[0]]){o=i;break}a||(a=i)}o=o||a}if(o)return o!==u[0]&&u.unshift(o),n[o]}(h,T,a)),w=function(t,e,n,r){var i,o,a,s,u,l={},c=t.dataTypes.slice();if(c[1])for(a in t.converters)l[a.toLowerCase()]=t.converters[a];for(o=c.shift();o;)if(t.responseFields[o]&&(n[t.responseFields[o]]=e),!u&&r&&t.dataFilter&&(e=t.dataFilter(e,t.dataType)),u=o,o=c.shift())if("*"===o)o=u;else if("*"!==u&&u!==o){if(!(a=l[u+" "+o]||l["* "+o]))for(i in l)if((s=i.split(" "))[1]===o&&(a=l[u+" "+s[0]]||l["* "+s[0]])){!0===a?a=l[i]:!0!==l[i]&&(o=s[0],c.unshift(s[1]));break}if(!0!==a)if(a&&t.throws)e=a(e);else try{e=a(e)}catch(t){return{state:"parsererror",error:a?t:"No conversion from "+u+" to "+o}}}return{state:"success",data:e}}(h,w,T,l),l?(h.ifModified&&((x=T.getResponseHeader("Last-Modified"))&&(C.lastModified[i]=x),(x=T.getResponseHeader("etag"))&&(C.etag[i]=x)),204===t||"HEAD"===h.type?_="nocontent":304===t?_="notmodified":(_=w.state,d=w.data,l=!(p=w.error))):(p=_,!t&&_||(_="error",t<0&&(t=0))),T.status=t,T.statusText=(e||_)+"",l?g.resolveWith(v,[d,_,T]):g.rejectWith(v,[T,_,p]),T.statusCode(b),b=void 0,f&&m.trigger(l?"ajaxSuccess":"ajaxError",[T,h,l?d:p]),y.fireWith(v,[T,_]),f&&(m.trigger("ajaxComplete",[T,h]),--C.active||C.event.trigger("ajaxStop")))}return T},getJSON:function(t,e,n){return C.get(t,e,n,"json")},getScript:function(t,e){return C.get(t,void 0,e,"script")}}),C.each(["get","post"],function(t,e){C[e]=function(t,n,r,i){return y(n)&&(i=i||r,r=n,n=void 0),C.ajax(C.extend({url:t,type:e,dataType:i,data:n,success:r},C.isPlainObject(t)&&t))}}),C._evalUrl=function(t,e){return C.ajax({url:t,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,converters:{"text script":function(){}},dataFilter:function(t){C.globalEval(t,e)}})},C.fn.extend({wrapAll:function(t){var e;return this[0]&&(y(t)&&(t=t.call(this[0])),e=C(t,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&e.insertBefore(this[0]),e.map(function(){for(var t=this;t.firstElementChild;)t=t.firstElementChild;return t}).append(this)),this},wrapInner:function(t){return y(t)?this.each(function(e){C(this).wrapInner(t.call(this,e))}):this.each(function(){var e=C(this),n=e.contents();n.length?n.wrapAll(t):e.append(t)})},wrap:function(t){var e=y(t);return this.each(function(n){C(this).wrapAll(e?t.call(this,n):t)})},unwrap:function(t){return this.parent(t).not("body").each(function(){C(this).replaceWith(this.childNodes)}),this}}),C.expr.pseudos.hidden=function(t){return!C.expr.pseudos.visible(t)},C.expr.pseudos.visible=function(t){return!!(t.offsetWidth||t.offsetHeight||t.getClientRects().length)},C.ajaxSettings.xhr=function(){try{return new n.XMLHttpRequest}catch(t){}};var We={0:200,1223:204},Ye=C.ajaxSettings.xhr();g.cors=!!Ye&&"withCredentials"in Ye,g.ajax=Ye=!!Ye,C.ajaxTransport(function(t){var e,r;if(g.cors||Ye&&!t.crossDomain)return{send:function(i,o){var a,s=t.xhr();if(s.open(t.type,t.url,t.async,t.username,t.password),t.xhrFields)for(a in t.xhrFields)s[a]=t.xhrFields[a];for(a in t.mimeType&&s.overrideMimeType&&s.overrideMimeType(t.mimeType),t.crossDomain||i["X-Requested-With"]||(i["X-Requested-With"]="XMLHttpRequest"),i)s.setRequestHeader(a,i[a]);e=function(t){return function(){e&&(e=r=s.onload=s.onerror=s.onabort=s.ontimeout=s.onreadystatechange=null,"abort"===t?s.abort():"error"===t?"number"!=typeof s.status?o(0,"error"):o(s.status,s.statusText):o(We[s.status]||s.status,s.statusText,"text"!==(s.responseType||"text")||"string"!=typeof s.responseText?{binary:s.response}:{text:s.responseText},s.getAllResponseHeaders()))}},s.onload=e(),r=s.onerror=s.ontimeout=e("error"),void 0!==s.onabort?s.onabort=r:s.onreadystatechange=function(){4===s.readyState&&n.setTimeout(function(){e&&r()})},e=e("abort");try{s.send(t.hasContent&&t.data||null)}catch(t){if(e)throw t}},abort:function(){e&&e()}}}),C.ajaxPrefilter(function(t){t.crossDomain&&(t.contents.script=!1)}),C.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(t){return C.globalEval(t),t}}}),C.ajaxPrefilter("script",function(t){void 0===t.cache&&(t.cache=!1),t.crossDomain&&(t.type="GET")}),C.ajaxTransport("script",function(t){var e,n;if(t.crossDomain||t.scriptAttrs)return{send:function(r,i){e=C(" \ No newline at end of file diff --git a/packages/Webkul/Velocity/src/Resources/assets/js/UI/components/product-quick-view.vue b/packages/Webkul/Velocity/src/Resources/assets/js/UI/components/product-quick-view.vue index 741d045d2..5d2284cfe 100644 --- a/packages/Webkul/Velocity/src/Resources/assets/js/UI/components/product-quick-view.vue +++ b/packages/Webkul/Velocity/src/Resources/assets/js/UI/components/product-quick-view.vue @@ -114,7 +114,7 @@ try { var output = render.call(this, this.$createElement) } catch (exception) { - console.log("something went wrong"); + console.log(this.__('error.something-went-wrong')); } this.$options.staticRenderFns = _staticRenderFns diff --git a/packages/Webkul/Velocity/src/Resources/assets/js/app.js b/packages/Webkul/Velocity/src/Resources/assets/js/app.js index 46585777c..8382cf0ea 100755 --- a/packages/Webkul/Velocity/src/Resources/assets/js/app.js +++ b/packages/Webkul/Velocity/src/Resources/assets/js/app.js @@ -41,6 +41,7 @@ Vue.component('carousel-component', require('./UI/components/carousel')); Vue.component('child-sidebar', require('./UI/components/child-sidebar')); Vue.component('card-list-header', require('./UI/components/card-header')); Vue.component('magnify-image', require('./UI/components/image-magnifier')); +Vue.component('compare-component', require('./UI/components/product-compare')); Vue.component('responsive-sidebar', require('./UI/components/responsive-sidebar')); Vue.component('product-quick-view', require('./UI/components/product-quick-view')); Vue.component('product-quick-view-btn', require('./UI/components/product-quick-view-btn')); diff --git a/packages/Webkul/Velocity/src/Resources/assets/sass/components/app.scss b/packages/Webkul/Velocity/src/Resources/assets/sass/components/app.scss index 44f1e3aa2..fa5ef1889 100644 --- a/packages/Webkul/Velocity/src/Resources/assets/sass/components/app.scss +++ b/packages/Webkul/Velocity/src/Resources/assets/sass/components/app.scss @@ -255,6 +255,8 @@ } .card-body { + cursor: default; + > div:last-child { margin-top: 10px; } @@ -274,17 +276,6 @@ } } - .wishlist-icon { - height: 38px; - display: table; - text-align: right; - - > i { - display: table-cell; - vertical-align: middle; - } - } - .add-to-cart-btn { width: 100%; position: relative; @@ -298,11 +289,24 @@ } } - ~ .wishlist-icon { + ~ a { right: 0; margin: 0; padding: 0; + height: 38px; + display: table; + cursor: pointer; + text-align: right; position: absolute; + + &.compare-icon { + right: 27px; + } + + > i { + display: table-cell; + vertical-align: middle; + } } } } @@ -708,9 +712,7 @@ .customer-sidebar { border-right: 1px solid $border-general; - } - .customer-sidebar { .account-details { text-align: center; padding: 25px 20px; @@ -775,6 +777,10 @@ &.downloadables::before { content: "\e926"; } + + &.compare::before { + content: "\e93b"; + } } } @@ -1024,6 +1030,51 @@ padding: 6px 12px; } } + + .compare-products { + margin-top: 20px; + + .col, + .col-2 { + padding: 0; + max-width: 35%; + + .row:nth-child(1) { + margin: 0; + + > div { + width: 100%; + margin-top: 10px; + + &.product-title { + margin-top: 15px; + } + } + + .image-wrapper { + img { + width: 100%; + } + } + + .stars { + i { + font-size: 16px; + } + } + + .close-btn { + right: 0; + width: unset; + position: absolute; + + &:hover { + font-weight: 600; + } + } + } + } + } } .account-items-list { @@ -1494,7 +1545,7 @@ span:nth-child(1), .special-price, .price-from > span:not(:nth-child(2)) { - font-size: 20px; + font-size: 20px !important; font-weight: 600; } @@ -2196,6 +2247,10 @@ @extend .small-padding; } } + + .VueCarousel-slide { + cursor: default; + } } .vue-slider { diff --git a/packages/Webkul/Velocity/src/Resources/lang/ar/app.php b/packages/Webkul/Velocity/src/Resources/lang/ar/app.php index 78d5805cb..64c6141db 100644 --- a/packages/Webkul/Velocity/src/Resources/lang/ar/app.php +++ b/packages/Webkul/Velocity/src/Resources/lang/ar/app.php @@ -8,6 +8,12 @@ return [ ], 'customer' => [ + 'compare' => [ + 'text' => 'قارن', + 'added' => 'تمت إضافة العنصر بنجاح لمقارنة القائمة', + 'removed' => 'تمت إزالة العنصر بنجاح من قائمة المقارنة', + 'empty-text' => "ليس لديك أي عناصر في قائمة المقارنة الخاصة بك", + ], 'login-form' => [ 'sign-up' => 'سجل', 'new-customer' => 'عميل جديد', diff --git a/packages/Webkul/Velocity/src/Resources/lang/en/app.php b/packages/Webkul/Velocity/src/Resources/lang/en/app.php index bfc0a98a7..72bfd0a74 100644 --- a/packages/Webkul/Velocity/src/Resources/lang/en/app.php +++ b/packages/Webkul/Velocity/src/Resources/lang/en/app.php @@ -188,6 +188,12 @@ return [ ], 'customer' => [ + 'compare' => [ + 'text' => 'Compare', + 'added' => 'Item successfully added to compare list', + 'removed' => 'Item successfully removed from compare list', + 'empty-text' => "You don't have any items in your compare list", + ], 'login-form' => [ 'sign-up' => 'Sign up', 'new-customer' => 'New Customer', diff --git a/packages/Webkul/Velocity/src/Resources/lang/pt_BR/app.php b/packages/Webkul/Velocity/src/Resources/lang/pt_BR/app.php index 5a91b3923..3f45ca682 100644 --- a/packages/Webkul/Velocity/src/Resources/lang/pt_BR/app.php +++ b/packages/Webkul/Velocity/src/Resources/lang/pt_BR/app.php @@ -8,6 +8,12 @@ return [ ], 'customer' => [ + 'compare' => [ + 'text' => 'Comparar', + 'added' => 'Item adicionado com sucesso à lista de comparação', + 'removed' => 'Item removido com sucesso da lista de comparação', + 'empty-text' => "Você não possui nenhum item na sua lista de comparação", + ], 'login-form' => [ 'sign-up' => 'inscrever-se', 'new-customer' => 'Novo cliente', diff --git a/packages/Webkul/Velocity/src/Resources/views/shop/UI/particals.blade.php b/packages/Webkul/Velocity/src/Resources/views/shop/UI/particals.blade.php index d679e055f..c566e55b6 100644 --- a/packages/Webkul/Velocity/src/Resources/views/shop/UI/particals.blade.php +++ b/packages/Webkul/Velocity/src/Resources/views/shop/UI/particals.blade.php @@ -9,7 +9,7 @@ star @@ -18,7 +18,7 @@