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 @@
+
+
\ 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 @@
+
+
\ 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 @@
+
+
\ 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 @@
+
+
\ 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 @@
+
+
\ 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 @@
+
+
\ 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 @@
+
+
\ 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 @@
+
+
\ 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 @@
+
+
\ 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 @@
+
+
\ 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)
+
+ | {{ $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
+ |
+
+ @endforeach
+
+
+
+
+
+
+@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)
+
+ | {{ $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
+ |
+
+ @endforeach
+
+
+
+
+
+
+@include ('bookingproduct::shop.products.view.booking.slots', [
+ 'bookingProduct' => $bookingProduct,
+ 'title' => __('bookingproduct::app.shop.products.book-a-table')
+ ])
+
+
+
+
+ @{{ errors.first('booking[qty]') }}
+
+
+
+
+
+
\ No newline at end of file
diff --git a/packages/Webkul/BookingProduct/src/Type/Booking.php b/packages/Webkul/BookingProduct/src/Type/Booking.php
new file mode 100644
index 000000000..1140c5a4a
--- /dev/null
+++ b/packages/Webkul/BookingProduct/src/Type/Booking.php
@@ -0,0 +1,169 @@
+
+ * @copyright 2018 Webkul Software Pvt Ltd (http://www.webkul.com)
+ */
+class Booking extends AbstractType
+{
+ /**
+ * Booking helper instance
+ *
+ * @var Booking
+ */
+ protected $bookingHelper;
+
+ /**
+ * Skip attribute for virtual product type
+ *
+ * @var array
+ */
+ protected $skipAttributes = ['width', 'height', 'depth', 'weight'];
+
+ /**
+ * Show quantity box
+ *
+ * @var boolean
+ */
+ protected $showQuantityBox = true;
+
+ /**
+ * @var array
+ */
+ protected $additionalViews = [
+ 'admin::catalog.products.accordians.images',
+ 'admin::catalog.products.accordians.categories',
+ 'bookingproduct::admin.catalog.products.accordians.booking',
+ 'admin::catalog.products.accordians.product-links'
+ ];
+
+ /**
+ * Create a new product type instance.
+ *
+ * @param Webkul\Attribute\Repositories\AttributeRepository $attributeRepository
+ * @param Webkul\Product\Repositories\ProductRepository $productRepository
+ * @param Webkul\Product\Repositories\ProductAttributeValueRepository $attributeValueRepository
+ * @param Webkul\Product\Repositories\ProductInventoryRepository $productInventoryRepository
+ * @param Webkul\Product\Repositories\ProductImageRepository $productImageRepository
+ * @param Webkul\BookingProduct\Helpers\BookingHelper $bookingHelper
+ * @param Webkul\Product\Helpers\ProductImage $productImageHelper
+ * @return void
+ */
+ public function __construct(
+ AttributeRepository $attributeRepository,
+ ProductRepository $productRepository,
+ ProductAttributeValueRepository $attributeValueRepository,
+ ProductInventoryRepository $productInventoryRepository,
+ ProductImageRepository $productImageRepository,
+ BookingHelper $bookingHelper,
+ ProductImage $productImageHelper
+ )
+ {
+ parent::__construct(
+ $attributeRepository,
+ $productRepository,
+ $attributeValueRepository,
+ $productInventoryRepository,
+ $productImageRepository,
+ $productImageHelper
+ );
+
+ $this->bookingHelper = $bookingHelper;
+ }
+
+ /**
+ * Return true if this product type is saleable
+ *
+ * @return array
+ */
+ public function isSaleable()
+ {
+ if (! $this->product->status)
+ return false;
+
+ return true;
+ }
+
+ /**
+ * Return true if this product can have inventory
+ *
+ * @return array
+ */
+ public function isStockable()
+ {
+ return false;
+ }
+
+ /**
+ * Return true if item can be moved to cart from wishlist
+ *
+ * @param CartItem $item
+ * @return boolean
+ */
+ public function canBeMovedFromWishlistToCart($item)
+ {
+ return true;
+ }
+
+ /**
+ * Add product. Returns error message if can't prepare product.
+ *
+ * @param array $data
+ * @return array
+ */
+ public function prepareForCart($data)
+ {
+ $products = parent::prepareForCart($data);
+
+ dd($products);
+ }
+
+ /**
+ *
+ * @param array $options1
+ * @param array $options2
+ * @return boolean
+ */
+ public function compareOptions($options1, $options2)
+ {
+ if ($this->product->id != $options2['product_id'])
+ return false;
+
+ return $options1['booking'] == $options2['booking'];
+ }
+
+ /**
+ * Returns additional information for items
+ *
+ * @param array $data
+ * @return array
+ */
+ public function getAdditionalOptions($data)
+ {
+ $data['attributes'] = [
+ [
+ 'attribute_name' => 'From',
+ 'option_id' => 0,
+ 'option_label' => "",
+ ], [
+ 'attribute_name' => 'To',
+ 'option_id' => 0,
+ 'option_label' => "",
+ ]];
+
+ return $data;
+ }
+}
\ No newline at end of file
diff --git a/packages/Webkul/BookingProduct/webpack.mix.js b/packages/Webkul/BookingProduct/webpack.mix.js
new file mode 100644
index 000000000..c82c06ed3
--- /dev/null
+++ b/packages/Webkul/BookingProduct/webpack.mix.js
@@ -0,0 +1,31 @@
+const mix = require("laravel-mix");
+
+if (mix == 'undefined') {
+ const { mix } = require("laravel-mix");
+}
+
+require("laravel-mix-merge-manifest");
+
+if (mix.inProduction()) {
+ var publicPath = 'publishable/assets';
+} else {
+ var publicPath = "../../../public/themes/default/assets";
+}
+
+mix.setPublicPath(publicPath).mergeManifest();
+mix.disableNotifications();
+
+mix.copyDirectory(__dirname + "/src/Resources/assets/images", publicPath + "/images")
+ .sass(__dirname + "/src/Resources/assets/sass/app.scss", "css/booking-product.css")
+ .options({
+ processCssUrls: false
+ });
+
+
+if (!mix.inProduction()) {
+ mix.sourceMaps();
+}
+
+if (mix.inProduction()) {
+ mix.version();
+}
\ No newline at end of file