Version Description
- Fixed issue with daylight saving time and Google Calendar
- Fixed formatting for {next_day_agenda}
Download this release
Release Info
Developer | Ladela |
Plugin | WordPress Online Booking and Scheduling Plugin – Bookly |
Version | 11.3 |
Comparing to | |
See all releases |
Code changes from version 7.6.1 to 11.3
- autoload.php +12 -12
- backend/AB_Backend.php +0 -111
- backend/Backend.php +131 -0
- backend/modules/appearance/AB_AppearanceController.php +0 -152
- backend/modules/appearance/Controller.php +202 -0
- backend/modules/appearance/resources/css/appearance.css +0 -42
- backend/modules/appearance/resources/js/appearance.js +279 -123
- backend/modules/appearance/templates/_1_service.php +154 -96
- backend/modules/appearance/templates/_2_time.php +0 -192
- backend/modules/appearance/templates/_3_details.php +0 -42
- backend/modules/appearance/templates/_3_time.php +165 -0
- backend/modules/appearance/templates/_4_cart.php +132 -0
- backend/modules/appearance/templates/_4_payment.php +0 -60
- backend/modules/appearance/templates/_5_details.php +63 -0
- backend/modules/appearance/templates/_5_done.php +0 -8
- backend/modules/appearance/templates/_6_payment.php +67 -0
- backend/modules/appearance/templates/_7_done.php +7 -0
- backend/modules/appearance/templates/_card_payment.php +23 -19
- backend/modules/appearance/templates/_codes.php +25 -16
- backend/modules/appearance/templates/_progress_tracker.php +35 -25
- backend/modules/appearance/templates/index.php +113 -77
- backend/modules/appointments/AB_AppointmentsController.php +0 -276
- backend/modules/appointments/Controller.php +246 -0
- backend/modules/appointments/resources/js/appointments.js +271 -0
- backend/modules/appointments/resources/js/ng-app.js +0 -208
- backend/modules/appointments/templates/_export_dialog.php +40 -0
- backend/modules/appointments/templates/_print_dialog.php +1 -0
- backend/modules/appointments/templates/index.php +106 -67
- backend/modules/calendar/AB_CalendarController.php +0 -488
- backend/modules/calendar/Components.php +62 -0
- backend/modules/calendar/Controller.php +584 -0
- backend/modules/calendar/forms/AB_AppointmentForm.php +0 -22
- backend/modules/calendar/forms/AB_CustomerAppointmentForm.php +0 -17
- backend/modules/calendar/resources/css/calendar.css +0 -215
- backend/modules/calendar/resources/css/fullcalendar.min.css +1 -1
- backend/modules/calendar/resources/js/calendar.js +58 -70
- backend/modules/calendar/resources/js/fullcalendar.min.js +5 -4
- backend/{resources/js/ng-edit_appointment_dialog.js → modules/calendar/resources/js/ng-appointment_dialog.js} +288 -131
- backend/modules/calendar/templates/_appointment_dialog.php +149 -0
- backend/modules/calendar/templates/_appointment_form.php +0 -108
- backend/modules/calendar/templates/_custom_fields_form.php +0 -77
- backend/modules/calendar/templates/_customer_details_dialog.php +98 -0
- backend/modules/calendar/templates/calendar.php +79 -49
- backend/modules/coupons/AB_CouponsController.php +0 -43
- backend/modules/coupons/Controller.php +64 -0
- backend/modules/coupons/forms/Coupon.php +19 -0
- backend/modules/coupons/resources/css/coupons.css +0 -20
- backend/modules/coupons/resources/js/coupons.js +61 -2
- backend/modules/coupons/templates/_list.php +0 -4
- backend/modules/coupons/templates/_modal.php +1 -0
- backend/modules/coupons/templates/index.php +40 -30
- backend/modules/custom_fields/AB_CustomFieldsController.php +0 -63
- backend/modules/custom_fields/Controller.php +88 -0
- backend/modules/custom_fields/resources/css/custom_fields.css +0 -86
- backend/modules/custom_fields/resources/js/custom_fields.js +86 -19
- backend/modules/custom_fields/templates/_services.php +32 -0
- backend/modules/custom_fields/templates/index.php +303 -129
- backend/modules/customer/AB_CustomerController.php +0 -254
- backend/modules/customer/forms/AB_CustomerForm.php +0 -28
- backend/modules/customer/resources/css/customers.css +0 -10
- backend/modules/customer/resources/js/ng-app.js +0 -356
- backend/modules/customer/templates/_import.php +0 -37
- backend/modules/customer/templates/index.php +0 -136
- backend/modules/customer/templates/ng-new_customer_dialog.php +0 -110
- backend/modules/customers/Components.php +44 -0
- backend/modules/customers/Controller.php +235 -0
- backend/modules/customers/forms/Customer.php +25 -0
- backend/modules/customers/resources/js/customers.js +222 -0
- backend/modules/customers/resources/js/ng-customer_dialog.js +122 -0
- backend/modules/customers/templates/_customer_dialog.php +60 -0
- backend/modules/customers/templates/_export.php +1 -0
- backend/modules/customers/templates/_import.php +36 -0
- backend/modules/customers/templates/index.php +89 -0
- backend/modules/debug/Controller.php +181 -0
- backend/modules/debug/resources/css/style.css +12 -0
- backend/modules/debug/resources/js/debug.js +3 -0
- backend/modules/debug/templates/index.php +71 -0
- backend/modules/notifications/AB_NotificationsController.php +0 -70
- backend/modules/notifications/Controller.php +108 -0
- backend/modules/notifications/forms/AB_NotificationsForm.php +0 -166
- backend/modules/notifications/forms/Notifications.php +213 -0
- backend/modules/notifications/resources/css/notifications.css +0 -29
- backend/modules/notifications/resources/js/ng-app.js +96 -0
- backend/modules/notifications/resources/js/notification.js +16 -12
- backend/modules/notifications/templates/_codes.php +33 -24
- backend/modules/notifications/templates/_codes_cart.php +16 -0
- backend/modules/notifications/templates/_codes_client_new_wp_user.php +15 -12
- backend/modules/notifications/templates/_codes_staff_agenda.php +7 -4
- backend/modules/notifications/templates/_test_email_notifications_modal.php +105 -0
- backend/modules/notifications/templates/index.php +135 -121
- backend/modules/payment/AB_PaymentController.php +0 -84
- backend/modules/payment/templates/_alert.php +0 -4
- backend/modules/payment/templates/index.php +0 -148
- backend/modules/payments/Components.php +34 -0
- backend/modules/payments/Controller.php +263 -0
- backend/modules/payments/resources/js/ng-payment_details_dialog.js +69 -0
- backend/modules/payments/resources/js/payments.js +184 -0
- backend/modules/payments/templates/_payment_details_dialog.php +21 -0
- backend/modules/payments/templates/details.php +132 -0
- backend/modules/payments/templates/index.php +89 -0
- backend/modules/service/AB_ServiceController.php +0 -282
- backend/modules/service/forms/AB_CategoryForm.php +0 -25
- backend/modules/service/forms/AB_ServiceForm.php +0 -47
- backend/modules/service/resources/css/service.css +0 -144
- backend/modules/service/resources/js/service.js +0 -344
- backend/modules/service/templates/_list.php +0 -177
- backend/modules/service/templates/index.php +0 -105
- backend/modules/services/Controller.php +288 -0
- backend/modules/services/forms/Category.php +22 -0
- backend/modules/services/forms/Service.php +52 -0
- backend/modules/services/resources/js/service.js +511 -0
- backend/modules/services/templates/_category_item.php +18 -0
- backend/modules/services/templates/_list.php +311 -0
- backend/modules/services/templates/index.php +104 -0
- backend/modules/settings/AB_SettingsController.php +0 -197
- backend/modules/settings/Controller.php +241 -0
- backend/modules/settings/forms/AB_BusinessHoursForm.php +0 -66
- backend/modules/settings/forms/AB_CompanyForm.php +0 -64
- backend/modules/settings/forms/BusinessHours.php +81 -0
- backend/modules/settings/forms/Payments.php +27 -0
- backend/modules/settings/resources/js/settings.js +145 -87
- backend/modules/settings/templates/_cartForm.php +42 -0
- backend/modules/settings/templates/_companyForm.php +49 -48
autoload.php
CHANGED
@@ -1,19 +1,19 @@
|
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
|
3 |
/**
|
4 |
-
*
|
|
|
5 |
*/
|
6 |
-
function
|
7 |
-
|
8 |
-
$
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
if (
|
14 |
-
require_once
|
15 |
}
|
16 |
}
|
17 |
}
|
18 |
-
|
19 |
-
spl_autoload_register( 'bookly_autoload' );
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
|
3 |
/**
|
4 |
+
* Bookly Lite autoload.
|
5 |
+
* @param $class
|
6 |
*/
|
7 |
+
function bookly_lite_loader( $class )
|
8 |
+
{
|
9 |
+
if ( preg_match( '/^BooklyLite\\\\(.+)?([^\\\\]+)$/U', ltrim( $class, '\\' ), $match ) ) {
|
10 |
+
$file = __DIR__ . DIRECTORY_SEPARATOR
|
11 |
+
. strtolower( str_replace( '\\', DIRECTORY_SEPARATOR, preg_replace( '/([a-z])([A-Z])/', '$1_$2', $match[1] ) ) )
|
12 |
+
. $match[2]
|
13 |
+
. '.php';
|
14 |
+
if ( is_readable( $file ) ) {
|
15 |
+
require_once $file;
|
16 |
}
|
17 |
}
|
18 |
}
|
19 |
+
spl_autoload_register( 'bookly_lite_loader', true, true );
|
|
backend/AB_Backend.php
DELETED
@@ -1,111 +0,0 @@
|
|
1 |
-
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
-
|
3 |
-
class AB_Backend {
|
4 |
-
|
5 |
-
public function __construct() {
|
6 |
-
add_action( 'admin_menu', array( $this, 'addAdminMenu' ) );
|
7 |
-
|
8 |
-
// Backend controllers.
|
9 |
-
$this->apearanceController = new AB_AppearanceController();
|
10 |
-
$this->calendarController = new AB_CalendarController();
|
11 |
-
$this->customerController = new AB_CustomerController();
|
12 |
-
$this->notificationsController = new AB_NotificationsController();
|
13 |
-
$this->paymentController = new AB_PaymentController();
|
14 |
-
$this->serviceController = new AB_ServiceController();
|
15 |
-
$this->smsController = new AB_SmsController();
|
16 |
-
$this->settingsController = new AB_SettingsController();
|
17 |
-
$this->staffController = new AB_StaffController();
|
18 |
-
$this->couponsController = new AB_CouponsController();
|
19 |
-
$this->customFieldsController = new AB_CustomFieldsController();
|
20 |
-
$this->appointmentsController = new AB_AppointmentsController();
|
21 |
-
|
22 |
-
// Frontend controllers that work via admin-ajax.php.
|
23 |
-
$this->bookingController = new AB_BookingController();
|
24 |
-
|
25 |
-
add_action( 'wp_loaded', array( $this, 'init' ) );
|
26 |
-
add_action( 'admin_init', array( $this, 'addTinyMCEPlugin' ) );
|
27 |
-
}
|
28 |
-
|
29 |
-
public function addTinyMCEPlugin() {
|
30 |
-
/** @var WP_User $current_user */
|
31 |
-
global $current_user;
|
32 |
-
new AB_TinyMCE_Plugin();
|
33 |
-
}
|
34 |
-
|
35 |
-
public function init() {
|
36 |
-
if ( ! session_id() ) {
|
37 |
-
@session_start();
|
38 |
-
}
|
39 |
-
|
40 |
-
if ( isset( $_POST[ 'action' ] ) ) {
|
41 |
-
switch ( $_POST[ 'action' ] ) {
|
42 |
-
case 'ab_update_staff':
|
43 |
-
$this->staffController->updateStaff();
|
44 |
-
break;
|
45 |
-
}
|
46 |
-
}
|
47 |
-
}
|
48 |
-
|
49 |
-
public function addAdminMenu() {
|
50 |
-
/** @var WP_User $current_user */
|
51 |
-
global $current_user;
|
52 |
-
|
53 |
-
// Translated submenu pages.
|
54 |
-
$calendar = __( 'Calendar', 'bookly' );
|
55 |
-
$appointments = __( 'Appointments', 'bookly' );
|
56 |
-
$staff_members = __( 'Staff Members', 'bookly' );
|
57 |
-
$services = __( 'Services', 'bookly' );
|
58 |
-
$sms = __( 'SMS Notifications', 'bookly' );
|
59 |
-
$notifications = __( 'Email Notifications', 'bookly' );
|
60 |
-
$customers = __( 'Customers', 'bookly' );
|
61 |
-
$payments = __( 'Payments', 'bookly' );
|
62 |
-
$appearance = __( 'Appearance', 'bookly' );
|
63 |
-
$settings = __( 'Settings', 'bookly' );
|
64 |
-
$coupons = __( 'Coupons', 'bookly' );
|
65 |
-
$custom_fields = __( 'Custom Fields', 'bookly' );
|
66 |
-
|
67 |
-
if ( $current_user->has_cap( 'administrator' ) || AB_Staff::query()->where( 'wp_user_id', $current_user->ID )->count() ) {
|
68 |
-
if ( function_exists( 'add_options_page' ) ) {
|
69 |
-
$dynamic_position = '80.0000001' . mt_rand( 1, 1000 ); // position always is under `Settings`
|
70 |
-
add_menu_page( 'Bookly', 'Bookly', 'read', 'ab-system', '',
|
71 |
-
plugins_url('resources/images/menu.png', __FILE__), $dynamic_position );
|
72 |
-
add_submenu_page( 'ab-system', $calendar, $calendar, 'read', 'ab-calendar',
|
73 |
-
array( $this->calendarController, 'index' ) );
|
74 |
-
add_submenu_page( 'ab-system', $appointments, $appointments, 'manage_options', 'ab-appointments',
|
75 |
-
array( $this->appointmentsController, 'index' ) );
|
76 |
-
if ( $current_user->has_cap( 'administrator' ) ) {
|
77 |
-
add_submenu_page( 'ab-system', $staff_members, $staff_members, 'manage_options', AB_StaffController::page_slug,
|
78 |
-
array( $this->staffController, 'index' ) );
|
79 |
-
} else {
|
80 |
-
if ( 1 == get_option( 'ab_settings_allow_staff_members_edit_profile' ) ) {
|
81 |
-
add_submenu_page( 'ab-system', __( 'Profile', 'bookly' ), __( 'Profile', 'bookly' ), 'read', AB_StaffController::page_slug,
|
82 |
-
array( $this->staffController, 'index' ) );
|
83 |
-
}
|
84 |
-
}
|
85 |
-
add_submenu_page( 'ab-system', $services, $services, 'manage_options', AB_ServiceController::page_slug,
|
86 |
-
array( $this->serviceController, 'index' ) );
|
87 |
-
add_submenu_page( 'ab-system', $customers, $customers, 'manage_options', AB_CustomerController::page_slug,
|
88 |
-
array( $this->customerController, 'index' ) );
|
89 |
-
add_submenu_page( 'ab-system', $notifications, $notifications, 'manage_options', 'ab-notifications',
|
90 |
-
array( $this->notificationsController, 'index' ) );
|
91 |
-
add_submenu_page( 'ab-system', $sms, $sms, 'manage_options', AB_SmsController::page_slug,
|
92 |
-
array( $this->smsController, 'index' ) );
|
93 |
-
add_submenu_page( 'ab-system', $payments, $payments, 'manage_options', 'ab-payments',
|
94 |
-
array( $this->paymentController, 'index' ) );
|
95 |
-
add_submenu_page( 'ab-system', $appearance, $appearance, 'manage_options', 'ab-appearance',
|
96 |
-
array( $this->apearanceController, 'index' ) );
|
97 |
-
add_submenu_page( 'ab-system', $custom_fields, $custom_fields, 'manage_options', 'ab-custom-fields',
|
98 |
-
array( $this->customFieldsController, 'index' ) );
|
99 |
-
add_submenu_page( 'ab-system', $coupons, $coupons, 'manage_options', 'ab-coupons',
|
100 |
-
array( $this->couponsController, 'index' ) );
|
101 |
-
add_submenu_page( 'ab-system', $settings, $settings, 'manage_options', AB_SettingsController::page_slug,
|
102 |
-
array( $this->settingsController, 'index' ) );
|
103 |
-
|
104 |
-
global $submenu;
|
105 |
-
do_action( 'bookly_addons_menu', 'ab-system' );
|
106 |
-
unset( $submenu[ 'ab-system' ][ 0 ] );
|
107 |
-
}
|
108 |
-
}
|
109 |
-
}
|
110 |
-
|
111 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/Backend.php
ADDED
@@ -0,0 +1,131 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace BooklyLite\Backend;
|
3 |
+
|
4 |
+
use BooklyLite\Backend\Modules;
|
5 |
+
use BooklyLite\Frontend;
|
6 |
+
use BooklyLite\Lib;
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Class Backend
|
10 |
+
* @package BooklyLite\Backend
|
11 |
+
*/
|
12 |
+
class Backend
|
13 |
+
{
|
14 |
+
public function __construct()
|
15 |
+
{
|
16 |
+
// Backend controllers.
|
17 |
+
$this->apearanceController = Modules\Appearance\Controller::getInstance();
|
18 |
+
$this->appointmentsController = Modules\Appointments\Controller::getInstance();
|
19 |
+
$this->calendarController = Modules\Calendar\Controller::getInstance();
|
20 |
+
$this->couponsController = Modules\Coupons\Controller::getInstance();
|
21 |
+
$this->customerController = Modules\Customers\Controller::getInstance();
|
22 |
+
$this->customFieldsController = Modules\CustomFields\Controller::getInstance();
|
23 |
+
$this->debugController = Modules\Debug\Controller::getInstance();
|
24 |
+
$this->notificationsController = Modules\Notifications\Controller::getInstance();
|
25 |
+
$this->paymentController = Modules\Payments\Controller::getInstance();
|
26 |
+
$this->serviceController = Modules\Services\Controller::getInstance();
|
27 |
+
$this->settingsController = Modules\Settings\Controller::getInstance();
|
28 |
+
$this->smsController = Modules\Sms\Controller::getInstance();
|
29 |
+
$this->staffController = Modules\Staff\Controller::getInstance();
|
30 |
+
|
31 |
+
// Frontend controllers that work via admin-ajax.php.
|
32 |
+
$this->bookingController = Frontend\Modules\Booking\Controller::getInstance();
|
33 |
+
$this->customerProfileController = Frontend\Modules\CustomerProfile\Controller::getInstance();
|
34 |
+
if ( ! Lib\Config::isPaymentDisabled( Lib\Entities\Payment::TYPE_AUTHORIZENET ) ) {
|
35 |
+
$this->authorizeNetController = Frontend\Modules\AuthorizeNet\Controller::getInstance();
|
36 |
+
}
|
37 |
+
if ( ! Lib\Config::isPaymentDisabled( Lib\Entities\Payment::TYPE_PAYULATAM ) ) {
|
38 |
+
$this->payulatamController = Frontend\Modules\PayuLatam\Controller::getInstance();
|
39 |
+
}
|
40 |
+
if ( ! Lib\Config::isPaymentDisabled( Lib\Entities\Payment::TYPE_STRIPE ) ) {
|
41 |
+
$this->stripeController = Frontend\Modules\Stripe\Controller::getInstance();
|
42 |
+
}
|
43 |
+
$this->wooCommerceController = Frontend\Modules\WooCommerce\Controller::getInstance();
|
44 |
+
|
45 |
+
add_action( 'admin_menu', array( $this, 'addAdminMenu' ) );
|
46 |
+
add_action( 'wp_loaded', array( $this, 'init' ) );
|
47 |
+
add_action( 'admin_init', array( $this, 'addTinyMCEPlugin' ) );
|
48 |
+
}
|
49 |
+
|
50 |
+
public function init()
|
51 |
+
{
|
52 |
+
if ( ! session_id() ) {
|
53 |
+
@session_start();
|
54 |
+
}
|
55 |
+
}
|
56 |
+
|
57 |
+
public function addTinyMCEPlugin()
|
58 |
+
{
|
59 |
+
new Modules\TinyMce\Plugin();
|
60 |
+
}
|
61 |
+
|
62 |
+
public function addAdminMenu()
|
63 |
+
{
|
64 |
+
/** @var \WP_User $current_user */
|
65 |
+
global $current_user;
|
66 |
+
|
67 |
+
// Translated submenu pages.
|
68 |
+
$calendar = __( 'Calendar', 'bookly' );
|
69 |
+
$appointments = __( 'Appointments', 'bookly' );
|
70 |
+
$staff_members = __( 'Staff Members', 'bookly' );
|
71 |
+
$services = __( 'Services', 'bookly' );
|
72 |
+
$sms = __( 'SMS Notifications', 'bookly' );
|
73 |
+
$notifications = __( 'Email Notifications', 'bookly' );
|
74 |
+
$customers = __( 'Customers', 'bookly' );
|
75 |
+
$payments = __( 'Payments', 'bookly' );
|
76 |
+
$appearance = __( 'Appearance', 'bookly' );
|
77 |
+
$settings = __( 'Settings', 'bookly' );
|
78 |
+
$coupons = __( 'Coupons', 'bookly' );
|
79 |
+
$custom_fields = __( 'Custom Fields', 'bookly' );
|
80 |
+
|
81 |
+
if ( $current_user->has_cap( 'administrator' ) || Lib\Entities\Staff::query()->where( 'wp_user_id', $current_user->ID )->count() ) {
|
82 |
+
if ( function_exists( 'add_options_page' ) ) {
|
83 |
+
$dynamic_position = '80.0000001' . mt_rand( 1, 1000 ); // position always is under `Settings`
|
84 |
+
add_menu_page( 'Bookly', 'Bookly', 'read', 'ab-system', '',
|
85 |
+
plugins_url( 'resources/images/menu.png', __FILE__ ), $dynamic_position );
|
86 |
+
add_submenu_page( 'ab-system', $calendar, $calendar, 'read', 'ab-calendar',
|
87 |
+
array( $this->calendarController, 'index' ) );
|
88 |
+
add_submenu_page( 'ab-system', $appointments, $appointments, 'manage_options', 'ab-appointments',
|
89 |
+
array( $this->appointmentsController, 'index' ) );
|
90 |
+
do_action( 'bookly_render_menu_after_appointments' );
|
91 |
+
if ( $current_user->has_cap( 'administrator' ) ) {
|
92 |
+
add_submenu_page( 'ab-system', $staff_members, $staff_members, 'manage_options', Modules\Staff\Controller::page_slug,
|
93 |
+
array( $this->staffController, 'index' ) );
|
94 |
+
} else {
|
95 |
+
if ( get_option( 'ab_settings_allow_staff_members_edit_profile' ) == 1 ) {
|
96 |
+
add_submenu_page( 'ab-system', __( 'Profile', 'bookly' ), __( 'Profile', 'bookly' ), 'read', Modules\Staff\Controller::page_slug,
|
97 |
+
array( $this->staffController, 'index' ) );
|
98 |
+
}
|
99 |
+
}
|
100 |
+
add_submenu_page( 'ab-system', $services, $services, 'manage_options', Modules\Services\Controller::page_slug,
|
101 |
+
array( $this->serviceController, 'index' ) );
|
102 |
+
add_submenu_page( 'ab-system', $customers, $customers, 'manage_options', Modules\Customers\Controller::page_slug,
|
103 |
+
array( $this->customerController, 'index' ) );
|
104 |
+
add_submenu_page( 'ab-system', $notifications, $notifications, 'manage_options', 'ab-notifications',
|
105 |
+
array( $this->notificationsController, 'index' ) );
|
106 |
+
add_submenu_page( 'ab-system', $sms, $sms, 'manage_options', Modules\Sms\Controller::page_slug,
|
107 |
+
array( $this->smsController, 'index' ) );
|
108 |
+
add_submenu_page( 'ab-system', $payments, $payments, 'manage_options', 'ab-payments',
|
109 |
+
array( $this->paymentController, 'index' ) );
|
110 |
+
add_submenu_page( 'ab-system', $appearance, $appearance, 'manage_options', 'ab-appearance',
|
111 |
+
array( $this->apearanceController, 'index' ) );
|
112 |
+
add_submenu_page( 'ab-system', $custom_fields, $custom_fields, 'manage_options', 'ab-custom-fields',
|
113 |
+
array( $this->customFieldsController, 'index' ) );
|
114 |
+
add_submenu_page( 'ab-system', $coupons, $coupons, 'manage_options', 'ab-coupons',
|
115 |
+
array( $this->couponsController, 'index' ) );
|
116 |
+
add_submenu_page( 'ab-system', $settings, $settings, 'manage_options', Modules\Settings\Controller::page_slug,
|
117 |
+
array( $this->settingsController, 'index' ) );
|
118 |
+
|
119 |
+
if ( isset ( $_GET['page'] ) && $_GET['page'] == 'ab-debug' ) {
|
120 |
+
add_submenu_page( 'ab-system', 'Debug', 'Debug', 'manage_options', 'ab-debug',
|
121 |
+
array( $this->debugController, 'index' ) );
|
122 |
+
}
|
123 |
+
|
124 |
+
global $submenu;
|
125 |
+
do_action( 'bookly_admin_menu', 'ab-system' );
|
126 |
+
unset ( $submenu['ab-system'][0] );
|
127 |
+
}
|
128 |
+
}
|
129 |
+
}
|
130 |
+
|
131 |
+
}
|
backend/modules/appearance/AB_AppearanceController.php
DELETED
@@ -1,152 +0,0 @@
|
|
1 |
-
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Class AB_AppearanceController
|
5 |
-
*/
|
6 |
-
class AB_AppearanceController extends AB_Controller {
|
7 |
-
|
8 |
-
/**
|
9 |
-
* Default Action
|
10 |
-
*/
|
11 |
-
public function index()
|
12 |
-
{
|
13 |
-
/** @var WP_Locale $wp_locale */
|
14 |
-
global $wp_locale;
|
15 |
-
|
16 |
-
$this->enqueueStyles( array(
|
17 |
-
'frontend' => array(
|
18 |
-
'css/intlTelInput.css',
|
19 |
-
'css/ladda.min.css',
|
20 |
-
'css/bookly-main.css',
|
21 |
-
'css/ab-columnizer.css',
|
22 |
-
'css/picker.classic.css',
|
23 |
-
'css/picker.classic.date.css',
|
24 |
-
'css/ab-picker.css',
|
25 |
-
),
|
26 |
-
'backend' => array(
|
27 |
-
'css/bookly.main-backend.css',
|
28 |
-
'bootstrap/css/bootstrap.min.css',
|
29 |
-
'css/bootstrap-editable.css',
|
30 |
-
),
|
31 |
-
'wp' => array(
|
32 |
-
'wp-color-picker',
|
33 |
-
),
|
34 |
-
'module' => array(
|
35 |
-
'css/appearance.css',
|
36 |
-
)
|
37 |
-
) );
|
38 |
-
|
39 |
-
$this->enqueueScripts( array(
|
40 |
-
'backend' => array(
|
41 |
-
'bootstrap/js/bootstrap.min.js' => array( 'jquery' ),
|
42 |
-
'js/bootstrap-editable.min.js' => array( 'jquery' ),
|
43 |
-
),
|
44 |
-
'frontend' => array(
|
45 |
-
'js/picker.js' => array( 'jquery' ),
|
46 |
-
'js/picker.date.js' => array( 'jquery' ),
|
47 |
-
'js/spin.min.js' => array( 'jquery' ),
|
48 |
-
'js/ladda.min.js' => array( 'jquery' ),
|
49 |
-
'js/intlTelInput.min.js' => array( 'ab-picker.date.js' )
|
50 |
-
),
|
51 |
-
'wp' => array(
|
52 |
-
'wp-color-picker',
|
53 |
-
),
|
54 |
-
'module' => array(
|
55 |
-
'js/appearance.js' => array( 'jquery' ),
|
56 |
-
)
|
57 |
-
) );
|
58 |
-
|
59 |
-
wp_localize_script( 'ab-picker.date.js', 'BooklyL10n', array(
|
60 |
-
'today' => __( 'Today', 'bookly' ),
|
61 |
-
'months' => array_values( $wp_locale->month ),
|
62 |
-
'days' => array_values( $wp_locale->weekday_abbrev ),
|
63 |
-
'nextMonth' => __( 'Next month', 'bookly' ),
|
64 |
-
'prevMonth' => __( 'Previous month', 'bookly' ),
|
65 |
-
'date_format' => AB_DateTimeUtils::convertFormat( 'date', AB_DateTimeUtils::FORMAT_PICKADATE ),
|
66 |
-
'start_of_week' => (int) get_option( 'start_of_week' ),
|
67 |
-
'intlTelInput_utils' => plugins_url( 'intlTelInput.utils.js', AB_PATH . '/frontend/resources/js/intlTelInput.utils.js' ),
|
68 |
-
) );
|
69 |
-
|
70 |
-
// Initialize steps (tabs).
|
71 |
-
$this->steps = array(
|
72 |
-
1 => get_option( 'ab_appearance_text_step_service' ),
|
73 |
-
2 => get_option( 'ab_appearance_text_step_time' ),
|
74 |
-
3 => get_option( 'ab_appearance_text_step_details' ),
|
75 |
-
4 => get_option( 'ab_appearance_text_step_payment' ),
|
76 |
-
5 => get_option( 'ab_appearance_text_step_done' )
|
77 |
-
);
|
78 |
-
|
79 |
-
// Render general layout.
|
80 |
-
$this->render( 'index' );
|
81 |
-
} // index
|
82 |
-
|
83 |
-
/**
|
84 |
-
* Update options
|
85 |
-
*/
|
86 |
-
public function executeUpdateAppearanceOptions()
|
87 |
-
{
|
88 |
-
if ( $this->hasParameter( 'options' ) ) {
|
89 |
-
$get_option = $this->getParameter( 'options' );
|
90 |
-
$options = array(
|
91 |
-
// Info text.
|
92 |
-
'ab_appearance_text_info_first_step' => $get_option['text_info_first_step'],
|
93 |
-
'ab_appearance_text_info_second_step' => $get_option['text_info_second_step'],
|
94 |
-
'ab_appearance_text_info_third_step' => $get_option['text_info_third_step'],
|
95 |
-
'ab_appearance_text_info_third_step_guest' => $get_option['text_info_third_step_guest'],
|
96 |
-
'ab_appearance_text_info_fourth_step' => $get_option['text_info_fourth_step'],
|
97 |
-
'ab_appearance_text_info_fifth_step' => $get_option['text_info_fifth_step'],
|
98 |
-
'ab_appearance_text_info_coupon' => $get_option['text_info_coupon'],
|
99 |
-
// Color.
|
100 |
-
'ab_appearance_color' => $get_option['color'],
|
101 |
-
// Step, label and option texts.
|
102 |
-
'ab_appearance_text_step_service' => $get_option['text_step_service'],
|
103 |
-
'ab_appearance_text_step_time' => $get_option['text_step_time'],
|
104 |
-
'ab_appearance_text_step_details' => $get_option['text_step_details'],
|
105 |
-
'ab_appearance_text_step_payment' => $get_option['text_step_payment'],
|
106 |
-
'ab_appearance_text_step_done' => $get_option['text_step_done'],
|
107 |
-
'ab_appearance_text_label_category' => $get_option['text_label_category'],
|
108 |
-
'ab_appearance_text_label_service' => $get_option['text_label_service'],
|
109 |
-
'ab_appearance_text_label_number_of_persons' => $get_option['text_label_number_of_persons'],
|
110 |
-
'ab_appearance_text_label_employee' => $get_option['text_label_employee'],
|
111 |
-
'ab_appearance_text_label_select_date' => $get_option['text_label_select_date'],
|
112 |
-
'ab_appearance_text_label_start_from' => $get_option['text_label_start_from'],
|
113 |
-
'ab_appearance_text_label_finish_by' => $get_option['text_label_finish_by'],
|
114 |
-
'ab_appearance_text_label_name' => $get_option['text_label_name'],
|
115 |
-
'ab_appearance_text_label_phone' => $get_option['text_label_phone'],
|
116 |
-
'ab_appearance_text_label_email' => $get_option['text_label_email'],
|
117 |
-
'ab_appearance_text_option_service' => $get_option['text_option_service'],
|
118 |
-
'ab_appearance_text_option_category' => $get_option['text_option_category'],
|
119 |
-
'ab_appearance_text_option_employee' => $get_option['text_option_employee'],
|
120 |
-
'ab_appearance_text_label_coupon' => $get_option['text_label_coupon'],
|
121 |
-
'ab_appearance_text_label_pay_locally' => $get_option['text_label_pay_locally'],
|
122 |
-
// Checkboxes.
|
123 |
-
'ab_appearance_show_progress_tracker' => $get_option['progress_tracker'],
|
124 |
-
'ab_appearance_show_blocked_timeslots' => $get_option['blocked_timeslots'],
|
125 |
-
'ab_appearance_show_day_one_column' => $get_option['day_one_column'],
|
126 |
-
'ab_appearance_show_calendar' => $get_option['show_calendar'],
|
127 |
-
);
|
128 |
-
|
129 |
-
// Save options.
|
130 |
-
foreach ( $options as $option_name => $option_value ) {
|
131 |
-
update_option( $option_name, $option_value );
|
132 |
-
// Register string for translate in WPML.
|
133 |
-
if ( strpos( $option_name, 'ab_appearance_text_' ) === 0 ) {
|
134 |
-
do_action( 'wpml_register_single_string', 'bookly', $option_name, $option_value );
|
135 |
-
}
|
136 |
-
}
|
137 |
-
}
|
138 |
-
exit;
|
139 |
-
}
|
140 |
-
|
141 |
-
/**
|
142 |
-
* Override parent method to add 'wp_ajax_ab_' prefix
|
143 |
-
* so current 'execute*' methods look nicer.
|
144 |
-
*
|
145 |
-
* @param string $prefix
|
146 |
-
*/
|
147 |
-
protected function registerWpActions( $prefix = '' )
|
148 |
-
{
|
149 |
-
parent::registerWpActions( 'wp_ajax_ab_' );
|
150 |
-
}
|
151 |
-
|
152 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/modules/appearance/Controller.php
ADDED
@@ -0,0 +1,202 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace BooklyLite\Backend\Modules\Appearance;
|
3 |
+
|
4 |
+
use BooklyLite\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Controller
|
8 |
+
* @package BooklyLite\Backend\Modules\Appearance
|
9 |
+
*/
|
10 |
+
class Controller extends Lib\Base\Controller
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Default Action
|
14 |
+
*/
|
15 |
+
public function index()
|
16 |
+
{
|
17 |
+
/** @var \WP_Locale $wp_locale */
|
18 |
+
global $wp_locale;
|
19 |
+
|
20 |
+
$this->enqueueStyles( array(
|
21 |
+
'frontend' => array_merge(
|
22 |
+
( get_option( 'ab_settings_phone_default_country' ) == 'disabled'
|
23 |
+
? array()
|
24 |
+
: array( 'css/intlTelInput.css' ) ),
|
25 |
+
array(
|
26 |
+
'css/ladda.min.css',
|
27 |
+
'css/picker.classic.css',
|
28 |
+
'css/picker.classic.date.css',
|
29 |
+
'css/bookly-main.css',
|
30 |
+
)
|
31 |
+
),
|
32 |
+
'backend' => array(
|
33 |
+
'bootstrap/css/bootstrap-theme.min.css',
|
34 |
+
'css/bootstrap-editable.css',
|
35 |
+
),
|
36 |
+
'wp' => array( 'wp-color-picker' ),
|
37 |
+
) );
|
38 |
+
|
39 |
+
$this->enqueueScripts( array(
|
40 |
+
'backend' => array(
|
41 |
+
'bootstrap/js/bootstrap.min.js' => array( 'jquery' ),
|
42 |
+
'js/bootstrap-editable.min.js' => array( 'jquery' ),
|
43 |
+
'js/alert.js' => array( 'jquery' ),
|
44 |
+
),
|
45 |
+
'frontend' => array_merge(
|
46 |
+
array(
|
47 |
+
'js/picker.js' => array( 'jquery' ),
|
48 |
+
'js/picker.date.js' => array( 'jquery' ),
|
49 |
+
'js/spin.min.js' => array( 'jquery' ),
|
50 |
+
'js/ladda.min.js' => array( 'jquery' ),
|
51 |
+
),
|
52 |
+
get_option( 'ab_settings_phone_default_country' ) == 'disabled'
|
53 |
+
? array()
|
54 |
+
: array( 'js/intlTelInput.min.js' => array( 'jquery' ) )
|
55 |
+
),
|
56 |
+
'wp' => array( 'wp-color-picker' ),
|
57 |
+
'module' => array( 'js/appearance.js' => array( 'jquery' ) )
|
58 |
+
) );
|
59 |
+
|
60 |
+
wp_localize_script( 'ab-picker.date.js', 'BooklyL10n', array(
|
61 |
+
'today' => __( 'Today', 'bookly' ),
|
62 |
+
'months' => array_values( $wp_locale->month ),
|
63 |
+
'days' => array_values( $wp_locale->weekday_abbrev ),
|
64 |
+
'nextMonth' => __( 'Next month', 'bookly' ),
|
65 |
+
'prevMonth' => __( 'Previous month', 'bookly' ),
|
66 |
+
'date_format' => Lib\Utils\DateTime::convertFormat( 'date', Lib\Utils\DateTime::FORMAT_PICKADATE ),
|
67 |
+
'start_of_week' => (int) get_option( 'start_of_week' ),
|
68 |
+
'saved' => __( 'Settings saved.', 'bookly' ),
|
69 |
+
'intlTelInput' => array(
|
70 |
+
'enabled' => ( get_option( 'ab_settings_phone_default_country' ) != 'disabled' ),
|
71 |
+
'utils' => plugins_url( 'intlTelInput.utils.js', Lib\Plugin::getDirectory() . '/frontend/resources/js/intlTelInput.utils.js' ),
|
72 |
+
'country' => get_option( 'ab_settings_phone_default_country' ),
|
73 |
+
)
|
74 |
+
) );
|
75 |
+
|
76 |
+
// Initialize steps (tabs).
|
77 |
+
$this->steps = array(
|
78 |
+
1 => get_option( 'ab_appearance_text_step_service' ),
|
79 |
+
get_option( 'ab_appearance_text_step_extras' ),
|
80 |
+
get_option( 'ab_appearance_text_step_time' ),
|
81 |
+
get_option( 'ab_appearance_text_step_cart' ),
|
82 |
+
get_option( 'ab_appearance_text_step_details' ),
|
83 |
+
get_option( 'ab_appearance_text_step_payment' ),
|
84 |
+
get_option( 'ab_appearance_text_step_done' )
|
85 |
+
);
|
86 |
+
|
87 |
+
// Render general layout.
|
88 |
+
$this->render( 'index' );
|
89 |
+
}
|
90 |
+
|
91 |
+
/**
|
92 |
+
* Update options
|
93 |
+
*/
|
94 |
+
public function executeUpdateAppearanceOptions()
|
95 |
+
{
|
96 |
+
if ( $this->hasParameter( 'options' ) ) {
|
97 |
+
$get_option = $this->getParameter( 'options' );
|
98 |
+
$options = array(
|
99 |
+
// Info text.
|
100 |
+
'ab_appearance_text_info_cart_step' => $get_option['text_info_cart_step'],
|
101 |
+
'ab_appearance_text_info_complete_step' => $get_option['text_info_complete_step'],
|
102 |
+
'ab_appearance_text_info_coupon' => $get_option['text_info_coupon'],
|
103 |
+
'ab_appearance_text_info_details_step' => $get_option['text_info_details_step'],
|
104 |
+
'ab_appearance_text_info_details_step_guest' => $get_option['text_info_details_step_guest'],
|
105 |
+
'ab_appearance_text_info_payment_step' => $get_option['text_info_payment_step'],
|
106 |
+
'ab_appearance_text_info_service_step' => $get_option['text_info_service_step'],
|
107 |
+
'ab_appearance_text_info_time_step' => $get_option['text_info_time_step'],
|
108 |
+
// Color.
|
109 |
+
'ab_appearance_color' => $get_option['color'],
|
110 |
+
// Step, label and option texts.
|
111 |
+
'ab_appearance_text_button_apply' => $get_option['text_button_apply'],
|
112 |
+
'ab_appearance_text_button_back' => $get_option['text_button_back'],
|
113 |
+
'ab_appearance_text_button_book_more' => $get_option['text_button_book_more'],
|
114 |
+
'ab_appearance_text_button_next' => $get_option['text_button_next'],
|
115 |
+
'ab_appearance_text_label_category' => $get_option['text_label_category'],
|
116 |
+
'ab_appearance_text_label_ccard_code' => $get_option['text_label_ccard_code'],
|
117 |
+
'ab_appearance_text_label_ccard_expire' => $get_option['text_label_ccard_expire'],
|
118 |
+
'ab_appearance_text_label_ccard_number' => $get_option['text_label_ccard_number'],
|
119 |
+
'ab_appearance_text_label_coupon' => $get_option['text_label_coupon'],
|
120 |
+
'ab_appearance_text_label_email' => $get_option['text_label_email'],
|
121 |
+
'ab_appearance_text_label_employee' => $get_option['text_label_employee'],
|
122 |
+
'ab_appearance_text_label_finish_by' => $get_option['text_label_finish_by'],
|
123 |
+
'ab_appearance_text_label_name' => $get_option['text_label_name'],
|
124 |
+
'ab_appearance_text_label_number_of_persons' => $get_option['text_label_number_of_persons'],
|
125 |
+
'ab_appearance_text_label_pay_ccard' => $get_option['text_label_pay_ccard'],
|
126 |
+
'ab_appearance_text_label_pay_locally' => $get_option['text_label_pay_locally'],
|
127 |
+
'ab_appearance_text_label_pay_mollie' => $get_option['text_label_pay_mollie'],
|
128 |
+
'ab_appearance_text_label_pay_paypal' => $get_option['text_label_pay_paypal'],
|
129 |
+
'ab_appearance_text_label_phone' => $get_option['text_label_phone'],
|
130 |
+
'ab_appearance_text_label_select_date' => $get_option['text_label_select_date'],
|
131 |
+
'ab_appearance_text_label_service' => $get_option['text_label_service'],
|
132 |
+
'ab_appearance_text_label_start_from' => $get_option['text_label_start_from'],
|
133 |
+
'ab_appearance_text_option_category' => $get_option['text_option_category'],
|
134 |
+
'ab_appearance_text_option_employee' => $get_option['text_option_employee'],
|
135 |
+
'ab_appearance_text_option_service' => $get_option['text_option_service'],
|
136 |
+
'ab_appearance_text_step_cart' => $get_option['text_step_cart'],
|
137 |
+
'ab_appearance_text_step_details' => $get_option['text_step_details'],
|
138 |
+
'ab_appearance_text_step_done' => $get_option['text_step_done'],
|
139 |
+
'ab_appearance_text_step_payment' => $get_option['text_step_payment'],
|
140 |
+
'ab_appearance_text_step_service' => $get_option['text_step_service'],
|
141 |
+
'ab_appearance_text_step_time' => $get_option['text_step_time'],
|
142 |
+
// Validator errors.
|
143 |
+
'ab_appearance_text_required_email' => $get_option['text_required_email'],
|
144 |
+
'ab_appearance_text_required_employee' => $get_option['text_required_employee'],
|
145 |
+
'ab_appearance_text_required_name' => $get_option['text_required_name'],
|
146 |
+
'ab_appearance_text_required_phone' => $get_option['text_required_phone'],
|
147 |
+
'ab_appearance_text_required_service' => $get_option['text_required_service'],
|
148 |
+
// Checkboxes.
|
149 |
+
'ab_appearance_required_employee' => $get_option['required_employee'],
|
150 |
+
'ab_appearance_show_blocked_timeslots' => $get_option['blocked_timeslots'],
|
151 |
+
'ab_appearance_show_calendar' => $get_option['show_calendar'],
|
152 |
+
'ab_appearance_show_day_one_column' => $get_option['day_one_column'],
|
153 |
+
'ab_appearance_show_progress_tracker' => $get_option['progress_tracker'],
|
154 |
+
'ab_appearance_staff_name_with_price' => $get_option['staff_name_with_price'],
|
155 |
+
);
|
156 |
+
|
157 |
+
if ( Lib\Config::extrasEnabled() ) {
|
158 |
+
$options['ab_appearance_text_info_extras_step'] = $get_option['text_info_extras_step'];
|
159 |
+
$options['ab_appearance_text_step_extras'] = $get_option['text_step_extras'];
|
160 |
+
}
|
161 |
+
if ( Lib\Config::locationsEnabled() ) {
|
162 |
+
$options['ab_appearance_required_location'] = $get_option['required_location'];
|
163 |
+
$options['ab_appearance_text_label_location'] = $get_option['text_label_location'];
|
164 |
+
$options['ab_appearance_text_option_location'] = $get_option['text_option_location'];
|
165 |
+
$options['ab_appearance_text_required_location'] = $get_option['text_required_location'];
|
166 |
+
}
|
167 |
+
if ( Lib\Config::multiplyAppointmentsEnabled() ) {
|
168 |
+
$options['ab_appearance_text_label_multiply'] = $get_option['text_label_multiply'];
|
169 |
+
}
|
170 |
+
|
171 |
+
// Save options.
|
172 |
+
foreach ( $options as $option_name => $option_value ) {
|
173 |
+
update_option( $option_name, $option_value );
|
174 |
+
// Register string for translate in WPML.
|
175 |
+
if ( strpos( $option_name, 'ab_appearance_text_' ) === 0 ) {
|
176 |
+
do_action( 'wpml_register_single_string', 'bookly', $option_name, $option_value );
|
177 |
+
}
|
178 |
+
}
|
179 |
+
}
|
180 |
+
exit;
|
181 |
+
}
|
182 |
+
|
183 |
+
/**
|
184 |
+
* Ajax request to dismiss appearance notice for current user.
|
185 |
+
*/
|
186 |
+
public function executeDismissAppearanceNotice()
|
187 |
+
{
|
188 |
+
update_user_meta( get_current_user_id(), Lib\Plugin::getPrefix() . 'dismiss_appearance_notice', 1 );
|
189 |
+
}
|
190 |
+
|
191 |
+
/**
|
192 |
+
* Override parent method to add 'wp_ajax_ab_' prefix
|
193 |
+
* so current 'execute*' methods look nicer.
|
194 |
+
*
|
195 |
+
* @param string $prefix
|
196 |
+
*/
|
197 |
+
protected function registerWpActions( $prefix = '' )
|
198 |
+
{
|
199 |
+
parent::registerWpActions( 'wp_ajax_ab_' );
|
200 |
+
}
|
201 |
+
|
202 |
+
}
|
backend/modules/appearance/resources/css/appearance.css
DELETED
@@ -1,42 +0,0 @@
|
|
1 |
-
/* Backend-Appearance */
|
2 |
-
#ab-appearance { max-width: 960px; }
|
3 |
-
div.controls {
|
4 |
-
float: right;
|
5 |
-
margin-right: 15px;
|
6 |
-
margin-top: 10px;
|
7 |
-
}
|
8 |
-
|
9 |
-
span.spinner {
|
10 |
-
float: left;
|
11 |
-
margin-right: 20px;
|
12 |
-
}
|
13 |
-
|
14 |
-
.editable-click {
|
15 |
-
cursor: pointer;
|
16 |
-
}
|
17 |
-
|
18 |
-
.ab-first-step .select-list {
|
19 |
-
background: transparent;
|
20 |
-
}
|
21 |
-
|
22 |
-
input.appearance-color-picker{
|
23 |
-
height: 24px !important;
|
24 |
-
background:#f3f3f3;
|
25 |
-
background-image:-webkit-gradient(linear,left top,left bottom,from(#fefefe),to(#f4f4f4));
|
26 |
-
background-image:-webkit-linear-gradient(top,#fefefe,#f4f4f4);
|
27 |
-
background-image:-moz-linear-gradient(top,#fefefe,#f4f4f4);
|
28 |
-
background-image:-o-linear-gradient(top,#fefefe,#f4f4f4);
|
29 |
-
background-image:linear-gradient(to bottom,#fefefe,#f4f4f4);
|
30 |
-
}
|
31 |
-
|
32 |
-
.editable-container.popover { z-index: 10001; }
|
33 |
-
.editable-container.editable-popup .arrow { margin-left: 0 !important; }
|
34 |
-
.editable-input > textarea { width: 475px!important; }
|
35 |
-
|
36 |
-
.editableform-loading { background: url('../../../../resources/images/loading.gif') center center no-repeat !important; }
|
37 |
-
.editable-clear-x { background: url('../../../../resources/images/clear.png') center center no-repeat !important; }
|
38 |
-
|
39 |
-
/* media query */
|
40 |
-
@media screen and (max-width: 782px) {
|
41 |
-
.ab-columnizer-wrap { height: 662px!important; }
|
42 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/modules/appearance/resources/js/appearance.js
CHANGED
@@ -1,23 +1,28 @@
|
|
1 |
jQuery(function($) {
|
2 |
var // Progress Tracker.
|
3 |
$progress_tracker_option = $('input#ab-progress-tracker-checkbox'),
|
|
|
4 |
// Time slots setting.
|
5 |
$blocked_timeslots_option = $('input#ab-blocked-timeslots-checkbox'),
|
6 |
$day_one_column_option = $('input#ab-day-one-column-checkbox'),
|
7 |
$show_calendar_option = $('input#ab-show-calendar-checkbox'),
|
8 |
-
|
9 |
-
$
|
10 |
-
$tab_content = $('div.tab-content'),
|
11 |
// Buttons.
|
12 |
$save_button = $('#ajax-send-appearance'),
|
13 |
$reset_button = $('button[type=reset]'),
|
14 |
// Texts.
|
15 |
$text_step_service = $('#ab-text-step-service'),
|
|
|
16 |
$text_step_time = $('#ab-text-step-time'),
|
|
|
17 |
$text_step_details = $('#ab-text-step-details'),
|
18 |
$text_step_payment = $('#ab-text-step-payment'),
|
19 |
$text_step_done = $('#ab-text-step-done'),
|
|
|
|
|
20 |
$text_label_category = $('#ab-text-label-category'),
|
|
|
21 |
$text_option_category = $('#ab-text-option-category'),
|
22 |
$text_option_service = $('#ab-text-option-service'),
|
23 |
$text_option_employee = $('#ab-text-option-employee'),
|
@@ -26,54 +31,85 @@ jQuery(function($) {
|
|
26 |
$text_label_employee = $('#ab-text-label-employee'),
|
27 |
$text_label_select_date = $('#ab-text-label-select_date'),
|
28 |
$text_label_start_from = $('#ab-text-label-start_from'),
|
|
|
|
|
|
|
|
|
29 |
$text_label_finish_by = $('#ab-text-label-finish_by'),
|
30 |
$text_label_name = $('#ab-text-label-name'),
|
31 |
$text_label_phone = $('#ab-text-label-phone'),
|
32 |
$text_label_email = $('#ab-text-label-email'),
|
33 |
$text_label_coupon = $('#ab-text-label-coupon'),
|
34 |
-
$text_info_service = $('#ab-text-info-
|
35 |
-
$
|
36 |
-
$
|
37 |
-
$
|
38 |
-
$
|
39 |
-
$
|
40 |
$text_info_coupon = $('#ab-text-info-coupon'),
|
41 |
-
$
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
42 |
$ab_editable = $('.ab_editable'),
|
43 |
$text_label_pay_locally = $('#ab-text-label-pay-locally'),
|
|
|
44 |
// Calendars.
|
45 |
$second_step_calendar = $('.ab-selected-date'),
|
46 |
-
$second_step_calendar_wrap = $('.ab-slot-calendar')
|
|
|
|
|
47 |
;
|
48 |
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
59 |
|
60 |
// menu fix for WP 3.8.1
|
61 |
$('#toplevel_page_ab-system > ul').css('margin-left', '0px');
|
62 |
|
63 |
// Tabs.
|
64 |
-
$
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
|
|
70 |
|
|
|
|
|
|
|
71 |
// Apply color from color picker.
|
72 |
var applyColor = function() {
|
73 |
var color_important = $color_picker.wpColorPicker('color') + '!important';
|
74 |
-
$('
|
75 |
-
$('div.ab-progress-tracker').find('li.ab-step-tabs').filter('.active').find('div.step').css('background', $color_picker.wpColorPicker('color'));
|
76 |
$('.ab-mobile-step_1 label').css('color', $color_picker.wpColorPicker('color'));
|
|
|
77 |
$('.ab-next-step, .ab-mobile-next-step').css('background', $color_picker.wpColorPicker('color'));
|
78 |
$('.ab-week-days label').css('background-color', $color_picker.wpColorPicker('color'));
|
79 |
$('.picker__frame').attr('style', 'background: ' + color_important);
|
@@ -84,12 +120,12 @@ jQuery(function($) {
|
|
84 |
$('.picker__day--selected').attr('style', 'color: ' + color_important);
|
85 |
$('.picker__button--clear').attr('style', 'color: ' + color_important);
|
86 |
$('.picker__button--today').attr('style', 'color: ' + color_important);
|
87 |
-
$('.ab-
|
|
|
88 |
'background': $color_picker.wpColorPicker('color'),
|
89 |
'border-color': $color_picker.wpColorPicker('color')
|
90 |
});
|
91 |
-
$('.ab-
|
92 |
-
$('.ab-columnizer .ab-available-hour').off().hover(
|
93 |
function() { // mouse-on
|
94 |
$(this).css({
|
95 |
'color': $color_picker.wpColorPicker('color'),
|
@@ -117,18 +153,15 @@ jQuery(function($) {
|
|
117 |
});
|
118 |
}
|
119 |
);
|
120 |
-
$('
|
121 |
-
$('.ab-
|
122 |
-
|
|
|
|
|
|
|
123 |
};
|
124 |
$color_picker.wpColorPicker({
|
125 |
-
change :
|
126 |
-
applyColor();
|
127 |
-
var style_arrow = '' +
|
128 |
-
'.picker__nav--next:before { border-left: 6px solid ' + $color_picker.wpColorPicker('color') + '!important; } ' +
|
129 |
-
'.picker__nav--prev:before { border-right: 6px solid ' + $color_picker.wpColorPicker('color') + '!important; }';
|
130 |
-
$('#ab_update_arrow').html(style_arrow);
|
131 |
-
}
|
132 |
});
|
133 |
// Init calendars.
|
134 |
$('.ab-date-from').pickadate({
|
@@ -166,8 +199,6 @@ jQuery(function($) {
|
|
166 |
onClose : function() {
|
167 |
this.open(false);
|
168 |
}
|
169 |
-
|
170 |
-
|
171 |
});
|
172 |
$second_step_calendar_wrap.find('.picker__holder').css({ top : '0px', left : '0px' });
|
173 |
$second_step_calendar_wrap.toggle($show_calendar_option.prop('checked'));
|
@@ -181,39 +212,66 @@ jQuery(function($) {
|
|
181 |
// Color.
|
182 |
'color' : $color_picker.wpColorPicker('color'),
|
183 |
// Info text.
|
184 |
-
'
|
185 |
-
'
|
186 |
-
'
|
187 |
-
'
|
188 |
-
'
|
189 |
-
'
|
190 |
-
'
|
|
|
|
|
191 |
// Step and label texts.
|
192 |
-
'text_step_service' :
|
193 |
-
'
|
194 |
-
'
|
195 |
-
'
|
196 |
-
'
|
197 |
-
'
|
198 |
-
'
|
199 |
-
'
|
200 |
-
'
|
201 |
-
'
|
202 |
-
'
|
203 |
-
'
|
204 |
-
'
|
205 |
-
'
|
206 |
-
'
|
207 |
-
'
|
208 |
-
'
|
209 |
-
'
|
210 |
-
'
|
211 |
-
'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
212 |
// Checkboxes.
|
213 |
'progress_tracker' : Number($progress_tracker_option.prop('checked')),
|
|
|
214 |
'blocked_timeslots' : Number($blocked_timeslots_option.prop('checked')),
|
215 |
'day_one_column' : Number($day_one_column_option.prop('checked')),
|
216 |
-
'show_calendar' : Number($show_calendar_option.prop('checked'))
|
|
|
|
|
217 |
} // options
|
218 |
}; // data
|
219 |
|
@@ -222,7 +280,7 @@ jQuery(function($) {
|
|
222 |
ladda.start();
|
223 |
$.post(ajaxurl, data, function (response) {
|
224 |
ladda.stop();
|
225 |
-
|
226 |
});
|
227 |
});
|
228 |
|
@@ -233,69 +291,105 @@ jQuery(function($) {
|
|
233 |
|
234 |
// Reset texts.
|
235 |
jQuery.each($('.editable'), function() {
|
236 |
-
|
237 |
-
|
|
|
238 |
|
239 |
-
|
240 |
-
|
241 |
-
$(this).
|
242 |
});
|
243 |
|
244 |
// default value for multiple inputs
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
245 |
$text_label_category.editable('setValue', {
|
246 |
label: $text_label_category.text(),
|
247 |
option: $text_option_category.text(),
|
248 |
-
id_option: $text_label_category.data('
|
249 |
});
|
250 |
|
251 |
$text_label_service.editable('setValue', {
|
252 |
label: $text_label_service.text(),
|
253 |
option: $text_option_service.text(),
|
254 |
-
id_option: $text_label_service.data('
|
|
|
|
|
|
|
255 |
});
|
256 |
|
257 |
$text_label_employee.editable('setValue', {
|
258 |
label: $text_label_employee.text(),
|
259 |
option: $text_option_employee.text(),
|
260 |
-
id_option: $text_label_employee.data('
|
|
|
|
|
|
|
261 |
});
|
262 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
263 |
});
|
264 |
|
265 |
$progress_tracker_option.change(function(){
|
266 |
-
$(
|
267 |
}).trigger('change');
|
268 |
|
269 |
-
var clickTwoStep = function() {
|
270 |
-
$tabs.children('li').removeClass('active');
|
271 |
-
$tabs.children('li[data-step-id="2"]').trigger('click').addClass('active');
|
272 |
-
};
|
273 |
-
|
274 |
var day_one_column = $('.ab-day-one-column'),
|
275 |
day_columns = $('.ab-day-columns');
|
276 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
277 |
// Change show calendar
|
278 |
$show_calendar_option.change(function() {
|
279 |
if (this.checked) {
|
280 |
$second_step_calendar_wrap.show();
|
281 |
-
day_columns.find('.col5,.col6,.col7').hide();
|
282 |
-
|
|
|
283 |
} else {
|
284 |
$second_step_calendar_wrap.hide();
|
285 |
-
|
286 |
-
day_columns.find('.col5,.col6,.col7').css('display','inline-block');
|
|
|
287 |
}
|
288 |
-
clickTwoStep();
|
289 |
});
|
290 |
|
291 |
// Change blocked time slots.
|
292 |
$blocked_timeslots_option.change(function(){
|
293 |
if (this.checked) {
|
294 |
-
$('.ab-
|
295 |
} else {
|
296 |
-
$('.ab-
|
297 |
}
|
298 |
-
clickTwoStep();
|
299 |
});
|
300 |
|
301 |
// Change day one column.
|
@@ -307,7 +401,6 @@ jQuery(function($) {
|
|
307 |
day_one_column.hide();
|
308 |
day_columns.show();
|
309 |
}
|
310 |
-
clickTwoStep();
|
311 |
});
|
312 |
|
313 |
// Clickable week-days.
|
@@ -330,6 +423,7 @@ jQuery(function($) {
|
|
330 |
$.extend(multiple.prototype, {
|
331 |
render: function() {
|
332 |
this.$input = this.$tpl.find('input');
|
|
|
333 |
},
|
334 |
|
335 |
value2html: function(value, element) {
|
@@ -339,6 +433,7 @@ jQuery(function($) {
|
|
339 |
}
|
340 |
$(element).text(value.label);
|
341 |
$('#' + value.id_option).text(value.option);
|
|
|
342 |
},
|
343 |
|
344 |
activate: function () {
|
@@ -349,29 +444,47 @@ jQuery(function($) {
|
|
349 |
if(!value) {
|
350 |
return;
|
351 |
}
|
|
|
|
|
|
|
352 |
this.$input.filter('[name="label"]').val(value.label);
|
353 |
this.$input.filter('[name="option"]').val(value.option);
|
354 |
this.$input.filter('[name="id_option"]').val(value.id_option);
|
|
|
|
|
355 |
},
|
356 |
|
357 |
input2value: function() {
|
358 |
return {
|
359 |
-
label:
|
360 |
-
option:
|
361 |
-
id_option:
|
|
|
|
|
|
|
362 |
};
|
363 |
}
|
364 |
});
|
365 |
|
366 |
multiple.defaults = $.extend({}, $.fn.editabletypes.abstractinput.defaults, {
|
367 |
-
tpl: '<div class="editable-multiple"><
|
368 |
-
|
|
|
369 |
|
370 |
inputclass: ''
|
371 |
});
|
372 |
|
373 |
$.fn.editabletypes.multiple = multiple;
|
374 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
375 |
$text_label_category.editable({
|
376 |
value: {
|
377 |
label: $text_label_category.text(),
|
@@ -383,45 +496,88 @@ jQuery(function($) {
|
|
383 |
value: {
|
384 |
label: $text_label_service.text(),
|
385 |
option: $text_option_service.text(),
|
386 |
-
id_option: $text_label_service.data('option-id')
|
|
|
|
|
|
|
387 |
}
|
388 |
});
|
389 |
$text_label_employee.editable({
|
390 |
value: {
|
391 |
label: $text_label_employee.text(),
|
392 |
option: $text_option_employee.text(),
|
393 |
-
id_option: $text_label_employee.data('option-id')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
394 |
}
|
395 |
});
|
396 |
|
397 |
-
$
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
398 |
$ab_editable.editable();
|
399 |
|
400 |
-
$.fn.editableform.template = '<form class="form-inline editableform"> <div class="control-group"> <div> <div class="editable-input"></div><div class="editable-buttons"></div></div><div
|
|
|
401 |
|
402 |
$ab_editable.on('shown', function(e, editable) {
|
|
|
403 |
$('.editable-notes').html($(e.target).data('notes'));
|
404 |
});
|
405 |
-
|
406 |
-
|
407 |
-
$("span[data-link-class='" + $(e.target).data('link-class') + "']").editable('setValue', params.newValue);
|
408 |
-
$("span." + $(e.target).data('link-class')).text(params.newValue);
|
409 |
});
|
410 |
|
411 |
-
|
412 |
-
|
413 |
-
|
414 |
-
|
415 |
-
|
416 |
-
|
417 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
418 |
|
419 |
-
|
420 |
-
|
421 |
-
|
422 |
-
|
423 |
-
|
424 |
-
jQuery('form.ab-stripe').show();
|
425 |
}
|
426 |
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
427 |
}); // jQuery
|
1 |
jQuery(function($) {
|
2 |
var // Progress Tracker.
|
3 |
$progress_tracker_option = $('input#ab-progress-tracker-checkbox'),
|
4 |
+
$staff_name_with_price_option = $('input#ab-staff-name-with-price-checkbox'),
|
5 |
// Time slots setting.
|
6 |
$blocked_timeslots_option = $('input#ab-blocked-timeslots-checkbox'),
|
7 |
$day_one_column_option = $('input#ab-day-one-column-checkbox'),
|
8 |
$show_calendar_option = $('input#ab-show-calendar-checkbox'),
|
9 |
+
$required_employee_option = $('input#ab-required-employee-checkbox'),
|
10 |
+
$required_location_option = $('input#ab-required-location-checkbox'),
|
|
|
11 |
// Buttons.
|
12 |
$save_button = $('#ajax-send-appearance'),
|
13 |
$reset_button = $('button[type=reset]'),
|
14 |
// Texts.
|
15 |
$text_step_service = $('#ab-text-step-service'),
|
16 |
+
$text_step_extras = $('#ab-text-step-extras'),
|
17 |
$text_step_time = $('#ab-text-step-time'),
|
18 |
+
$text_step_cart = $('#ab-text-step-cart'),
|
19 |
$text_step_details = $('#ab-text-step-details'),
|
20 |
$text_step_payment = $('#ab-text-step-payment'),
|
21 |
$text_step_done = $('#ab-text-step-done'),
|
22 |
+
$text_label_location = $('#ab-text-label-location'),
|
23 |
+
$text_label_multiply = $('#ab-text-label-multiply'),
|
24 |
$text_label_category = $('#ab-text-label-category'),
|
25 |
+
$text_option_location = $('#ab-text-option-location'),
|
26 |
$text_option_category = $('#ab-text-option-category'),
|
27 |
$text_option_service = $('#ab-text-option-service'),
|
28 |
$text_option_employee = $('#ab-text-option-employee'),
|
31 |
$text_label_employee = $('#ab-text-label-employee'),
|
32 |
$text_label_select_date = $('#ab-text-label-select_date'),
|
33 |
$text_label_start_from = $('#ab-text-label-start_from'),
|
34 |
+
$text_button_next = $('#ab-text-button-next'),
|
35 |
+
$text_button_back = $('#ab-text-button-back'),
|
36 |
+
$text_button_book_more = $('#ab-text-button-book-more'),
|
37 |
+
$text_button_apply = $('#ab-text-button-apply'),
|
38 |
$text_label_finish_by = $('#ab-text-label-finish_by'),
|
39 |
$text_label_name = $('#ab-text-label-name'),
|
40 |
$text_label_phone = $('#ab-text-label-phone'),
|
41 |
$text_label_email = $('#ab-text-label-email'),
|
42 |
$text_label_coupon = $('#ab-text-label-coupon'),
|
43 |
+
$text_info_service = $('#ab-text-info-service'),
|
44 |
+
$text_info_extras = $('#ab-text-info-extras'),
|
45 |
+
$text_info_time = $('#ab-text-info-time'),
|
46 |
+
$text_info_cart = $('#ab-text-info-cart'),
|
47 |
+
$text_info_details = $('#ab-text-info-details'),
|
48 |
+
$text_info_details_guest = $('#ab-text-info-details-guest'),
|
49 |
$text_info_coupon = $('#ab-text-info-coupon'),
|
50 |
+
$text_info_payment = $('#ab-text-info-payment'),
|
51 |
+
$text_info_complete = $('#ab-text-info-complete'),
|
52 |
+
$text_label_pay_paypal = $('#ab-text-label-pay-paypal'),
|
53 |
+
$text_label_pay_ccard = $('#ab-text-label-pay-ccard'),
|
54 |
+
$text_label_ccard_number = $('#ab-text-label-ccard-number'),
|
55 |
+
$text_label_ccard_expire = $('#ab-text-label-ccard-expire'),
|
56 |
+
$text_label_ccard_code = $('#ab-text-label-ccard-code'),
|
57 |
+
$color_picker = $('.bookly-js-color-picker'),
|
58 |
$ab_editable = $('.ab_editable'),
|
59 |
$text_label_pay_locally = $('#ab-text-label-pay-locally'),
|
60 |
+
$text_label_pay_mollie = $('#ab-text-label-pay-mollie'),
|
61 |
// Calendars.
|
62 |
$second_step_calendar = $('.ab-selected-date'),
|
63 |
+
$second_step_calendar_wrap = $('.ab-slot-calendar'),
|
64 |
+
// Step settings.
|
65 |
+
$step_settings = $('#bookly-js-step-settings')
|
66 |
;
|
67 |
|
68 |
+
if (BooklyL10n.intlTelInput.enabled) {
|
69 |
+
$('.ab-user-phone').intlTelInput({
|
70 |
+
preferredCountries: [BooklyL10n.intlTelInput.country],
|
71 |
+
defaultCountry: BooklyL10n.intlTelInput.country,
|
72 |
+
geoIpLookup: function (callback) {
|
73 |
+
$.get(ajaxurl, {action: 'ab_ip_info'}, function () {
|
74 |
+
}, 'json').always(function (resp) {
|
75 |
+
var countryCode = (resp && resp.country) ? resp.country : '';
|
76 |
+
callback(countryCode);
|
77 |
+
});
|
78 |
+
},
|
79 |
+
utilsScript: BooklyL10n.intlTelInput.utils
|
80 |
+
});
|
81 |
+
}
|
82 |
+
|
83 |
+
$staff_name_with_price_option.on('change', function () {
|
84 |
+
var staff = $('.ab-select-employee').val();
|
85 |
+
if (staff) {
|
86 |
+
$('.ab-select-employee').val(staff * -1);
|
87 |
+
}
|
88 |
+
$('.employee-name-price').toggle($staff_name_with_price_option.prop("checked"));
|
89 |
+
$('.employee-name').toggle(!$staff_name_with_price_option.prop("checked"));
|
90 |
+
}).trigger('change');
|
91 |
|
92 |
// menu fix for WP 3.8.1
|
93 |
$('#toplevel_page_ab-system > ul').css('margin-left', '0px');
|
94 |
|
95 |
// Tabs.
|
96 |
+
$('li.bookly-nav-item').on('shown.bs.tab', function (e) {
|
97 |
+
$step_settings.children().hide();
|
98 |
+
switch (e.target.getAttribute('data-target')) {
|
99 |
+
case '#ab-step-1': $step_settings.find('#bookly-js-step-service').show(); break;
|
100 |
+
case '#ab-step-3': $step_settings.find('#bookly-js-step-time').show(); break;
|
101 |
+
}
|
102 |
+
});
|
103 |
|
104 |
+
function getEditableValue(val) {
|
105 |
+
return $.trim(val == 'Empty' ? '' : val);
|
106 |
+
}
|
107 |
// Apply color from color picker.
|
108 |
var applyColor = function() {
|
109 |
var color_important = $color_picker.wpColorPicker('color') + '!important';
|
110 |
+
$('.ab-progress-tracker').find('.active').css('color', $color_picker.wpColorPicker('color')).find('.step').css('background', $color_picker.wpColorPicker('color'));
|
|
|
111 |
$('.ab-mobile-step_1 label').css('color', $color_picker.wpColorPicker('color'));
|
112 |
+
$('.bookly-js-actions > a').css('background-color', $color_picker.wpColorPicker('color'));
|
113 |
$('.ab-next-step, .ab-mobile-next-step').css('background', $color_picker.wpColorPicker('color'));
|
114 |
$('.ab-week-days label').css('background-color', $color_picker.wpColorPicker('color'));
|
115 |
$('.picker__frame').attr('style', 'background: ' + color_important);
|
120 |
$('.picker__day--selected').attr('style', 'color: ' + color_important);
|
121 |
$('.picker__button--clear').attr('style', 'color: ' + color_important);
|
122 |
$('.picker__button--today').attr('style', 'color: ' + color_important);
|
123 |
+
$('.ab-extra-step .bookly-extras-thumb.bookly-extras-selected').css('border-color', $color_picker.wpColorPicker('color'));
|
124 |
+
$('.ab-columnizer .ab-day').css({
|
125 |
'background': $color_picker.wpColorPicker('color'),
|
126 |
'border-color': $color_picker.wpColorPicker('color')
|
127 |
});
|
128 |
+
$('.ab-columnizer .ab-hour').off().hover(
|
|
|
129 |
function() { // mouse-on
|
130 |
$(this).css({
|
131 |
'color': $color_picker.wpColorPicker('color'),
|
153 |
});
|
154 |
}
|
155 |
);
|
156 |
+
$('.ab-details-step label').css('color', $color_picker.wpColorPicker('color'));
|
157 |
+
$('.ab-card-form label').css('color', $color_picker.wpColorPicker('color'));
|
158 |
+
$('.ab-nav-tabs .ladda-button, .ab-nav-steps .ladda-button, .ab-btn, .bookly-round-button').css('background-color', $color_picker.wpColorPicker('color'));
|
159 |
+
$('.ab-back-step, .ab-next-step').css('background', $color_picker.wpColorPicker('color'));
|
160 |
+
var style_arrow = '.picker__nav--next:before { border-left: 6px solid ' + $color_picker.wpColorPicker('color') + '!important; } .picker__nav--prev:before { border-right: 6px solid ' + $color_picker.wpColorPicker('color') + '!important; }';
|
161 |
+
$('#ab--style-arrow').html(style_arrow);
|
162 |
};
|
163 |
$color_picker.wpColorPicker({
|
164 |
+
change : applyColor
|
|
|
|
|
|
|
|
|
|
|
|
|
165 |
});
|
166 |
// Init calendars.
|
167 |
$('.ab-date-from').pickadate({
|
199 |
onClose : function() {
|
200 |
this.open(false);
|
201 |
}
|
|
|
|
|
202 |
});
|
203 |
$second_step_calendar_wrap.find('.picker__holder').css({ top : '0px', left : '0px' });
|
204 |
$second_step_calendar_wrap.toggle($show_calendar_option.prop('checked'));
|
212 |
// Color.
|
213 |
'color' : $color_picker.wpColorPicker('color'),
|
214 |
// Info text.
|
215 |
+
'text_info_service_step' : getEditableValue($text_info_service.text()),
|
216 |
+
'text_info_extras_step' : getEditableValue($text_info_extras.text()),
|
217 |
+
'text_info_time_step' : getEditableValue($text_info_time.text()),
|
218 |
+
'text_info_cart_step' : getEditableValue($text_info_cart.text()),
|
219 |
+
'text_info_details_step' : getEditableValue($text_info_details.text()),
|
220 |
+
'text_info_details_step_guest' : getEditableValue($text_info_details_guest.text()),
|
221 |
+
'text_info_payment_step' : getEditableValue($text_info_payment.text()),
|
222 |
+
'text_info_complete_step' : getEditableValue($text_info_complete.text()),
|
223 |
+
'text_info_coupon' : getEditableValue($text_info_coupon.text()),
|
224 |
// Step and label texts.
|
225 |
+
'text_step_service' : getEditableValue($text_step_service.text()),
|
226 |
+
'text_step_extras' : getEditableValue($text_step_extras.text()),
|
227 |
+
'text_step_time' : getEditableValue($text_step_time.text()),
|
228 |
+
'text_step_cart' : getEditableValue($text_step_cart.text()),
|
229 |
+
'text_step_details' : getEditableValue($text_step_details.text()),
|
230 |
+
'text_step_payment' : getEditableValue($text_step_payment.text()),
|
231 |
+
'text_step_done' : getEditableValue($text_step_done.text()),
|
232 |
+
'text_label_location' : getEditableValue($text_label_location.text()),
|
233 |
+
'text_label_category' : getEditableValue($text_label_category.text()),
|
234 |
+
'text_label_service' : getEditableValue($text_label_service.text()),
|
235 |
+
'text_label_number_of_persons' : getEditableValue($text_label_number_of_persons.text()),
|
236 |
+
'text_label_multiply' : getEditableValue($text_label_multiply.text()),
|
237 |
+
'text_label_employee' : getEditableValue($text_label_employee.text()),
|
238 |
+
'text_label_select_date' : getEditableValue($text_label_select_date.text()),
|
239 |
+
'text_label_start_from' : getEditableValue($text_label_start_from.text()),
|
240 |
+
'text_button_next' : getEditableValue($text_button_next.text()),
|
241 |
+
'text_button_back' : getEditableValue($text_button_back.text()),
|
242 |
+
'text_button_apply' : getEditableValue($text_button_apply.text()),
|
243 |
+
'text_button_book_more' : getEditableValue($text_button_book_more.text()),
|
244 |
+
'text_label_finish_by' : getEditableValue($text_label_finish_by.text()),
|
245 |
+
'text_label_name' : getEditableValue($text_label_name.text()),
|
246 |
+
'text_label_phone' : getEditableValue($text_label_phone.text()),
|
247 |
+
'text_label_email' : getEditableValue($text_label_email.text()),
|
248 |
+
'text_label_coupon' : getEditableValue($text_label_coupon.text()),
|
249 |
+
'text_option_location' : getEditableValue($text_option_location.text()),
|
250 |
+
'text_option_category' : getEditableValue($text_option_category.text()),
|
251 |
+
'text_option_service' : getEditableValue($text_option_service.text()),
|
252 |
+
'text_option_employee' : getEditableValue($text_option_employee.text()),
|
253 |
+
'text_label_pay_locally' : getEditableValue($text_label_pay_locally.text()),
|
254 |
+
'text_label_pay_mollie' : getEditableValue($text_label_pay_mollie.text()),
|
255 |
+
'text_label_pay_paypal' : getEditableValue($text_label_pay_paypal.text()),
|
256 |
+
'text_label_pay_ccard' : getEditableValue($text_label_pay_ccard.text()),
|
257 |
+
'text_label_ccard_number' : getEditableValue($text_label_ccard_number.text()),
|
258 |
+
'text_label_ccard_expire' : getEditableValue($text_label_ccard_expire.text()),
|
259 |
+
'text_label_ccard_code' : getEditableValue($text_label_ccard_code.text()),
|
260 |
+
// Validator.
|
261 |
+
'text_required_location' : getEditableValue($('#ab_appearance_text_required_location').html()),
|
262 |
+
'text_required_service' : getEditableValue($('#ab_appearance_text_required_service').html()),
|
263 |
+
'text_required_employee' : getEditableValue($('#ab_appearance_text_required_employee').html()),
|
264 |
+
'text_required_name' : getEditableValue($('#ab_appearance_text_required_name').html()),
|
265 |
+
'text_required_phone' : getEditableValue($('#ab_appearance_text_required_phone').html()),
|
266 |
+
'text_required_email' : getEditableValue($('#ab_appearance_text_required_email').html()),
|
267 |
// Checkboxes.
|
268 |
'progress_tracker' : Number($progress_tracker_option.prop('checked')),
|
269 |
+
'staff_name_with_price': Number($staff_name_with_price_option.prop('checked')),
|
270 |
'blocked_timeslots' : Number($blocked_timeslots_option.prop('checked')),
|
271 |
'day_one_column' : Number($day_one_column_option.prop('checked')),
|
272 |
+
'show_calendar' : Number($show_calendar_option.prop('checked')),
|
273 |
+
'required_employee' : Number($required_employee_option.prop('checked')),
|
274 |
+
'required_location' : Number($required_location_option.prop('checked'))
|
275 |
} // options
|
276 |
}; // data
|
277 |
|
280 |
ladda.start();
|
281 |
$.post(ajaxurl, data, function (response) {
|
282 |
ladda.stop();
|
283 |
+
booklyAlert({success : [BooklyL10n.saved]});
|
284 |
});
|
285 |
});
|
286 |
|
291 |
|
292 |
// Reset texts.
|
293 |
jQuery.each($('.editable'), function() {
|
294 |
+
$(this).text($(this).data('default')); //default value for texts
|
295 |
+
$(this).editable('setValue', $(this).data('default')); // default value for editable inputs
|
296 |
+
});
|
297 |
|
298 |
+
// Reset texts.
|
299 |
+
jQuery.each($('.ab-service-list, .ab-employee-list'), function() {
|
300 |
+
$(this).html($(this).data('default')); //default value
|
301 |
});
|
302 |
|
303 |
// default value for multiple inputs
|
304 |
+
|
305 |
+
$text_label_location.editable('setValue', {
|
306 |
+
label: $text_label_location.text(),
|
307 |
+
option: $text_option_location.text(),
|
308 |
+
id_option: $text_label_location.data('option-id'),
|
309 |
+
extended: true,
|
310 |
+
option2: $text_label_location.data('default-error'),
|
311 |
+
id_option2: $text_label_location.data('error-id')
|
312 |
+
});
|
313 |
+
|
314 |
$text_label_category.editable('setValue', {
|
315 |
label: $text_label_category.text(),
|
316 |
option: $text_option_category.text(),
|
317 |
+
id_option: $text_label_category.data('option-id')
|
318 |
});
|
319 |
|
320 |
$text_label_service.editable('setValue', {
|
321 |
label: $text_label_service.text(),
|
322 |
option: $text_option_service.text(),
|
323 |
+
id_option: $text_label_service.data('option-id'),
|
324 |
+
extended: true,
|
325 |
+
option2: $text_label_service.data('default-error'),
|
326 |
+
id_option2: $text_label_service.data('error-id')
|
327 |
});
|
328 |
|
329 |
$text_label_employee.editable('setValue', {
|
330 |
label: $text_label_employee.text(),
|
331 |
option: $text_option_employee.text(),
|
332 |
+
id_option: $text_label_employee.data('option-id'),
|
333 |
+
extended: true,
|
334 |
+
option2: $text_label_employee.data('default-error'),
|
335 |
+
id_option2: $text_label_employee.data('error-id')
|
336 |
});
|
337 |
|
338 |
+
$text_label_name.editable('setValue', {
|
339 |
+
label: $text_label_name.text(),
|
340 |
+
option: $text_label_name.data('default-error'),
|
341 |
+
id_option: $text_label_name.data('option-id')
|
342 |
+
});
|
343 |
+
|
344 |
+
$text_label_phone.editable('setValue', {
|
345 |
+
label: $text_label_phone.text(),
|
346 |
+
option: $text_label_phone.data('default-error'),
|
347 |
+
id_option: $text_label_phone.data('option-id')
|
348 |
+
});
|
349 |
+
|
350 |
+
$text_label_email.editable('setValue', {
|
351 |
+
label: $text_label_email.text(),
|
352 |
+
option: $text_label_email.data('default-error'),
|
353 |
+
id_option: $text_label_email.data('option-id')
|
354 |
+
});
|
355 |
});
|
356 |
|
357 |
$progress_tracker_option.change(function(){
|
358 |
+
$('.ab-progress-tracker').toggle($(this).is(':checked'));
|
359 |
}).trigger('change');
|
360 |
|
|
|
|
|
|
|
|
|
|
|
361 |
var day_one_column = $('.ab-day-one-column'),
|
362 |
day_columns = $('.ab-day-columns');
|
363 |
|
364 |
+
if ($show_calendar_option.prop('checked')) {
|
365 |
+
$second_step_calendar_wrap.show();
|
366 |
+
day_columns.find('.col3,.col4,.col5,.col6,.col7').hide();
|
367 |
+
day_columns.find('.col2 button:gt(0)').attr('style', 'display: none !important');
|
368 |
+
day_one_column.find('.col2,.col3,.col4,.col5,.col6,.col7').hide();
|
369 |
+
}
|
370 |
+
|
371 |
// Change show calendar
|
372 |
$show_calendar_option.change(function() {
|
373 |
if (this.checked) {
|
374 |
$second_step_calendar_wrap.show();
|
375 |
+
day_columns.find('.col3,.col4,.col5,.col6,.col7').hide();
|
376 |
+
day_columns.find('.col2 button:gt(0)').attr('style', 'display: none !important');
|
377 |
+
day_one_column.find('.col2,.col3,.col4,.col5,.col6,.col7').hide();
|
378 |
} else {
|
379 |
$second_step_calendar_wrap.hide();
|
380 |
+
day_columns.find('.col2 button:gt(0)').attr('style', 'display: block !important');
|
381 |
+
day_columns.find('.col3,.col4,.col5,.col6,.col7').css('display','inline-block');
|
382 |
+
day_one_column.find('.col2,.col3,.col4,.col5,.col6,.col7').css('display','inline-block');
|
383 |
}
|
|
|
384 |
});
|
385 |
|
386 |
// Change blocked time slots.
|
387 |
$blocked_timeslots_option.change(function(){
|
388 |
if (this.checked) {
|
389 |
+
$('.ab-hour.no-booked').removeClass('no-booked').addClass('booked');
|
390 |
} else {
|
391 |
+
$('.ab-hour.booked').removeClass('booked').addClass('no-booked');
|
392 |
}
|
|
|
393 |
});
|
394 |
|
395 |
// Change day one column.
|
401 |
day_one_column.hide();
|
402 |
day_columns.show();
|
403 |
}
|
|
|
404 |
});
|
405 |
|
406 |
// Clickable week-days.
|
423 |
$.extend(multiple.prototype, {
|
424 |
render: function() {
|
425 |
this.$input = this.$tpl.find('input');
|
426 |
+
this.$more = jQuery('div.ad--extend', this.tpl);
|
427 |
},
|
428 |
|
429 |
value2html: function(value, element) {
|
433 |
}
|
434 |
$(element).text(value.label);
|
435 |
$('#' + value.id_option).text(value.option);
|
436 |
+
$('#' + value.id_option2).text(value.option2);
|
437 |
},
|
438 |
|
439 |
activate: function () {
|
444 |
if(!value) {
|
445 |
return;
|
446 |
}
|
447 |
+
if (value.extended) {
|
448 |
+
this.$more.show();
|
449 |
+
}
|
450 |
this.$input.filter('[name="label"]').val(value.label);
|
451 |
this.$input.filter('[name="option"]').val(value.option);
|
452 |
this.$input.filter('[name="id_option"]').val(value.id_option);
|
453 |
+
this.$input.filter('[name="option2"]').val(value.option2);
|
454 |
+
this.$input.filter('[name="id_option2"]').val(value.id_option2);
|
455 |
},
|
456 |
|
457 |
input2value: function() {
|
458 |
return {
|
459 |
+
label: this.$input.filter('[name="label"]').val(),
|
460 |
+
option: this.$input.filter('[name="option"]').val(),
|
461 |
+
id_option: this.$input.filter('[name="id_option"]').val(),
|
462 |
+
option2: this.$input.filter('[name="option2"]').val(),
|
463 |
+
id_option2: this.$input.filter('[name="id_option2"]').val(),
|
464 |
+
extended: this.$more.is(':visible')
|
465 |
};
|
466 |
}
|
467 |
});
|
468 |
|
469 |
multiple.defaults = $.extend({}, $.fn.editabletypes.abstractinput.defaults, {
|
470 |
+
tpl: '<div class="editable-multiple"><input type="text" name="label" class="form-control input-sm" /></div>'+
|
471 |
+
'<div style="margin-top:5px;" class="editable-multiple"><input type="text" name="option" class="form-control input-sm" /><input type="hidden" name="id_option" /></div><div class="ad--extend" style="display:none">'+
|
472 |
+
'<div style="margin-top:5px;" class="editable-multiple"><input type="text" name="option2" class="form-control input-sm" /><input type="hidden" name="id_option2" /></div></div></div>',
|
473 |
|
474 |
inputclass: ''
|
475 |
});
|
476 |
|
477 |
$.fn.editabletypes.multiple = multiple;
|
478 |
+
$text_label_location.editable({
|
479 |
+
value: {
|
480 |
+
label: $text_label_location.text(),
|
481 |
+
option: $text_option_location.text(),
|
482 |
+
id_option: $text_label_location.data('option-id'),
|
483 |
+
extended: true,
|
484 |
+
option2: $text_label_location.data('default-error'),
|
485 |
+
id_option2: $text_label_location.data('error-id')
|
486 |
+
}
|
487 |
+
});
|
488 |
$text_label_category.editable({
|
489 |
value: {
|
490 |
label: $text_label_category.text(),
|
496 |
value: {
|
497 |
label: $text_label_service.text(),
|
498 |
option: $text_option_service.text(),
|
499 |
+
id_option: $text_label_service.data('option-id'),
|
500 |
+
extended: true,
|
501 |
+
option2: $text_label_service.data('default-error'),
|
502 |
+
id_option2: $text_label_service.data('error-id')
|
503 |
}
|
504 |
});
|
505 |
$text_label_employee.editable({
|
506 |
value: {
|
507 |
label: $text_label_employee.text(),
|
508 |
option: $text_option_employee.text(),
|
509 |
+
id_option: $text_label_employee.data('option-id'),
|
510 |
+
extended: true,
|
511 |
+
option2: $text_label_employee.data('default-error'),
|
512 |
+
id_option2: $text_label_employee.data('error-id')
|
513 |
+
}
|
514 |
+
});
|
515 |
+
|
516 |
+
$text_label_name.editable({
|
517 |
+
value: {
|
518 |
+
label: $text_label_name.text(),
|
519 |
+
option: $text_label_name.data('default-error'),
|
520 |
+
id_option: $text_label_name.data('option-id')
|
521 |
+
}
|
522 |
+
});
|
523 |
+
|
524 |
+
$text_label_phone.editable({
|
525 |
+
value: {
|
526 |
+
label: $text_label_phone.text(),
|
527 |
+
option: $text_label_phone.data('default-error'),
|
528 |
+
id_option: $text_label_phone.data('option-id')
|
529 |
}
|
530 |
});
|
531 |
|
532 |
+
$text_label_email.editable({
|
533 |
+
value: {
|
534 |
+
label: $text_label_email.text(),
|
535 |
+
option: $text_label_email.data('default-error'),
|
536 |
+
id_option: $text_label_email.data('option-id')
|
537 |
+
}
|
538 |
+
});
|
539 |
+
|
540 |
+
$text_info_service.add('#ab-text-info-time').add('#ab-text-info-details').add('#ab-text-info-payment').add('#ab-text-info-complete').add('#ab-text-info-coupon').editable({placement: 'right'});
|
541 |
$ab_editable.editable();
|
542 |
|
543 |
+
$.fn.editableform.template = '<form class="form-inline editableform"> <div class="control-group"> <div> <div class="editable-input"></div><div class="editable-buttons"></div></div><div class="editable-notes"></div><div class="editable-error-block"></div></div> </form>';
|
544 |
+
$.fn.editableform.buttons = '<div class="btn-group btn-group-sm"><button type="submit" class="btn btn-success editable-submit"><span class="glyphicon glyphicon-ok"></span></button><button type="button" class="btn btn-default editable-cancel"><span class="glyphicon glyphicon-remove"></span></button></div>';
|
545 |
|
546 |
$ab_editable.on('shown', function(e, editable) {
|
547 |
+
$('.popover').find('.arrow').removeClass().addClass('popover-arrow');
|
548 |
$('.editable-notes').html($(e.target).data('notes'));
|
549 |
});
|
550 |
+
$('[data-type="multiple"]').on('shown', function(e, editable) {
|
551 |
+
$('.popover').find('.arrow').removeClass().addClass('popover-arrow');
|
|
|
|
|
552 |
});
|
553 |
|
554 |
+
$("[data-mirror^='text_']").on('save', function (e, params) {
|
555 |
+
$("." + $(e.target).data('mirror')).editable('setValue', params.newValue);
|
556 |
+
switch ($(e.target).data('mirror')){
|
557 |
+
case 'text_services':
|
558 |
+
$(".ab-service-list").html(params.newValue.label);
|
559 |
+
break;
|
560 |
+
case 'text_locations':
|
561 |
+
$(".ab-location-list").html(params.newValue.label);
|
562 |
+
break;
|
563 |
+
case 'text_employee':
|
564 |
+
$(".ab-employee-list").html(params.newValue.label);
|
565 |
+
break;
|
566 |
+
}
|
567 |
+
});
|
568 |
|
569 |
+
$('input[type=radio]').change(function () {
|
570 |
+
if ($('.ab-card-payment').is(':checked')) {
|
571 |
+
$('form.ab-card-form').show();
|
572 |
+
} else {
|
573 |
+
$('form.ab-card-form').hide();
|
|
|
574 |
}
|
575 |
});
|
576 |
+
|
577 |
+
$('#bookly-js-hint-alert').on('closed.bs.alert', function () {
|
578 |
+
$.ajax({
|
579 |
+
url: ajaxurl,
|
580 |
+
data: { action: 'ab_dismiss_appearance_notice' }
|
581 |
+
});
|
582 |
+
})
|
583 |
}); // jQuery
|
backend/modules/appearance/templates/_1_service.php
CHANGED
@@ -2,21 +2,30 @@
|
|
2 |
/** @var WP_Locale $wp_locale */
|
3 |
global $wp_locale;
|
4 |
?>
|
5 |
-
<div class="
|
|
|
6 |
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
<span data-inputclass="input-xxlarge" data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_info_first_step' ) ) ?>" class="ab-bold ab_editable" id="ab-text-info-first" data-rows="7" data-type="textarea"><?php echo esc_html( get_option( 'ab_appearance_text_info_first_step' ) ) ?></span>
|
13 |
</div>
|
14 |
-
<div class=ab-
|
15 |
-
<div class="
|
16 |
-
|
17 |
-
<
|
18 |
-
|
19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
20 |
<option value="" class="editable" id="ab-text-option-category" data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_option_category' ) ) ?>"><?php echo esc_html( get_option( 'ab_appearance_text_option_category' ) ) ?></option>
|
21 |
<option value="1">Cosmetic Dentistry</option>
|
22 |
<option value="2">Invisalign</option>
|
@@ -25,113 +34,162 @@
|
|
25 |
</select>
|
26 |
</div>
|
27 |
</div>
|
28 |
-
<div class="ab-formGroup
|
29 |
-
<label
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
41 |
</select>
|
42 |
</div>
|
43 |
</div>
|
44 |
-
<div class="ab-formGroup
|
45 |
-
<label
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
<option value="
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
59 |
</select>
|
60 |
</div>
|
61 |
</div>
|
62 |
-
<div class="ab-formGroup
|
63 |
-
<label data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_label_number_of_persons' ) ) ?>" class="
|
64 |
-
<div
|
65 |
-
<select class="ab-
|
66 |
-
<option
|
67 |
-
<option
|
68 |
-
<option
|
69 |
</select>
|
70 |
</div>
|
71 |
</div>
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
</button>
|
76 |
-
</div>
|
77 |
-
<div class="ab-mobile-step_2">
|
78 |
-
<div class="ab-row-fluid">
|
79 |
-
<div class="ab-available-date ab-formGroup ab-lastGroup ab-left">
|
80 |
-
<label data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_label_select_date' ) ) ?>" class="ab_editable ab-nowrap" id="ab-text-label-select_date" data-type="text"><?php echo esc_html( get_option( 'ab_appearance_text_label_select_date' ) ) ?></label>
|
81 |
-
<div class="ab-input-wrap ab-formField">
|
82 |
-
<span class="ab-date-wrap">
|
83 |
-
<input style="background: white" class="ab-date-from ab-formElement picker__input--active" type="text" data-value="<?php echo date( 'Y-m-d' ) ?>" />
|
84 |
-
</span>
|
85 |
-
</div>
|
86 |
</div>
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
</label>
|
95 |
-
</li>
|
96 |
-
<?php endforeach ?>
|
97 |
-
</ul>
|
98 |
</div>
|
99 |
-
|
100 |
-
|
101 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
102 |
<div>
|
103 |
-
|
104 |
-
<?php for ( $i = 28800; $i <= 64800; $i += 3600 ): ?>
|
105 |
-
<option><?php echo AB_DateTimeUtils::formatTime( $i ) ?></option>
|
106 |
-
<?php endfor ?>
|
107 |
-
</select>
|
108 |
</div>
|
109 |
</div>
|
110 |
-
|
111 |
-
|
|
|
112 |
<div>
|
113 |
-
<
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
</select>
|
118 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
119 |
</div>
|
120 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
121 |
</div>
|
122 |
-
<div class="ab-
|
123 |
-
<
|
124 |
-
<span><?php _e( 'Back', 'bookly' ) ?></span>
|
125 |
-
</button>
|
126 |
-
<button class="ab-right ab-next-step ab-btn ladda-button">
|
127 |
-
<span><?php _e( 'Next', 'bookly' ) ?></span>
|
128 |
-
</button>
|
129 |
</div>
|
|
|
130 |
</div>
|
131 |
</div>
|
132 |
</div>
|
133 |
</div>
|
134 |
-
<style
|
|
|
|
|
|
|
|
|
|
|
135 |
.picker__nav--next:before { border-left: 6px solid <?php echo get_option( 'ab_appearance_color' ) ?>!important; }
|
136 |
.picker__nav--prev:before { border-right: 6px solid <?php echo get_option( 'ab_appearance_color' ) ?>!important; }
|
137 |
</style>
|
2 |
/** @var WP_Locale $wp_locale */
|
3 |
global $wp_locale;
|
4 |
?>
|
5 |
+
<div class="bookly-form">
|
6 |
+
<?php include '_progress_tracker.php' ?>
|
7 |
|
8 |
+
<div class="ab-service-step">
|
9 |
+
<div class="ab-row">
|
10 |
+
<span data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_info_service_step' ) ) ?>"
|
11 |
+
class="ab_editable ab-bold ab-desc" id="ab-text-info-service"
|
12 |
+
data-rows="7" data-type="textarea"><?php echo esc_html( get_option( 'ab_appearance_text_info_service_step' ) ) ?></span>
|
|
|
13 |
</div>
|
14 |
+
<div class="ab-mobile-step_1 ab-row">
|
15 |
+
<div class="bookly-js-chain-item bookly-table ab-row">
|
16 |
+
<?php if ( \BooklyLite\Lib\Config::locationsEnabled() ) : ?>
|
17 |
+
<div class="ab-formGroup">
|
18 |
+
<?php do_action( 'bookly_locations_render_appearance' ) ?>
|
19 |
+
</div>
|
20 |
+
<?php endif ?>
|
21 |
+
<div class="ab-formGroup">
|
22 |
+
<label data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_label_category' ) ) ?>"
|
23 |
+
class="text_category_label"
|
24 |
+
id="ab-text-label-category" data-type="multiple"
|
25 |
+
data-option-id="ab-text-option-category"
|
26 |
+
><?php echo esc_html( get_option( 'ab_appearance_text_label_category' ) ) ?></label>
|
27 |
+
<div>
|
28 |
+
<select class="ab-select-mobile ab-select-category">
|
29 |
<option value="" class="editable" id="ab-text-option-category" data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_option_category' ) ) ?>"><?php echo esc_html( get_option( 'ab_appearance_text_option_category' ) ) ?></option>
|
30 |
<option value="1">Cosmetic Dentistry</option>
|
31 |
<option value="2">Invisalign</option>
|
34 |
</select>
|
35 |
</div>
|
36 |
</div>
|
37 |
+
<div class="ab-formGroup">
|
38 |
+
<label>
|
39 |
+
<span
|
40 |
+
data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_label_service' ) ) ?>"
|
41 |
+
data-default-error="<?php echo esc_attr( get_option( 'ab_appearance_text_required_service' ) ) ?>"
|
42 |
+
data-error-id="ab_appearance_text_required_service"
|
43 |
+
id="ab-text-label-service"
|
44 |
+
data-mirror="text_services"
|
45 |
+
data-type="multiple"
|
46 |
+
data-option-id="ab-text-option-service"
|
47 |
+
><?php echo esc_html( get_option( 'ab_appearance_text_label_service' ) ) ?></span>
|
48 |
+
</label>
|
49 |
+
<div>
|
50 |
+
<select class="ab-select-mobile ab-select-service">
|
51 |
+
<option class="editable" id="ab-text-option-service" data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_option_service' ) ) ?>"><?php echo esc_html( get_option( 'ab_appearance_text_option_service' ) ) ?></option>
|
52 |
+
<option>Crown and Bridge</option>
|
53 |
+
<option>Teeth Whitening</option>
|
54 |
+
<option>Veneers</option>
|
55 |
+
<option>Invisalign (invisable braces)</option>
|
56 |
+
<option>Orthodontics (braces)</option>
|
57 |
+
<option>Wisdom tooth Removal</option>
|
58 |
+
<option>Root Canal Treatment</option>
|
59 |
+
<option>Dentures</option>
|
60 |
</select>
|
61 |
</div>
|
62 |
</div>
|
63 |
+
<div class="ab-formGroup">
|
64 |
+
<label class="text_employee_label">
|
65 |
+
<span
|
66 |
+
data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_label_employee' ) ) ?>"
|
67 |
+
data-default-error="<?php echo esc_attr( get_option( 'ab_appearance_text_required_employee' ) ) ?>"
|
68 |
+
data-error-id="ab_appearance_text_required_employee"
|
69 |
+
id="ab-text-label-employee"
|
70 |
+
data-mirror="text_employee"
|
71 |
+
data-type="multiple"
|
72 |
+
data-option-id="ab-text-option-employee"
|
73 |
+
><?php echo esc_html( get_option( 'ab_appearance_text_label_employee' ) ) ?></span>
|
74 |
+
</label>
|
75 |
+
<div>
|
76 |
+
<select class="ab-select-mobile ab-select-employee">
|
77 |
+
<option value="0" class="editable" id="ab-text-option-employee" data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_option_employee' ) ) ?>"><?php echo esc_html( get_option( 'ab_appearance_text_option_employee' ) ) ?></option>
|
78 |
+
<option value="1" class="employee-name-price">Nick Knight (<?php echo \BooklyLite\Lib\Utils\Common::formatPrice( 350 ) ?>)</option>
|
79 |
+
<option value="-1" class="employee-name">Nick Knight</option>
|
80 |
+
<option value="2" class="employee-name-price">Jane Howard (<?php echo \BooklyLite\Lib\Utils\Common::formatPrice( 375 ) ?>)</option>
|
81 |
+
<option value="-2" class="employee-name">Jane Howard</option>
|
82 |
+
<option value="3" class="employee-name-price">Ashley Stamp (<?php echo \BooklyLite\Lib\Utils\Common::formatPrice( 300 ) ?>)</option>
|
83 |
+
<option value="-3" class="employee-name">Ashley Stamp</option>
|
84 |
+
<option value="4" class="employee-name-price">Bradley Tannen (<?php echo \BooklyLite\Lib\Utils\Common::formatPrice( 400 ) ?>)</option>
|
85 |
+
<option value="-4" class="employee-name">Bradley Tannen</option>
|
86 |
+
<option value="5" class="employee-name-price">Wayne Turner (<?php echo \BooklyLite\Lib\Utils\Common::formatPrice( 350 ) ?>)</option>
|
87 |
+
<option value="-5" class="employee-name">Wayne Turner</option>
|
88 |
+
<option value="6" class="employee-name-price">Emily Taylor (<?php echo \BooklyLite\Lib\Utils\Common::formatPrice( 350 ) ?>)</option>
|
89 |
+
<option value="-6" class="employee-name">Emily Taylor</option>
|
90 |
+
<option value="7" class="employee-name-price">Hugh Canberg (<?php echo \BooklyLite\Lib\Utils\Common::formatPrice( 380 ) ?>)</option>
|
91 |
+
<option value="-7" class="employee-name">Hugh Canberg</option>
|
92 |
+
<option value="8" class="employee-name-price">Jim Gonzalez (<?php echo \BooklyLite\Lib\Utils\Common::formatPrice( 390 ) ?>)</option>
|
93 |
+
<option value="-8" class="employee-name">Jim Gonzalez</option>
|
94 |
+
<option value="9" class="employee-name-price">Nancy Stinson (<?php echo \BooklyLite\Lib\Utils\Common::formatPrice( 360 ) ?>)</option>
|
95 |
+
<option value="-9" class="employee-name">Nancy Stinson</option>
|
96 |
+
<option value="10" class="employee-name-price">Marry Murphy (<?php echo \BooklyLite\Lib\Utils\Common::formatPrice( 350 ) ?>)</option>
|
97 |
+
<option value="-10" class="employee-name">Marry Murphy</option>
|
98 |
</select>
|
99 |
</div>
|
100 |
</div>
|
101 |
+
<div class="ab-formGroup">
|
102 |
+
<label data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_label_number_of_persons' ) ) ?>" class="ab_editable" data-type="text" id="ab-text-label-number-of-persons"><?php echo esc_html( get_option( 'ab_appearance_text_label_number_of_persons' ) ) ?></label>
|
103 |
+
<div>
|
104 |
+
<select class="ab-select-mobile ab-select-number-of-persons">
|
105 |
+
<option>1</option>
|
106 |
+
<option>2</option>
|
107 |
+
<option>3</option>
|
108 |
</select>
|
109 |
</div>
|
110 |
</div>
|
111 |
+
<?php if ( \BooklyLite\Lib\Config::multiplyAppointmentsEnabled() ) : ?>
|
112 |
+
<div class="ab-formGroup">
|
113 |
+
<?php do_action( 'bookly_multiply_render_appearance' ) ?>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
114 |
</div>
|
115 |
+
<?php endif ?>
|
116 |
+
<?php if ( \BooklyLite\Lib\Config::chainAppointmentsEnabled() ) : ?>
|
117 |
+
<div class="ab-formGroup">
|
118 |
+
<label></label>
|
119 |
+
<div class="bookly-js-actions">
|
120 |
+
<button type="button" class="bookly-round-button" data-action="plus"><img src="<?php echo plugins_url( 'appointment-booking/frontend/resources/images/plus.png' ) ?>" /></button>
|
121 |
+
</div>
|
|
|
|
|
|
|
|
|
122 |
</div>
|
123 |
+
<?php endif ?>
|
124 |
+
</div>
|
125 |
+
|
126 |
+
<div class="ab-right ab-mobile-next-step ab-btn ab-none" onclick="return false">
|
127 |
+
<span class="text_next ab_editable" id="ab-text-button-next" data-mirror="text_next" data-type="text" data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_button_next' ) ) ?>"><?php echo esc_html( get_option( 'ab_appearance_text_button_next' ) ) ?></span>
|
128 |
+
</div>
|
129 |
+
</div>
|
130 |
+
<div class="ab-mobile-step_2">
|
131 |
+
<div class="ab-row">
|
132 |
+
<div class="ab-left">
|
133 |
+
<div class="ab-available-date ab-left">
|
134 |
+
<div class="ab-formGroup">
|
135 |
+
<label data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_label_select_date' ) ) ?>" class="ab_editable" id="ab-text-label-select_date" data-type="text"><?php echo esc_html( get_option( 'ab_appearance_text_label_select_date' ) ) ?></label>
|
136 |
<div>
|
137 |
+
<input class="ab-date-from" style="background-color: #fff;" type="text" data-value="<?php echo date( 'Y-m-d' ) ?>" />
|
|
|
|
|
|
|
|
|
138 |
</div>
|
139 |
</div>
|
140 |
+
</div>
|
141 |
+
<div class="ab-week-days bookly-table ab-left">
|
142 |
+
<?php foreach ( $wp_locale->weekday_abbrev as $weekday_abbrev ) : ?>
|
143 |
<div>
|
144 |
+
<div class="bookly-font-bold"><?php echo $weekday_abbrev ?></div>
|
145 |
+
<label class="active">
|
146 |
+
<input class="ab-week-day" value="1" checked="checked" type="checkbox">
|
147 |
+
</label>
|
|
|
148 |
</div>
|
149 |
+
<?php endforeach ?>
|
150 |
+
</div>
|
151 |
+
</div>
|
152 |
+
<div class="ab-time-range ab-left">
|
153 |
+
<div class="ab-formGroup ab-time-from ab-left">
|
154 |
+
<label data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_label_start_from' ) ) ?>" class="ab_editable" id="ab-text-label-start_from" data-type="text"><?php echo esc_html( get_option( 'ab_appearance_text_label_start_from' ) ) ?></label>
|
155 |
+
<div>
|
156 |
+
<select class="ab-select-time-from">
|
157 |
+
<?php for ( $i = 28800; $i <= 64800; $i += 3600 ) : ?>
|
158 |
+
<option><?php echo \BooklyLite\Lib\Utils\DateTime::formatTime( $i ) ?></option>
|
159 |
+
<?php endfor ?>
|
160 |
+
</select>
|
161 |
</div>
|
162 |
</div>
|
163 |
+
<div class="ab-formGroup ab-time-to ab-left">
|
164 |
+
<label data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_label_finish_by' ) ) ?>" class="ab_editable" id="ab-text-label-finish_by" data-type="text"><?php echo esc_html( get_option( 'ab_appearance_text_label_finish_by' ) ) ?></label>
|
165 |
+
<div>
|
166 |
+
<select class="ab-select-time-to">
|
167 |
+
<?php for ( $i = 28800; $i <= 64800; $i += 3600 ) : ?>
|
168 |
+
<option<?php selected( $i == 64800 ) ?>><?php echo \BooklyLite\Lib\Utils\DateTime::formatTime( $i ) ?></option>
|
169 |
+
<?php endfor ?>
|
170 |
+
</select>
|
171 |
+
</div>
|
172 |
+
</div>
|
173 |
+
</div>
|
174 |
+
</div>
|
175 |
+
<div class="ab-row ab-nav-steps">
|
176 |
+
<div class="ab-right ab-mobile-prev-step ab-btn ab-none">
|
177 |
+
<span class="text_back ab_editable" id="ab-text-button-back" data-mirror="text_back" data-type="text" data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_button_back' ) ) ?>"><?php echo esc_html( get_option( 'ab_appearance_text_button_back' ) ) ?></span>
|
178 |
</div>
|
179 |
+
<div class="ab-right ab-next-step ab-btn">
|
180 |
+
<span class="text_next ab_editable" id="ab-text-button-next" data-mirror="text_next" data-type="text" data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_button_next' ) ) ?>"><?php echo esc_html( get_option( 'ab_appearance_text_button_next' ) ) ?></span>
|
|
|
|
|
|
|
|
|
|
|
181 |
</div>
|
182 |
+
<button class="ab-left ab-goto-cart bookly-round-button ladda-button"><span><img src="<?php echo plugins_url( 'appointment-booking/frontend/resources/images/cart.png' ) ?>" /></span></button>
|
183 |
</div>
|
184 |
</div>
|
185 |
</div>
|
186 |
</div>
|
187 |
+
<div style="display: none">
|
188 |
+
<?php foreach ( array( 'ab_appearance_text_required_service', 'ab_appearance_text_required_name', 'ab_appearance_text_required_phone', 'ab_appearance_text_required_email', 'ab_appearance_text_required_employee', 'ab_appearance_text_required_location' ) as $validator ) : ?>
|
189 |
+
<div id="<?php echo $validator ?>"><?php echo get_option( $validator ) ?></div>
|
190 |
+
<?php endforeach ?>
|
191 |
+
</div>
|
192 |
+
<style id="ab--style-arrow">
|
193 |
.picker__nav--next:before { border-left: 6px solid <?php echo get_option( 'ab_appearance_color' ) ?>!important; }
|
194 |
.picker__nav--prev:before { border-right: 6px solid <?php echo get_option( 'ab_appearance_color' ) ?>!important; }
|
195 |
</style>
|
backend/modules/appearance/templates/_2_time.php
DELETED
@@ -1,192 +0,0 @@
|
|
1 |
-
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
|
2 |
-
<div id="ab-booking-form" class="ab-booking-form">
|
3 |
-
<!-- Progress Tracker-->
|
4 |
-
<?php $step = 2; include '_progress_tracker.php'; ?>
|
5 |
-
|
6 |
-
<div class="ab-row-fluid">
|
7 |
-
<span data-inputclass="input-xxlarge" data-notes="<?php echo esc_attr( $this->render( '_codes', array( 'step' => 2 ), false ) ) ?>" data-placement="bottom" data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_info_second_step' ) ) ?>" class="ab-text-info-second-preview ab-row-fluid ab_editable" id="ab-text-info-second" data-type="textarea"><?php echo esc_html( get_option( 'ab_appearance_text_info_second_step' ) ) ?></span>
|
8 |
-
</div>
|
9 |
-
<!-- timeslots -->
|
10 |
-
<div class="ab-columnizer-wrap" style="height: 400px;">
|
11 |
-
<div class="ab-columnizer">
|
12 |
-
<div class="ab-time-screen ab-day-columns" style="display: <?php echo get_option( 'ab_appearance_show_day_one_column' ) == 1 ? ' none' : 'block' ?>">
|
13 |
-
<div style="margin-right: 40px;" class="ab-input-wrap ab-slot-calendar">
|
14 |
-
<span class="ab-date-wrap">
|
15 |
-
<input style="display: none" class="ab-selected-date ab-formElement" type="text" data-value="<?php echo date( 'Y-m-d' ) ?>" />
|
16 |
-
</span>
|
17 |
-
</div>
|
18 |
-
<div class="ab-column col1">
|
19 |
-
<button class="ab-available-day ab-first-child"><?php echo date_i18n( 'D, M d' ) ?></button>
|
20 |
-
<?php for ( $i = 50400; $i <= 57600; $i += 900 ): ?>
|
21 |
-
<button class="ab-available-hour ladda-button<?php if ( mt_rand( 0, 1 ) ) echo get_option( 'ab_appearance_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked' ?>">
|
22 |
-
<span class="ladda-label">
|
23 |
-
<i class="ab-hour-icon"><span></span></i>
|
24 |
-
<?php echo AB_DateTimeUtils::formatTime( $i ) ?>
|
25 |
-
</span>
|
26 |
-
</button>
|
27 |
-
<?php endfor ?>
|
28 |
-
</div>
|
29 |
-
<div class="ab-column col2">
|
30 |
-
<?php for ( $i = 58500; $i <= 63900; $i += 900 ): ?>
|
31 |
-
<button class="ab-available-hour ladda-button<?php if ( mt_rand( 0, 1 ) ) echo get_option( 'ab_appearance_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked' ?>">
|
32 |
-
<span class="ladda-label">
|
33 |
-
<i class="ab-hour-icon"><span></span></i><?php echo AB_DateTimeUtils::formatTime( $i ) ?>
|
34 |
-
</span>
|
35 |
-
</button>
|
36 |
-
<?php endfor ?>
|
37 |
-
<button class="ab-available-day ab-first-child"><?php echo date_i18n( 'D, M d', strtotime( '+1 day' ) ) ?></button>
|
38 |
-
<button class="ab-available-hour ladda-button ab-last-child">
|
39 |
-
<span class="ladda-label">
|
40 |
-
<i class="ab-hour-icon"><span></span></i><?php echo AB_DateTimeUtils::formatTime( 28800 ) ?>
|
41 |
-
</span>
|
42 |
-
</button>
|
43 |
-
<button class="ab-available-hour ladda-button ab-last-child">
|
44 |
-
<span class="ladda-label">
|
45 |
-
<i class="ab-hour-icon"><span></span></i><?php echo AB_DateTimeUtils::formatTime( 29700 ) ?>
|
46 |
-
</span>
|
47 |
-
</button>
|
48 |
-
</div>
|
49 |
-
<div class="ab-column col3">
|
50 |
-
<?php for ( $i = 30600; $i <= 38700; $i += 900 ): ?>
|
51 |
-
<button class="ab-available-hour ladda-button<?php if ( mt_rand( 0, 1 ) ) echo get_option( 'ab_appearance_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked' ?>">
|
52 |
-
<span class="ladda-label">
|
53 |
-
<i class="ab-hour-icon"><span></span></i><?php echo AB_DateTimeUtils::formatTime( $i ) ?>
|
54 |
-
</span>
|
55 |
-
</button>
|
56 |
-
<?php endfor ?>
|
57 |
-
</div>
|
58 |
-
<div class="ab-column col4">
|
59 |
-
<?php for ( $i = 39600; $i <= 47700; $i += 900 ): ?>
|
60 |
-
<button class="ab-available-hour ladda-button<?php if ( mt_rand( 0, 1 ) ) echo get_option( 'ab_appearance_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked' ?>">
|
61 |
-
<span class="ladda-label">
|
62 |
-
<i class="ab-hour-icon"><span></span></i><?php echo AB_DateTimeUtils::formatTime( $i ) ?>
|
63 |
-
</span>
|
64 |
-
</button>
|
65 |
-
<?php endfor ?>
|
66 |
-
</div>
|
67 |
-
<div class="ab-column col5" style="display:<?php echo get_option( 'ab_appearance_show_calendar' ) == 1 ? ' none' : ' inline-block' ?>">
|
68 |
-
<?php for ( $i = 48600; $i <= 56700; $i += 900 ): ?>
|
69 |
-
<button class="ab-available-hour ladda-button<?php if ( mt_rand( 0, 1 ) ) echo get_option( 'ab_appearance_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked' ?>">
|
70 |
-
<span class="ladda-label">
|
71 |
-
<i class="ab-hour-icon"><span></span></i><?php echo AB_DateTimeUtils::formatTime( $i ) ?>
|
72 |
-
</span>
|
73 |
-
</button>
|
74 |
-
<?php endfor ?>
|
75 |
-
</div>
|
76 |
-
<div class="ab-column col6" style="display: <?php echo get_option( 'ab_appearance_show_calendar' ) == 1 ? ' none' : 'inline-block' ?>">
|
77 |
-
<?php for ( $i = 57600; $i <= 63900; $i += 900 ): ?>
|
78 |
-
<button class="ab-available-hour ladda-button<?php if ( mt_rand( 0, 1 ) ) echo get_option( 'ab_appearance_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked' ?>">
|
79 |
-
<span class="ladda-label">
|
80 |
-
<i class="ab-hour-icon"><span></span></i><?php echo AB_DateTimeUtils::formatTime( $i ) ?>
|
81 |
-
</span>
|
82 |
-
</button>
|
83 |
-
<?php endfor ?>
|
84 |
-
<button class="ab-available-day ab-first-child"><?php echo date_i18n( 'D, M d', strtotime( '+2 days' ) ) ?></button>
|
85 |
-
<button class="ab-available-hour ladda-button ab-last-child">
|
86 |
-
<span class="ladda-label">
|
87 |
-
<i class="ab-hour-icon"><span></span></i><?php echo AB_DateTimeUtils::formatTime( 28800 ) ?>
|
88 |
-
</span>
|
89 |
-
</button>
|
90 |
-
</div>
|
91 |
-
<div class="ab-column col7" style="display:<?php echo get_option( 'ab_appearance_show_calendar' ) == 1 ? ' none' : ' inline-block' ?>">
|
92 |
-
<?php for ( $i = 29700; $i <= 37800; $i += 900 ): ?>
|
93 |
-
<button class="ab-available-hour ladda-button<?php if ( mt_rand( 0, 1 ) ) echo get_option( 'ab_appearance_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked' ?>">
|
94 |
-
<span class="ladda-label">
|
95 |
-
<i class="ab-hour-icon"><span></span></i><?php echo AB_DateTimeUtils::formatTime( $i ) ?>
|
96 |
-
</span>
|
97 |
-
</button>
|
98 |
-
<?php endfor ?>
|
99 |
-
</div>
|
100 |
-
</div>
|
101 |
-
|
102 |
-
<div class="ab-time-screen ab-day-one-column" style="display: <?php echo get_option( 'ab_appearance_show_day_one_column' ) == 1 ? ' block' : 'none' ?>">
|
103 |
-
<div style="margin-right: 40px;" class="ab-input-wrap ab-slot-calendar">
|
104 |
-
<span class="ab-date-wrap">
|
105 |
-
<input style="display: none" class="ab-selected-date ab-formElement" type="text" data-value="<?php echo date( 'Y-m-d' ) ?>" />
|
106 |
-
</span>
|
107 |
-
</div>
|
108 |
-
<div class="ab-column col1">
|
109 |
-
<button class="ab-available-day ab-first-child"><?php echo date_i18n( 'D, M d' ) ?></button>
|
110 |
-
<?php for ( $i = 28800; $i <= 36000; $i += 900 ): ?>
|
111 |
-
<button class="ab-available-hour ladda-button<?php if ( mt_rand( 0, 1 ) ) echo get_option( 'ab_appearance_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked' ?>">
|
112 |
-
<span class="ladda-label">
|
113 |
-
<i class="ab-hour-icon"><span></span></i><?php echo AB_DateTimeUtils::formatTime( $i ) ?>
|
114 |
-
</span>
|
115 |
-
</button>
|
116 |
-
<?php endfor ?>
|
117 |
-
</div>
|
118 |
-
<div class="ab-column col2">
|
119 |
-
<button class="ab-available-day ab-first-child"><?php echo date_i18n( 'D, M d', strtotime( '+1 day' ) ) ?></button>
|
120 |
-
<?php for ( $i = 28800; $i <= 36000; $i += 900 ): ?>
|
121 |
-
<button class="ab-available-hour ladda-button<?php if ( mt_rand( 0, 1 ) ) echo get_option( 'ab_appearance_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked' ?>">
|
122 |
-
<span class="ladda-label">
|
123 |
-
<i class="ab-hour-icon"><span></span></i><?php echo AB_DateTimeUtils::formatTime( $i ) ?>
|
124 |
-
</span>
|
125 |
-
</button>
|
126 |
-
<?php endfor ?>
|
127 |
-
</div>
|
128 |
-
<div class="ab-column col3">
|
129 |
-
<button class="ab-available-day ab-first-child"><?php echo date_i18n( 'D, M d', strtotime( '+2 days' ) ) ?></button>
|
130 |
-
<?php for ( $i = 28800; $i <= 36000; $i += 900 ): ?>
|
131 |
-
<button class="ab-available-hour ladda-button<?php if ( mt_rand( 0, 1 ) ) echo get_option( 'ab_appearance_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked' ?>">
|
132 |
-
<span class="ladda-label">
|
133 |
-
<i class="ab-hour-icon"><span></span></i><?php echo AB_DateTimeUtils::formatTime( $i ) ?>
|
134 |
-
</span>
|
135 |
-
</button>
|
136 |
-
<?php endfor ?>
|
137 |
-
</div>
|
138 |
-
<div class="ab-column col4">
|
139 |
-
<button class="ab-available-day ab-first-child"><?php echo date_i18n( 'D, M d', strtotime( '+3 days' ) ) ?></button>
|
140 |
-
<?php for ( $i = 28800; $i <= 36000; $i += 900 ): ?>
|
141 |
-
<button class="ab-available-hour ladda-button<?php if ( mt_rand( 0, 1 ) ) echo get_option( 'ab_appearance_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked' ?>">
|
142 |
-
<span class="ladda-label">
|
143 |
-
<i class="ab-hour-icon"><span></span></i><?php echo AB_DateTimeUtils::formatTime( $i ) ?>
|
144 |
-
</span>
|
145 |
-
</button>
|
146 |
-
<?php endfor ?>
|
147 |
-
</div>
|
148 |
-
<div class="ab-column col5" style="display: <?php echo get_option( 'ab_appearance_show_calendar' ) == 1 ? ' none' : 'inline-block' ?>">
|
149 |
-
<button class="ab-available-day ab-first-child"><?php echo date_i18n( 'D, M d', strtotime( '+4 days' ) ) ?></button>
|
150 |
-
<?php for ( $i = 28800; $i <= 36000; $i += 900 ): ?>
|
151 |
-
<button class="ab-available-hour ladda-button<?php if ( mt_rand( 0, 1 ) ) echo get_option( 'ab_appearance_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked' ?>">
|
152 |
-
<span class="ladda-label">
|
153 |
-
<i class="ab-hour-icon"><span></span></i><?php echo AB_DateTimeUtils::formatTime( $i ) ?>
|
154 |
-
</span>
|
155 |
-
</button>
|
156 |
-
<?php endfor ?>
|
157 |
-
</div>
|
158 |
-
<div class="ab-column col6" style="display: <?php echo get_option( 'ab_appearance_show_calendar' ) == 1 ? ' none' : 'inline-block' ?>">
|
159 |
-
<button class="ab-available-day ab-first-child"><?php echo date_i18n( 'D, M d', strtotime( '+5 days' ) ) ?></button>
|
160 |
-
<?php for ( $i = 28800; $i <= 36000; $i += 900 ): ?>
|
161 |
-
<button class="ab-available-hour ladda-button<?php if ( mt_rand( 0, 1 ) ) echo get_option( 'ab_appearance_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked' ?>">
|
162 |
-
<span class="ladda-label">
|
163 |
-
<i class="ab-hour-icon"><span></span></i><?php echo AB_DateTimeUtils::formatTime( $i ) ?>
|
164 |
-
</span>
|
165 |
-
</button>
|
166 |
-
<?php endfor ?>
|
167 |
-
</div>
|
168 |
-
<div class="ab-column col7" style="display: <?php echo get_option( 'ab_appearance_show_calendar' ) == 1 ? ' none' : 'inline-block' ?>">
|
169 |
-
<button class="ab-available-day ab-first-child"><?php echo date_i18n( 'D, M d', strtotime( '+6 days' ) ) ?></button>
|
170 |
-
<?php for ( $i = 28800; $i <= 36000; $i += 900 ): ?>
|
171 |
-
<button class="ab-available-hour ladda-button<?php if ( mt_rand( 0, 1 ) ) echo get_option( 'ab_appearance_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked' ?>">
|
172 |
-
<span class="ladda-label">
|
173 |
-
<i class="ab-hour-icon"><span></span></i><?php echo AB_DateTimeUtils::formatTime( $i ) ?>
|
174 |
-
</span>
|
175 |
-
</button>
|
176 |
-
<?php endfor ?>
|
177 |
-
</div>
|
178 |
-
</div>
|
179 |
-
</div>
|
180 |
-
</div>
|
181 |
-
<div class="ab-row-fluid ab-nav-steps last-row ab-clear">
|
182 |
-
<button class="ab-time-next ab-btn ab-right ladda-button">
|
183 |
-
<span class="ab_label">></span>
|
184 |
-
</button>
|
185 |
-
<button class="ab-time-prev ab-btn ab-right ladda-button">
|
186 |
-
<span class="ab_label"><</span>
|
187 |
-
</button>
|
188 |
-
<button class="ab-left ab-to-first-step ab-btn ladda-button">
|
189 |
-
<span><?php _e( 'Back', 'bookly' ) ?></span>
|
190 |
-
</button>
|
191 |
-
</div>
|
192 |
-
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/modules/appearance/templates/_3_details.php
DELETED
@@ -1,42 +0,0 @@
|
|
1 |
-
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
|
2 |
-
<div class="ab-booking-form">
|
3 |
-
<!-- Progress Tracker-->
|
4 |
-
<?php $step = 3; include '_progress_tracker.php'; ?>
|
5 |
-
|
6 |
-
<div class="ab-row-fluid">
|
7 |
-
<span data-inputclass="input-xxlarge" data-notes="<?php echo esc_attr( $this->render( '_codes', array( 'step' => 3, 'login' => false ), false ) ) ?>" data-placement="bottom" data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_info_third_step' ) ) ?>" class="ab-text-info-third-preview ab-row-fluid ab_editable" id="ab-text-info-third" data-type="textarea"><?php echo esc_html( get_option( 'ab_appearance_text_info_third_step' ) ) ?></span>
|
8 |
-
</div>
|
9 |
-
<div class="ab-row-fluid">
|
10 |
-
<span data-inputclass="input-xxlarge" data-title="<?php _e( 'Visible to non-logged in customers only', 'bookly' ) ?>" data-notes="<?php echo esc_attr( $this->render( '_codes', array( 'step' => 3, 'login' => true ), false ) ) ?>" data-placement="bottom" data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_info_third_step_guest' ) ) ?>" class="ab-text-info-third-preview ab-row-fluid ab_editable" id="ab-text-info-third-guest" data-type="textarea"><?php echo esc_html( get_option( 'ab_appearance_text_info_third_step_guest' ) ) ?></span>
|
11 |
-
</div>
|
12 |
-
<form class="ab-third-step">
|
13 |
-
<div class="ab-row-fluid ab-col-phone" style="height: 55px; overflow: visible;">
|
14 |
-
<div class="ab-formGroup ab-left">
|
15 |
-
<label data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_label_name' ) ) ?>" class="ab-formLabel text_name_label ab_editable" id="ab-text-label-name" data-type="text"><?php echo esc_html( get_option( 'ab_appearance_text_label_name' ) ) ?></label>
|
16 |
-
<div class="ab-formField">
|
17 |
-
<input class="ab-formElement" type="text" value="" maxlength="60" />
|
18 |
-
</div>
|
19 |
-
</div>
|
20 |
-
<div class="ab-formGroup ab-left">
|
21 |
-
<label data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_label_phone' ) ) ?>" class="ab-formLabel text_phone_label ab_editable" id="ab-text-label-phone" data-type="text"><?php echo esc_html( get_option( 'ab_appearance_text_label_phone' ) ) ?></label>
|
22 |
-
<div class="ab-formField">
|
23 |
-
<input class="ab-formElement ab-user-phone" maxlength="30" type="tel" value="" />
|
24 |
-
</div>
|
25 |
-
</div>
|
26 |
-
<div class="ab-formGroup ab-left">
|
27 |
-
<label data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_label_email' ) ) ?>" class="ab-formLabel text_email_label ab_editable" id="ab-text-label-email" data-type="text"><?php echo esc_html( get_option( 'ab_appearance_text_label_email' ) ) ?></label>
|
28 |
-
<div class="ab-formField" style="margin-right: 0">
|
29 |
-
<input class="ab-formElement" maxlength="40" type="text" value="" />
|
30 |
-
</div>
|
31 |
-
</div>
|
32 |
-
</div>
|
33 |
-
</form>
|
34 |
-
<div class="ab-row-fluid last-row ab-nav-steps ab-clear">
|
35 |
-
<button class="ab-left ab-to-second-step ab-btn ladda-button">
|
36 |
-
<span><?php _e( 'Back', 'bookly' ) ?></span>
|
37 |
-
</button>
|
38 |
-
<button class="ab-right ab-to-fourth-step ab-btn ladda-button">
|
39 |
-
<span><?php _e( 'Next', 'bookly' ) ?></span>
|
40 |
-
</button>
|
41 |
-
</div>
|
42 |
-
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/modules/appearance/templates/_3_time.php
ADDED
@@ -0,0 +1,165 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
|
2 |
+
<div class="bookly-form">
|
3 |
+
<?php include '_progress_tracker.php' ?>
|
4 |
+
|
5 |
+
<div class="ab-row">
|
6 |
+
<span data-inputclass="input-xxlarge" data-notes="<?php echo esc_attr( $this->render( '_codes', array( 'step' => 3 ), false ) ) ?>" data-placement="bottom" data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_info_time_step' ) ) ?>" class="ab_editable" id="ab-text-info-time" data-type="textarea"><?php echo esc_html( get_option( 'ab_appearance_text_info_time_step' ) ) ?></span>
|
7 |
+
</div>
|
8 |
+
<!-- timeslots -->
|
9 |
+
<div class="ab-time-step">
|
10 |
+
<div class="ab-columnizer-wrap">
|
11 |
+
<div class="ab-columnizer">
|
12 |
+
<div class="ab-time-screen ab-day-columns" style="display: <?php echo get_option( 'ab_appearance_show_day_one_column' ) == 1 ? ' none' : 'block' ?>">
|
13 |
+
<div class="ab-input-wrap ab-slot-calendar">
|
14 |
+
<span class="ab-date-wrap">
|
15 |
+
<input style="display: none" class="ab-selected-date ab-formElement" type="text" data-value="<?php echo date( 'Y-m-d' ) ?>" />
|
16 |
+
</span>
|
17 |
+
</div>
|
18 |
+
<div class="ab-column col1">
|
19 |
+
<button class="ab-day ab-first-child"><?php echo date_i18n( 'D, M d' ) ?></button>
|
20 |
+
<?php for ( $i = 28800; $i <= 57600; $i += 3600 ) : ?>
|
21 |
+
<button class="ab-hour ladda-button<?php if ( mt_rand( 0, 1 ) ) echo get_option( 'ab_appearance_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked' ?>">
|
22 |
+
<span class="ladda-label">
|
23 |
+
<i class="ab-hour-icon"><span></span></i>
|
24 |
+
<?php echo \BooklyLite\Lib\Utils\DateTime::formatTime( $i ) ?>
|
25 |
+
</span>
|
26 |
+
</button>
|
27 |
+
<?php endfor ?>
|
28 |
+
</div>
|
29 |
+
<div class="ab-column col2">
|
30 |
+
<button class="ab-hour ladda-button ab-last-child">
|
31 |
+
<span class="ladda-label">
|
32 |
+
<i class="ab-hour-icon"><span></span></i><?php echo \BooklyLite\Lib\Utils\DateTime::formatTime( 61200 ) ?>
|
33 |
+
</span>
|
34 |
+
</button>
|
35 |
+
<button class="ab-day ab-first-child" style="display: <?php echo get_option( 'ab_appearance_show_calendar' ) == 1 ? ' none' : 'block' ?>"><?php echo date_i18n( 'D, M d', strtotime( '+1 day' ) ) ?></button>
|
36 |
+
<?php for ( $i = 28800; $i <= 54000; $i += 3600 ) : ?>
|
37 |
+
<button class="ab-hour ladda-button<?php if ( mt_rand( 0, 1 ) ) echo get_option( 'ab_appearance_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked' ?>" style="display: <?php echo get_option( 'ab_appearance_show_calendar' ) == 1 ? ' none' : 'block' ?>">
|
38 |
+
<span class="ladda-label">
|
39 |
+
<i class="ab-hour-icon"><span></span></i><?php echo \BooklyLite\Lib\Utils\DateTime::formatTime( $i ) ?>
|
40 |
+
</span>
|
41 |
+
</button>
|
42 |
+
<?php endfor ?>
|
43 |
+
</div>
|
44 |
+
<div class="ab-column col3" style="display: <?php echo get_option( 'ab_appearance_show_calendar' ) == 1 ? ' none' : 'inline-block' ?>">
|
45 |
+
<?php for ( $i = 57600; $i <= 61200; $i += 3600 ) : ?>
|
46 |
+
<button class="ab-hour ladda-button<?php if ( mt_rand( 0, 1 ) ) echo get_option( 'ab_appearance_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked' ?>">
|
47 |
+
<span class="ladda-label">
|
48 |
+
<i class="ab-hour-icon"><span></span></i><?php echo \BooklyLite\Lib\Utils\DateTime::formatTime( $i ) ?>
|
49 |
+
</span>
|
50 |
+
</button>
|
51 |
+
<?php endfor ?>
|
52 |
+
<button class="ab-day ab-first-child"><?php echo date_i18n( 'D, M d', strtotime( '+2 day' ) ) ?></button>
|
53 |
+
<?php for ( $i = 28800; $i <= 50400; $i += 3600 ) : ?>
|
54 |
+
<button class="ab-hour ladda-button<?php if ( mt_rand( 0, 1 ) ) echo get_option( 'ab_appearance_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked' ?>">
|
55 |
+
<span class="ladda-label">
|
56 |
+
<i class="ab-hour-icon"><span></span></i><?php echo \BooklyLite\Lib\Utils\DateTime::formatTime( $i ) ?>
|
57 |
+
</span>
|
58 |
+
</button>
|
59 |
+
<?php endfor ?>
|
60 |
+
</div>
|
61 |
+
<div class="ab-column col4" style="display: <?php echo get_option( 'ab_appearance_show_calendar' ) == 1 ? ' none' : 'inline-block' ?>">
|
62 |
+
<?php for ( $i = 54000; $i <= 61200; $i += 3600 ) : ?>
|
63 |
+
<button class="ab-hour ladda-button<?php if ( mt_rand( 0, 1 ) ) echo get_option( 'ab_appearance_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked' ?>">
|
64 |
+
<span class="ladda-label">
|
65 |
+
<i class="ab-hour-icon"><span></span></i><?php echo \BooklyLite\Lib\Utils\DateTime::formatTime( $i ) ?>
|
66 |
+
</span>
|
67 |
+
</button>
|
68 |
+
<?php endfor ?>
|
69 |
+
<button class="ab-day ab-first-child"><?php echo date_i18n( 'D, M d', strtotime( '+3 day' ) ) ?></button>
|
70 |
+
<?php for ( $i = 28800; $i <= 46800; $i += 3600 ) : ?>
|
71 |
+
<button class="ab-hour ladda-button<?php if ( mt_rand( 0, 1 ) ) echo get_option( 'ab_appearance_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked' ?>">
|
72 |
+
<span class="ladda-label">
|
73 |
+
<i class="ab-hour-icon"><span></span></i><?php echo \BooklyLite\Lib\Utils\DateTime::formatTime( $i ) ?>
|
74 |
+
</span>
|
75 |
+
</button>
|
76 |
+
<?php endfor ?>
|
77 |
+
</div>
|
78 |
+
<div class="ab-column col5" style="display:<?php echo get_option( 'ab_appearance_show_calendar' ) == 1 ? ' none' : ' inline-block' ?>">
|
79 |
+
<?php for ( $i = 50400; $i <= 61200; $i += 3600 ) : ?>
|
80 |
+
<button class="ab-hour ladda-button<?php if ( mt_rand( 0, 1 ) ) echo get_option( 'ab_appearance_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked' ?>">
|
81 |
+
<span class="ladda-label">
|
82 |
+
<i class="ab-hour-icon"><span></span></i><?php echo \BooklyLite\Lib\Utils\DateTime::formatTime( $i ) ?>
|
83 |
+
</span>
|
84 |
+
</button>
|
85 |
+
<?php endfor ?>
|
86 |
+
<button class="ab-day ab-first-child"><?php echo date_i18n( 'D, M d', strtotime( '+4 day' ) ) ?></button>
|
87 |
+
<?php for ( $i = 28800; $i <= 43200; $i += 3600 ) : ?>
|
88 |
+
<button class="ab-hour ladda-button<?php if ( mt_rand( 0, 1 ) ) echo get_option( 'ab_appearance_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked' ?>">
|
89 |
+
<span class="ladda-label">
|
90 |
+
<i class="ab-hour-icon"><span></span></i><?php echo \BooklyLite\Lib\Utils\DateTime::formatTime( $i ) ?>
|
91 |
+
</span>
|
92 |
+
</button>
|
93 |
+
<?php endfor ?>
|
94 |
+
</div>
|
95 |
+
<div class="ab-column col6" style="display: <?php echo get_option( 'ab_appearance_show_calendar' ) == 1 ? ' none' : 'inline-block' ?>">
|
96 |
+
<?php for ( $i = 46800; $i <= 61200; $i += 3600 ) : ?>
|
97 |
+
<button class="ab-hour ladda-button<?php if ( mt_rand( 0, 1 ) ) echo get_option( 'ab_appearance_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked' ?>">
|
98 |
+
<span class="ladda-label">
|
99 |
+
<i class="ab-hour-icon"><span></span></i><?php echo \BooklyLite\Lib\Utils\DateTime::formatTime( $i ) ?>
|
100 |
+
</span>
|
101 |
+
</button>
|
102 |
+
<?php endfor ?>
|
103 |
+
<button class="ab-day ab-first-child"><?php echo date_i18n( 'D, M d', strtotime( '+5 day' ) ) ?></button>
|
104 |
+
<?php for ( $i = 28800; $i <= 39600; $i += 3600 ) : ?>
|
105 |
+
<button class="ab-hour ladda-button<?php if ( mt_rand( 0, 1 ) ) echo get_option( 'ab_appearance_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked' ?>">
|
106 |
+
<span class="ladda-label">
|
107 |
+
<i class="ab-hour-icon"><span></span></i><?php echo \BooklyLite\Lib\Utils\DateTime::formatTime( $i ) ?>
|
108 |
+
</span>
|
109 |
+
</button>
|
110 |
+
<?php endfor ?>
|
111 |
+
</div>
|
112 |
+
<div class="ab-column col7" style="display:<?php echo get_option( 'ab_appearance_show_calendar' ) == 1 ? ' none' : ' inline-block' ?>">
|
113 |
+
<?php for ( $i = 43200; $i <= 61200; $i += 3600 ) : ?>
|
114 |
+
<button class="ab-hour ladda-button<?php if ( mt_rand( 0, 1 ) ) echo get_option( 'ab_appearance_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked' ?>">
|
115 |
+
<span class="ladda-label">
|
116 |
+
<i class="ab-hour-icon"><span></span></i><?php echo \BooklyLite\Lib\Utils\DateTime::formatTime( $i ) ?>
|
117 |
+
</span>
|
118 |
+
</button>
|
119 |
+
<?php endfor ?>
|
120 |
+
<button class="ab-day ab-first-child"><?php echo date_i18n( 'D, M d', strtotime( '+6 day' ) ) ?></button>
|
121 |
+
<?php for ( $i = 28800; $i <= 36000; $i += 3600 ) : ?>
|
122 |
+
<button class="ab-hour ladda-button<?php if ( mt_rand( 0, 1 ) ) echo get_option( 'ab_appearance_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked' ?>">
|
123 |
+
<span class="ladda-label">
|
124 |
+
<i class="ab-hour-icon"><span></span></i><?php echo \BooklyLite\Lib\Utils\DateTime::formatTime( $i ) ?>
|
125 |
+
</span>
|
126 |
+
</button>
|
127 |
+
<?php endfor ?>
|
128 |
+
</div>
|
129 |
+
</div>
|
130 |
+
|
131 |
+
<div class="ab-time-screen ab-day-one-column" style="display: <?php echo get_option( 'ab_appearance_show_day_one_column' ) == 1 ? ' block' : 'none' ?>">
|
132 |
+
<div class="ab-input-wrap ab-slot-calendar">
|
133 |
+
<span class="ab-date-wrap">
|
134 |
+
<input style="display: none" class="ab-selected-date ab-formElement" type="text" data-value="<?php echo date( 'Y-m-d' ) ?>" />
|
135 |
+
</span>
|
136 |
+
</div>
|
137 |
+
<?php for ( $i = 1; $i <= 7; ++ $i ) : ?>
|
138 |
+
<div class="ab-column col<?php echo $i ?>">
|
139 |
+
<button class="ab-day ab-first-child"><?php echo date_i18n( 'D, M d', strtotime( '+' . ( $i - 1 ) . ' day' ) ) ?></button>
|
140 |
+
<?php for ( $j = 28800; $j <= 61200; $j += 3600 ) : ?>
|
141 |
+
<button class="ab-hour ladda-button<?php if ( mt_rand( 0, 1 ) ) echo get_option( 'ab_appearance_show_blocked_timeslots' ) == 1 ? ' booked' : ' no-booked' ?>">
|
142 |
+
<span class="ladda-label">
|
143 |
+
<i class="ab-hour-icon"><span></span></i><?php echo \BooklyLite\Lib\Utils\DateTime::formatTime( $j ) ?>
|
144 |
+
</span>
|
145 |
+
</button>
|
146 |
+
<?php endfor ?>
|
147 |
+
</div>
|
148 |
+
<?php endfor ?>
|
149 |
+
</div>
|
150 |
+
</div>
|
151 |
+
</div>
|
152 |
+
</div>
|
153 |
+
<div class="ab-row ab-nav-steps">
|
154 |
+
<button class="ab-time-next ab-btn ab-right ladda-button">
|
155 |
+
<span class="ab_label">></span>
|
156 |
+
</button>
|
157 |
+
<button class="ab-time-prev ab-btn ab-right ladda-button">
|
158 |
+
<span class="ab_label"><</span>
|
159 |
+
</button>
|
160 |
+
<div class="ab-left ab-back-step ab-btn">
|
161 |
+
<span class="text_back ab_editable" id="ab-text-button-back" data-mirror="text_back" data-type="text" data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_button_back' ) ) ?>"><?php echo esc_html( get_option( 'ab_appearance_text_button_back' ) ) ?></span>
|
162 |
+
</div>
|
163 |
+
<button class="ab-left ab-goto-cart bookly-round-button ladda-button" data-style="zoom-in" data-spinner-size="30"><span class="ladda-label"><img src="<?php echo plugins_url( 'appointment-booking/frontend/resources/images/cart.png' ) ?>" /></span></button>
|
164 |
+
</div>
|
165 |
+
</div>
|
backend/modules/appearance/templates/_4_cart.php
ADDED
@@ -0,0 +1,132 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
|
2 |
+
<div class="bookly-form">
|
3 |
+
<?php include '_progress_tracker.php' ?>
|
4 |
+
|
5 |
+
<div class="ab-row">
|
6 |
+
<span data-inputclass="input-xxlarge" data-notes="<?php echo esc_attr( $this->render( '_codes', array( 'step' => 4 ), false ) ) ?>" data-placement="bottom" data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_info_cart_step' ) ) ?>" class="ab_editable" id="ab-text-info-cart" data-type="textarea"><?php echo esc_html( get_option( 'ab_appearance_text_info_cart_step' ) ) ?></span>
|
7 |
+
</div>
|
8 |
+
|
9 |
+
<div class="ab-row">
|
10 |
+
<div class="ab-btn ab-add-item ab-inline-block">
|
11 |
+
<span class="ab_editable" id="ab-text-button-book-more" data-type="text" data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_button_book_more' ) ) ?>"><?php echo esc_html( get_option( 'ab_appearance_text_button_book_more' ) ) ?></span>
|
12 |
+
</div>
|
13 |
+
</div>
|
14 |
+
|
15 |
+
<div class="ab-cart-step">
|
16 |
+
<div class="ab-cart ab-row">
|
17 |
+
<table>
|
18 |
+
<thead class="ab-desktop-version">
|
19 |
+
<tr>
|
20 |
+
<th data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_label_service' ) ) ?>" class="ab-service-list"><?php echo esc_html( get_option( 'ab_appearance_text_label_service' ) ) ?></th>
|
21 |
+
<th><?php _e( 'Date', 'bookly' ) ?></th>
|
22 |
+
<th><?php _e( 'Time', 'bookly' ) ?></th>
|
23 |
+
<th data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_label_employee' ) ) ?>" class="ab-employee-list"><?php echo \BooklyLite\Lib\Utils\Common::getTranslatedOption( 'ab_appearance_text_label_employee' ) ?></th>
|
24 |
+
<th><?php _e( 'Price', 'bookly' ) ?></th>
|
25 |
+
<th></th>
|
26 |
+
</tr>
|
27 |
+
</thead>
|
28 |
+
<tbody class="ab-desktop-version">
|
29 |
+
<tr>
|
30 |
+
<td>Crown and Bridge</td>
|
31 |
+
<td><?php echo \BooklyLite\Lib\Utils\DateTime::formatDate( strtotime( '+2 days' ) ) ?></td>
|
32 |
+
<td><?php echo \BooklyLite\Lib\Utils\DateTime::formatTime( 28800 ) ?></td>
|
33 |
+
<td>Nick Knight</td>
|
34 |
+
<td><?php echo \BooklyLite\Lib\Utils\Common::formatPrice( 350 ) ?></td>
|
35 |
+
<td>
|
36 |
+
<button class="bookly-round-button ladda-button" title="<?php esc_attr_e( 'Edit', 'bookly' ) ?>"><img src="<?php echo plugins_url( 'appointment-booking/frontend/resources/images/edit.png' ) ?>" /></button>
|
37 |
+
<button class="bookly-round-button ladda-button" title="<?php esc_attr_e( 'Remove', 'bookly' ) ?>"><img src="<?php echo plugins_url( 'appointment-booking/frontend/resources/images/delete.png' ) ?>" /></button>
|
38 |
+
</td>
|
39 |
+
</tr>
|
40 |
+
<tr>
|
41 |
+
<td>Teeth Whitening</td>
|
42 |
+
<td><?php echo \BooklyLite\Lib\Utils\DateTime::formatDate( strtotime( '+3 days' ) ) ?></td>
|
43 |
+
<td><?php echo \BooklyLite\Lib\Utils\DateTime::formatTime( 57600 ) ?></td>
|
44 |
+
<td>Wayne Turner</td>
|
45 |
+
<td><?php echo \BooklyLite\Lib\Utils\Common::formatPrice( 400 ) ?></td>
|
46 |
+
<td>
|
47 |
+
<button class="bookly-round-button ladda-button" title="<?php esc_attr_e( 'Edit', 'bookly' ) ?>"><img src="<?php echo plugins_url( 'appointment-booking/frontend/resources/images/edit.png' ) ?>" /></button>
|
48 |
+
<button class="bookly-round-button ladda-button" title="<?php esc_attr_e( 'Remove', 'bookly' ) ?>"><img src="<?php echo plugins_url( 'appointment-booking/frontend/resources/images/delete.png' ) ?>" /></button>
|
49 |
+
</td>
|
50 |
+
</tr>
|
51 |
+
</tbody>
|
52 |
+
<tbody class="ab-mobile-version">
|
53 |
+
<tr>
|
54 |
+
<th data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_label_service' ) ) ?>" class="ab-service-list"><?php echo \BooklyLite\Lib\Utils\Common::getTranslatedOption( 'ab_appearance_text_label_service' ) ?></th>
|
55 |
+
<td>Crown and Bridge</td>
|
56 |
+
</tr>
|
57 |
+
<tr>
|
58 |
+
<th><?php _e( 'Date', 'bookly' ) ?></th>
|
59 |
+
<td><?php echo \BooklyLite\Lib\Utils\DateTime::formatDate( strtotime( '+2 days' ) ) ?></td>
|
60 |
+
</tr>
|
61 |
+
<tr>
|
62 |
+
<th><?php _e( 'Time', 'bookly' ) ?></th>
|
63 |
+
<td><?php echo \BooklyLite\Lib\Utils\DateTime::formatTime( 28800 ) ?></td>
|
64 |
+
</tr>
|
65 |
+
<tr>
|
66 |
+
<th data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_label_employee' ) ) ?>" class="ab-employee-list"><?php echo \BooklyLite\Lib\Utils\Common::getTranslatedOption( 'ab_appearance_text_label_employee' ) ?></th>
|
67 |
+
<td>Nick Knight</td>
|
68 |
+
</tr>
|
69 |
+
<tr>
|
70 |
+
<th><?php _e( 'Price', 'bookly' ) ?></th>
|
71 |
+
<td><?php echo \BooklyLite\Lib\Utils\Common::formatPrice( 350 ) ?></td>
|
72 |
+
</tr>
|
73 |
+
<tr>
|
74 |
+
<th></th>
|
75 |
+
<td>
|
76 |
+
<button class="bookly-round-button ladda-button" title="<?php esc_attr_e( 'Edit', 'bookly' ) ?>"><img src="<?php echo plugins_url( 'appointment-booking/frontend/resources/images/edit.png' ) ?>" /></button>
|
77 |
+
<button class="bookly-round-button ladda-button" title="<?php esc_attr_e( 'Remove', 'bookly' ) ?>"><img src="<?php echo plugins_url( 'appointment-booking/frontend/resources/images/delete.png' ) ?>" /></button>
|
78 |
+
</td>
|
79 |
+
</tr>
|
80 |
+
<tr>
|
81 |
+
<th data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_label_service' ) ) ?>" class="ab-service-list"><?php echo \BooklyLite\Lib\Utils\Common::getTranslatedOption( 'ab_appearance_text_label_service' ) ?></th>
|
82 |
+
<td>Teeth Whitening</td>
|
83 |
+
</tr>
|
84 |
+
<tr>
|
85 |
+
<th><?php _e( 'Date', 'bookly' ) ?></th>
|
86 |
+
<td><?php echo \BooklyLite\Lib\Utils\DateTime::formatDate( strtotime( '+3 days' ) ) ?></td>
|
87 |
+
</tr>
|
88 |
+
<tr>
|
89 |
+
<th><?php _e( 'Time', 'bookly' ) ?></th>
|
90 |
+
<td><?php echo \BooklyLite\Lib\Utils\DateTime::formatTime( 57600 ) ?></td>
|
91 |
+
</tr>
|
92 |
+
<tr>
|
93 |
+
<th data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_label_employee' ) ) ?>" class="ab-employee-list"><?php echo \BooklyLite\Lib\Utils\Common::getTranslatedOption( 'ab_appearance_text_label_employee' ) ?></th>
|
94 |
+
<td>Wayne Turner</td>
|
95 |
+
</tr>
|
96 |
+
<tr>
|
97 |
+
<th><?php _e( 'Price', 'bookly' ) ?></th>
|
98 |
+
<td><?php echo \BooklyLite\Lib\Utils\Common::formatPrice( 400 ) ?></td>
|
99 |
+
</tr>
|
100 |
+
<tr>
|
101 |
+
<th></th>
|
102 |
+
<td>
|
103 |
+
<button class="bookly-round-button ladda-button" title="<?php esc_attr_e( 'Edit', 'bookly' ) ?>"><img src="<?php echo plugins_url( 'appointment-booking/frontend/resources/images/edit.png' ) ?>" /></button>
|
104 |
+
<button class="bookly-round-button ladda-button" title="<?php esc_attr_e( 'Remove', 'bookly' ) ?>"><img src="<?php echo plugins_url( 'appointment-booking/frontend/resources/images/delete.png' ) ?>" /></button>
|
105 |
+
</td>
|
106 |
+
</tr>
|
107 |
+
</tbody>
|
108 |
+
<tfoot class="ab-desktop-version">
|
109 |
+
<tr>
|
110 |
+
<td colspan="4"><strong><?php _e( 'Total', 'bookly' ) ?>:</strong></td>
|
111 |
+
<td><strong class="bookly-js-total-price"><?php echo \BooklyLite\Lib\Utils\Common::formatPrice( 750 ) ?></strong></td>
|
112 |
+
<td></td>
|
113 |
+
</tr>
|
114 |
+
</tfoot>
|
115 |
+
<tfoot class="ab-mobile-version">
|
116 |
+
<tr>
|
117 |
+
<th><strong><?php _e( 'Total', 'bookly' ) ?>:</strong></th>
|
118 |
+
<td><strong class="bookly-js-total-price"><?php echo \BooklyLite\Lib\Utils\Common::formatPrice( 750 ) ?></strong></td>
|
119 |
+
</tr>
|
120 |
+
</tfoot>
|
121 |
+
</table>
|
122 |
+
</div>
|
123 |
+
</div>
|
124 |
+
<div class="ab-row ab-nav-steps">
|
125 |
+
<div class="ab-left ab-back-step ab-btn">
|
126 |
+
<span class="text_back ab_editable" id="ab-text-button-back" data-mirror="text_back" data-type="text" data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_button_back' ) ) ?>"><?php echo esc_html( get_option( 'ab_appearance_text_button_back' ) ) ?></span>
|
127 |
+
</div>
|
128 |
+
<div class="ab-right ab-next-step ab-btn">
|
129 |
+
<span class="text_next ab_editable" id="ab-text-button-next" data-mirror="text_next" data-type="text" data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_button_next' ) ) ?>"><?php echo esc_html( get_option( 'ab_appearance_text_button_next' ) ) ?></span>
|
130 |
+
</div>
|
131 |
+
</div>
|
132 |
+
</div>
|
backend/modules/appearance/templates/_4_payment.php
DELETED
@@ -1,60 +0,0 @@
|
|
1 |
-
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
|
2 |
-
<div class="ab-booking-form">
|
3 |
-
<!-- Progress Tracker-->
|
4 |
-
<?php $step = 4; include '_progress_tracker.php'; ?>
|
5 |
-
<!-- payment -->
|
6 |
-
<div class="ab-payment">
|
7 |
-
<!-- Coupons -->
|
8 |
-
<div class="ab-row-fluid">
|
9 |
-
<span data-inputclass="input-xxlarge" data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_info_coupon' ) ) ?>" data-notes="<?php echo esc_attr( $this->render( '_codes', array( 'step' => 4 ), false ) ) ?>" class="ab-text-info-coupon-preview ab-row-fluid ab_editable" id="ab-text-info-coupon" data-type="textarea"><?php echo esc_html( get_option( 'ab_appearance_text_info_coupon' ) ) ?></span>
|
10 |
-
</div>
|
11 |
-
|
12 |
-
<div style="margin-bottom: 15px">
|
13 |
-
<span data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_label_coupon' ) ) ?>" class="ab_editable editable editable-click inline-block" id="ab-text-label-coupon" data-type="text"><?php echo esc_html( get_option( 'ab_appearance_text_label_coupon' ) ) ?></span>
|
14 |
-
<div class="ab-inline-block">
|
15 |
-
<input class="ab-user-coupon ab-inline-block" maxlength="40" type="text" value="" />
|
16 |
-
<button class="ab-btn ladda-button orange ab-inline-block"><?php _e( 'Apply', 'bookly' ) ?></button>
|
17 |
-
</div>
|
18 |
-
</div>
|
19 |
-
<div class="ab-clear"></div>
|
20 |
-
|
21 |
-
<div class="ab-row-fluid">
|
22 |
-
<span data-inputclass="input-xxlarge" data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_info_fourth_step' ) ) ?>" data-notes="<?php echo esc_attr( $this->render( '_codes', array( 'step' => 4 ), false ) ) ?>" class="ab-text-info-fourth-preview ab-row-fluid ab_editable" id="ab-text-info-fourth" data-type="textarea"><?php echo esc_html( get_option( 'ab_appearance_text_info_fourth_step' ) ) ?></span>
|
23 |
-
</div>
|
24 |
-
|
25 |
-
<!-- label -->
|
26 |
-
<div class="ab-row-fluid">
|
27 |
-
<label>
|
28 |
-
<input type="radio" name="payment" class="ab-local-payment" checked="checked" value="local"/>
|
29 |
-
<span id="ab-text-label-pay-locally" class="ab_editable" data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_label_pay_locally' ) ) ?>"><?php echo esc_html( get_option( 'ab_appearance_text_label_pay_locally' ) ) ?></span>
|
30 |
-
</label>
|
31 |
-
</div>
|
32 |
-
<div class="ab-row-fluid">
|
33 |
-
<label>
|
34 |
-
<input type="radio" name="payment" class="ab-paypal-payment" value="paypal" />
|
35 |
-
<?php _e( 'I will pay now with PayPal', 'bookly' ) ?>
|
36 |
-
<img src="<?php echo plugins_url( 'frontend/resources/images/paypal.png', AB_PATH . '/main.php' ) ?>" style="margin-left: 10px;" alt="paypal" />
|
37 |
-
</label>
|
38 |
-
</div>
|
39 |
-
<div class="ab-row-fluid">
|
40 |
-
<label>
|
41 |
-
<input type="radio" name="payment" class="ab-stripe-payment" value="stripe"/>
|
42 |
-
<?php _e( 'I will pay now with Credit Card', 'bookly' ) ?>
|
43 |
-
<img style="margin-left: 10px;" src="<?php echo plugins_url( 'frontend/resources/images/cards.png', AB_PATH . '/main.php' ) ?>" alt="cards" />
|
44 |
-
</label>
|
45 |
-
<form class="ab-stripe ab-clearBottom" style="margin-top:15px;display: none;">
|
46 |
-
<?php include "_card_payment.php" ?>
|
47 |
-
</form>
|
48 |
-
</div>
|
49 |
-
|
50 |
-
<!-- buttons -->
|
51 |
-
<div class="ab-local-pay-button ab-row-fluid ab-nav-steps last-row">
|
52 |
-
<button class="ab-left ab-to-third-step ab-btn ladda-button">
|
53 |
-
<span><?php _e( 'Back', 'bookly' ) ?></span>
|
54 |
-
</button>
|
55 |
-
<button class="ab-right ab-final-step ab-btn ladda-button">
|
56 |
-
<span><?php _e( 'Next', 'bookly' ) ?></span>
|
57 |
-
</button>
|
58 |
-
</div>
|
59 |
-
</div>
|
60 |
-
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/modules/appearance/templates/_5_details.php
ADDED
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
|
2 |
+
<div class="bookly-form">
|
3 |
+
<?php include '_progress_tracker.php' ?>
|
4 |
+
|
5 |
+
<div class="ab-row">
|
6 |
+
<span data-inputclass="input-xxlarge" data-notes="<?php echo esc_attr( $this->render( '_codes', array( 'step' => 5, 'login' => false ), false ) ) ?>" data-placement="bottom" data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_info_details_step' ) ) ?>" class="ab_editable" id="ab-text-info-details" data-type="textarea"><?php echo esc_html( get_option( 'ab_appearance_text_info_details_step' ) ) ?></span>
|
7 |
+
</div>
|
8 |
+
<div class="ab-row">
|
9 |
+
<span data-inputclass="input-xxlarge" data-title="<?php _e( 'Visible to non-logged in customers only', 'bookly' ) ?>" data-notes="<?php echo esc_attr( $this->render( '_codes', array( 'step' => 5, 'login' => true ), false ) ) ?>" data-placement="bottom" data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_info_details_step_guest' ) ) ?>" class="ab_editable" id="ab-text-info-details-guest" data-type="textarea"><?php echo esc_html( get_option( 'ab_appearance_text_info_details_step_guest' ) ) ?></span>
|
10 |
+
</div>
|
11 |
+
<div class="ab-details-step">
|
12 |
+
<div class="ab-row bookly-table">
|
13 |
+
<div class="ab-formGroup">
|
14 |
+
<label>
|
15 |
+
<span
|
16 |
+
data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_label_name' ) ) ?>"
|
17 |
+
data-default-error="<?php echo esc_attr( get_option( 'ab_appearance_text_required_name' ) ) ?>"
|
18 |
+
id="ab-text-label-name"
|
19 |
+
data-type="multiple"
|
20 |
+
data-option-id="ab_appearance_text_required_name"><?php echo esc_html( get_option( 'ab_appearance_text_label_name' ) ) ?></span>
|
21 |
+
</label>
|
22 |
+
|
23 |
+
<div>
|
24 |
+
<input type="text" value="" maxlength="60" />
|
25 |
+
</div>
|
26 |
+
</div>
|
27 |
+
<div class="ab-formGroup">
|
28 |
+
<label>
|
29 |
+
<span
|
30 |
+
data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_label_phone' ) ) ?>"
|
31 |
+
data-default-error="<?php echo esc_attr( get_option( 'ab_appearance_text_required_phone' ) ) ?>"
|
32 |
+
id="ab-text-label-phone"
|
33 |
+
data-type="multiple"
|
34 |
+
data-option-id="ab_appearance_text_required_phone"><?php echo esc_html( get_option( 'ab_appearance_text_label_phone' ) ) ?></span>
|
35 |
+
</label>
|
36 |
+
<div>
|
37 |
+
<input type="text" class="<?php if ( get_option( 'ab_settings_phone_default_country' ) != 'disabled' ) : ?>ab-user-phone<?php endif ?>" value="" />
|
38 |
+
</div>
|
39 |
+
</div>
|
40 |
+
<div class="ab-formGroup">
|
41 |
+
<label>
|
42 |
+
<span
|
43 |
+
data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_label_email' ) ) ?>"
|
44 |
+
data-default-error="<?php echo esc_attr( get_option( 'ab_appearance_text_required_email' ) ) ?>"
|
45 |
+
id="ab-text-label-email"
|
46 |
+
data-type="multiple"
|
47 |
+
data-option-id="ab_appearance_text_required_email"><?php echo esc_html( get_option( 'ab_appearance_text_label_email' ) ) ?></span>
|
48 |
+
</label>
|
49 |
+
<div>
|
50 |
+
<input maxlength="40" type="text" value="" />
|
51 |
+
</div>
|
52 |
+
</div>
|
53 |
+
</div>
|
54 |
+
</div>
|
55 |
+
<div class="ab-row ab-nav-steps">
|
56 |
+
<div class="ab-left ab-back-step ab-btn">
|
57 |
+
<span class="text_back ab_editable" id="ab-text-button-back" data-mirror="text_back" data-type="text" data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_button_back' ) ) ?>"><?php echo esc_html( get_option( 'ab_appearance_text_button_back' ) ) ?></span>
|
58 |
+
</div>
|
59 |
+
<div class="ab-right ab-next-step ab-btn">
|
60 |
+
<span class="text_next ab_editable" id="ab-text-button-next" data-mirror="text_next" data-type="text" data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_button_next' ) ) ?>"><?php echo esc_html( get_option( 'ab_appearance_text_button_next' ) ) ?></span>
|
61 |
+
</div>
|
62 |
+
</div>
|
63 |
+
</div>
|
backend/modules/appearance/templates/_5_done.php
DELETED
@@ -1,8 +0,0 @@
|
|
1 |
-
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
|
2 |
-
<div class="ab-booking-form">
|
3 |
-
<!-- Progress Tracker-->
|
4 |
-
<?php $step = 5; include '_progress_tracker.php'; ?>
|
5 |
-
<div class="ab-row-fluid">
|
6 |
-
<span data-inputclass="input-xxlarge" data-link-class="ab-text-info-fifth" class="ab-text-info-fifth-preview ab_editable" data-notes="<?php echo esc_attr( $this->render( '_codes', array( 'step' => 5 ), false ) ) ?>" data-placement="bottom" data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_info_fifth_step' ) ) ?>" id="ab-text-info-fifth" data-type="textarea"><?php echo nl2br( esc_html( get_option( 'ab_appearance_text_info_fifth_step' ) ) ) ?></span>
|
7 |
-
</div>
|
8 |
-
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/modules/appearance/templates/_6_payment.php
ADDED
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
|
2 |
+
<div class="bookly-form">
|
3 |
+
<?php include '_progress_tracker.php' ?>
|
4 |
+
<!-- Coupons -->
|
5 |
+
<div class="ab-row">
|
6 |
+
<span data-inputclass="input-xxlarge" data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_info_coupon' ) ) ?>" data-notes="<?php echo esc_attr( $this->render( '_codes', array( 'step' => 6 ), false ) ) ?>" class="ab_editable" id="ab-text-info-coupon" data-type="textarea"><?php echo esc_html( get_option( 'ab_appearance_text_info_coupon' ) ) ?></span>
|
7 |
+
</div>
|
8 |
+
|
9 |
+
<div class="ab-row ab-list">
|
10 |
+
<span data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_label_coupon' ) ) ?>" class="ab_editable editable editable-click inline-block" id="ab-text-label-coupon" data-type="text"><?php echo esc_html( get_option( 'ab_appearance_text_label_coupon' ) ) ?></span>
|
11 |
+
<div class="ab-inline-block">
|
12 |
+
<input class="ab-user-coupon" type="text" />
|
13 |
+
<div class="ab-btn btn-apply-coupon">
|
14 |
+
<span class="ab_editable" id="ab-text-button-apply" data-type="text" data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_button_apply' ) ) ?>"><?php echo esc_html( get_option( 'ab_appearance_text_button_apply' ) ) ?></span>
|
15 |
+
</div>
|
16 |
+
</div>
|
17 |
+
</div>
|
18 |
+
<div class="ab-payment-nav">
|
19 |
+
<div class="ab-row">
|
20 |
+
<span data-inputclass="input-xxlarge" data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_info_payment_step' ) ) ?>" data-notes="<?php echo esc_attr( $this->render( '_codes', compact( 'step' ), false ) ) ?>" class="ab_editable" id="ab-text-info-payment" data-type="textarea"><?php echo esc_html( get_option( 'ab_appearance_text_info_payment_step' ) ) ?></span>
|
21 |
+
</div>
|
22 |
+
|
23 |
+
<div class="ab-row ab-list">
|
24 |
+
<label>
|
25 |
+
<input type="radio" name="payment" checked="checked" />
|
26 |
+
<span id="ab-text-label-pay-locally" class="ab_editable" data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_label_pay_locally' ) ) ?>"><?php echo esc_html( get_option( 'ab_appearance_text_label_pay_locally' ) ) ?></span>
|
27 |
+
</label>
|
28 |
+
</div>
|
29 |
+
|
30 |
+
<div class="ab-row ab-list">
|
31 |
+
<label>
|
32 |
+
<input type="radio" name="payment" />
|
33 |
+
<span data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_label_pay_paypal' ) ) ?>" class="ab_editable editable editable-click inline-block" id="ab-text-label-pay-paypal" data-type="text"><?php echo esc_html( get_option( 'ab_appearance_text_label_pay_paypal' ) ) ?></span>
|
34 |
+
<img src="<?php echo plugins_url( 'frontend/resources/images/paypal.png', \BooklyLite\Lib\Plugin::getMainFile() ) ?>" alt="paypal" />
|
35 |
+
</label>
|
36 |
+
</div>
|
37 |
+
|
38 |
+
<div class="ab-row ab-list">
|
39 |
+
<label>
|
40 |
+
<input type="radio" name="payment" class="ab-card-payment" />
|
41 |
+
<span data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_label_pay_ccard' ) ) ?>" class="ab_editable editable editable-click inline-block" id="ab-text-label-pay-ccard" data-type="text"><?php echo esc_html( get_option( 'ab_appearance_text_label_pay_ccard' ) ) ?></span>
|
42 |
+
<img src="<?php echo plugins_url( 'frontend/resources/images/cards.png', \BooklyLite\Lib\Plugin::getMainFile() ) ?>" alt="cards" />
|
43 |
+
</label>
|
44 |
+
<form class="ab-card-form ab-clearBottom" style="margin-top:15px;display: none;">
|
45 |
+
<?php include '_card_payment.php' ?>
|
46 |
+
</form>
|
47 |
+
</div>
|
48 |
+
|
49 |
+
<div class="ab-row ab-list">
|
50 |
+
<label>
|
51 |
+
<input type="radio" name="payment" />
|
52 |
+
<span id="ab-text-label-pay-mollie" class="ab_editable" data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_label_pay_mollie' ) ) ?>"><?php echo esc_html( get_option( 'ab_appearance_text_label_pay_mollie' ) ) ?></span>
|
53 |
+
<img src="<?php echo plugins_url( 'frontend/resources/images/mollie.png', \BooklyLite\Lib\Plugin::getMainFile() ) ?>" alt="mollie" />
|
54 |
+
</label>
|
55 |
+
</div>
|
56 |
+
</div>
|
57 |
+
|
58 |
+
<!-- buttons -->
|
59 |
+
<div class="ab-row ab-nav-steps">
|
60 |
+
<div class="ab-left ab-back-step ab-btn">
|
61 |
+
<span class="text_back ab_editable" id="ab-text-button-back" data-mirror="text_back" data-type="text" data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_button_back' ) ) ?>"><?php echo esc_html( get_option( 'ab_appearance_text_button_back' ) ) ?></span>
|
62 |
+
</div>
|
63 |
+
<div class="ab-right ab-next-step ab-btn">
|
64 |
+
<span class="text_next ab_editable" id="ab-text-button-next" data-mirror="text_next" data-type="text" data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_button_next' ) ) ?>"><?php echo esc_html( get_option( 'ab_appearance_text_button_next' ) ) ?></span>
|
65 |
+
</div>
|
66 |
+
</div>
|
67 |
+
</div>
|
backend/modules/appearance/templates/_7_done.php
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
|
2 |
+
<div class="bookly-form">
|
3 |
+
<?php include '_progress_tracker.php' ?>
|
4 |
+
<div class="ab-row">
|
5 |
+
<span data-inputclass="input-xxlarge" class="ab_editable" data-notes="<?php echo esc_attr( $this->render( '_codes', array( 'step' => 7 ), false ) ) ?>" data-placement="bottom" data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_info_complete_step' ) ) ?>" id="ab-text-info-complete" data-type="textarea"><?php echo nl2br( esc_html( get_option( 'ab_appearance_text_info_complete_step' ) ) ) ?></span>
|
6 |
+
</div>
|
7 |
+
</div>
|
backend/modules/appearance/templates/_card_payment.php
CHANGED
@@ -1,34 +1,38 @@
|
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
|
2 |
-
<div class="ab-row-
|
3 |
-
<div class="ab-formGroup
|
4 |
-
<label
|
5 |
-
|
6 |
-
|
|
|
|
|
7 |
</div>
|
8 |
</div>
|
9 |
-
<div class="ab-formGroup
|
10 |
-
<label
|
11 |
-
|
12 |
-
|
13 |
-
|
|
|
|
|
14 |
<option value="<?php echo $i ?>"><?php printf( '%02d', $i ) ?></option>
|
15 |
<?php endfor ?>
|
16 |
</select>
|
17 |
-
<select class="ab-
|
18 |
-
<?php for ( $i = date( 'Y' ); $i <= date( 'Y' ) + 10; ++ $i ): ?>
|
19 |
<option value="<?php echo $i ?>"><?php echo $i ?></option>
|
20 |
<?php endfor ?>
|
21 |
</select>
|
22 |
</div>
|
23 |
</div>
|
24 |
</div>
|
25 |
-
<div class="ab-row-
|
26 |
-
<div class="ab-formGroup
|
27 |
-
<label
|
28 |
-
|
29 |
-
|
|
|
|
|
30 |
</div>
|
31 |
</div>
|
32 |
-
<div class="ab-clear"></div>
|
33 |
-
<div class="ab-error ab-bold ab-card-error"></div>
|
34 |
</div>
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
|
2 |
+
<div class="ab-row bookly-table">
|
3 |
+
<div class="ab-formGroup" style="width:200px!important">
|
4 |
+
<label>
|
5 |
+
<span data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_label_ccard_number' ) ) ?>" class="ab_editable editable editable-click inline-block" id="ab-text-label-ccard-number" data-type="text"><?php echo esc_html( get_option( 'ab_appearance_text_label_ccard_number' ) ) ?></span>
|
6 |
+
</label>
|
7 |
+
<div>
|
8 |
+
<input type="text" />
|
9 |
</div>
|
10 |
</div>
|
11 |
+
<div class="ab-formGroup">
|
12 |
+
<label>
|
13 |
+
<span data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_label_ccard_expire' ) ) ?>" class="ab_editable editable editable-click inline-block" id="ab-text-label-ccard-expire" data-type="text"><?php echo esc_html( get_option( 'ab_appearance_text_label_ccard_expire' ) ) ?></span>
|
14 |
+
</label>
|
15 |
+
<div>
|
16 |
+
<select class="ab-card-exp">
|
17 |
+
<?php for ( $i = 1; $i <= 12; ++ $i ) : ?>
|
18 |
<option value="<?php echo $i ?>"><?php printf( '%02d', $i ) ?></option>
|
19 |
<?php endfor ?>
|
20 |
</select>
|
21 |
+
<select class="ab-card-exp">
|
22 |
+
<?php for ( $i = date( 'Y' ); $i <= date( 'Y' ) + 10; ++ $i ) : ?>
|
23 |
<option value="<?php echo $i ?>"><?php echo $i ?></option>
|
24 |
<?php endfor ?>
|
25 |
</select>
|
26 |
</div>
|
27 |
</div>
|
28 |
</div>
|
29 |
+
<div class="ab-row ab-clearBottom">
|
30 |
+
<div class="ab-formGroup">
|
31 |
+
<label>
|
32 |
+
<span data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_label_ccard_code' ) ) ?>" class="ab_editable editable editable-click inline-block" id="ab-text-label-ccard-code" data-type="text"><?php echo esc_html( get_option( 'ab_appearance_text_label_ccard_code' ) ) ?></span>
|
33 |
+
</label>
|
34 |
+
<div>
|
35 |
+
<input class="ab-card-cvc" type="text" />
|
36 |
</div>
|
37 |
</div>
|
|
|
|
|
38 |
</div>
|
backend/modules/appearance/templates/_codes.php
CHANGED
@@ -1,16 +1,25 @@
|
|
1 |
-
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
+
$codes = array(
|
3 |
+
array( 'code' => 'category_name', 'description' => __( 'name of category', 'bookly' ), ),
|
4 |
+
array( 'code' => 'login_form', 'description' => __( 'login form', 'bookly' ),
|
5 |
+
'step' => 5,
|
6 |
+
'login' => true,
|
7 |
+
),
|
8 |
+
array( 'code' => 'number_of_persons', 'description' => __( 'number of persons', 'bookly' ), ),
|
9 |
+
array( 'code' => 'booking_number', 'description' => __( 'booking number', 'bookly' ),
|
10 |
+
'step' => 7,
|
11 |
+
),
|
12 |
+
array( 'code' => 'service_date', 'description' => __( 'date of service', 'bookly' ),
|
13 |
+
'min_step' => 3,
|
14 |
+
),
|
15 |
+
array( 'code' => 'service_info', 'description' => __( 'info of service', 'bookly' ), ),
|
16 |
+
array( 'code' => 'service_name', 'description' => __( 'name of service', 'bookly' ), ),
|
17 |
+
array( 'code' => 'service_price', 'description' => __( 'price of service', 'bookly' ), ),
|
18 |
+
array( 'code' => 'service_time', 'description' => __( 'time of service', 'bookly' ),
|
19 |
+
'min_step' => 3,
|
20 |
+
),
|
21 |
+
array( 'code' => 'staff_info', 'description' => __( 'info of staff', 'bookly' ), ),
|
22 |
+
array( 'code' => 'staff_name', 'description' => __( 'name of staff', 'bookly' ), ),
|
23 |
+
array( 'code' => 'total_price', 'description' => __( 'total price of booking', 'bookly' ), ),
|
24 |
+
);
|
25 |
+
\BooklyLite\Lib\Utils\Common::Codes( apply_filters( 'bookly_appearance_short_codes', $codes ), $step, isset( $login ) ? $login : false );
|
backend/modules/appearance/templates/_progress_tracker.php
CHANGED
@@ -1,25 +1,35 @@
|
|
1 |
-
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
<
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
<
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
<
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
+
$i = 1;
|
3 |
+
?>
|
4 |
+
<div class="ab-progress-tracker bookly-table">
|
5 |
+
<div class="active">
|
6 |
+
<?php echo $i ++ ?>. <span data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_step_service' ) ) ?>" data-mirror="text_service" class="text_service ab_editable" id="ab-text-step-service" data-type="text"><?php echo esc_html( get_option( 'ab_appearance_text_step_service' ) ) ?></span>
|
7 |
+
<div class="step"></div>
|
8 |
+
</div>
|
9 |
+
<?php if ( \BooklyLite\Lib\Config::extrasEnabled() ) : ?>
|
10 |
+
<div <?php if ( ( $step >= 2 ) ) : ?>class="active"<?php endif ?>>
|
11 |
+
<?php echo $i ++ ?>. <span data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_step_extras' ) ) ?>" data-mirror="text_extras" class="text_extras ab_editable" id="ab-text-step-extras" data-type="text"><?php echo esc_html(get_option( 'ab_appearance_text_step_extras' ) ) ?></span>
|
12 |
+
<div class="step"></div>
|
13 |
+
</div>
|
14 |
+
<?php endif ?>
|
15 |
+
<div <?php if ( $step >= 3 ) : ?>class="active"<?php endif ?>>
|
16 |
+
<?php echo $i ++ ?>. <span data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_step_time' ) ) ?>" data-mirror="text_time" class="text_time ab_editable" id="ab-text-step-time" data-type="text"><?php echo esc_html(get_option( 'ab_appearance_text_step_time' ) ) ?></span>
|
17 |
+
<div class="step"></div>
|
18 |
+
</div>
|
19 |
+
<div <?php if ( $step >= 4 ) : ?>class="active"<?php endif ?>>
|
20 |
+
<?php echo $i ++ ?>. <span data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_step_cart' ) ) ?>" data-mirror="text_cart" class="text_cart ab_editable" id="ab-text-step-cart" data-type="text"><?php echo esc_html( get_option( 'ab_appearance_text_step_cart' ) ) ?></span>
|
21 |
+
<div class="step"></div>
|
22 |
+
</div>
|
23 |
+
<div <?php if ( $step >= 5 ) : ?>class="active"<?php endif ?>>
|
24 |
+
<?php echo $i ++ ?>. <span data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_step_details' ) ) ?>" data-mirror="text_details" class="text_details ab_editable" id="ab-text-step-details" data-type="text"><?php echo esc_html( get_option( 'ab_appearance_text_step_details' ) ) ?></span>
|
25 |
+
<div class="step"></div>
|
26 |
+
</div>
|
27 |
+
<div <?php if ( $step >= 6 ) : ?>class="active"<?php endif ?>>
|
28 |
+
<?php echo $i ++ ?>. <span data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_step_payment' ) ) ?>" data-mirror="text_payment" class="text_payment ab_editable" id="ab-text-step-payment" data-type="text"><?php echo esc_html( get_option( 'ab_appearance_text_step_payment' ) ) ?></span>
|
29 |
+
<div class="step"></div>
|
30 |
+
</div>
|
31 |
+
<div <?php if ( $step >= 7 ) : ?>class="active"<?php endif ?>>
|
32 |
+
<?php echo $i ++ ?>. <span data-default="<?php echo esc_attr( get_option( 'ab_appearance_text_step_done' ) ) ?>" data-mirror="text_done" class="text_done ab_editable" id="ab-text-step-done" data-type="text"><?php echo esc_html( get_option( 'ab_appearance_text_step_done' ) ) ?></span>
|
33 |
+
<div class="step"></div>
|
34 |
+
</div>
|
35 |
+
</div>
|
backend/modules/appearance/templates/index.php
CHANGED
@@ -1,90 +1,126 @@
|
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
|
2 |
-
<div
|
3 |
-
<div class="
|
4 |
-
<
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
<input id=ab-progress-tracker-checkbox name=ab-progress-tracker-checkbox <?php checked( get_option( 'ab_appearance_show_progress_tracker' ) ) ?> type=checkbox />
|
18 |
-
<b><?php _e( 'Show form progress tracker', 'bookly' ) ?></b>
|
19 |
-
</label>
|
20 |
</div>
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
</
|
28 |
</div>
|
29 |
</div>
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
36 |
</div>
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
44 |
</div>
|
45 |
</div>
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
case 2: // Time
|
67 |
-
include '_2_time.php'; break;
|
68 |
-
case 3: // Details
|
69 |
-
include '_3_details.php'; break;
|
70 |
-
case 4: // Payment
|
71 |
-
include '_4_payment.php'; break;
|
72 |
-
case 5: // Done
|
73 |
-
include '_5_done.php'; break;
|
74 |
-
}
|
75 |
-
?>
|
76 |
</div>
|
77 |
-
|
78 |
-
|
79 |
-
<div class="text-right">
|
80 |
-
<?php _e( 'Click on the underlined text to edit.', 'bookly' ) ?>
|
81 |
</div>
|
82 |
-
|
|
|
|
|
|
|
83 |
</div>
|
84 |
</div>
|
85 |
</div>
|
86 |
-
<div class="panel-footer">
|
87 |
-
<?php AB_Utils::submitButton( 'ajax-send-appearance' ) ?>
|
88 |
-
<?php AB_Utils::resetButton() ?>
|
89 |
-
</div>
|
90 |
</div>
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
|
2 |
+
<div id="bookly-tbs" class="wrap">
|
3 |
+
<div class="bookly-tbs-body">
|
4 |
+
<div class="page-header text-right clearfix">
|
5 |
+
<div class="bookly-page-title">
|
6 |
+
<?php _e( 'Appearance', 'bookly' ) ?>
|
7 |
+
</div>
|
8 |
+
</div>
|
9 |
+
<div class="panel panel-default bookly-main">
|
10 |
+
<div class="panel-body">
|
11 |
+
<div id="ab-appearance">
|
12 |
+
<div class="row">
|
13 |
+
<div class="col-sm-3 col-lg-2 bookly-color-picker-wrapper">
|
14 |
+
<input type="text" name="color" class="bookly-js-color-picker"
|
15 |
+
value="<?php echo esc_attr( get_option( 'ab_appearance_color' ) ) ?>"
|
16 |
+
data-selected="<?php echo esc_attr( get_option( 'ab_appearance_color' ) ) ?>" />
|
|
|
|
|
|
|
17 |
</div>
|
18 |
+
<div class="col-sm-9 col-lg-10">
|
19 |
+
<div class="checkbox">
|
20 |
+
<label>
|
21 |
+
<input type="checkbox" id=ab-progress-tracker-checkbox <?php checked( get_option( 'ab_appearance_show_progress_tracker' ) ) ?>>
|
22 |
+
<?php _e( 'Show form progress tracker', 'bookly' ) ?>
|
23 |
+
</label>
|
24 |
+
</div>
|
25 |
</div>
|
26 |
</div>
|
27 |
+
|
28 |
+
<ul class="bookly-nav bookly-nav-tabs bookly-margin-top-lg" role="tablist">
|
29 |
+
<?php $i = 1 ?>
|
30 |
+
<?php foreach ( $steps as $step => $step_name ) : ?>
|
31 |
+
<?php if ( $step != 2 || \BooklyLite\Lib\Config::extrasEnabled() ) : ?>
|
32 |
+
<li class="bookly-nav-item <?php if ( $step == 1 ) : ?>active<?php endif ?>" data-target="#ab-step-<?php echo $step ?>" data-toggle="tab">
|
33 |
+
<?php echo $i++ ?>. <?php echo esc_html( $step_name ) ?>
|
34 |
+
</li>
|
35 |
+
<?php endif ?>
|
36 |
+
<?php endforeach ?>
|
37 |
+
</ul>
|
38 |
+
|
39 |
+
<?php if ( ! get_user_meta( get_current_user_id(), \BooklyLite\Lib\Plugin::getPrefix() . 'dismiss_appearance_notice', true ) ): ?>
|
40 |
+
<div class="alert alert-info alert-dismissible fade in bookly-margin-top-lg bookly-margin-bottom-remove" id="bookly-js-hint-alert" role="alert">
|
41 |
+
<button type="button" class="close" data-dismiss="alert">×</button>
|
42 |
+
<?php _e( 'Click on the underlined text to edit.', 'bookly' ) ?>
|
43 |
</div>
|
44 |
+
<?php endif ?>
|
45 |
+
|
46 |
+
<div class="row" id="bookly-js-step-settings">
|
47 |
+
<div id="bookly-js-step-service" class="bookly-margin-top-lg">
|
48 |
+
<div class="col-md-12">
|
49 |
+
<?php do_action( 'bookly_render_appearance_step_service_settings' ) ?>
|
50 |
+
<div class="col-md-4">
|
51 |
+
<div class="checkbox">
|
52 |
+
<label>
|
53 |
+
<input type="checkbox" id=ab-required-employee-checkbox <?php checked( get_option( 'ab_appearance_required_employee' ) ) ?>>
|
54 |
+
<?php _e( 'Make selecting employee required', 'bookly' ) ?>
|
55 |
+
</label>
|
56 |
+
</div>
|
57 |
+
</div>
|
58 |
+
<div class="col-md-4">
|
59 |
+
<div class="checkbox">
|
60 |
+
<label>
|
61 |
+
<input type="checkbox" id=ab-staff-name-with-price-checkbox <?php checked( get_option( 'ab_appearance_staff_name_with_price' ) ) ?>>
|
62 |
+
<?php _e( 'Show service price next to employee name', 'bookly' ) ?>
|
63 |
+
</label>
|
64 |
+
</div>
|
65 |
+
</div>
|
66 |
+
</div>
|
67 |
+
</div>
|
68 |
+
<div id="bookly-js-step-time" class="bookly-margin-top-lg" style="display:none">
|
69 |
+
<div class="col-md-4">
|
70 |
+
<div class="checkbox">
|
71 |
+
<label>
|
72 |
+
<input type="checkbox" id="ab-show-calendar-checkbox" <?php checked( get_option( 'ab_appearance_show_calendar' ) ) ?>>
|
73 |
+
<?php _e( 'Show calendar', 'bookly' ) ?>
|
74 |
+
</label>
|
75 |
+
</div>
|
76 |
+
</div>
|
77 |
+
<div class="col-md-4">
|
78 |
+
<div class="checkbox">
|
79 |
+
<label>
|
80 |
+
<input type="checkbox" id="ab-blocked-timeslots-checkbox" <?php checked( get_option( 'ab_appearance_show_blocked_timeslots' ) ) ?>>
|
81 |
+
<?php _e( 'Show blocked timeslots', 'bookly' ) ?>
|
82 |
+
</label>
|
83 |
+
</div>
|
84 |
+
</div>
|
85 |
+
<div class="col-md-4">
|
86 |
+
<div class="checkbox">
|
87 |
+
<label>
|
88 |
+
<input type="checkbox" id="ab-day-one-column-checkbox" <?php checked( get_option( 'ab_appearance_show_day_one_column' ) ) ?>>
|
89 |
+
<?php _e( 'Show each day in one column', 'bookly' ) ?>
|
90 |
+
</label>
|
91 |
+
</div>
|
92 |
+
</div>
|
93 |
</div>
|
94 |
</div>
|
95 |
+
|
96 |
+
<div class="panel panel-default bookly-margin-top-lg">
|
97 |
+
<div class="panel-body">
|
98 |
+
<div class="tab-content">
|
99 |
+
<?php foreach ( $steps as $step => $step_name ) : ?>
|
100 |
+
<div id="ab-step-<?php echo $step ?>" class="tab-pane <?php if ( $step == 1 ) : ?>active<?php endif ?>" data-target="<?php echo $step ?>">
|
101 |
+
<?php // Render unique data per step
|
102 |
+
switch ( $step ) :
|
103 |
+
case 1: include '_1_service.php'; break;
|
104 |
+
case 2: do_action( 'bookly_extras_render_appearance_tab', $this->render( '_progress_tracker', array( 'step' => $step ), false ) );
|
105 |
+
break;
|
106 |
+
case 3: include '_3_time.php'; break;
|
107 |
+
case 4: include '_4_cart.php'; break;
|
108 |
+
case 5: include '_5_details.php'; break;
|
109 |
+
case 6: include '_6_payment.php'; break;
|
110 |
+
case 7: include '_7_done.php'; break;
|
111 |
+
endswitch ?>
|
112 |
+
</div>
|
113 |
+
<?php endforeach ?>
|
114 |
+
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
115 |
</div>
|
116 |
+
</div>
|
117 |
+
|
|
|
|
|
118 |
</div>
|
119 |
+
</div>
|
120 |
+
<div class="panel-footer">
|
121 |
+
<?php \BooklyLite\Lib\Utils\Common::submitButton( 'ajax-send-appearance' ) ?>
|
122 |
+
<?php \BooklyLite\Lib\Utils\Common::resetButton() ?>
|
123 |
</div>
|
124 |
</div>
|
125 |
</div>
|
|
|
|
|
|
|
|
|
126 |
</div>
|
backend/modules/appointments/AB_AppointmentsController.php
DELETED
@@ -1,276 +0,0 @@
|
|
1 |
-
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Class AB_AppointmentsController
|
5 |
-
*/
|
6 |
-
class AB_AppointmentsController extends AB_Controller {
|
7 |
-
|
8 |
-
public function index()
|
9 |
-
{
|
10 |
-
/** @var WP_Locale $wp_locale */
|
11 |
-
global $wp_locale;
|
12 |
-
|
13 |
-
$this->enqueueStyles( array(
|
14 |
-
'frontend' => array(
|
15 |
-
'css/intlTelInput.css',
|
16 |
-
),
|
17 |
-
'backend' => array(
|
18 |
-
'css/jquery-ui-theme/jquery-ui.min.css',
|
19 |
-
'css/bookly.main-backend.css',
|
20 |
-
'bootstrap/css/bootstrap.min.css',
|
21 |
-
'css/daterangepicker.css',
|
22 |
-
'css/chosen.min.css'
|
23 |
-
)
|
24 |
-
) );
|
25 |
-
|
26 |
-
$this->enqueueScripts( array(
|
27 |
-
'backend' => array(
|
28 |
-
'bootstrap/js/bootstrap.min.js' => array( 'jquery' ),
|
29 |
-
'js/angular.min.js',
|
30 |
-
'js/angular-sanitize.min.js' => array( 'ab-angular.min.js' ),
|
31 |
-
'js/angular-ui-utils-0.2.1.min.js' => array( 'ab-angular.min.js' ),
|
32 |
-
'js/ng-new_customer_dialog.js' => array( 'ab-intlTelInput.min.js', 'ab-angular.min.js' ),
|
33 |
-
'js/angular-ui-date-0.0.8.js' => array( 'ab-angular.min.js' ),
|
34 |
-
'js/moment.min.js',
|
35 |
-
'js/daterangepicker.js' => array( 'jquery' ),
|
36 |
-
'js/chosen.jquery.min.js' => array( 'jquery' ),
|
37 |
-
'js/ng-edit_appointment_dialog.js' => array( 'ab-angular-ui-date-0.0.8.js', 'jquery-ui-datepicker' ),
|
38 |
-
),
|
39 |
-
'frontend' => array(
|
40 |
-
'js/intlTelInput.min.js',
|
41 |
-
),
|
42 |
-
'module' => array(
|
43 |
-
'js/ng-app.js' => array( 'jquery', 'ab-angular.min.js', 'ab-angular-ui-utils-0.2.1.min.js' ),
|
44 |
-
),
|
45 |
-
) );
|
46 |
-
|
47 |
-
wp_localize_script( 'ab-ng-app.js', 'BooklyL10n', array(
|
48 |
-
'today' => __( 'Today', 'bookly' ),
|
49 |
-
'yesterday' => __( 'Yesterday', 'bookly' ),
|
50 |
-
'last_7' => __( 'Last 7 Days', 'bookly' ),
|
51 |
-
'last_30' => __( 'Last 30 Days', 'bookly' ),
|
52 |
-
'this_month' => __( 'This Month', 'bookly' ),
|
53 |
-
'next_month' => __( 'Next Month', 'bookly' ),
|
54 |
-
'custom_range' => __( 'Custom Range', 'bookly' ),
|
55 |
-
'apply' => __( 'Apply', 'bookly' ),
|
56 |
-
'cancel' => __( 'Cancel', 'bookly' ),
|
57 |
-
'to' => __( 'To', 'bookly' ),
|
58 |
-
'from' => __( 'From', 'bookly' ),
|
59 |
-
'editAppointment' => __( 'Edit appointment', 'bookly' ),
|
60 |
-
'newAppointment' => __( 'New appointment', 'bookly' ),
|
61 |
-
'longMonths' => array_values( $wp_locale->month ),
|
62 |
-
'shortMonths' => array_values( $wp_locale->month_abbrev ),
|
63 |
-
'shortDays' => array_values( $wp_locale->weekday_abbrev ),
|
64 |
-
'dpDateFormat' => AB_DateTimeUtils::convertFormat( 'date', AB_DateTimeUtils::FORMAT_JQUERY_DATEPICKER ),
|
65 |
-
'mjsDateFormat' => AB_DateTimeUtils::convertFormat( 'date', AB_DateTimeUtils::FORMAT_MOMENT_JS ),
|
66 |
-
'startOfWeek' => (int) get_option( 'start_of_week' ),
|
67 |
-
'country' => get_option( 'ab_settings_phone_default_country' ),
|
68 |
-
'intlTelInput_utils' => plugins_url( 'intlTelInput.utils.js', AB_PATH . '/frontend/resources/js/intlTelInput.utils.js' ),
|
69 |
-
'please_select_at_least_one_row' => __( 'Please select at least one appointment.', 'bookly' ),
|
70 |
-
));
|
71 |
-
|
72 |
-
$this->render( 'index' );
|
73 |
-
}
|
74 |
-
|
75 |
-
/**
|
76 |
-
* Get list of appointments.
|
77 |
-
*/
|
78 |
-
public function executeGetAppointments()
|
79 |
-
{
|
80 |
-
$response = array(
|
81 |
-
'appointments' => array(),
|
82 |
-
'total' => 0,
|
83 |
-
'pages' => 0,
|
84 |
-
'active_page' => 0
|
85 |
-
);
|
86 |
-
|
87 |
-
$page = intval( $this->getParameter( 'page' ) );
|
88 |
-
$sort = in_array( $this->getParameter( 'sort' ), array( 'staff_name', 'service_title', 'start_date', 'price' ) )
|
89 |
-
? $this->getParameter( 'sort' ) : 'start_date';
|
90 |
-
$order = in_array( $this->getParameter( 'order' ), array( 'asc', 'desc' ) ) ? $this->getParameter( 'order' ) : 'asc';
|
91 |
-
|
92 |
-
$start_date = new DateTime( $this->getParameter( 'date_start' ) );
|
93 |
-
$start_date = $start_date->format( 'Y-m-d H:i:s' );
|
94 |
-
$end_date = new DateTime( $this->getParameter( 'date_end' ) );
|
95 |
-
$end_date = $end_date->modify( '+1 day' )->format( 'Y-m-d H:i:s' );
|
96 |
-
|
97 |
-
$items_per_page = 20;
|
98 |
-
$total = AB_Appointment::query()->whereBetween( 'start_date', $start_date, $end_date )->count();
|
99 |
-
$pages = ceil( $total / $items_per_page );
|
100 |
-
if ( $page < 1 || $page > $pages ) {
|
101 |
-
$page = 1;
|
102 |
-
}
|
103 |
-
|
104 |
-
if ( $total ) {
|
105 |
-
$query = AB_CustomerAppointment::query( 'ca' )
|
106 |
-
->select( 'ca.id,
|
107 |
-
ca.number_of_persons,
|
108 |
-
ca.coupon_discount,
|
109 |
-
ca.coupon_deduction,
|
110 |
-
ca.appointment_id,
|
111 |
-
a.start_date,
|
112 |
-
a.end_date,
|
113 |
-
a.staff_id,
|
114 |
-
st.full_name AS staff_name,
|
115 |
-
s.title AS service_title,
|
116 |
-
s.duration AS service_duration,
|
117 |
-
c.name AS customer_name,
|
118 |
-
ss.price' )
|
119 |
-
->leftJoin( 'AB_Appointment', 'a', 'a.id = ca.appointment_id' )
|
120 |
-
->leftJoin( 'AB_Service', 's', 's.id = a.service_id' )
|
121 |
-
->leftJoin( 'AB_Customer', 'c', 'c.id = ca.customer_id' )
|
122 |
-
->leftJoin( 'AB_Staff', 'st', 'st.id = a.staff_id' )
|
123 |
-
->leftJoin( 'AB_StaffService', 'ss', 'ss.staff_id = st.id AND ss.service_id = s.id')
|
124 |
-
->whereBetween( 'a.start_date', $start_date, $end_date )
|
125 |
-
->sortBy( $sort )
|
126 |
-
->order( $order );
|
127 |
-
|
128 |
-
// LIMIT.
|
129 |
-
$start = ( $page - 1 ) * $items_per_page;
|
130 |
-
$query->offset( $start )->limit( $items_per_page );
|
131 |
-
|
132 |
-
$rows = $query->fetchArray();
|
133 |
-
foreach ( $rows as &$row ) {
|
134 |
-
$row['price'] *= $row['number_of_persons'];
|
135 |
-
$row['price'] = AB_Utils::formatPrice( $row['price'] );
|
136 |
-
$row['start_date_f'] = AB_DateTimeUtils::formatDateTime( $row['start_date'] );
|
137 |
-
$row['service_duration'] = AB_DateTimeUtils::secondsToInterval( $row['service_duration'] );
|
138 |
-
}
|
139 |
-
|
140 |
-
// Populate response.
|
141 |
-
$response['appointments'] = $rows;
|
142 |
-
$response['total'] = $total;
|
143 |
-
$response['pages'] = $pages;
|
144 |
-
$response['active_page'] = $page;
|
145 |
-
}
|
146 |
-
|
147 |
-
wp_send_json_success( $response );
|
148 |
-
}
|
149 |
-
|
150 |
-
/**
|
151 |
-
* Delete customer appointment.
|
152 |
-
*/
|
153 |
-
public function executeDeleteCustomerAppointment()
|
154 |
-
{
|
155 |
-
if ( $this->hasParameter( 'ids' ) ) {
|
156 |
-
foreach ( $this->getParameter( 'ids' ) as $id ) {
|
157 |
-
$customer_appointment = new AB_CustomerAppointment();
|
158 |
-
$customer_appointment->load( $id );
|
159 |
-
|
160 |
-
$appointment = new AB_Appointment();
|
161 |
-
$appointment->load( $customer_appointment->get( 'appointment_id' ) );
|
162 |
-
|
163 |
-
$customer_appointment->delete();
|
164 |
-
|
165 |
-
// Delete appointment, if there aren't customers.
|
166 |
-
$count = AB_CustomerAppointment::query()->where( 'appointment_id', $customer_appointment->get( 'appointment_id' ) )->count();
|
167 |
-
|
168 |
-
if ( ! $count ) {
|
169 |
-
$appointment->delete();
|
170 |
-
} else {
|
171 |
-
$appointment->handleGoogleCalendar();
|
172 |
-
}
|
173 |
-
}
|
174 |
-
}
|
175 |
-
wp_send_json_success();
|
176 |
-
}
|
177 |
-
|
178 |
-
/**
|
179 |
-
* Export Appointment to CSV
|
180 |
-
*/
|
181 |
-
public function executeExportToCSV()
|
182 |
-
{
|
183 |
-
$start_date = new DateTime( $this->getParameter( 'date_start' ) );
|
184 |
-
$start_date = $start_date->format( 'Y-m-d H:i:s' );
|
185 |
-
$end_date = new DateTime( $this->getParameter( 'date_end' ) );
|
186 |
-
$end_date = $end_date->modify( '+1 day' )->format( 'Y-m-d H:i:s' );
|
187 |
-
$delimiter = $this->getParameter( 'delimiter', ',' );
|
188 |
-
|
189 |
-
header( 'Content-Type: text/csv; charset=utf-8' );
|
190 |
-
header( 'Content-Disposition: attachment; filename=Appointments.csv' );
|
191 |
-
|
192 |
-
$header = array(
|
193 |
-
__( 'Booking Time', 'bookly' ),
|
194 |
-
__( 'Staff Member', 'bookly' ),
|
195 |
-
__( 'Service', 'bookly' ),
|
196 |
-
__( 'Duration', 'bookly' ),
|
197 |
-
__( 'Price', 'bookly' ),
|
198 |
-
__( 'Customer', 'bookly' ),
|
199 |
-
__( 'Phone', 'bookly' ),
|
200 |
-
__( 'Email', 'bookly' ),
|
201 |
-
);
|
202 |
-
|
203 |
-
$custom_fields = array();
|
204 |
-
$fields_data = json_decode( get_option( 'ab_custom_fields' ) );
|
205 |
-
foreach ( $fields_data as $field_data ) {
|
206 |
-
$custom_fields[$field_data->id] = '';
|
207 |
-
$header[] = $field_data->label;
|
208 |
-
}
|
209 |
-
|
210 |
-
$output = fopen( 'php://output', 'w' );
|
211 |
-
fwrite( $output, pack( 'CCC', 0xef, 0xbb, 0xbf ) );
|
212 |
-
fputcsv( $output, $header, $delimiter );
|
213 |
-
$rows = AB_CustomerAppointment::query()
|
214 |
-
->select( 'r.id,
|
215 |
-
r.number_of_persons,
|
216 |
-
r.coupon_discount,
|
217 |
-
r.coupon_deduction,
|
218 |
-
st.full_name AS staff_name,
|
219 |
-
s.title AS service_title,
|
220 |
-
s.duration AS service_duration,
|
221 |
-
c.name AS customer_name,
|
222 |
-
c.phone AS customer_phone,
|
223 |
-
c.email AS customer_email,
|
224 |
-
ss.price,
|
225 |
-
a.start_date' )
|
226 |
-
->leftJoin( 'AB_Appointment', 'a', 'a.id = r.appointment_id' )
|
227 |
-
->leftJoin( 'AB_Service', 's', 's.id = a.service_id' )
|
228 |
-
->leftJoin( 'AB_Staff', 'st', 'st.id = a.staff_id' )
|
229 |
-
->leftJoin( 'AB_Customer', 'c', 'c.id = r.customer_id' )
|
230 |
-
->leftJoin( 'AB_StaffService', 'ss', 'ss.staff_id = st.id AND ss.service_id = s.id' )
|
231 |
-
->whereBetween( 'a.start_date', $start_date, $end_date )
|
232 |
-
->sortBy( 'a.start_date' )
|
233 |
-
->order( AB_Query::ORDER_DESCENDING )
|
234 |
-
->fetchArray();
|
235 |
-
|
236 |
-
foreach( $rows as $row ) {
|
237 |
-
$row['price'] *= $row['number_of_persons'];
|
238 |
-
|
239 |
-
$row_data = array(
|
240 |
-
$row['start_date'],
|
241 |
-
$row['staff_name'],
|
242 |
-
$row['service_title'],
|
243 |
-
AB_DateTimeUtils::secondsToInterval( $row['service_duration'] ),
|
244 |
-
AB_Utils::formatPrice( $row['price'] ),
|
245 |
-
$row['customer_name'],
|
246 |
-
$row['customer_phone'],
|
247 |
-
$row['customer_email'],
|
248 |
-
);
|
249 |
-
|
250 |
-
$customer_appointment = new AB_CustomerAppointment();
|
251 |
-
$customer_appointment->load( $row['id'] );
|
252 |
-
foreach ( $customer_appointment->getCustomFields() as $custom_field ) {
|
253 |
-
$custom_fields[$custom_field['id']] = $custom_field['value'];
|
254 |
-
}
|
255 |
-
|
256 |
-
fputcsv( $output, array_merge( $row_data, $custom_fields ), $delimiter );
|
257 |
-
|
258 |
-
$custom_fields = array_map( function () { return ''; }, $custom_fields );
|
259 |
-
}
|
260 |
-
fclose( $output );
|
261 |
-
|
262 |
-
exit();
|
263 |
-
}
|
264 |
-
|
265 |
-
/**
|
266 |
-
* Override parent method to add 'wp_ajax_ab_' prefix
|
267 |
-
* so current 'execute*' methods look nicer.
|
268 |
-
*
|
269 |
-
* @param string $prefix
|
270 |
-
*/
|
271 |
-
protected function registerWpActions( $prefix = '' )
|
272 |
-
{
|
273 |
-
parent::registerWpActions( 'wp_ajax_ab_' );
|
274 |
-
}
|
275 |
-
|
276 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/modules/appointments/Controller.php
ADDED
@@ -0,0 +1,246 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace BooklyLite\Backend\Modules\Appointments;
|
3 |
+
|
4 |
+
use BooklyLite\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Controller
|
8 |
+
* @package BooklyLite\Backend\Modules\Appointments
|
9 |
+
*/
|
10 |
+
class Controller extends Lib\Base\Controller
|
11 |
+
{
|
12 |
+
public function index()
|
13 |
+
{
|
14 |
+
/** @var \WP_Locale $wp_locale */
|
15 |
+
global $wp_locale;
|
16 |
+
|
17 |
+
$this->enqueueStyles( array(
|
18 |
+
'frontend' => array( 'css/ladda.min.css', ),
|
19 |
+
'backend' => array(
|
20 |
+
'bootstrap/css/bootstrap-theme.min.css',
|
21 |
+
'css/daterangepicker.css',
|
22 |
+
),
|
23 |
+
) );
|
24 |
+
|
25 |
+
$this->enqueueScripts( array(
|
26 |
+
'backend' => array(
|
27 |
+
'bootstrap/js/bootstrap.min.js' => array( 'jquery' ),
|
28 |
+
'js/datatables.min.js' => array( 'jquery' ),
|
29 |
+
'js/moment.min.js',
|
30 |
+
'js/daterangepicker.js' => array( 'jquery' ),
|
31 |
+
'js/alert.js' => array( 'jquery' ),
|
32 |
+
),
|
33 |
+
'frontend' => array(
|
34 |
+
'js/spin.min.js' => array( 'jquery' ),
|
35 |
+
'js/ladda.min.js' => array( 'jquery' ),
|
36 |
+
),
|
37 |
+
'module' => array( 'js/appointments.js' => array( 'ab-datatables.min.js' ), ),
|
38 |
+
) );
|
39 |
+
|
40 |
+
// Custom fields without captcha field.
|
41 |
+
$custom_fields = array_filter( json_decode( get_option( 'ab_custom_fields' ) ), function( $field ) {
|
42 |
+
return ! in_array( $field->type, array( 'captcha', 'text-content' ) );
|
43 |
+
} );
|
44 |
+
|
45 |
+
wp_localize_script( 'ab-appointments.js', 'BooklyL10n', array(
|
46 |
+
'tomorrow' => __( 'Tomorrow', 'bookly' ),
|
47 |
+
'today' => __( 'Today', 'bookly' ),
|
48 |
+
'yesterday' => __( 'Yesterday', 'bookly' ),
|
49 |
+
'last_7' => __( 'Last 7 Days', 'bookly' ),
|
50 |
+
'last_30' => __( 'Last 30 Days', 'bookly' ),
|
51 |
+
'this_month' => __( 'This Month', 'bookly' ),
|
52 |
+
'next_month' => __( 'Next Month', 'bookly' ),
|
53 |
+
'custom_range' => __( 'Custom Range', 'bookly' ),
|
54 |
+
'apply' => __( 'Apply', 'bookly' ),
|
55 |
+
'cancel' => __( 'Cancel', 'bookly' ),
|
56 |
+
'to' => __( 'To', 'bookly' ),
|
57 |
+
'from' => __( 'From', 'bookly' ),
|
58 |
+
'calendar' => array(
|
59 |
+
'longMonths' => array_values( $wp_locale->month ),
|
60 |
+
'shortMonths' => array_values( $wp_locale->month_abbrev ),
|
61 |
+
'longDays' => array_values( $wp_locale->weekday ),
|
62 |
+
'shortDays' => array_values( $wp_locale->weekday_abbrev ),
|
63 |
+
),
|
64 |
+
'mjsDateFormat' => Lib\Utils\DateTime::convertFormat( 'date', Lib\Utils\DateTime::FORMAT_MOMENT_JS ),
|
65 |
+
'startOfWeek' => (int) get_option( 'start_of_week' ),
|
66 |
+
'are_you_sure' => __( 'Are you sure?', 'bookly' ),
|
67 |
+
'zeroRecords' => __( 'No appointments for selected period.', 'bookly' ),
|
68 |
+
'processing' => __( 'Processing...', 'bookly' ),
|
69 |
+
'edit' => __( 'Edit', 'bookly' ),
|
70 |
+
'cf_columns' => array_map( function ( $custom_field ) { return $custom_field->id; }, $custom_fields ),
|
71 |
+
'limitations' => __( '<b class="h4">This function is disabled in the Lite version of Bookly.</b><br><br>If you find the plugin useful for your business please consider buying a licence for the full version.<br>It costs just $59 and for this money you will get many useful functions, lifetime free updates and excellent support!<br>More information can be found here', 'bookly' ) . ': <a href="http://booking-wp-plugin.com" target="_blank" class="alert-link">http://booking-wp-plugin.com</a>',
|
72 |
+
) );
|
73 |
+
|
74 |
+
// Filters data
|
75 |
+
$staff_members = Lib\Entities\Staff::query( 's' )->select( 's.id, s.full_name' )->fetchArray();
|
76 |
+
$customers = Lib\Entities\Customer::query( 'c' )->select( 'c.id, c.name' )->fetchArray();
|
77 |
+
$services = Lib\Entities\Service::query( 's' )->select( 's.id, s.title' )->where( 'type', Lib\Entities\Service::TYPE_SIMPLE )->fetchArray();
|
78 |
+
|
79 |
+
$this->render( 'index', compact( 'custom_fields', 'staff_members', 'customers', 'services' ) );
|
80 |
+
}
|
81 |
+
|
82 |
+
/**
|
83 |
+
* Get list of appointments.
|
84 |
+
*/
|
85 |
+
public function executeGetAppointments()
|
86 |
+
{
|
87 |
+
$columns = $this->getParameter( 'columns' );
|
88 |
+
$order = $this->getParameter( 'order' );
|
89 |
+
$filter = $this->getParameter( 'filter' );
|
90 |
+
|
91 |
+
$query = Lib\Entities\CustomerAppointment::query( 'ca' )
|
92 |
+
->select( 'a.id,
|
93 |
+
ca.payment_id,
|
94 |
+
ca.status,
|
95 |
+
ca.id AS ca_id,
|
96 |
+
ca.extras,
|
97 |
+
a.start_date,
|
98 |
+
a.extras_duration,
|
99 |
+
c.name AS customer_name,
|
100 |
+
c.phone AS customer_phone,
|
101 |
+
c.email AS customer_email,
|
102 |
+
s.title AS service_title,
|
103 |
+
s.duration AS service_duration,
|
104 |
+
st.full_name AS staff_name,
|
105 |
+
p.paid AS payment,
|
106 |
+
p.total AS payment_total,
|
107 |
+
p.type AS payment_type,
|
108 |
+
p.status AS payment_status' )
|
109 |
+
->leftJoin( 'Appointment', 'a', 'a.id = ca.appointment_id' )
|
110 |
+
->leftJoin( 'Service', 's', 's.id = a.service_id' )
|
111 |
+
->leftJoin( 'Customer', 'c', 'c.id = ca.customer_id' )
|
112 |
+
->leftJoin( 'Payment', 'p', 'p.id = ca.payment_id' )
|
113 |
+
->leftJoin( 'Staff', 'st', 'st.id = a.staff_id' )
|
114 |
+
->leftJoin( 'StaffService', 'ss', 'ss.staff_id = st.id AND ss.service_id = s.id' );
|
115 |
+
|
116 |
+
$total = $query->count();
|
117 |
+
|
118 |
+
if ( $filter['id'] != '' ) {
|
119 |
+
$query->where( 'a.id', $filter['id'] );
|
120 |
+
}
|
121 |
+
|
122 |
+
list ( $start, $end ) = explode( ' - ', $filter['date'], 2 );
|
123 |
+
$end = date( 'Y-m-d', strtotime( '+1 day', strtotime( $end ) ) );
|
124 |
+
$query->whereBetween( 'a.start_date', $start, $end );
|
125 |
+
|
126 |
+
if ( $filter['staff'] != -1 ) {
|
127 |
+
$query->where( 'a.staff_id', $filter['staff'] );
|
128 |
+
}
|
129 |
+
|
130 |
+
if ( $filter['customer'] != -1 ) {
|
131 |
+
$query->where( 'ca.customer_id', $filter['customer'] );
|
132 |
+
}
|
133 |
+
|
134 |
+
if ( $filter['service'] != -1 ) {
|
135 |
+
$query->where( 'a.service_id', $filter['service'] );
|
136 |
+
}
|
137 |
+
|
138 |
+
if ( $filter['status'] != -1 ) {
|
139 |
+
$query->where( 'ca.status', $filter['status'] );
|
140 |
+
}
|
141 |
+
|
142 |
+
foreach ( $order as $sort_by ) {
|
143 |
+
$query->sortBy( str_replace( '.', '_', $columns[ $sort_by['column'] ]['data'] ) )
|
144 |
+
->order( $sort_by['dir'] == 'desc' ? Lib\Query::ORDER_DESCENDING : Lib\Query::ORDER_ASCENDING );
|
145 |
+
}
|
146 |
+
|
147 |
+
$custom_fields = array();
|
148 |
+
$fields_data = array_filter( json_decode( get_option( 'ab_custom_fields' ) ), function( $field ) {
|
149 |
+
return ! in_array( $field->type, array( 'captcha', 'text-content' ) );
|
150 |
+
} );
|
151 |
+
foreach ( $fields_data as $field_data ) {
|
152 |
+
$custom_fields[ $field_data->id ] = '';
|
153 |
+
}
|
154 |
+
|
155 |
+
$data = array();
|
156 |
+
foreach ( $query->fetchArray() as $row ) {
|
157 |
+
// Service duration.
|
158 |
+
$service_duration = Lib\Utils\DateTime::secondsToInterval( $row['service_duration'] );
|
159 |
+
if ( $row['extras_duration'] > 0 ) {
|
160 |
+
$service_duration .= ' + ' . Lib\Utils\DateTime::secondsToInterval( $row['extras_duration'] );
|
161 |
+
}
|
162 |
+
// Appointment status.
|
163 |
+
$row['status'] = Lib\Entities\CustomerAppointment::statusToString( $row['status'] );
|
164 |
+
|
165 |
+
// Payment title.
|
166 |
+
$payment_title = '';
|
167 |
+
if ( $row['payment'] !== null ) {
|
168 |
+
$payment_title = Lib\Utils\Common::formatPrice( $row['payment'] );
|
169 |
+
if ( $row['payment'] != $row['payment_total'] ) {
|
170 |
+
$payment_title = sprintf( __( '%s of %s', 'bookly' ), $payment_title, Lib\Utils\Common::formatPrice( $row['payment_total'] ) );
|
171 |
+
}
|
172 |
+
$payment_title .= sprintf(
|
173 |
+
' %s <span%s>%s</span>',
|
174 |
+
Lib\Entities\Payment::typeToString( $row['payment_type'] ),
|
175 |
+
$row['payment_status'] == Lib\Entities\Payment::STATUS_PENDING ? ' class="text-danger"' : '',
|
176 |
+
Lib\Entities\Payment::statusToString( $row['payment_status'] )
|
177 |
+
);
|
178 |
+
}
|
179 |
+
// Custom fields
|
180 |
+
$customer_appointment = new Lib\Entities\CustomerAppointment();
|
181 |
+
$customer_appointment->load( $row['ca_id'] );
|
182 |
+
foreach ( $customer_appointment->getCustomFields() as $custom_field ) {
|
183 |
+
$custom_fields[ $custom_field['id'] ] = $custom_field['value'];
|
184 |
+
}
|
185 |
+
|
186 |
+
// Add extras
|
187 |
+
$row = apply_filters( 'bookly_appointment_data', $row, false );
|
188 |
+
|
189 |
+
$data[] = array(
|
190 |
+
'id' => $row['id'],
|
191 |
+
'start_date' => Lib\Utils\DateTime::formatDateTime( $row['start_date'] ),
|
192 |
+
'staff' => array(
|
193 |
+
'name' => $row['staff_name'],
|
194 |
+
),
|
195 |
+
'customer' => array(
|
196 |
+
'name' => $row['customer_name'],
|
197 |
+
'phone' => $row['customer_phone'],
|
198 |
+
'email' => $row['customer_email'],
|
199 |
+
),
|
200 |
+
'service' => array(
|
201 |
+
'title' => $row['service_title'],
|
202 |
+
'duration' => $service_duration,
|
203 |
+
'extras' => is_array( $row['extras'] ) ? $row['extras'] : array(),
|
204 |
+
),
|
205 |
+
'status' => $row['status'],
|
206 |
+
'payment' => $payment_title,
|
207 |
+
'custom_fields' => $custom_fields,
|
208 |
+
'ca_id' => $row['ca_id'],
|
209 |
+
'payment_id' => $row['payment_id'],
|
210 |
+
);
|
211 |
+
|
212 |
+
$custom_fields = array_map( function () { return ''; }, $custom_fields );
|
213 |
+
}
|
214 |
+
|
215 |
+
wp_send_json( array(
|
216 |
+
'draw' => ( int ) $this->getParameter( 'draw' ),
|
217 |
+
'recordsTotal' => $total,
|
218 |
+
'recordsFiltered' => count( $data ),
|
219 |
+
'data' => $data,
|
220 |
+
) );
|
221 |
+
}
|
222 |
+
|
223 |
+
/**
|
224 |
+
* Delete customer appointments.
|
225 |
+
*/
|
226 |
+
public function executeDeleteCustomerAppointments()
|
227 |
+
{
|
228 |
+
/** @var Lib\Entities\CustomerAppointment $ca */
|
229 |
+
foreach ( Lib\Entities\CustomerAppointment::query()->whereIn( 'id', $this->getParameter( 'data', array() ) )->find() as $ca ) {
|
230 |
+
$ca->deleteCascade();
|
231 |
+
}
|
232 |
+
wp_send_json_success();
|
233 |
+
}
|
234 |
+
|
235 |
+
/**
|
236 |
+
* Override parent method to add 'wp_ajax_ab_' prefix
|
237 |
+
* so current 'execute*' methods look nicer.
|
238 |
+
*
|
239 |
+
* @param string $prefix
|
240 |
+
*/
|
241 |
+
protected function registerWpActions( $prefix = '' )
|
242 |
+
{
|
243 |
+
parent::registerWpActions( 'wp_ajax_ab_' );
|
244 |
+
}
|
245 |
+
|
246 |
+
}
|
backend/modules/appointments/resources/js/appointments.js
ADDED
@@ -0,0 +1,271 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
jQuery(function($) {
|
2 |
+
|
3 |
+
var
|
4 |
+
$appointments_list = $('#bookly-appointments-list'),
|
5 |
+
$check_all_button = $('#bookly-check-all'),
|
6 |
+
$id_filter = $('#bookly-filter-id'),
|
7 |
+
$date_filter = $('#bookly-filter-date'),
|
8 |
+
$staff_filter = $('#bookly-filter-staff'),
|
9 |
+
$customer_filter = $('#bookly-filter-customer'),
|
10 |
+
$service_filter = $('#bookly-filter-service'),
|
11 |
+
$status_filter = $('#bookly-filter-status'),
|
12 |
+
$add_button = $('#bookly-add'),
|
13 |
+
$print_dialog = $('#bookly-print-dialog'),
|
14 |
+
$print_button = $('#bookly-print'),
|
15 |
+
$export_dialog = $('#bookly-export-dialog'),
|
16 |
+
$export_button = $('#bookly-export'),
|
17 |
+
$delete_button = $('#bookly-delete')
|
18 |
+
;
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Init DataTables.
|
22 |
+
*/
|
23 |
+
var columns = [
|
24 |
+
{ data: 'id', responsivePriority: 2 },
|
25 |
+
{ data: 'start_date', responsivePriority: 2 },
|
26 |
+
{ data: 'staff.name', responsivePriority: 2 },
|
27 |
+
{ data: 'customer.name', responsivePriority: 2 },
|
28 |
+
{ data: 'customer.phone', responsivePriority: 3 },
|
29 |
+
{ data: 'customer.email', responsivePriority: 3 },
|
30 |
+
{
|
31 |
+
data: 'service.title',
|
32 |
+
responsivePriority: 2,
|
33 |
+
render: function ( data, type, row, meta ) {
|
34 |
+
if(!$.isEmptyObject(row.service.extras)) {
|
35 |
+
var extras = '<ul class="bookly-list list-dots">';
|
36 |
+
$.each(row.service.extras, function (key, item) {
|
37 |
+
extras += '<li><nobr>' + item.title + '</nobr></li>';
|
38 |
+
});
|
39 |
+
extras += '</ul>';
|
40 |
+
return data + extras;
|
41 |
+
}
|
42 |
+
else {
|
43 |
+
return data;
|
44 |
+
}
|
45 |
+
}
|
46 |
+
},
|
47 |
+
{ data: 'service.duration', responsivePriority: 2 },
|
48 |
+
{ data: 'status', responsivePriority: 2 },
|
49 |
+
{
|
50 |
+
data: 'payment',
|
51 |
+
responsivePriority: 2,
|
52 |
+
render: function ( data, type, row, meta ) {
|
53 |
+
return '<a href="#bookly-payment-details-modal" data-toggle="modal" data-payment_id="' + row.payment_id + '">' + data + '</a>';
|
54 |
+
}
|
55 |
+
}
|
56 |
+
];
|
57 |
+
$.each(BooklyL10n.cf_columns, function (i, cf_id) {
|
58 |
+
columns.push({
|
59 |
+
data: 'custom_fields.' + cf_id,
|
60 |
+
responsivePriority: 4,
|
61 |
+
orderable: false
|
62 |
+
});
|
63 |
+
});
|
64 |
+
var dt = $appointments_list.DataTable({
|
65 |
+
order: [[ 1, 'desc' ]],
|
66 |
+
info: false,
|
67 |
+
paging: false,
|
68 |
+
searching: false,
|
69 |
+
processing: true,
|
70 |
+
responsive: true,
|
71 |
+
serverSide: true,
|
72 |
+
ajax: {
|
73 |
+
url : ajaxurl,
|
74 |
+
type: 'POST',
|
75 |
+
data: function (d) {
|
76 |
+
return $.extend({action: 'ab_get_appointments'}, {
|
77 |
+
filter: {
|
78 |
+
id : $id_filter.val(),
|
79 |
+
date : $date_filter.data('date'),
|
80 |
+
staff : $staff_filter.val(),
|
81 |
+
customer: $customer_filter.val(),
|
82 |
+
service : $service_filter.val(),
|
83 |
+
status : $status_filter.val()
|
84 |
+
}
|
85 |
+
}, d);
|
86 |
+
}
|
87 |
+
},
|
88 |
+
columns: columns.concat([
|
89 |
+
{
|
90 |
+
responsivePriority: 1,
|
91 |
+
orderable: false,
|
92 |
+
render: function ( data, type, row, meta ) {
|
93 |
+
return '<button type="button" class="btn btn-default"><i class="glyphicon glyphicon-edit"></i> ' + BooklyL10n.edit + '</a>';
|
94 |
+
}
|
95 |
+
},
|
96 |
+
{
|
97 |
+
responsivePriority: 1,
|
98 |
+
orderable: false,
|
99 |
+
render: function ( data, type, row, meta ) {
|
100 |
+
return '<input type="checkbox" value="' + row.ca_id + '" />';
|
101 |
+
}
|
102 |
+
}
|
103 |
+
]),
|
104 |
+
language: {
|
105 |
+
zeroRecords: BooklyL10n.zeroRecords,
|
106 |
+
processing: BooklyL10n.processing
|
107 |
+
}
|
108 |
+
});
|
109 |
+
|
110 |
+
/**
|
111 |
+
* Add appointment.
|
112 |
+
*/
|
113 |
+
$add_button.on('click', function () {
|
114 |
+
showAppointmentDialog(
|
115 |
+
null,
|
116 |
+
null,
|
117 |
+
moment(),
|
118 |
+
function(event) {
|
119 |
+
dt.ajax.reload();
|
120 |
+
}
|
121 |
+
)
|
122 |
+
});
|
123 |
+
|
124 |
+
/**
|
125 |
+
* Edit appointment.
|
126 |
+
*/
|
127 |
+
$appointments_list.on('click', 'button', function (e) {
|
128 |
+
e.preventDefault();
|
129 |
+
var data = dt.row($(this).closest('td')).data();
|
130 |
+
showAppointmentDialog(
|
131 |
+
data.id,
|
132 |
+
null,
|
133 |
+
null,
|
134 |
+
function(event) {
|
135 |
+
dt.ajax.reload();
|
136 |
+
}
|
137 |
+
)
|
138 |
+
});
|
139 |
+
|
140 |
+
/**
|
141 |
+
* Export.
|
142 |
+
*/
|
143 |
+
$export_button.on('click', function () {
|
144 |
+
var columns = [];
|
145 |
+
$export_dialog.find('input:checked').each(function () {
|
146 |
+
columns.push(this.value);
|
147 |
+
});
|
148 |
+
var config = {
|
149 |
+
autoPrint: false,
|
150 |
+
fieldSeparator: $('#bookly-csv-delimiter').val(),
|
151 |
+
exportOptions: {
|
152 |
+
columns: columns
|
153 |
+
},
|
154 |
+
filename: 'Appointments'
|
155 |
+
};
|
156 |
+
$.fn.dataTable.ext.buttons.csvHtml5.action(null, dt, null, $.extend({}, $.fn.dataTable.ext.buttons.csvHtml5, config));
|
157 |
+
});
|
158 |
+
|
159 |
+
$('.bookly-limitation').on('click', function () {
|
160 |
+
booklyAlert({error: [BooklyL10n.limitations]});
|
161 |
+
});
|
162 |
+
|
163 |
+
/**
|
164 |
+
* Select all appointments.
|
165 |
+
*/
|
166 |
+
$check_all_button.on('change', function () {
|
167 |
+
$appointments_list.find('tbody input:checkbox').prop('checked', this.checked);
|
168 |
+
});
|
169 |
+
|
170 |
+
/**
|
171 |
+
* On appointment select.
|
172 |
+
*/
|
173 |
+
$appointments_list.on('change', 'tbody input:checkbox', function () {
|
174 |
+
$check_all_button.prop('checked', $appointments_list.find('tbody input:not(:checked)').length == 0);
|
175 |
+
});
|
176 |
+
|
177 |
+
/**
|
178 |
+
* Delete appointments.
|
179 |
+
*/
|
180 |
+
$delete_button.on('click', function () {
|
181 |
+
if (confirm(BooklyL10n.are_you_sure)) {
|
182 |
+
var ladda = Ladda.create(this);
|
183 |
+
ladda.start();
|
184 |
+
|
185 |
+
var data = [];
|
186 |
+
var $checkboxes = $appointments_list.find('tbody input:checked');
|
187 |
+
$checkboxes.each(function () {
|
188 |
+
data.push(this.value);
|
189 |
+
});
|
190 |
+
|
191 |
+
$.ajax({
|
192 |
+
url : ajaxurl,
|
193 |
+
type : 'POST',
|
194 |
+
data : {
|
195 |
+
action : 'ab_delete_customer_appointments',
|
196 |
+
data : data
|
197 |
+
},
|
198 |
+
dataType : 'json',
|
199 |
+
success : function(response) {
|
200 |
+
ladda.stop();
|
201 |
+
if (response.success) {
|
202 |
+
dt.rows($checkboxes.closest('td')).remove().draw();
|
203 |
+
} else {
|
204 |
+
alert(response.data.message);
|
205 |
+
}
|
206 |
+
}
|
207 |
+
});
|
208 |
+
}
|
209 |
+
});
|
210 |
+
|
211 |
+
/**
|
212 |
+
* Init date range picker.
|
213 |
+
*/
|
214 |
+
moment.locale('en', {
|
215 |
+
months : BooklyL10n.calendar.longMonths,
|
216 |
+
monthsShort : BooklyL10n.calendar.shortMonths,
|
217 |
+
weekdays : BooklyL10n.calendar.longDays,
|
218 |
+
weekdaysShort: BooklyL10n.calendar.shortDays,
|
219 |
+
weekdaysMin : BooklyL10n.calendar.shortDays
|
220 |
+
});
|
221 |
+
|
222 |
+
var picker_ranges = {};
|
223 |
+
picker_ranges[BooklyL10n.yesterday] = [moment().subtract(1, 'days'), moment().subtract(1, 'days')];
|
224 |
+
picker_ranges[BooklyL10n.today] = [moment(), moment()];
|
225 |
+
picker_ranges[BooklyL10n.tomorrow] = [moment().add(1, 'days'), moment().add(1, 'days')];
|
226 |
+
picker_ranges[BooklyL10n.last_7] = [moment().subtract(7, 'days'), moment()];
|
227 |
+
picker_ranges[BooklyL10n.last_30] = [moment().subtract(30, 'days'), moment()];
|
228 |
+
picker_ranges[BooklyL10n.this_month] = [moment().startOf('month'), moment().endOf('month')];
|
229 |
+
picker_ranges[BooklyL10n.next_month] = [moment().add(1, 'month').startOf('month'), moment().add(1, 'month').endOf('month')];
|
230 |
+
|
231 |
+
$date_filter.daterangepicker(
|
232 |
+
{
|
233 |
+
parentEl: $date_filter.parent(),
|
234 |
+
startDate: moment().startOf('month'),
|
235 |
+
endDate: moment().endOf('month'),
|
236 |
+
ranges: picker_ranges,
|
237 |
+
locale: {
|
238 |
+
applyLabel : BooklyL10n.apply,
|
239 |
+
cancelLabel: BooklyL10n.cancel,
|
240 |
+
fromLabel : BooklyL10n.from,
|
241 |
+
toLabel : BooklyL10n.to,
|
242 |
+
customRangeLabel: BooklyL10n.custom_range,
|
243 |
+
daysOfWeek : BooklyL10n.calendar.shortDays,
|
244 |
+
monthNames : BooklyL10n.calendar.longMonths,
|
245 |
+
firstDay : parseInt(BooklyL10n.startOfWeek),
|
246 |
+
format : BooklyL10n.mjsDateFormat
|
247 |
+
}
|
248 |
+
},
|
249 |
+
function(start, end) {
|
250 |
+
var format = 'YYYY-MM-DD';
|
251 |
+
$date_filter
|
252 |
+
.data('date', start.format(format) + ' - ' + end.format(format))
|
253 |
+
.find('span')
|
254 |
+
.html(start.format(BooklyL10n.mjsDateFormat) + ' - ' + end.format(BooklyL10n.mjsDateFormat));
|
255 |
+
}
|
256 |
+
);
|
257 |
+
|
258 |
+
/**
|
259 |
+
* On filters change.
|
260 |
+
*/
|
261 |
+
$('.bookly-js-chosen-select').chosen({
|
262 |
+
allow_single_deselect: true,
|
263 |
+
disable_search_threshold: 10
|
264 |
+
});
|
265 |
+
$id_filter.on('keyup', function () { dt.ajax.reload(); });
|
266 |
+
$date_filter.on('apply.daterangepicker', function () { dt.ajax.reload(); });
|
267 |
+
$staff_filter.on('change', function () { dt.ajax.reload(); });
|
268 |
+
$customer_filter.on('change', function () { dt.ajax.reload(); });
|
269 |
+
$service_filter.on('change', function () { dt.ajax.reload(); });
|
270 |
+
$status_filter.on('change', function () { dt.ajax.reload(); });
|
271 |
+
});
|
backend/modules/appointments/resources/js/ng-app.js
DELETED
@@ -1,208 +0,0 @@
|
|
1 |
-
;(function() {
|
2 |
-
|
3 |
-
var module = angular.module('appointments', ['ui.utils', 'ui.date', 'ngSanitize']);
|
4 |
-
|
5 |
-
module.factory('dataSource', function($q, $rootScope) {
|
6 |
-
var ds = {
|
7 |
-
appointments : [],
|
8 |
-
total : 0,
|
9 |
-
pages : [],
|
10 |
-
loadData : function(params) {
|
11 |
-
var deferred = $q.defer();
|
12 |
-
jQuery.ajax({
|
13 |
-
url : ajaxurl,
|
14 |
-
type : 'POST',
|
15 |
-
data : jQuery.extend({ action : 'ab_get_appointments' }, params),
|
16 |
-
dataType : 'json',
|
17 |
-
success : function(response) {
|
18 |
-
if (response.success) {
|
19 |
-
ds.appointments = response.data.appointments;
|
20 |
-
ds.total = response.data.total;
|
21 |
-
ds.pages = [];
|
22 |
-
for (var i = 0; i < response.data.pages; ++ i) {
|
23 |
-
ds.pages.push({
|
24 |
-
number : i + 1,
|
25 |
-
active : response.data.active_page == i + 1
|
26 |
-
});
|
27 |
-
}
|
28 |
-
}
|
29 |
-
$rootScope.$apply(deferred.resolve);
|
30 |
-
},
|
31 |
-
error : function() {
|
32 |
-
ds.appointments = [];
|
33 |
-
ds.total = 0;
|
34 |
-
$rootScope.$apply(deferred.resolve);
|
35 |
-
}
|
36 |
-
});
|
37 |
-
|
38 |
-
return deferred.promise;
|
39 |
-
}
|
40 |
-
};
|
41 |
-
|
42 |
-
return ds;
|
43 |
-
});
|
44 |
-
|
45 |
-
module.controller('appointmentsCtrl', function($scope, dataSource) {
|
46 |
-
// Set up initial data.
|
47 |
-
var params = {
|
48 |
-
page : 1,
|
49 |
-
sort : 'start_date',
|
50 |
-
order : 'desc',
|
51 |
-
date_start : '',
|
52 |
-
date_end : ''
|
53 |
-
};
|
54 |
-
$scope.loading = true;
|
55 |
-
$scope.css_class = {
|
56 |
-
staff_name : '',
|
57 |
-
customer_name : '',
|
58 |
-
service_title : '',
|
59 |
-
start_date : 'desc',
|
60 |
-
service_duration: '',
|
61 |
-
price : ''
|
62 |
-
};
|
63 |
-
|
64 |
-
var format = 'YYYY-MM-DD';
|
65 |
-
$scope.date_start = moment().startOf('month').format(format);
|
66 |
-
$scope.date_end = moment().endOf('month').format(format);
|
67 |
-
|
68 |
-
// Set up data source (data will be loaded in reload function).
|
69 |
-
$scope.dataSource = dataSource;
|
70 |
-
|
71 |
-
$scope.reload = function( opt ) {
|
72 |
-
$scope.loading = true;
|
73 |
-
if (opt !== undefined) {
|
74 |
-
if (opt.sort !== undefined) {
|
75 |
-
if (params.sort === opt.sort) {
|
76 |
-
// Toggle order when sorting by the same field.
|
77 |
-
params.order = params.order === 'asc' ? 'desc' : 'asc';
|
78 |
-
} else {
|
79 |
-
params.order = 'asc';
|
80 |
-
}
|
81 |
-
$scope.css_class = {
|
82 |
-
staff_name : '',
|
83 |
-
customer_name : '',
|
84 |
-
service_title : '',
|
85 |
-
start_date : '',
|
86 |
-
service_duration: '',
|
87 |
-
price : ''
|
88 |
-
};
|
89 |
-
$scope.css_class[opt.sort] = params.order;
|
90 |
-
}
|
91 |
-
jQuery.extend(params, opt);
|
92 |
-
}
|
93 |
-
params.date_start = $scope.date_start;
|
94 |
-
params.date_end = $scope.date_end;
|
95 |
-
dataSource.loadData(params).then(function() {
|
96 |
-
$scope.loading = false;
|
97 |
-
});
|
98 |
-
};
|
99 |
-
|
100 |
-
$scope.reload();
|
101 |
-
|
102 |
-
/**
|
103 |
-
* New appointment.
|
104 |
-
*
|
105 |
-
* @param appointment
|
106 |
-
*/
|
107 |
-
$scope.newAppointment = function() {
|
108 |
-
showAppointmentDialog(
|
109 |
-
null,
|
110 |
-
null,
|
111 |
-
moment(),
|
112 |
-
null,
|
113 |
-
function(event) {
|
114 |
-
$scope.$apply(function($scope) {
|
115 |
-
$scope.reload();
|
116 |
-
});
|
117 |
-
}
|
118 |
-
)
|
119 |
-
};
|
120 |
-
|
121 |
-
/**
|
122 |
-
* Edit appointment.
|
123 |
-
*
|
124 |
-
* @param appointment
|
125 |
-
*/
|
126 |
-
$scope.editAppointment = function(appointment) {
|
127 |
-
showAppointmentDialog(
|
128 |
-
appointment.appointment_id,
|
129 |
-
appointment.staff_id,
|
130 |
-
moment(appointment.start_date),
|
131 |
-
moment(appointment.end_date),
|
132 |
-
function(event) {
|
133 |
-
$scope.$apply(function($scope) {
|
134 |
-
$scope.reload();
|
135 |
-
});
|
136 |
-
}
|
137 |
-
)
|
138 |
-
};
|
139 |
-
|
140 |
-
/**
|
141 |
-
* Delete customer appointments.
|
142 |
-
*/
|
143 |
-
$scope.deleteAppointments = function() {
|
144 |
-
var ids = [];
|
145 |
-
jQuery('table input[type=checkbox]:checked').each(function() {
|
146 |
-
ids.push(jQuery(this).data('appointment_id'));
|
147 |
-
});
|
148 |
-
if( ids.length ) {
|
149 |
-
$scope.loading = true;
|
150 |
-
jQuery.ajax({
|
151 |
-
url: ajaxurl,
|
152 |
-
type: 'POST',
|
153 |
-
data: {
|
154 |
-
action: 'ab_delete_customer_appointment',
|
155 |
-
ids: ids
|
156 |
-
},
|
157 |
-
dataType: 'json',
|
158 |
-
success: function (response) {
|
159 |
-
$scope.$apply(function ($scope) {
|
160 |
-
$scope.reload();
|
161 |
-
});
|
162 |
-
}
|
163 |
-
});
|
164 |
-
} else{
|
165 |
-
alert(BooklyL10n.please_select_at_least_one_row);
|
166 |
-
}
|
167 |
-
};
|
168 |
-
|
169 |
-
// Init date range picker.
|
170 |
-
var picker_ranges = {};
|
171 |
-
picker_ranges[BooklyL10n.today] = [moment(), moment()];
|
172 |
-
picker_ranges[BooklyL10n.yesterday] = [moment().subtract(1, 'days'), moment().subtract(1, 'days')];
|
173 |
-
picker_ranges[BooklyL10n.last_7] = [moment().subtract(7, 'days'), moment()];
|
174 |
-
picker_ranges[BooklyL10n.last_30] = [moment().subtract(30, 'days'), moment()];
|
175 |
-
picker_ranges[BooklyL10n.this_month] = [moment().startOf('month'), moment().endOf('month')];
|
176 |
-
picker_ranges[BooklyL10n.next_month] = [moment().add(1, 'month').startOf('month'), moment().add(1, 'month').endOf('month')];
|
177 |
-
|
178 |
-
jQuery('#reportrange').daterangepicker(
|
179 |
-
{
|
180 |
-
startDate: moment().startOf('month'),
|
181 |
-
endDate: moment().endOf('month'),
|
182 |
-
ranges: picker_ranges,
|
183 |
-
locale: {
|
184 |
-
applyLabel : BooklyL10n.apply,
|
185 |
-
cancelLabel: BooklyL10n.cancel,
|
186 |
-
fromLabel : BooklyL10n.from,
|
187 |
-
toLabel : BooklyL10n.to,
|
188 |
-
customRangeLabel: BooklyL10n.custom_range,
|
189 |
-
daysOfWeek : BooklyL10n.shortDays,
|
190 |
-
monthNames : BooklyL10n.longMonths,
|
191 |
-
firstDay : parseInt(BooklyL10n.startOfWeek),
|
192 |
-
format : BooklyL10n.mjsDateFormat
|
193 |
-
}
|
194 |
-
},
|
195 |
-
function(start, end) {
|
196 |
-
jQuery('#reportrange span').html(start.format(BooklyL10n.mjsDateFormat) + ' - ' + end.format(BooklyL10n.mjsDateFormat));
|
197 |
-
$scope.$apply(function($scope){
|
198 |
-
$scope.date_start = start.format(format);
|
199 |
-
$scope.date_end = end.format(format);
|
200 |
-
$scope.reload();
|
201 |
-
});
|
202 |
-
}
|
203 |
-
);
|
204 |
-
});
|
205 |
-
|
206 |
-
// Bootstrap 'appointmentForm' application.
|
207 |
-
angular.bootstrap(document.getElementById('ab-appointment-form'), ['appointmentForm']);
|
208 |
-
})();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/modules/appointments/templates/_export_dialog.php
ADDED
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
|
2 |
+
<div id="bookly-export-dialog" class="modal fade" tabindex=-1 role="dialog">
|
3 |
+
<div class="modal-dialog">
|
4 |
+
<div class="modal-content">
|
5 |
+
<div class="modal-header">
|
6 |
+
<button type="button" class="close" data-dismiss="modal"><span>×</span></button>
|
7 |
+
<div class="modal-title h2"><?php _e( 'Export to CSV', 'bookly' ) ?></div>
|
8 |
+
</div>
|
9 |
+
<div class="modal-body">
|
10 |
+
<div class="form-group">
|
11 |
+
<label for="bookly-csv-delimiter"><?php _e( 'Delimiter', 'bookly' ) ?></label>
|
12 |
+
<select id="bookly-csv-delimiter" class="form-control">
|
13 |
+
<option value=","><?php _e( 'Comma (,)', 'bookly' ) ?></option>
|
14 |
+
<option value=";"><?php _e( 'Semicolon (;)', 'bookly' ) ?></option>
|
15 |
+
</select>
|
16 |
+
</div>
|
17 |
+
<div class="form-group">
|
18 |
+
<div class="checkbox"><label><input checked value="0" type="checkbox" /><?php _e( 'No.', 'bookly' ) ?></label></div>
|
19 |
+
<div class="checkbox"><label><input checked value="1" type="checkbox" /><?php _e( 'Booking Time', 'bookly' ) ?></label></div>
|
20 |
+
<div class="checkbox"><label><input checked value="2" type="checkbox" /><?php echo \BooklyLite\Lib\Utils\Common::getTranslatedOption( 'ab_appearance_text_label_employee' ) ?></label></div>
|
21 |
+
<div class="checkbox"><label><input checked value="3" type="checkbox" /><?php _e( 'Customer Name', 'bookly' ) ?></label></div>
|
22 |
+
<div class="checkbox"><label><input checked value="4" type="checkbox" /><?php _e( 'Customer Phone', 'bookly' ) ?></label></div>
|
23 |
+
<div class="checkbox"><label><input checked value="5" type="checkbox" /><?php _e( 'Customer Email', 'bookly' ) ?></label></div>
|
24 |
+
<div class="checkbox"><label><input checked value="6" type="checkbox" /><?php echo \BooklyLite\Lib\Utils\Common::getTranslatedOption( 'ab_appearance_text_label_service' ) ?></label></div>
|
25 |
+
<div class="checkbox"><label><input checked value="7" type="checkbox" /><?php _e( 'Duration', 'bookly' ) ?></label></div>
|
26 |
+
<div class="checkbox"><label><input checked value="8" type="checkbox" /><?php _e( 'Status', 'bookly' ) ?></label></div>
|
27 |
+
<div class="checkbox"><label><input checked value="9" type="checkbox" /><?php _e( 'Payment', 'bookly' ) ?></label></div>
|
28 |
+
<?php $i = 10; foreach ( $custom_fields as $custom_field ) : ?>
|
29 |
+
<div class="checkbox"><label><input checked value="<?php echo $i ++ ?>" type="checkbox"/><?php echo $custom_field->label ?></label></div>
|
30 |
+
<?php endforeach ?>
|
31 |
+
</div>
|
32 |
+
</div>
|
33 |
+
<div class="modal-footer">
|
34 |
+
<button type="submit" class="btn btn-lg btn-success" id="bookly-export" data-dismiss="modal">
|
35 |
+
<?php _e( 'Export to CSV', 'bookly' ) ?>
|
36 |
+
</button>
|
37 |
+
</div>
|
38 |
+
</div>
|
39 |
+
</div>
|
40 |
+
</div>
|
backend/modules/appointments/templates/_print_dialog.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
|
backend/modules/appointments/templates/index.php
CHANGED
@@ -1,79 +1,118 @@
|
|
1 |
-
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
8 |
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
13 |
</div>
|
14 |
-
<input type="hidden" name="date_start" ng-value="date_start" />
|
15 |
-
<input type="hidden" name="date_end" ng-value="date_end" />
|
16 |
-
<span class="help-inline"><?php _e( 'Delimiter', 'bookly' ) ?></span>
|
17 |
-
<select name="delimiter" style="width: 125px;height: 30px" class="form-control">
|
18 |
-
<option value=","><?php _e( 'Comma (,)', 'bookly' ) ?></option>
|
19 |
-
<option value=";"><?php _e( 'Semicolon (;)', 'bookly' ) ?></option>
|
20 |
-
</select>
|
21 |
-
<button type="submit" class="btn btn-info"><?php _e( 'Export to CSV', 'bookly' ) ?></button>
|
22 |
-
<button type="button" class="btn btn-info pull-right" ng-click="newAppointment()"><?php _e( 'New appointment', 'bookly' ) ?></button>
|
23 |
-
</form>
|
24 |
|
25 |
-
|
26 |
-
<table id="ab_appointments_list" class="table table-striped" cellspacing=0 cellpadding=0 border=0 style="clear: both;">
|
27 |
<thead>
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
36 |
</thead>
|
37 |
-
<tbody>
|
38 |
-
<tr ng-repeat="appointment in dataSource.appointments">
|
39 |
-
<td>{{appointment.start_date_f}}</td>
|
40 |
-
<td>{{appointment.staff_name}}</td>
|
41 |
-
<td>{{appointment.customer_name}}</td>
|
42 |
-
<td>{{appointment.service_title}}</td>
|
43 |
-
<td>{{appointment.service_duration}}</td>
|
44 |
-
<td>{{appointment.price}}</td>
|
45 |
-
<td>
|
46 |
-
<button class="btn btn-info pull-right" ng-click="editAppointment(appointment)">
|
47 |
-
<?php _e( 'Edit', 'bookly' ) ?>
|
48 |
-
</button>
|
49 |
-
</td>
|
50 |
-
<td><input type="checkbox" data-appointment_id="{{appointment.id}}"></td>
|
51 |
-
</tr>
|
52 |
-
</tbody>
|
53 |
</table>
|
54 |
-
<div ng-hide="dataSource.appointments.length || loading" class="alert alert-info"><?php _e( 'No appointments for selected period.', 'bookly' ) ?></div>
|
55 |
-
</div>
|
56 |
|
57 |
-
|
58 |
-
|
59 |
-
<div class="btn-group" ng-hide="dataSource.pages.length == 1">
|
60 |
-
<button ng-click="reload({page:page.number})" class="btn btn-default" ng-repeat="page in dataSource.pages" ng-switch on="page.active">
|
61 |
-
<span ng-switch-when="true">{{page.number}}</span>
|
62 |
-
<a href="" ng-switch-default>{{page.number}}</a>
|
63 |
-
</button>
|
64 |
-
</div>
|
65 |
-
</div>
|
66 |
-
<div class="col-xs-4">
|
67 |
-
<a class="btn btn-info pull-right" ng-click="deleteAppointments()"><?php _e( 'Delete', 'bookly' ) ?></a>
|
68 |
</div>
|
69 |
</div>
|
70 |
-
|
71 |
-
<div ng-show="loading" class="loading-indicator">
|
72 |
-
<span class="ab-loader"></span>
|
73 |
-
</div>
|
74 |
-
</div>
|
75 |
-
<div id="ab-appointment-form">
|
76 |
-
<?php include AB_PATH . '/backend/modules/calendar/templates/_appointment_form.php' ?>
|
77 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
78 |
</div>
|
79 |
</div>
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
+
use \BooklyLite\Lib\Entities\CustomerAppointment;
|
3 |
+
?>
|
4 |
+
<div id="bookly-tbs" class="wrap">
|
5 |
+
<div class="bookly-tbs-body">
|
6 |
+
<div class="page-header text-right clearfix">
|
7 |
+
<div class="bookly-page-title">
|
8 |
+
<?php _e( 'Appointments', 'bookly' ) ?>
|
9 |
+
</div>
|
10 |
+
</div>
|
11 |
+
<div class="panel panel-default bookly-main">
|
12 |
+
<div class="panel-body">
|
13 |
+
<div class="row">
|
14 |
+
<div class="form-inline bookly-margin-bottom-lg text-right">
|
15 |
+
<div class="form-group">
|
16 |
+
<button type="button" class="btn btn-default bookly-btn-block-xs" data-toggle="modal" data-target="#bookly-export-dialog"><i class="glyphicon glyphicon-export"></i> <?php _e( 'Export to CSV', 'bookly' ) ?></button>
|
17 |
+
</div>
|
18 |
+
<div class="form-group">
|
19 |
+
<button type="button" class="btn btn-default bookly-btn-block-xs bookly-limitation"><i class="glyphicon glyphicon-print"></i> <?php _e( 'Print', 'bookly' ) ?></button>
|
20 |
+
</div>
|
21 |
+
<div class="form-group">
|
22 |
+
<button type="button" class="btn btn-success bookly-btn-block-xs" id="bookly-add"><i class="glyphicon glyphicon-plus"></i> <?php _e( 'New appointment', 'bookly' ) ?></button>
|
23 |
+
</div>
|
24 |
+
</div>
|
25 |
|
26 |
+
<div class="col-md-4 col-lg-1">
|
27 |
+
<div class="form-group">
|
28 |
+
<input class="form-control" type="text" id="bookly-filter-id" placeholder="<?php esc_attr_e( 'No.', 'bookly' ) ?>" />
|
29 |
+
</div>
|
30 |
+
</div>
|
31 |
+
<div class="col-md-4 col-lg-3">
|
32 |
+
<div class="bookly-margin-bottom-lg bookly-relative">
|
33 |
+
<button type="button" class="btn btn-block btn-default" id="bookly-filter-date" data-date="<?php echo date( 'Y-m-d', strtotime( 'first day of' ) ) ?> - <?php echo date( 'Y-m-d', strtotime( 'last day of' ) ) ?>">
|
34 |
+
<i class="dashicons dashicons-calendar-alt"></i>
|
35 |
+
<span>
|
36 |
+
<?php echo date_i18n( get_option( 'date_format' ), strtotime( 'first day of' ) ) ?> - <?php echo date_i18n( get_option( 'date_format' ), strtotime( 'last day of' ) ) ?>
|
37 |
+
</span>
|
38 |
+
</button>
|
39 |
+
</div>
|
40 |
+
</div>
|
41 |
+
<div class="col-md-4 col-lg-2">
|
42 |
+
<div class="form-group">
|
43 |
+
<select class="form-control bookly-js-chosen-select" id="bookly-filter-staff" data-placeholder="<?php echo esc_attr( \BooklyLite\Lib\Utils\Common::getTranslatedOption( 'ab_appearance_text_label_employee' ) ) ?>">
|
44 |
+
<option value="-1"></option>
|
45 |
+
<?php foreach ( $staff_members as $staff ) : ?>
|
46 |
+
<option value="<?php echo $staff['id'] ?>"><?php esc_html_e( $staff['full_name'] ) ?></option>
|
47 |
+
<?php endforeach ?>
|
48 |
+
</select>
|
49 |
+
</div>
|
50 |
+
</div>
|
51 |
+
<div class="clearfix visible-md-block"></div>
|
52 |
+
<div class="col-md-4 col-lg-2">
|
53 |
+
<div class="form-group">
|
54 |
+
<select class="form-control bookly-js-chosen-select" id="bookly-filter-customer" data-placeholder="<?php esc_attr_e( 'Customer', 'bookly' ) ?>">
|
55 |
+
<option value="-1"></option>
|
56 |
+
<?php foreach ( $customers as $customer ) : ?>
|
57 |
+
<option value="<?php echo $customer['id'] ?>"><?php esc_html_e( $customer['name'] ) ?></option>
|
58 |
+
<?php endforeach ?>
|
59 |
+
</select>
|
60 |
+
</div>
|
61 |
+
</div>
|
62 |
+
<div class="col-md-4 col-lg-2">
|
63 |
+
<div class="form-group">
|
64 |
+
<select class="form-control bookly-js-chosen-select" id="bookly-filter-service" data-placeholder="<?php echo esc_attr( \BooklyLite\Lib\Utils\Common::getTranslatedOption( 'ab_appearance_text_label_service' ) ) ?>">
|
65 |
+
<option value="-1"></option>
|
66 |
+
<?php foreach ( $services as $service ) : ?>
|
67 |
+
<option value="<?php echo $service['id'] ?>"><?php esc_html_e( $service['title'] ) ?></option>
|
68 |
+
<?php endforeach ?>
|
69 |
+
</select>
|
70 |
+
</div>
|
71 |
+
</div>
|
72 |
+
<div class="col-md-4 col-lg-2">
|
73 |
+
<div class="form-group">
|
74 |
+
<select class="form-control bookly-js-chosen-select" id="bookly-filter-status" data-placeholder="<?php esc_attr_e( 'Status', 'bookly' ) ?>">
|
75 |
+
<option value="-1"></option>
|
76 |
+
<option value="<?php echo CustomerAppointment::STATUS_PENDING ?>"><?php echo CustomerAppointment::statusToString( CustomerAppointment::STATUS_PENDING ) ?></option>
|
77 |
+
<option value="<?php echo CustomerAppointment::STATUS_APPROVED ?>"><?php echo CustomerAppointment::statusToString( CustomerAppointment::STATUS_APPROVED ) ?></option>
|
78 |
+
<option value="<?php echo CustomerAppointment::STATUS_CANCELLED ?>"><?php echo CustomerAppointment::statusToString( CustomerAppointment::STATUS_CANCELLED ) ?></option>
|
79 |
+
</select>
|
80 |
+
</div>
|
81 |
+
</div>
|
82 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
83 |
|
84 |
+
<table id="bookly-appointments-list" class="table table-striped" width="100%">
|
|
|
85 |
<thead>
|
86 |
+
<tr>
|
87 |
+
<th><?php _e( 'No.', 'bookly' ) ?></th>
|
88 |
+
<th><?php _e( 'Appointment Date', 'bookly' ) ?></th>
|
89 |
+
<th><?php echo \BooklyLite\Lib\Utils\Common::getTranslatedOption( 'ab_appearance_text_label_employee' ) ?></th>
|
90 |
+
<th><?php _e( 'Customer Name', 'bookly' ) ?></th>
|
91 |
+
<th><?php _e( 'Customer Phone', 'bookly' ) ?></th>
|
92 |
+
<th><?php _e( 'Customer Email', 'bookly' ) ?></th>
|
93 |
+
<th><?php echo \BooklyLite\Lib\Utils\Common::getTranslatedOption( 'ab_appearance_text_label_service' ) ?></th>
|
94 |
+
<th><?php _e( 'Duration', 'bookly' ) ?></th>
|
95 |
+
<th><?php _e( 'Status', 'bookly' ) ?></th>
|
96 |
+
<th><?php _e( 'Payment', 'bookly' ) ?></th>
|
97 |
+
<?php foreach ( $custom_fields as $custom_field ) : ?>
|
98 |
+
<th><?php echo $custom_field->label ?></th>
|
99 |
+
<?php endforeach ?>
|
100 |
+
<th></th>
|
101 |
+
<th width="16"><input type="checkbox" id="bookly-check-all" /></th>
|
102 |
+
</tr>
|
103 |
</thead>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
104 |
</table>
|
|
|
|
|
105 |
|
106 |
+
<div class="text-right bookly-margin-top-lg">
|
107 |
+
<?php \BooklyLite\Lib\Utils\Common::deleteButton() ?>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
108 |
</div>
|
109 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
110 |
</div>
|
111 |
+
|
112 |
+
<?php include '_export_dialog.php' ?>
|
113 |
+
<?php include '_print_dialog.php' ?>
|
114 |
+
|
115 |
+
<?php \BooklyLite\Backend\Modules\Calendar\Components::getInstance()->renderAppointmentDialog() ?>
|
116 |
+
<?php do_action( 'bookly_render_component_appointments' ) ?>
|
117 |
</div>
|
118 |
</div>
|
backend/modules/calendar/AB_CalendarController.php
DELETED
@@ -1,488 +0,0 @@
|
|
1 |
-
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Class AB_CalendarController
|
5 |
-
*/
|
6 |
-
class AB_CalendarController extends AB_Controller {
|
7 |
-
|
8 |
-
protected function getPermissions()
|
9 |
-
{
|
10 |
-
return array( '_this' => 'user' );
|
11 |
-
}
|
12 |
-
|
13 |
-
public function index()
|
14 |
-
{
|
15 |
-
/** @var WP_Locale $wp_locale */
|
16 |
-
global $wp_locale;
|
17 |
-
|
18 |
-
$this->enqueueStyles( array(
|
19 |
-
'frontend' => array(
|
20 |
-
'css/intlTelInput.css',
|
21 |
-
),
|
22 |
-
'module' => array(
|
23 |
-
'css/calendar.css',
|
24 |
-
'css/fullcalendar.min.css',
|
25 |
-
),
|
26 |
-
'backend' => array(
|
27 |
-
'css/chosen.min.css',
|
28 |
-
'css/jquery-ui-theme/jquery-ui.min.css',
|
29 |
-
'css/bookly.main-backend.css',
|
30 |
-
'bootstrap/css/bootstrap.min.css',
|
31 |
-
),
|
32 |
-
) );
|
33 |
-
|
34 |
-
$this->enqueueScripts( array(
|
35 |
-
'backend' => array(
|
36 |
-
'js/angular.min.js' => array( 'jquery' ),
|
37 |
-
'js/angular-ui-date-0.0.8.js' => array( 'ab-angular.min.js' ),
|
38 |
-
'js/ng-new_customer_dialog.js' => array( 'ab-angular.min.js' ),
|
39 |
-
'js/moment.min.js' => array( 'jquery' ),
|
40 |
-
'bootstrap/js/bootstrap.min.js' => array( 'jquery' ),
|
41 |
-
'js/chosen.jquery.min.js' => array( 'jquery' ),
|
42 |
-
'js/ng-edit_appointment_dialog.js' => array( 'ab-angular-ui-date-0.0.8.js', 'jquery-ui-datepicker' ),
|
43 |
-
),
|
44 |
-
'module' => array(
|
45 |
-
'js/fullcalendar.min.js' => array( 'ab-moment.min.js' ),
|
46 |
-
'js/fc-multistaff-view.js' => array( 'ab-fullcalendar.min.js' ),
|
47 |
-
'js/calendar.js' => array( 'ab-fc-multistaff-view.js', 'ab-intlTelInput.min.js' ),
|
48 |
-
),
|
49 |
-
'frontend' => array(
|
50 |
-
'js/intlTelInput.min.js' => array( 'jquery' ),
|
51 |
-
)
|
52 |
-
) );
|
53 |
-
|
54 |
-
$slot_length_minutes = get_option( 'ab_settings_time_slot_length', '15' );
|
55 |
-
$slot = new DateInterval( 'PT' . $slot_length_minutes . 'M' );
|
56 |
-
|
57 |
-
$this->staff_members = AB_Utils::isCurrentUserAdmin()
|
58 |
-
? AB_Staff::query()->sortBy( 'position' )->find()
|
59 |
-
: AB_Staff::query()->where( 'wp_user_id', get_current_user_id() )->find();
|
60 |
-
|
61 |
-
|
62 |
-
wp_localize_script( 'ab-calendar.js', 'BooklyL10n', array(
|
63 |
-
'slotDuration' => $slot->format( '%H:%I:%S' ),
|
64 |
-
'shortMonths' => array_values( $wp_locale->month_abbrev ),
|
65 |
-
'longMonths' => array_values( $wp_locale->month ),
|
66 |
-
'shortDays' => array_values( $wp_locale->weekday_abbrev ),
|
67 |
-
'longDays' => array_values( $wp_locale->weekday ),
|
68 |
-
'AM' => $wp_locale->meridiem[ 'AM' ],
|
69 |
-
'PM' => $wp_locale->meridiem[ 'PM' ],
|
70 |
-
'dpDateFormat' => AB_DateTimeUtils::convertFormat( 'date', AB_DateTimeUtils::FORMAT_JQUERY_DATEPICKER ),
|
71 |
-
'mjsDateFormat' => AB_DateTimeUtils::convertFormat( 'date', AB_DateTimeUtils::FORMAT_MOMENT_JS ),
|
72 |
-
'mjsTimeFormat' => AB_DateTimeUtils::convertFormat( 'time', AB_DateTimeUtils::FORMAT_MOMENT_JS ),
|
73 |
-
'today' => __( 'Today', 'bookly' ),
|
74 |
-
'week' => __( 'Week', 'bookly' ),
|
75 |
-
'day' => __( 'Day', 'bookly' ),
|
76 |
-
'month' => __( 'Month', 'bookly' ),
|
77 |
-
'allDay' => __( 'All Day', 'bookly' ),
|
78 |
-
'noStaffSelected' => __( 'No staff selected', 'bookly' ),
|
79 |
-
'newAppointment' => __( 'New appointment', 'bookly' ),
|
80 |
-
'editAppointment' => __( 'Edit appointment', 'bookly' ),
|
81 |
-
'are_you_sure' => __( 'Are you sure?', 'bookly' ),
|
82 |
-
'startOfWeek' => (int) get_option( 'start_of_week' ),
|
83 |
-
'country' => get_option( 'ab_settings_phone_default_country' ),
|
84 |
-
'intlTelInput_utils' => plugins_url( 'intlTelInput.utils.js', AB_PATH . '/frontend/resources/js/intlTelInput.utils.js' ),
|
85 |
-
) );
|
86 |
-
|
87 |
-
$this->render( 'calendar' );
|
88 |
-
}
|
89 |
-
|
90 |
-
/**
|
91 |
-
* Get data for FullCalendar.
|
92 |
-
*
|
93 |
-
* @return json
|
94 |
-
*/
|
95 |
-
public function executeGetStaffAppointments()
|
96 |
-
{
|
97 |
-
$result = array();
|
98 |
-
$staff_members = array();
|
99 |
-
$one_day = new DateInterval( 'P1D' );
|
100 |
-
$start_date = new DateTime( $this->getParameter( 'start' ) );
|
101 |
-
$end_date = new DateTime( $this->getParameter( 'end' ) );
|
102 |
-
// FullCalendar sends end date as 1 day further.
|
103 |
-
$end_date->sub( $one_day );
|
104 |
-
|
105 |
-
if ( AB_Utils::isCurrentUserAdmin() ) {
|
106 |
-
$staff_members = AB_Staff::query()
|
107 |
-
->where( 'id', 1 )
|
108 |
-
->find();
|
109 |
-
} else {
|
110 |
-
$staff_members[] = AB_Staff::query()
|
111 |
-
->where( 'wp_user_id', get_current_user_id() )
|
112 |
-
->findOne();
|
113 |
-
}
|
114 |
-
|
115 |
-
foreach ( $staff_members as $staff ) {
|
116 |
-
/** @var AB_Staff $staff */
|
117 |
-
$result = array_merge( $result, $staff->getAppointmentsForFC( $start_date, $end_date ) );
|
118 |
-
|
119 |
-
// Schedule.
|
120 |
-
$items = $staff->getScheduleItems();
|
121 |
-
$day = clone $start_date;
|
122 |
-
// Find previous day end time.
|
123 |
-
$last_end = clone $day;
|
124 |
-
$last_end->sub( $one_day );
|
125 |
-
$w = $day->format( 'w' );
|
126 |
-
$end_time = $items[ $w > 0 ? $w : 7 ]->get( 'end_time' );
|
127 |
-
if ( $end_time !== null ) {
|
128 |
-
$end_time = explode( ':', $end_time );
|
129 |
-
$last_end->setTime( $end_time[0], $end_time[1] );
|
130 |
-
} else {
|
131 |
-
$last_end->setTime( 24, 0 );
|
132 |
-
}
|
133 |
-
// Do the loop.
|
134 |
-
while ( $day <= $end_date ) {
|
135 |
-
do {
|
136 |
-
/** @var AB_StaffScheduleItem $item */
|
137 |
-
$item = $items[ $day->format( 'w' ) + 1 ];
|
138 |
-
if ( $item->get( 'start_time' ) && ! $staff->isOnHoliday( $day ) ) {
|
139 |
-
$start = $last_end->format( 'Y-m-d H:i:s' );
|
140 |
-
$end = $day->format( 'Y-m-d '.$item->get( 'start_time' ) );
|
141 |
-
if ( $start < $end ) {
|
142 |
-
$result[] = array(
|
143 |
-
'start' => $start,
|
144 |
-
'end' => $end,
|
145 |
-
'rendering' => 'background',
|
146 |
-
'staffId' => $staff->get( 'id' ),
|
147 |
-
);
|
148 |
-
}
|
149 |
-
$last_end = clone $day;
|
150 |
-
$end_time = explode( ':', $item->get( 'end_time' ) );
|
151 |
-
$last_end->setTime( $end_time[0], $end_time[1] );
|
152 |
-
|
153 |
-
// Breaks.
|
154 |
-
foreach ( $item->getBreaksList() as $break ) {
|
155 |
-
$result[] = array(
|
156 |
-
'start' => $day->format( 'Y-m-d '.$break['start_time'] ),
|
157 |
-
'end' => $day->format( 'Y-m-d '.$break['end_time'] ),
|
158 |
-
'rendering' => 'background',
|
159 |
-
'staffId' => $staff->get( 'id' ),
|
160 |
-
);
|
161 |
-
}
|
162 |
-
|
163 |
-
break;
|
164 |
-
}
|
165 |
-
|
166 |
-
$result[] = array(
|
167 |
-
'start' => $last_end->format( 'Y-m-d H:i:s' ),
|
168 |
-
'end' => $day->format( 'Y-m-d 24:00:00' ),
|
169 |
-
'rendering' => 'background',
|
170 |
-
'staffId' => $staff->get( 'id' ),
|
171 |
-
);
|
172 |
-
$last_end = clone $day;
|
173 |
-
$last_end->setTime( 24, 0 );
|
174 |
-
|
175 |
-
} while ( 0 );
|
176 |
-
|
177 |
-
$day->add( $one_day );
|
178 |
-
}
|
179 |
-
|
180 |
-
if ( $last_end->format( 'H' ) != 24 ) {
|
181 |
-
$result[] = array(
|
182 |
-
'start' => $last_end->format( 'Y-m-d H:i:s' ),
|
183 |
-
'end' => $last_end->format( 'Y-m-d 24:00:00' ),
|
184 |
-
'rendering' => 'background',
|
185 |
-
'staffId' => $staff->get( 'id' ),
|
186 |
-
);
|
187 |
-
}
|
188 |
-
}
|
189 |
-
|
190 |
-
wp_send_json( $result );
|
191 |
-
}
|
192 |
-
|
193 |
-
/**
|
194 |
-
* Get data needed for appointment form initialisation.
|
195 |
-
*/
|
196 |
-
public function executeGetDataForAppointmentForm()
|
197 |
-
{
|
198 |
-
$result = array(
|
199 |
-
'staff' => array(),
|
200 |
-
'customers' => array(),
|
201 |
-
'start_time' => array(),
|
202 |
-
'end_time' => array(),
|
203 |
-
'time_interval' => get_option( 'ab_settings_time_slot_length' ) * 60
|
204 |
-
);
|
205 |
-
|
206 |
-
// Staff list.
|
207 |
-
$staff_members = AB_Staff::query()->where( 'id', 1 )->sortBy( 'position' )->find();
|
208 |
-
|
209 |
-
|
210 |
-
/** @var AB_Staff $staff_member */
|
211 |
-
foreach ( $staff_members as $staff_member ) {
|
212 |
-
$services = array();
|
213 |
-
foreach ( $staff_member->getStaffServices() as $staff_service ) {
|
214 |
-
$services[] = array(
|
215 |
-
'id' => $staff_service->service->get( 'id' ),
|
216 |
-
'title' => sprintf(
|
217 |
-
'%s (%s)',
|
218 |
-
$staff_service->service->get( 'title' ),
|
219 |
-
AB_DateTimeUtils::secondsToInterval( $staff_service->service->get( 'duration' ) )
|
220 |
-
),
|
221 |
-
'duration' => $staff_service->service->get( 'duration' ),
|
222 |
-
'capacity' => $staff_service->get( 'capacity' )
|
223 |
-
);
|
224 |
-
}
|
225 |
-
$result['staff'][] = array(
|
226 |
-
'id' => 1,
|
227 |
-
'full_name' => $staff_member->get( 'full_name' ),
|
228 |
-
'services' => $services
|
229 |
-
);
|
230 |
-
}
|
231 |
-
|
232 |
-
// Customers list.
|
233 |
-
foreach ( AB_Customer::query()->sortBy( 'name' )->find() as $customer ) {
|
234 |
-
$name = $customer->get( 'name' );
|
235 |
-
if ( $customer->get( 'email' ) != '' || $customer->get( 'phone' ) != '' ) {
|
236 |
-
$name .= ' (' . trim( $customer->get( 'email' ) . ', ' . $customer->get( 'phone' ) , ', ') . ')';
|
237 |
-
}
|
238 |
-
|
239 |
-
$result[ 'customers' ][] = array(
|
240 |
-
'id' => $customer->get( 'id' ),
|
241 |
-
'name' => $name,
|
242 |
-
'custom_fields' => array(),
|
243 |
-
'number_of_persons' => 1,
|
244 |
-
);
|
245 |
-
}
|
246 |
-
|
247 |
-
// Time list.
|
248 |
-
$ts_length = AB_Config::getTimeSlotLength();
|
249 |
-
$time_start = 0;
|
250 |
-
$time_end = DAY_IN_SECONDS * 2;
|
251 |
-
|
252 |
-
// Run the loop.
|
253 |
-
while ( $time_start <= $time_end ) {
|
254 |
-
$slot = array(
|
255 |
-
'value' => AB_DateTimeUtils::buildTimeString( $time_start, false ),
|
256 |
-
'title' => AB_DateTimeUtils::formatTime( $time_start )
|
257 |
-
);
|
258 |
-
if ( $time_start < DAY_IN_SECONDS ) {
|
259 |
-
$result['start_time'][] = $slot;
|
260 |
-
}
|
261 |
-
$result['end_time'][] = $slot;
|
262 |
-
$time_start += $ts_length;
|
263 |
-
}
|
264 |
-
|
265 |
-
wp_send_json( $result );
|
266 |
-
}
|
267 |
-
|
268 |
-
/**
|
269 |
-
* Get appointment data when editing an appointment.
|
270 |
-
*/
|
271 |
-
public function executeGetDataForAppointment()
|
272 |
-
{
|
273 |
-
$response = array( 'success' => false, 'data' => array( 'customers' => array() ) );
|
274 |
-
|
275 |
-
$appointment = new AB_Appointment();
|
276 |
-
if ( $appointment->load( $this->getParameter( 'id' ) ) ) {
|
277 |
-
$response['success'] = true;
|
278 |
-
|
279 |
-
$info = AB_Appointment::query( 'a' )
|
280 |
-
->select( 'ss.capacity AS max_capacity, SUM( ca.number_of_persons ) AS total_number_of_persons, a.staff_id, a.service_id, a.start_date, a.end_date' )
|
281 |
-
->leftJoin( 'AB_CustomerAppointment', 'ca', 'ca.appointment_id = a.id' )
|
282 |
-
->leftJoin( 'AB_StaffService', 'ss', 'ss.staff_id = a.staff_id AND ss.service_id = a.service_id' )
|
283 |
-
->where( 'a.id', $appointment->get( 'id' ) )
|
284 |
-
->fetchRow();
|
285 |
-
|
286 |
-
$response['data']['total_number_of_persons'] = $info['total_number_of_persons'];
|
287 |
-
$response['data']['max_capacity'] = $info['max_capacity'];
|
288 |
-
$response['data']['start_date'] = $info['start_date'];
|
289 |
-
$response['data']['end_date'] = $info['end_date'];
|
290 |
-
$response['data']['staff_id'] = $info['staff_id'];
|
291 |
-
$response['data']['service_id'] = $info['service_id'];
|
292 |
-
|
293 |
-
$customer_appointments = AB_CustomerAppointment::query()->where( 'appointment_id', $appointment->get( 'id' ) )->fetchArray();
|
294 |
-
|
295 |
-
foreach ( $customer_appointments as $customer_appointment ) {
|
296 |
-
$response[ 'data' ][ 'customers' ][] = array(
|
297 |
-
'id' => $customer_appointment['customer_id'],
|
298 |
-
'custom_fields' => $customer_appointment['custom_fields'] ? json_decode( $customer_appointment['custom_fields'], true ) : array(),
|
299 |
-
'number_of_persons' => $customer_appointment['number_of_persons']
|
300 |
-
);
|
301 |
-
}
|
302 |
-
}
|
303 |
-
wp_send_json( $response );
|
304 |
-
}
|
305 |
-
|
306 |
-
/**
|
307 |
-
* Save appointment form (for both create and edit).
|
308 |
-
*/
|
309 |
-
public function executeSaveAppointmentForm()
|
310 |
-
{
|
311 |
-
$response = array( 'success' => false );
|
312 |
-
|
313 |
-
$start_date = date( 'Y-m-d H:i:s', strtotime( $this->getParameter( 'start_date' ) ) );
|
314 |
-
$end_date = date( 'Y-m-d H:i:s', strtotime( $this->getParameter( 'end_date' ) ) );
|
315 |
-
$staff_id = $this->getParameter( 'staff_id' );
|
316 |
-
$service_id = $this->getParameter( 'service_id' );
|
317 |
-
$appointment_id = $this->getParameter( 'id', 0 );
|
318 |
-
$customers = json_decode( $this->getParameter( 'customers', '[]' ), true );
|
319 |
-
|
320 |
-
$staff_service = new AB_StaffService();
|
321 |
-
$staff_service->loadBy( array(
|
322 |
-
'staff_id' => $staff_id,
|
323 |
-
'service_id' => $service_id
|
324 |
-
) );
|
325 |
-
|
326 |
-
// Check for errors.
|
327 |
-
if ( ! $service_id ) {
|
328 |
-
$response['errors']['service_required'] = true;
|
329 |
-
}
|
330 |
-
if ( empty ( $customers ) ) {
|
331 |
-
$response['errors']['customers_required'] = true;
|
332 |
-
}
|
333 |
-
if ( !$this->dateIntervalIsAvailableForAppointment( $start_date, $end_date, $staff_id, $appointment_id ) ) {
|
334 |
-
$response['errors']['date_interval_not_available'] = true;
|
335 |
-
}
|
336 |
-
$number_of_persons = 0;
|
337 |
-
foreach ( $customers as $customer ) {
|
338 |
-
$number_of_persons += $customer['number_of_persons'];
|
339 |
-
}
|
340 |
-
if ( $number_of_persons > $staff_service->get( 'capacity' ) ) {
|
341 |
-
$response['errors']['overflow_capacity'] = __( 'The number of customers should be not more than ', 'bookly' ) . $staff_service->get( 'capacity' );
|
342 |
-
}
|
343 |
-
if ( ! $this->getParameter( 'start_date' ) ) {
|
344 |
-
$response['errors']['time_interval'] = __( 'Start time must not be empty', 'bookly' );
|
345 |
-
} elseif ( ! $this->getParameter( 'end_date' ) ) {
|
346 |
-
$response['errors']['time_interval'] = __( 'End time must not be empty', 'bookly' );
|
347 |
-
} elseif ( $start_date == $end_date ) {
|
348 |
-
$response['errors']['time_interval'] = __( 'End time must not be equal to start time', 'bookly' );
|
349 |
-
}
|
350 |
-
|
351 |
-
// If no errors then try to save the appointment.
|
352 |
-
if ( !isset ( $response[ 'errors' ] ) ) {
|
353 |
-
$appointment = new AB_Appointment();
|
354 |
-
if ( $appointment_id ) {
|
355 |
-
// Edit.
|
356 |
-
$appointment->load( $appointment_id );
|
357 |
-
}
|
358 |
-
$appointment->set( 'start_date', $start_date );
|
359 |
-
$appointment->set( 'end_date', $end_date );
|
360 |
-
$appointment->set( 'staff_id', $staff_id );
|
361 |
-
$appointment->set( 'service_id', $service_id );
|
362 |
-
|
363 |
-
if ( $appointment->save() !== false ) {
|
364 |
-
// Save customers.
|
365 |
-
$appointment->setCustomers( $customers );
|
366 |
-
|
367 |
-
// Google Calendar.
|
368 |
-
$appointment->handleGoogleCalendar();
|
369 |
-
|
370 |
-
$startDate = new DateTime( $appointment->get( 'start_date' ) );
|
371 |
-
$endDate = new DateTime( $appointment->get( 'end_date' ) );
|
372 |
-
$desc = array();
|
373 |
-
if ( $staff_service->get( 'capacity' ) == 1 ) {
|
374 |
-
$customer_appointments = $appointment->getCustomerAppointments();
|
375 |
-
if ( !empty ( $customer_appointments ) ) {
|
376 |
-
$ca = $customer_appointments[ 0 ]->customer;
|
377 |
-
foreach ( array( 'name', 'phone', 'email' ) as $data_entry ) {
|
378 |
-
$entry_value = $ca->get( $data_entry );
|
379 |
-
if ( $entry_value ) {
|
380 |
-
$desc[] = '<div class="fc-employee">' . esc_html( $entry_value ) . '</div>';
|
381 |
-
}
|
382 |
-
}
|
383 |
-
|
384 |
-
foreach ( $customer_appointments[0]->getCustomFields() as $custom_field ) {
|
385 |
-
$desc[] = '<div class="fc-notes">' . wp_strip_all_tags( $custom_field['label'] ) . ': ' . esc_html( $custom_field['value'] ) . '</div>';
|
386 |
-
}
|
387 |
-
}
|
388 |
-
} else {
|
389 |
-
$signed_up = 0;
|
390 |
-
foreach ( $appointment->getCustomerAppointments() as $ca ) {
|
391 |
-
$signed_up += $ca->get( 'number_of_persons' );
|
392 |
-
}
|
393 |
-
|
394 |
-
$desc[] = '<div class="fc-notes">' . __( 'Signed up', 'bookly' ) . ' ' . $signed_up . '</div>';
|
395 |
-
$desc[] = '<div class="fc-notes">' . __( 'Capacity', 'bookly' ) . ' ' . $staff_service->get( 'capacity' ) . '</div>';
|
396 |
-
}
|
397 |
-
|
398 |
-
$service = new AB_Service();
|
399 |
-
$service->load( $service_id );
|
400 |
-
|
401 |
-
$response['success'] = true;
|
402 |
-
$response['data'] = array(
|
403 |
-
'id' => (int)$appointment->get( 'id' ),
|
404 |
-
'start' => $startDate->format( 'Y-m-d H:i:s' ),
|
405 |
-
'end' => $endDate->format( 'Y-m-d H:i:s' ),
|
406 |
-
'desc' => implode( '', $desc ),
|
407 |
-
'title' => $service->get( 'title' ) ? $service->get( 'title' ) : __( 'Untitled', 'bookly' ),
|
408 |
-
'color' => $service->get( 'color' ),
|
409 |
-
'staffId' => $appointment->get( 'staff_id' ),
|
410 |
-
);
|
411 |
-
} else {
|
412 |
-
$response[ 'errors' ] = array( 'db' => __( 'Could not save appointment in database.', 'bookly' ) );
|
413 |
-
}
|
414 |
-
}
|
415 |
-
|
416 |
-
wp_send_json( $response );
|
417 |
-
}
|
418 |
-
|
419 |
-
public function executeCheckAppointmentDateSelection()
|
420 |
-
{
|
421 |
-
$start_date = $this->getParameter( 'start_date' );
|
422 |
-
$end_date = $this->getParameter( 'end_date' );
|
423 |
-
$staff_id = $this->getParameter( 'staff_id' );
|
424 |
-
$service_id = $this->getParameter( 'service_id' );
|
425 |
-
$appointment_id = $this->getParameter( 'appointment_id' );
|
426 |
-
$timestamp_diff = strtotime( $end_date ) - strtotime( $start_date );
|
427 |
-
|
428 |
-
$result = array(
|
429 |
-
'date_interval_not_available' => false,
|
430 |
-
'date_interval_warning' => false,
|
431 |
-
);
|
432 |
-
|
433 |
-
if ( !$this->dateIntervalIsAvailableForAppointment( $start_date, $end_date, $staff_id, $appointment_id ) ) {
|
434 |
-
$result['date_interval_not_available'] = true;
|
435 |
-
}
|
436 |
-
|
437 |
-
if ( $service_id ) {
|
438 |
-
$service = new AB_Service();
|
439 |
-
$service->load( $service_id );
|
440 |
-
|
441 |
-
$duration = $service->get( 'duration' );
|
442 |
-
|
443 |
-
// Service duration interval is not equal to.
|
444 |
-
$result['date_interval_warning'] = ( $timestamp_diff != $duration );
|
445 |
-
}
|
446 |
-
|
447 |
-
wp_send_json( $result );
|
448 |
-
}
|
449 |
-
|
450 |
-
public function executeDeleteAppointment()
|
451 |
-
{
|
452 |
-
$appointment = new AB_Appointment();
|
453 |
-
$appointment->load( $this->getParameter( 'appointment_id' ) );
|
454 |
-
$appointment->delete();
|
455 |
-
exit;
|
456 |
-
}
|
457 |
-
|
458 |
-
/**
|
459 |
-
* @param $start_date
|
460 |
-
* @param $end_date
|
461 |
-
* @param $staff_id
|
462 |
-
* @param $appointment_id
|
463 |
-
* @return bool
|
464 |
-
*/
|
465 |
-
private function dateIntervalIsAvailableForAppointment( $start_date, $end_date, $staff_id, $appointment_id )
|
466 |
-
{
|
467 |
-
return AB_Appointment::query()
|
468 |
-
->whereNot( 'id', $appointment_id )
|
469 |
-
->where( 'staff_id', $staff_id )
|
470 |
-
->whereRaw(
|
471 |
-
'(start_date > %s AND start_date < %s OR (end_date > %s AND end_date < %s) OR (start_date < %s AND end_date > %s) OR (start_date = %s OR end_date = %s) )',
|
472 |
-
array( $start_date, $end_date, $start_date, $end_date, $start_date, $end_date, $start_date, $end_date )
|
473 |
-
)
|
474 |
-
->count() == 0;
|
475 |
-
}
|
476 |
-
|
477 |
-
/**
|
478 |
-
* Override parent method to add 'wp_ajax_ab_' prefix
|
479 |
-
* so current 'execute*' methods look nicer.
|
480 |
-
*
|
481 |
-
* @param string $prefix
|
482 |
-
*/
|
483 |
-
protected function registerWpActions( $prefix = '' )
|
484 |
-
{
|
485 |
-
parent::registerWpActions( 'wp_ajax_ab_' );
|
486 |
-
}
|
487 |
-
|
488 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/modules/calendar/Components.php
ADDED
@@ -0,0 +1,62 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace BooklyLite\Backend\Modules\Calendar;
|
3 |
+
|
4 |
+
use BooklyLite\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Components
|
8 |
+
* @package BooklyLite\Backend\Modules\Calendar
|
9 |
+
*/
|
10 |
+
class Components extends Lib\Base\Components
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Render appointment dialog.
|
14 |
+
* @throws \Exception
|
15 |
+
*/
|
16 |
+
public function renderAppointmentDialog()
|
17 |
+
{
|
18 |
+
global $wp_locale;
|
19 |
+
|
20 |
+
$this->enqueueStyles( array(
|
21 |
+
'backend' => array( 'css/jquery-ui-theme/jquery-ui.min.css', ),
|
22 |
+
) );
|
23 |
+
|
24 |
+
$this->enqueueScripts( array(
|
25 |
+
'backend' => array(
|
26 |
+
'js/angular.min.js' => array( 'jquery' ),
|
27 |
+
'js/angular-ui-date-0.0.8.js' => array( 'ab-angular.min.js' ),
|
28 |
+
'js/moment.min.js' => array( 'jquery' ),
|
29 |
+
'js/chosen.jquery.min.js' => array( 'jquery' ),
|
30 |
+
'js/help.js' => array( 'jquery' ),
|
31 |
+
),
|
32 |
+
'module' => array(
|
33 |
+
'js/ng-appointment_dialog.js' => array( 'ab-angular-ui-date-0.0.8.js', 'jquery-ui-datepicker' ),
|
34 |
+
)
|
35 |
+
) );
|
36 |
+
|
37 |
+
wp_localize_script( 'ab-ng-appointment_dialog.js', 'BooklyL10nAppDialog', array(
|
38 |
+
'calendar' => array(
|
39 |
+
'shortMonths' => array_values( $wp_locale->month_abbrev ),
|
40 |
+
'longMonths' => array_values( $wp_locale->month ),
|
41 |
+
'shortDays' => array_values( $wp_locale->weekday_abbrev ),
|
42 |
+
'longDays' => array_values( $wp_locale->weekday ),
|
43 |
+
),
|
44 |
+
'dpDateFormat' => Lib\Utils\DateTime::convertFormat( 'date', Lib\Utils\DateTime::FORMAT_JQUERY_DATEPICKER ),
|
45 |
+
'startOfWeek' => (int) get_option( 'start_of_week' ),
|
46 |
+
'cf_per_service' => (int) Lib\Config::customFieldsPerService(),
|
47 |
+
'title' => array(
|
48 |
+
'edit_appointment' => __( 'Edit appointment', 'bookly' ),
|
49 |
+
'new_appointment' => __( 'New appointment', 'bookly' ),
|
50 |
+
),
|
51 |
+
) );
|
52 |
+
|
53 |
+
// Custom fields without captcha field.
|
54 |
+
$custom_fields = array_filter(
|
55 |
+
json_decode( get_option( 'ab_custom_fields' ) ),
|
56 |
+
function( $field ) { return ! in_array( $field->type, array( 'captcha', 'text-content' ) ); }
|
57 |
+
);
|
58 |
+
|
59 |
+
$this->render( '_appointment_dialog', compact( 'custom_fields' ) );
|
60 |
+
}
|
61 |
+
|
62 |
+
}
|
backend/modules/calendar/Controller.php
ADDED
@@ -0,0 +1,584 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace BooklyLite\Backend\Modules\Calendar;
|
3 |
+
|
4 |
+
use \BooklyLite\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Controller
|
8 |
+
* @package BooklyLite\Backend\Modules\Calendar
|
9 |
+
*/
|
10 |
+
class Controller extends Lib\Base\Controller
|
11 |
+
{
|
12 |
+
protected function getPermissions()
|
13 |
+
{
|
14 |
+
return array( '_this' => 'user' );
|
15 |
+
}
|
16 |
+
|
17 |
+
public function index()
|
18 |
+
{
|
19 |
+
/** @var \WP_Locale $wp_locale */
|
20 |
+
global $wp_locale;
|
21 |
+
|
22 |
+
$this->enqueueStyles( array(
|
23 |
+
'module' => array( 'css/fullcalendar.min.css', ),
|
24 |
+
'backend' => array( 'bootstrap/css/bootstrap-theme.min.css', ),
|
25 |
+
) );
|
26 |
+
|
27 |
+
$this->enqueueScripts( array(
|
28 |
+
'backend' => array( 'bootstrap/js/bootstrap.min.js' => array( 'jquery' ), ),
|
29 |
+
'module' => array(
|
30 |
+
'js/fullcalendar.min.js' => array( 'ab-moment.min.js' ),
|
31 |
+
'js/fc-multistaff-view.js' => array( 'ab-fullcalendar.min.js' ),
|
32 |
+
'js/calendar.js' => array( 'ab-fc-multistaff-view.js' ),
|
33 |
+
),
|
34 |
+
) );
|
35 |
+
|
36 |
+
$slot_length_minutes = get_option( 'ab_settings_time_slot_length', '15' );
|
37 |
+
$slot = new \DateInterval( 'PT' . $slot_length_minutes . 'M' );
|
38 |
+
|
39 |
+
$staff_members = Lib\Utils\Common::isCurrentUserAdmin()
|
40 |
+
? Lib\Entities\Staff::query()->sortBy( 'position' )->find()
|
41 |
+
: Lib\Entities\Staff::query()->where( 'wp_user_id', get_current_user_id() )->find();
|
42 |
+
|
43 |
+
wp_localize_script( 'ab-calendar.js', 'BooklyL10n', array(
|
44 |
+
'slotDuration' => $slot->format( '%H:%I:%S' ),
|
45 |
+
'calendar' => array(
|
46 |
+
'shortMonths' => array_values( $wp_locale->month_abbrev ),
|
47 |
+
'longMonths' => array_values( $wp_locale->month ),
|
48 |
+
'shortDays' => array_values( $wp_locale->weekday_abbrev ),
|
49 |
+
'longDays' => array_values( $wp_locale->weekday ),
|
50 |
+
),
|
51 |
+
'dpDateFormat' => Lib\Utils\DateTime::convertFormat( 'date', Lib\Utils\DateTime::FORMAT_JQUERY_DATEPICKER ),
|
52 |
+
'mjsDateFormat' => Lib\Utils\DateTime::convertFormat( 'date', Lib\Utils\DateTime::FORMAT_MOMENT_JS ),
|
53 |
+
'mjsTimeFormat' => Lib\Utils\DateTime::convertFormat( 'time', Lib\Utils\DateTime::FORMAT_MOMENT_JS ),
|
54 |
+
'today' => __( 'Today', 'bookly' ),
|
55 |
+
'week' => __( 'Week', 'bookly' ),
|
56 |
+
'day' => __( 'Day', 'bookly' ),
|
57 |
+
'month' => __( 'Month', 'bookly' ),
|
58 |
+
'allDay' => __( 'All Day', 'bookly' ),
|
59 |
+
'delete' => __( 'Delete', 'bookly' ),
|
60 |
+
'noStaffSelected' => __( 'No staff selected', 'bookly' ),
|
61 |
+
'are_you_sure' => __( 'Are you sure?', 'bookly' ),
|
62 |
+
'startOfWeek' => (int) get_option( 'start_of_week' ),
|
63 |
+
) );
|
64 |
+
|
65 |
+
$this->render( 'calendar', compact( 'staff_members' ) );
|
66 |
+
}
|
67 |
+
|
68 |
+
/**
|
69 |
+
* Get data for FullCalendar.
|
70 |
+
*
|
71 |
+
* return string json
|
72 |
+
*/
|
73 |
+
public function executeGetStaffAppointments()
|
74 |
+
{
|
75 |
+
$result = array();
|
76 |
+
$staff_members = array();
|
77 |
+
$one_day = new \DateInterval( 'P1D' );
|
78 |
+
$start_date = new \DateTime( $this->getParameter( 'start' ) );
|
79 |
+
$end_date = new \DateTime( $this->getParameter( 'end' ) );
|
80 |
+
// FullCalendar sends end date as 1 day further.
|
81 |
+
$end_date->sub( $one_day );
|
82 |
+
|
83 |
+
if ( Lib\Utils\Common::isCurrentUserAdmin() ) {
|
84 |
+
$staff_members = Lib\Entities\Staff::query()
|
85 |
+
->whereIn( 'id', 1 )
|
86 |
+
->find();
|
87 |
+
} else {
|
88 |
+
$staff_members[] = Lib\Entities\Staff::query()
|
89 |
+
->where( 'wp_user_id', get_current_user_id() )
|
90 |
+
->findOne();
|
91 |
+
}
|
92 |
+
|
93 |
+
foreach ( $staff_members as $staff ) {
|
94 |
+
/** @var Lib\Entities\Staff $staff */
|
95 |
+
$result = array_merge( $result, $staff->getAppointmentsForFC( $start_date, $end_date ) );
|
96 |
+
|
97 |
+
// Schedule.
|
98 |
+
$items = $staff->getScheduleItems();
|
99 |
+
$day = clone $start_date;
|
100 |
+
// Find previous day end time.
|
101 |
+
$last_end = clone $day;
|
102 |
+
$last_end->sub( $one_day );
|
103 |
+
$w = intval( $day->format( 'w' ) );
|
104 |
+
$end_time = $items[ $w > 0 ? $w : 7 ]->get( 'end_time' );
|
105 |
+
if ( $end_time !== null ) {
|
106 |
+
$end_time = explode( ':', $end_time );
|
107 |
+
$last_end->setTime( $end_time[0], $end_time[1] );
|
108 |
+
} else {
|
109 |
+
$last_end->setTime( 24, 0 );
|
110 |
+
}
|
111 |
+
// Do the loop.
|
112 |
+
while ( $day <= $end_date ) {
|
113 |
+
do {
|
114 |
+
/** @var Lib\Entities\StaffScheduleItem $item */
|
115 |
+
$item = $items[ intval( $day->format( 'w' ) ) + 1 ];
|
116 |
+
if ( $item->get( 'start_time' ) && ! $staff->isOnHoliday( $day ) ) {
|
117 |
+
$start = $last_end->format( 'Y-m-d H:i:s' );
|
118 |
+
$end = $day->format( 'Y-m-d ' . $item->get( 'start_time' ) );
|
119 |
+
if ( $start < $end ) {
|
120 |
+
$result[] = array(
|
121 |
+
'start' => $start,
|
122 |
+
'end' => $end,
|
123 |
+
'rendering' => 'background',
|
124 |
+
'staffId' => $staff->get( 'id' ),
|
125 |
+
);
|
126 |
+
}
|
127 |
+
$last_end = clone $day;
|
128 |
+
$end_time = explode( ':', $item->get( 'end_time' ) );
|
129 |
+
$last_end->setTime( $end_time[0], $end_time[1] );
|
130 |
+
|
131 |
+
// Breaks.
|
132 |
+
foreach ( $item->getBreaksList() as $break ) {
|
133 |
+
$result[] = array(
|
134 |
+
'start' => $day->format( 'Y-m-d ' . $break['start_time'] ),
|
135 |
+
'end' => $day->format( 'Y-m-d ' . $break['end_time'] ),
|
136 |
+
'rendering' => 'background',
|
137 |
+
'staffId' => $staff->get( 'id' ),
|
138 |
+
);
|
139 |
+
}
|
140 |
+
|
141 |
+
break;
|
142 |
+
}
|
143 |
+
|
144 |
+
$result[] = array(
|
145 |
+
'start' => $last_end->format( 'Y-m-d H:i:s' ),
|
146 |
+
'end' => $day->format( 'Y-m-d 24:00:00' ),
|
147 |
+
'rendering' => 'background',
|
148 |
+
'staffId' => $staff->get( 'id' ),
|
149 |
+
);
|
150 |
+
$last_end = clone $day;
|
151 |
+
$last_end->setTime( 24, 0 );
|
152 |
+
|
153 |
+
} while ( 0 );
|
154 |
+
|
155 |
+
$day->add( $one_day );
|
156 |
+
}
|
157 |
+
|
158 |
+
if ( $last_end->format( 'H' ) != 24 ) {
|
159 |
+
$result[] = array(
|
160 |
+
'start' => $last_end->format( 'Y-m-d H:i:s' ),
|
161 |
+
'end' => $last_end->format( 'Y-m-d 24:00:00' ),
|
162 |
+
'rendering' => 'background',
|
163 |
+
'staffId' => $staff->get( 'id' ),
|
164 |
+
);
|
165 |
+
}
|
166 |
+
}
|
167 |
+
|
168 |
+
wp_send_json( $result );
|
169 |
+
}
|
170 |
+
|
171 |
+
/**
|
172 |
+
* Get data needed for appointment form initialisation.
|
173 |
+
*/
|
174 |
+
public function executeGetDataForAppointmentForm()
|
175 |
+
{
|
176 |
+
$result = array(
|
177 |
+
'staff' => array(),
|
178 |
+
'customers' => array(),
|
179 |
+
'start_time' => array(),
|
180 |
+
'end_time' => array(),
|
181 |
+
'time_interval' => Lib\Config::getTimeSlotLength(),
|
182 |
+
'status' => array(
|
183 |
+
'items' => array(
|
184 |
+
'pending' => Lib\Entities\CustomerAppointment::statusToString( Lib\Entities\CustomerAppointment::STATUS_PENDING ),
|
185 |
+
'approved' => Lib\Entities\CustomerAppointment::statusToString( Lib\Entities\CustomerAppointment::STATUS_APPROVED ),
|
186 |
+
'cancelled' => Lib\Entities\CustomerAppointment::statusToString( Lib\Entities\CustomerAppointment::STATUS_CANCELLED ),
|
187 |
+
),
|
188 |
+
'default' => get_option( 'ab_settings_default_appointment_status' ),
|
189 |
+
),
|
190 |
+
);
|
191 |
+
|
192 |
+
// Staff list.
|
193 |
+
$staff_members = Lib\Utils\Common::isCurrentUserAdmin()
|
194 |
+
? Lib\Entities\Staff::query()->sortBy( 'position' )->find()
|
195 |
+
: Lib\Entities\Staff::query()->where( 'wp_user_id', get_current_user_id() )->find();
|
196 |
+
|
197 |
+
/** @var Lib\Entities\Staff $staff_member */
|
198 |
+
foreach ( $staff_members as $staff_member ) {
|
199 |
+
$services = array();
|
200 |
+
foreach ( $staff_member->getStaffServices() as $staff_service ) {
|
201 |
+
$services[] = array(
|
202 |
+
'id' => $staff_service->service->get( 'id' ),
|
203 |
+
'title' => sprintf(
|
204 |
+
'%s (%s)',
|
205 |
+
$staff_service->service->get( 'title' ),
|
206 |
+
Lib\Utils\DateTime::secondsToInterval( $staff_service->service->get( 'duration' ) )
|
207 |
+
),
|
208 |
+
'duration' => $staff_service->service->get( 'duration' ),
|
209 |
+
'capacity' => $staff_service->get( 'capacity' )
|
210 |
+
);
|
211 |
+
}
|
212 |
+
$result['staff'][] = array(
|
213 |
+
'id' => $staff_member->get( 'id' ),
|
214 |
+
'full_name' => $staff_member->get( 'full_name' ),
|
215 |
+
'services' => $services
|
216 |
+
);
|
217 |
+
}
|
218 |
+
|
219 |
+
// Customers list.
|
220 |
+
foreach ( Lib\Entities\Customer::query()->sortBy( 'name' )->find() as $customer ) {
|
221 |
+
$name = $customer->get( 'name' );
|
222 |
+
if ( $customer->get( 'email' ) != '' || $customer->get( 'phone' ) != '' ) {
|
223 |
+
$name .= ' (' . trim( $customer->get( 'email' ) . ', ' . $customer->get( 'phone' ) , ', ' ) . ')';
|
224 |
+
}
|
225 |
+
|
226 |
+
$result['customers'][] = array(
|
227 |
+
'id' => $customer->get( 'id' ),
|
228 |
+
'name' => $name,
|
229 |
+
'custom_fields' => array(),
|
230 |
+
'number_of_persons' => 1,
|
231 |
+
);
|
232 |
+
}
|
233 |
+
|
234 |
+
// Time list.
|
235 |
+
$ts_length = Lib\Config::getTimeSlotLength();
|
236 |
+
$time_start = 0;
|
237 |
+
$time_end = DAY_IN_SECONDS * 2;
|
238 |
+
|
239 |
+
// Run the loop.
|
240 |
+
while ( $time_start <= $time_end ) {
|
241 |
+
$slot = array(
|
242 |
+
'value' => Lib\Utils\DateTime::buildTimeString( $time_start, false ),
|
243 |
+
'title' => Lib\Utils\DateTime::formatTime( $time_start )
|
244 |
+
);
|
245 |
+
if ( $time_start < DAY_IN_SECONDS ) {
|
246 |
+
$result['start_time'][] = $slot;
|
247 |
+
}
|
248 |
+
$result['end_time'][] = $slot;
|
249 |
+
$time_start += $ts_length;
|
250 |
+
}
|
251 |
+
|
252 |
+
wp_send_json( $result );
|
253 |
+
}
|
254 |
+
|
255 |
+
/**
|
256 |
+
* Get appointment data when editing an appointment.
|
257 |
+
*/
|
258 |
+
public function executeGetDataForAppointment()
|
259 |
+
{
|
260 |
+
$response = array( 'success' => false, 'data' => array( 'customers' => array() ) );
|
261 |
+
|
262 |
+
$appointment = new Lib\Entities\Appointment();
|
263 |
+
if ( $appointment->load( $this->getParameter( 'id' ) ) ) {
|
264 |
+
$response['success'] = true;
|
265 |
+
|
266 |
+
$info = Lib\Entities\Appointment::query( 'a' )
|
267 |
+
->select( 'ss.capacity AS max_capacity, SUM( ca.number_of_persons ) AS total_number_of_persons, a.staff_id, a.service_id, a.start_date, a.end_date, a.internal_note' )
|
268 |
+
->leftJoin( 'CustomerAppointment', 'ca', 'ca.appointment_id = a.id' )
|
269 |
+
->leftJoin( 'StaffService', 'ss', 'ss.staff_id = a.staff_id AND ss.service_id = a.service_id' )
|
270 |
+
->where( 'a.id', $appointment->get( 'id' ) )
|
271 |
+
->fetchRow();
|
272 |
+
|
273 |
+
$response['data']['total_number_of_persons'] = $info['total_number_of_persons'];
|
274 |
+
$response['data']['max_capacity'] = $info['max_capacity'];
|
275 |
+
$response['data']['start_date'] = $info['start_date'];
|
276 |
+
$response['data']['end_date'] = $info['end_date'];
|
277 |
+
$response['data']['staff_id'] = $info['staff_id'];
|
278 |
+
$response['data']['service_id'] = $info['service_id'];
|
279 |
+
$response['data']['internal_note'] = $info['internal_note'];
|
280 |
+
$customers = Lib\Entities\CustomerAppointment::query( 'ca' )
|
281 |
+
->select( 'ca.id,
|
282 |
+
ca.customer_id,
|
283 |
+
ca.custom_fields,
|
284 |
+
ca.extras,
|
285 |
+
ca.number_of_persons,
|
286 |
+
ca.status,
|
287 |
+
ca.payment_id,
|
288 |
+
ca.compound_service_id,
|
289 |
+
ca.compound_token,
|
290 |
+
ca.location_id,
|
291 |
+
p.paid AS payment,
|
292 |
+
p.total AS payment_total,
|
293 |
+
p.type AS payment_type,
|
294 |
+
p.details AS payment_details,
|
295 |
+
p.status AS payment_status' )
|
296 |
+
->leftJoin( 'Payment', 'p', 'p.id = ca.payment_id' )
|
297 |
+
->where( 'ca.appointment_id', $appointment->get( 'id' ) )
|
298 |
+
->fetchArray();
|
299 |
+
foreach ( $customers as $customer ) {
|
300 |
+
$payment_title = '';
|
301 |
+
if ( $customer['payment'] !== null ) {
|
302 |
+
$payment_title = Lib\Utils\Common::formatPrice( $customer['payment'] );
|
303 |
+
if ( $customer['payment'] != $customer['payment_total'] ) {
|
304 |
+
$payment_title = sprintf( __( '%s of %s', 'bookly' ), $payment_title, Lib\Utils\Common::formatPrice( $customer['payment_total'] ) );
|
305 |
+
}
|
306 |
+
$payment_title .= sprintf(
|
307 |
+
' %s <span%s>%s</span>',
|
308 |
+
Lib\Entities\Payment::typeToString( $customer['payment_type'] ),
|
309 |
+
$customer['payment_status'] == Lib\Entities\Payment::STATUS_PENDING ? ' class="text-danger"' : '',
|
310 |
+
Lib\Entities\Payment::statusToString( $customer['payment_status'] )
|
311 |
+
);
|
312 |
+
}
|
313 |
+
$compound_service = '';
|
314 |
+
if ( $customer['compound_service_id'] !== null ) {
|
315 |
+
$service = new Lib\Entities\Service();
|
316 |
+
if ( $service->load( $customer['compound_service_id'] ) ) {
|
317 |
+
$compound_service = $service->getTitle();
|
318 |
+
}
|
319 |
+
}
|
320 |
+
$response['data']['customers'][] = array(
|
321 |
+
'id' => $customer['customer_id'],
|
322 |
+
'ca_id' => $customer['id'],
|
323 |
+
'compound_service' => $compound_service,
|
324 |
+
'compound_token' => $customer['compound_token'],
|
325 |
+
'custom_fields' => (array) json_decode( $customer['custom_fields'], true ),
|
326 |
+
'extras' => (array) json_decode( $customer['extras'], true ),
|
327 |
+
'location_id' => $customer['location_id'],
|
328 |
+
'number_of_persons' => $customer['number_of_persons'],
|
329 |
+
'payment_id' => $customer['payment_id'],
|
330 |
+
'payment_type' => $customer['payment'] != $customer['payment_total'] ? 'partial' : 'full',
|
331 |
+
'payment_title' => $payment_title,
|
332 |
+
'status' => $customer['status'],
|
333 |
+
);
|
334 |
+
}
|
335 |
+
}
|
336 |
+
wp_send_json( $response );
|
337 |
+
}
|
338 |
+
|
339 |
+
/**
|
340 |
+
* Save appointment form (for both create and edit).
|
341 |
+
*/
|
342 |
+
public function executeSaveAppointmentForm()
|
343 |
+
{
|
344 |
+
$response = array( 'success' => false );
|
345 |
+
|
346 |
+
$internal_note = $this->getParameter( 'internal_note' );
|
347 |
+
$start_date = $this->getParameter( 'start_date' );
|
348 |
+
$end_date = $this->getParameter( 'end_date' );
|
349 |
+
$staff_id = 1;
|
350 |
+
$service_id = $this->getParameter( 'service_id' );
|
351 |
+
$appointment_id = $this->getParameter( 'id', 0 );
|
352 |
+
$customers = json_decode( $this->getParameter( 'customers', '[]' ), true );
|
353 |
+
|
354 |
+
$staff_service = new Lib\Entities\StaffService();
|
355 |
+
$staff_service->loadBy( array(
|
356 |
+
'staff_id' => $staff_id,
|
357 |
+
'service_id' => $service_id
|
358 |
+
) );
|
359 |
+
|
360 |
+
// Check for errors.
|
361 |
+
if ( ! $start_date ) {
|
362 |
+
$response['errors']['time_interval'] = __( 'Start time must not be empty', 'bookly' );
|
363 |
+
} elseif ( ! $end_date ) {
|
364 |
+
$response['errors']['time_interval'] = __( 'End time must not be empty', 'bookly' );
|
365 |
+
} elseif ( $start_date == $end_date ) {
|
366 |
+
$response['errors']['time_interval'] = __( 'End time must not be equal to start time', 'bookly' );
|
367 |
+
}
|
368 |
+
if ( ! $service_id ) {
|
369 |
+
$response['errors']['service_required'] = true;
|
370 |
+
}
|
371 |
+
if ( empty ( $customers ) ) {
|
372 |
+
$response['errors']['customers_required'] = true;
|
373 |
+
}
|
374 |
+
$total_number_of_persons = 0;
|
375 |
+
$max_extras_duration = 0;
|
376 |
+
foreach ( $customers as $customer ) {
|
377 |
+
if ( $customer['status'] != Lib\Entities\CustomerAppointment::STATUS_CANCELLED ) {
|
378 |
+
$total_number_of_persons += $customer['number_of_persons'];
|
379 |
+
$extras_duration = apply_filters( 'bookly_extras_get_total_duration', 0, $customer['extras'] );
|
380 |
+
if ( $extras_duration > $max_extras_duration ) {
|
381 |
+
$max_extras_duration = $extras_duration;
|
382 |
+
}
|
383 |
+
}
|
384 |
+
}
|
385 |
+
if ( $total_number_of_persons > $staff_service->get( 'capacity' ) ) {
|
386 |
+
$response['errors']['overflow_capacity'] = sprintf(
|
387 |
+
__( 'The number of customers should not be more than %d', 'bookly' ),
|
388 |
+
$staff_service->get( 'capacity' )
|
389 |
+
);
|
390 |
+
}
|
391 |
+
$total_end_date = $end_date;
|
392 |
+
if ( $max_extras_duration > 0 ) {
|
393 |
+
$total_end_date = date_create( $end_date )->modify( '+' . $max_extras_duration . ' sec' )->format( 'Y-m-d H:i:s' );
|
394 |
+
}
|
395 |
+
if ( ! $this->dateIntervalIsAvailableForAppointment( $start_date, $total_end_date, $staff_id, $appointment_id ) ) {
|
396 |
+
$response['errors']['date_interval_not_available'] = true;
|
397 |
+
}
|
398 |
+
|
399 |
+
// If no errors then try to save the appointment.
|
400 |
+
if ( ! isset ( $response['errors'] ) ) {
|
401 |
+
$appointment = new Lib\Entities\Appointment();
|
402 |
+
if ( $appointment_id ) {
|
403 |
+
// Edit.
|
404 |
+
$appointment->load( $appointment_id );
|
405 |
+
}
|
406 |
+
$appointment->set( 'internal_note', $internal_note );
|
407 |
+
$appointment->set( 'start_date', $start_date );
|
408 |
+
$appointment->set( 'end_date', $end_date );
|
409 |
+
$appointment->set( 'staff_id', $staff_id );
|
410 |
+
$appointment->set( 'service_id', $service_id );
|
411 |
+
$appointment->set( 'extras_duration', $max_extras_duration );
|
412 |
+
|
413 |
+
if ( $appointment->save() !== false ) {
|
414 |
+
// Save customer appointments.
|
415 |
+
$ca_status_changed = $appointment->saveCustomerAppointments( $customers );
|
416 |
+
$customer_appointments = $appointment->getCustomerAppointments( true );
|
417 |
+
|
418 |
+
// Google Calendar.
|
419 |
+
$appointment->handleGoogleCalendar();
|
420 |
+
|
421 |
+
// Send notifications.
|
422 |
+
$notification = $this->getParameter( 'notification' );
|
423 |
+
if ( $notification != 'no' ) {
|
424 |
+
foreach ( $customer_appointments as $ca ) {
|
425 |
+
// Send notification.
|
426 |
+
if ( $notification == 'all' || in_array( $ca->get( 'id' ), $ca_status_changed ) ) {
|
427 |
+
Lib\NotificationSender::send( $ca );
|
428 |
+
}
|
429 |
+
}
|
430 |
+
}
|
431 |
+
|
432 |
+
// Prepare response.
|
433 |
+
$desc = '';
|
434 |
+
// Display customer information only if there is 1 customer.
|
435 |
+
if ( count( $customer_appointments ) == 1 ) {
|
436 |
+
// Customer information.
|
437 |
+
$customer = $customer_appointments[0]->customer;
|
438 |
+
foreach ( array( 'name', 'phone', 'email' ) as $data_entry ) {
|
439 |
+
$entry_value = $customer->get( $data_entry );
|
440 |
+
if ( $entry_value ) {
|
441 |
+
$desc .= '<div>' . esc_html( $entry_value ) . '</div>';
|
442 |
+
}
|
443 |
+
}
|
444 |
+
$desc = apply_filters( 'bookly_calendar_appointment_description', $desc, $customer_appointments[0] );
|
445 |
+
// Custom fields.
|
446 |
+
$custom_fields = $customer_appointments[0]->getCustomFields();
|
447 |
+
if ( ! empty( $custom_fields ) ) {
|
448 |
+
$desc .= '<br/>';
|
449 |
+
foreach ( $custom_fields as $custom_field ) {
|
450 |
+
$desc .= '<div>' . wp_strip_all_tags( $custom_field['label'] ) . ': ' . esc_html( $custom_field['value'] ) . '</div>';
|
451 |
+
}
|
452 |
+
}
|
453 |
+
// Payment.
|
454 |
+
$payment = Lib\Entities\Payment::query()->select( 'total, type AS payment_gateway, status AS payment_status' )
|
455 |
+
->where( 'id', $customer_appointments[0]->get( 'payment_id' ) )
|
456 |
+
->fetchRow();
|
457 |
+
if ( $payment ) {
|
458 |
+
$desc .= sprintf(
|
459 |
+
'<br/><div>%s: %s %s %s</div>',
|
460 |
+
__( 'Payment', 'bookly' ),
|
461 |
+
Lib\Utils\Common::formatPrice( $payment['total'] ),
|
462 |
+
Lib\Entities\Payment::typeToString( $payment['payment_gateway'] ),
|
463 |
+
Lib\Entities\Payment::statusToString( $payment['payment_status'] )
|
464 |
+
);
|
465 |
+
}
|
466 |
+
// Status.
|
467 |
+
$desc .= sprintf( '<br/><div>%s: %s</div>', __( 'Status', 'bookly' ), $customer_appointments[0]->getStatusTitle() );
|
468 |
+
// Signed up & Capacity.
|
469 |
+
if ( $staff_service->get( 'capacity' ) > 1 ) {
|
470 |
+
$signed_up = 0;
|
471 |
+
foreach ( $customer_appointments as $ca ) {
|
472 |
+
$signed_up += $ca->get( 'number_of_persons' );
|
473 |
+
}
|
474 |
+
$desc .= '<br/><div>' . __( 'Signed up', 'bookly' ) . ': ' . $signed_up . '</div>';
|
475 |
+
$desc .= '<div>' . __( 'Capacity', 'bookly' ) . ': ' . $staff_service->get( 'capacity' ) . '</div>';
|
476 |
+
}
|
477 |
+
} else {
|
478 |
+
if ( $staff_service->get( 'capacity' ) > 1 ) {
|
479 |
+
$signed_up = 0;
|
480 |
+
foreach ( $customer_appointments as $ca ) {
|
481 |
+
$signed_up += $ca->get( 'number_of_persons' );
|
482 |
+
}
|
483 |
+
$desc .= '<div>' . __( 'Signed up', 'bookly' ) . ': ' . $signed_up . '</div>';
|
484 |
+
$desc .= '<div>' . __( 'Capacity', 'bookly' ) . ': ' . $staff_service->get( 'capacity' ) . '</div>';
|
485 |
+
}
|
486 |
+
}
|
487 |
+
|
488 |
+
$startDate = new \DateTime( $appointment->get( 'start_date' ) );
|
489 |
+
$endDate = new \DateTime( $appointment->get( 'end_date' ) );
|
490 |
+
$endDate->modify( '+' . (int) $appointment->get( 'extras_duration' ) . ' sec' );
|
491 |
+
|
492 |
+
$service = new Lib\Entities\Service();
|
493 |
+
$service->load( $service_id );
|
494 |
+
|
495 |
+
$response['success'] = true;
|
496 |
+
$response['data'] = array(
|
497 |
+
'id' => (int) $appointment->get( 'id' ),
|
498 |
+
'start' => $startDate->format( 'Y-m-d H:i:s' ),
|
499 |
+
'end' => $endDate->format( 'Y-m-d H:i:s' ),
|
500 |
+
'desc' => $desc,
|
501 |
+
'title' => $service->get( 'title' ) ? $service->get( 'title' ) : __( 'Untitled', 'bookly' ),
|
502 |
+
'color' => $service->get( 'color' ),
|
503 |
+
'staffId' => $appointment->get( 'staff_id' ),
|
504 |
+
);
|
505 |
+
} else {
|
506 |
+
$response['errors'] = array( 'db' => __( 'Could not save appointment in database.', 'bookly' ) );
|
507 |
+
}
|
508 |
+
}
|
509 |
+
|
510 |
+
wp_send_json( $response );
|
511 |
+
}
|
512 |
+
|
513 |
+
public function executeCheckAppointmentDateSelection()
|
514 |
+
{
|
515 |
+
$start_date = $this->getParameter( 'start_date' );
|
516 |
+
$end_date = $this->getParameter( 'end_date' );
|
517 |
+
$staff_id = 1;
|
518 |
+
$service_id = $this->getParameter( 'service_id' );
|
519 |
+
$appointment_id = $this->getParameter( 'appointment_id' );
|
520 |
+
$timestamp_diff = strtotime( $end_date ) - strtotime( $start_date );
|
521 |
+
|
522 |
+
$result = array(
|
523 |
+
'date_interval_not_available' => false,
|
524 |
+
'date_interval_warning' => false,
|
525 |
+
);
|
526 |
+
|
527 |
+
if ( ! $this->dateIntervalIsAvailableForAppointment( $start_date, $end_date, $staff_id, $appointment_id ) ) {
|
528 |
+
$result['date_interval_not_available'] = true;
|
529 |
+
}
|
530 |
+
|
531 |
+
if ( $service_id ) {
|
532 |
+
$service = new Lib\Entities\Service();
|
533 |
+
$service->load( $service_id );
|
534 |
+
|
535 |
+
$duration = $service->get( 'duration' );
|
536 |
+
|
537 |
+
// Service duration interval is not equal to.
|
538 |
+
$result['date_interval_warning'] = ( $timestamp_diff != $duration );
|
539 |
+
}
|
540 |
+
|
541 |
+
wp_send_json( $result );
|
542 |
+
}
|
543 |
+
|
544 |
+
public function executeDeleteAppointment()
|
545 |
+
{
|
546 |
+
$appointment = new Lib\Entities\Appointment();
|
547 |
+
$appointment->load( $this->getParameter( 'appointment_id' ) );
|
548 |
+
$appointment->delete();
|
549 |
+
exit;
|
550 |
+
}
|
551 |
+
|
552 |
+
/**
|
553 |
+
* @param $start_date
|
554 |
+
* @param $end_date
|
555 |
+
* @param $staff_id
|
556 |
+
* @param $appointment_id
|
557 |
+
* @return bool
|
558 |
+
*/
|
559 |
+
private function dateIntervalIsAvailableForAppointment( $start_date, $end_date, $staff_id, $appointment_id )
|
560 |
+
{
|
561 |
+
return Lib\Entities\Appointment::query( 'a' )
|
562 |
+
->leftJoin( 'CustomerAppointment', 'ca', 'ca.appointment_id = a.id' )
|
563 |
+
->whereNot( 'a.id', $appointment_id )
|
564 |
+
->where( 'a.staff_id', $staff_id )
|
565 |
+
->whereNot( 'ca.status', Lib\Entities\CustomerAppointment::STATUS_CANCELLED )
|
566 |
+
->whereRaw(
|
567 |
+
'(start_date > %s AND start_date < %s OR (end_date > %s AND end_date < %s) OR (start_date < %s AND end_date > %s) OR (start_date = %s OR end_date = %s) )',
|
568 |
+
array( $start_date, $end_date, $start_date, $end_date, $start_date, $end_date, $start_date, $end_date )
|
569 |
+
)
|
570 |
+
->count() == 0;
|
571 |
+
}
|
572 |
+
|
573 |
+
/**
|
574 |
+
* Override parent method to add 'wp_ajax_ab_' prefix
|
575 |
+
* so current 'execute*' methods look nicer.
|
576 |
+
*
|
577 |
+
* @param string $prefix
|
578 |
+
*/
|
579 |
+
protected function registerWpActions( $prefix = '' )
|
580 |
+
{
|
581 |
+
parent::registerWpActions( 'wp_ajax_ab_' );
|
582 |
+
}
|
583 |
+
|
584 |
+
}
|
backend/modules/calendar/forms/AB_AppointmentForm.php
DELETED
@@ -1,22 +0,0 @@
|
|
1 |
-
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Class AB_AppointmentForm
|
5 |
-
*/
|
6 |
-
class AB_AppointmentForm extends AB_Form {
|
7 |
-
|
8 |
-
/**
|
9 |
-
* Constructor.
|
10 |
-
*/
|
11 |
-
public function __construct()
|
12 |
-
{
|
13 |
-
parent::$entity_class = 'AB_Appointment';
|
14 |
-
parent::__construct();
|
15 |
-
}
|
16 |
-
|
17 |
-
public function configure()
|
18 |
-
{
|
19 |
-
//$this->setFields( array() );
|
20 |
-
}
|
21 |
-
|
22 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/modules/calendar/forms/AB_CustomerAppointmentForm.php
DELETED
@@ -1,17 +0,0 @@
|
|
1 |
-
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Class AB_AppointmentForm
|
5 |
-
*/
|
6 |
-
class AB_CustomerAppointmentForm extends AB_Form {
|
7 |
-
|
8 |
-
/**
|
9 |
-
* Constructor.
|
10 |
-
*/
|
11 |
-
public function __construct()
|
12 |
-
{
|
13 |
-
parent::$entity_class = 'AB_CustomerAppointment';
|
14 |
-
parent::__construct();
|
15 |
-
}
|
16 |
-
|
17 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/modules/calendar/resources/css/calendar.css
DELETED
@@ -1,215 +0,0 @@
|
|
1 |
-
/* calendar nav */
|
2 |
-
.ab-calendar-day, .ab-calendar-week, .ab-calendar-today, .btn-list, .btn-dropdown {
|
3 |
-
display: inline-block;
|
4 |
-
padding: 2px 10px;
|
5 |
-
margin-bottom: 0;
|
6 |
-
font-size: 12px;
|
7 |
-
line-height: 20px;
|
8 |
-
color: #fff;
|
9 |
-
text-align: center;
|
10 |
-
text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75);
|
11 |
-
vertical-align: middle;
|
12 |
-
cursor: pointer;
|
13 |
-
background-color: #21759b;
|
14 |
-
background-image: -webkit-gradient(linear,left top,left bottom,from(#2a95c5),to(#21759b));
|
15 |
-
background-image: -webkit-linear-gradient(top,#2a95c5,#21759b);
|
16 |
-
background-image: -moz-linear-gradient(top,#2a95c5,#21759b);
|
17 |
-
background-image: -ms-linear-gradient(top,#2a95c5,#21759b);
|
18 |
-
background-image: -o-linear-gradient(top,#2a95c5,#21759b);
|
19 |
-
background-image: linear-gradient(to bottom,#2a95c5,#21759b);
|
20 |
-
background-repeat: repeat-x;
|
21 |
-
border: 1px solid #21759b;
|
22 |
-
border-color: #e6e6e6 #e6e6e6 #1e6a8d;
|
23 |
-
border-bottom-color: #b3b3b3;
|
24 |
-
-webkit-border-radius: 4px;
|
25 |
-
-moz-border-radius: 4px;
|
26 |
-
border-radius: 4px;
|
27 |
-
-webkit-box-shadow: inset 0 1px 0 rgba(120,200,230,0.5);
|
28 |
-
moz-box-shadow: inset 0 1px 0 rgba(120,200,230,0.5);
|
29 |
-
box-shadow: inset 0 1px 0 rgba(120,200,230,0.5);
|
30 |
-
}
|
31 |
-
.ab-calendar-day { border-bottom-right-radius: 0px; border-top-right-radius: 0px; margin-top: 0; }
|
32 |
-
.ab-calendar-week { border-bottom-left-radius: 0px; border-top-left-radius: 0px; margin-left: -3px; margin-top: 0; border-left: 0; }
|
33 |
-
.ab-calendar-today { margin: 0 5px; border-radius: 3px; }
|
34 |
-
.ab-nav-calendar { height: 30px; }
|
35 |
-
.ab-nav-calendar .ab-button-active { background: #1f6a8c; }
|
36 |
-
.ab-nav-calendar .ab-popup-wrapper { margin-left: 10px; }
|
37 |
-
.ab-popup-table td { white-space: nowrap; padding: 5px; border: 0!important; }
|
38 |
-
.ab-popup-table td > p { margin: 0; }
|
39 |
-
|
40 |
-
.ab-btn-group .btn-list,
|
41 |
-
.ab-btn-group .btn-dropdown {
|
42 |
-
margin: 0;
|
43 |
-
border: 0;
|
44 |
-
float: left;
|
45 |
-
position: relative;
|
46 |
-
}
|
47 |
-
.ab-btn-group .btn-list {
|
48 |
-
border-bottom-right-radius: 0px;
|
49 |
-
border-top-right-radius: 0px;
|
50 |
-
padding-left: 25px;
|
51 |
-
}
|
52 |
-
.ab-btn-group .btn-dropdown {
|
53 |
-
font-size: 9px;
|
54 |
-
padding-left: 0;
|
55 |
-
border-bottom-left-radius: 0px;
|
56 |
-
border-top-left-radius: 0px;
|
57 |
-
}
|
58 |
-
.ab-icon-button {
|
59 |
-
position: absolute;
|
60 |
-
top: 3px;
|
61 |
-
left: 3px;
|
62 |
-
width: 15px;
|
63 |
-
height: 15px;
|
64 |
-
}
|
65 |
-
.ab-icon-button.ab-services {
|
66 |
-
background: url("../images/box-small.png") 2px 2px no-repeat;
|
67 |
-
}
|
68 |
-
td.fc-axis.fc-time.fc-widget-content { vertical-align: top; }
|
69 |
-
|
70 |
-
/** week picker */
|
71 |
-
.ab-week-picker-wrapper { width: 350px; position: relative; margin-top: 2px; }
|
72 |
-
.ab-week-picker-wrapper i.glyphicon-calendar { position: absolute; left: 46px; top: 8px; z-index: 9; }
|
73 |
-
.ab-week-picker-wrapper .ab-week-picker { clear: both; left: -1px; position: absolute; top: 30px; z-index: 9; display: none; }
|
74 |
-
.ab-week-picker-data { border-radius: 3px; background: url("../images/calendar.png") 2px 4px no-repeat; padding: 4px 7px 4px 25px; height: 16px; border: 1px solid black; }
|
75 |
-
#appendedPrependedInput { background-color: #FFFFFF; cursor: default; }
|
76 |
-
|
77 |
-
/** appointment dialog */
|
78 |
-
.ab-appointment-popup .ui-widget-header {
|
79 |
-
background: none!important;
|
80 |
-
border: none!important;
|
81 |
-
border-bottom: 1px solid #eee!important;
|
82 |
-
margin: -5px -5px 15px -5px!important;
|
83 |
-
font-size: 24.5px!important;
|
84 |
-
font-family: sans-serif!important;
|
85 |
-
line-height: 26px!important;
|
86 |
-
padding: 9px 15px!important;
|
87 |
-
}
|
88 |
-
|
89 |
-
.ab-appointment-popup .loading-indicator img { display: block; margin: 20px auto; }
|
90 |
-
.ab-appointment-popup .dialog-button-wrapper { }
|
91 |
-
|
92 |
-
.ab-appointment-popup .dialog-content .ab-appointment-field-value { width: 110px; vertical-align: top; }
|
93 |
-
.ab-appointment-popup .dialog-content td { padding-top: 7px; text-align: left; white-space: nowrap; }
|
94 |
-
.ab-appointment-popup .dialog-content select { width: auto; max-width: 100%; }
|
95 |
-
.ab-appointment-popup .dialog-content { color: #333333!important; font-family: "Helvetica Neue",Helvetica,Arial,sans-serif!important; font-size: 14px!important; }
|
96 |
-
.ab-appointment-popup .dialog-content .ab-reset-form { color: #21759b; }
|
97 |
-
.ab-appointment-popup .dialog-content .ab-reset-form:hover { color: #d54e21; }
|
98 |
-
|
99 |
-
/** calendars */
|
100 |
-
#day_calendar_wrapper { margin-top: 55px; }
|
101 |
-
.ab-calendar-inner .table-responsive { position: relative; }
|
102 |
-
.ab-calendar-inner .ab-loading-inner {
|
103 |
-
position: absolute;
|
104 |
-
left: 0;
|
105 |
-
top: 50px;
|
106 |
-
right: 0;
|
107 |
-
bottom: 0;
|
108 |
-
background-color: rgba(255, 255, 255, 0.7);
|
109 |
-
z-index: 9;
|
110 |
-
}
|
111 |
-
.ab-calendar-inner .ab-loading-inner .ab-loader { position: absolute; left: 50%; top: 50%; width: auto; }
|
112 |
-
|
113 |
-
/** wp elements */
|
114 |
-
#update-nag, .update-nag, #wpfooter { display: none; }
|
115 |
-
#wpbody-content { padding-bottom: 0; }
|
116 |
-
|
117 |
-
.ab-date-picker { border: 1px solid black!important; padding: 4px 4px 4px 7px; border-radius: 3px; width: 65px; }
|
118 |
-
.ab-date-paginator { margin-left: 10px; }
|
119 |
-
.ab-date-paginator > a {
|
120 |
-
float: left;
|
121 |
-
text-decoration: none;
|
122 |
-
color: black;
|
123 |
-
padding: 4px 2px;
|
124 |
-
display: block;
|
125 |
-
border: 1px solid black;
|
126 |
-
margin-left: -1px;
|
127 |
-
width: 18px;
|
128 |
-
text-align: center;
|
129 |
-
}
|
130 |
-
.ab-date-paginator > a.first {
|
131 |
-
border-bottom-left-radius: 10px;
|
132 |
-
-webkit-border-bottom-left-radius: 10px;
|
133 |
-
-moz-border-bottom-left-radius: 10px;
|
134 |
-
-o-border-bottom-left-radius: 10px;
|
135 |
-
border-top-left-radius: 10px;
|
136 |
-
-webkit-border-top-left-radius: 10px;
|
137 |
-
-moz-border-top-left-radius: 10px;
|
138 |
-
-o-border-top-left-radius: 10px;
|
139 |
-
}
|
140 |
-
.ab-date-paginator > a.last {
|
141 |
-
border-bottom-right-radius: 10px;
|
142 |
-
-webkit-border-bottom-right-radius: 10px;
|
143 |
-
-moz-border-bottom-right-radius: 10px;
|
144 |
-
-o-border-bottom-right-radius: 10px;
|
145 |
-
border-top-right-radius: 10px;
|
146 |
-
-webkit-border-top-right-radius: 10px;
|
147 |
-
-moz-border-top-right-radius: 10px;
|
148 |
-
-o-border-top-right-radius: 10px;
|
149 |
-
}
|
150 |
-
|
151 |
-
.fc-cal-event {
|
152 |
-
background-color: #DDDDDD;
|
153 |
-
line-height: 16px!important;
|
154 |
-
opacity: 1;
|
155 |
-
}
|
156 |
-
.fc-cal-event .fc-time {
|
157 |
-
background-color : #DDDDDD;
|
158 |
-
border-left-color : #DDDDDD;
|
159 |
-
border-right-color : #DDDDDD;
|
160 |
-
border-top-color : #DDDDDD;
|
161 |
-
border-bottom-color : #ABABAB;
|
162 |
-
padding : 3.5px 0;
|
163 |
-
}
|
164 |
-
|
165 |
-
.pagination { margin: 0!important; }
|
166 |
-
|
167 |
-
.nav-tabs > .active > a { background: #1f6a8c!important; color: white!important; border-left: 0!important; }
|
168 |
-
.ab-date-calendar { padding-left: 25px!important; background: url("../images/calendar.png") 5px 8px no-repeat; }
|
169 |
-
|
170 |
-
/* Custom fields */
|
171 |
-
.dialog-content .ab-formGroup { margin-bottom: 15px; }
|
172 |
-
.ab-customer-list { margin: 6px 0 0 0; }
|
173 |
-
.ab-create-customer > a,
|
174 |
-
.ab-customer-list li a { cursor: pointer; border-bottom: 1px dashed #21759B; }
|
175 |
-
.ab-customer-list li a + .icon-remove { cursor: pointer; }
|
176 |
-
.ab-create-customer > a:hover,
|
177 |
-
.ab-customer-list li a:hover { text-decoration: none; color: #21759b; border-color: #21759b; }
|
178 |
-
|
179 |
-
/* Appointment dialog */
|
180 |
-
/*#ab_appointment_dialog { overflow: visible; }*/
|
181 |
-
#ab_appointment_dialog .chzn-container { font-family: Verdana,Arial,sans-serif; font-size: 14px; }
|
182 |
-
#ab_custom_fields_dialog * { font-family: "Helvetica Neue",Helvetica,Arial,sans-serif!important; }
|
183 |
-
|
184 |
-
.fc-content .icon-rt { float: right; position: absolute; top: 4px; right: 4px; cursor: pointer; }
|
185 |
-
.fc-view-container { box-shadow: 0 0 3px silver; }
|
186 |
-
.fc-widget-header table th { padding: 10px; }
|
187 |
-
.ab-head-calendar { margin-bottom: 0!important; }
|
188 |
-
.fc button { padding: 6px 12px!important; height: auto!important; }
|
189 |
-
.fc-time-grid-event.fc-short .fc-time:before { font-size: 12px; }
|
190 |
-
.fc-ltr .fc-time-grid .fc-event-container { margin: 0!important; }
|
191 |
-
.fc-time-grid-event .fc-time,
|
192 |
-
.fc-time-grid-event.fc-short .fc-title { font-size: 12px!important; padding: 0 5px!important; padding-top: 3px!important; }
|
193 |
-
.fc-time-grid-event.fc-short .fc-title { padding-top: 5px!important; display: block!important; }
|
194 |
-
.fc-day-grid-event,
|
195 |
-
.fc-time-grid-event { cursor: pointer; }
|
196 |
-
.fc-time-grid-event .fc-title { padding: 0 5px!important; padding-top: 5px!important; }
|
197 |
-
#full_calendar_wrapper .table-responsive { overflow: visible; }
|
198 |
-
.fc-toolbar h2 { position: relative; padding-right: 25px; }
|
199 |
-
.fc-toolbar h2:before {
|
200 |
-
content: "\e252";
|
201 |
-
font-family: 'Glyphicons Halflings';
|
202 |
-
position: absolute;
|
203 |
-
right: 0;
|
204 |
-
font-size: 19px;
|
205 |
-
top: 7px;
|
206 |
-
}
|
207 |
-
.fc-toolbar h2:hover,
|
208 |
-
.fc-toolbar h2:hover:before { color: #337ab7; cursor: pointer; }
|
209 |
-
|
210 |
-
/* media query */
|
211 |
-
@media screen and (max-width: 782px) {
|
212 |
-
#full_calendar_wrapper .table-responsive { overflow-y: hidden; }
|
213 |
-
}
|
214 |
-
|
215 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/modules/calendar/resources/css/fullcalendar.min.css
CHANGED
@@ -1,5 +1,5 @@
|
|
1 |
/*!
|
2 |
-
* FullCalendar v2.
|
3 |
* Docs & License: http://fullcalendar.io/
|
4 |
* (c) 2015 Adam Shaw
|
5 |
*/.fc{direction:ltr;text-align:left}.fc-rtl{text-align:right}body .fc{font-size:1em}.fc-unthemed .fc-divider,.fc-unthemed .fc-popover,.fc-unthemed .fc-row,.fc-unthemed tbody,.fc-unthemed td,.fc-unthemed th,.fc-unthemed thead{border-color:#ddd}.fc-unthemed .fc-popover{background-color:#fff}.fc-unthemed .fc-divider,.fc-unthemed .fc-popover .fc-header{background:#eee}.fc-unthemed .fc-popover .fc-header .fc-close{color:#666}.fc-unthemed .fc-today{background:#fcf8e3}.fc-highlight{background:#bce8f1;opacity:.3;filter:alpha(opacity=30)}.fc-bgevent{background:#8fdf82;opacity:.3;filter:alpha(opacity=30)}.fc-nonbusiness{background:#d7d7d7}.fc-icon{display:inline-block;width:1em;height:1em;line-height:1em;font-size:1em;text-align:center;overflow:hidden;font-family:"Courier New",Courier,monospace;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.fc-icon:after{position:relative;margin:0 -1em}.fc-icon-left-single-arrow:after{content:"\02039";font-weight:700;font-size:200%;top:-7%;left:3%}.fc-icon-right-single-arrow:after{content:"\0203A";font-weight:700;font-size:200%;top:-7%;left:-3%}.fc-icon-left-double-arrow:after{content:"\000AB";font-size:160%;top:-7%}.fc-icon-right-double-arrow:after{content:"\000BB";font-size:160%;top:-7%}.fc-icon-left-triangle:after{content:"\25C4";font-size:125%;top:3%;left:-2%}.fc-icon-right-triangle:after{content:"\25BA";font-size:125%;top:3%;left:2%}.fc-icon-down-triangle:after{content:"\25BC";font-size:125%;top:2%}.fc-icon-x:after{content:"\000D7";font-size:200%;top:6%}.fc button{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;margin:0;height:2.1em;padding:0 .6em;font-size:1em;white-space:nowrap;cursor:pointer}.fc button::-moz-focus-inner{margin:0;padding:0}.fc-state-default{border:1px solid}.fc-state-default.fc-corner-left{border-top-left-radius:4px;border-bottom-left-radius:4px}.fc-state-default.fc-corner-right{border-top-right-radius:4px;border-bottom-right-radius:4px}.fc button .fc-icon{position:relative;top:-.05em;margin:0 .2em;vertical-align:middle}.fc-state-default{background-color:#f5f5f5;background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(to bottom,#fff,#e6e6e6);background-repeat:repeat-x;border-color:#e6e6e6 #e6e6e6 #bfbfbf;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);color:#333;text-shadow:0 1px 1px rgba(255,255,255,.75);box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05)}.fc-state-active,.fc-state-disabled,.fc-state-down,.fc-state-hover{color:#333;background-color:#e6e6e6}.fc-state-hover{color:#333;text-decoration:none;background-position:0 -15px;-webkit-transition:background-position .1s linear;-moz-transition:background-position .1s linear;-o-transition:background-position .1s linear;transition:background-position .1s linear}.fc-state-active,.fc-state-down{background-color:#ccc;background-image:none;box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05)}.fc-state-disabled{cursor:default;background-image:none;opacity:.65;filter:alpha(opacity=65);box-shadow:none}.fc-button-group{display:inline-block}.fc .fc-button-group>*{float:left;margin:0 0 0 -1px}.fc .fc-button-group>:first-child{margin-left:0}.fc-popover{position:absolute;box-shadow:0 2px 6px rgba(0,0,0,.15)}.fc-popover .fc-header{padding:2px 4px}.fc-popover .fc-header .fc-title{margin:0 2px}.fc-popover .fc-header .fc-close{cursor:pointer}.fc-ltr .fc-popover .fc-header .fc-title,.fc-rtl .fc-popover .fc-header .fc-close{float:left}.fc-ltr .fc-popover .fc-header .fc-close,.fc-rtl .fc-popover .fc-header .fc-title{float:right}.fc-unthemed .fc-popover{border-width:1px;border-style:solid}.fc-unthemed .fc-popover .fc-header .fc-close{font-size:.9em;margin-top:2px}.fc-popover>.ui-widget-header+.ui-widget-content{border-top:0}.fc-divider{border-style:solid;border-width:1px}hr.fc-divider{height:0;margin:0;padding:0 0 2px;border-width:1px 0}.fc-clear{clear:both}.fc-bg,.fc-bgevent-skeleton,.fc-helper-skeleton,.fc-highlight-skeleton{position:absolute;top:0;left:0;right:0}.fc-bg{bottom:0}.fc-bg table{height:100%}.fc table{width:100%;table-layout:fixed;border-collapse:collapse;border-spacing:0;font-size:1em}.fc th{text-align:center}.fc td,.fc th{border-style:solid;border-width:1px;padding:0;vertical-align:top}.fc td.fc-today{border-style:double}.fc .fc-row{border-style:solid;border-width:0}.fc-row table{border-left:0 hidden transparent;border-right:0 hidden transparent;border-bottom:0 hidden transparent}.fc-row:first-child table{border-top:0 hidden transparent}.fc-row{position:relative}.fc-row .fc-bg{z-index:1}.fc-row .fc-bgevent-skeleton,.fc-row .fc-highlight-skeleton{bottom:0}.fc-row .fc-bgevent-skeleton table,.fc-row .fc-highlight-skeleton table{height:100%}.fc-row .fc-bgevent-skeleton td,.fc-row .fc-highlight-skeleton td{border-color:transparent}.fc-row .fc-bgevent-skeleton{z-index:2}.fc-row .fc-highlight-skeleton{z-index:3}.fc-row .fc-content-skeleton{position:relative;z-index:4;padding-bottom:2px}.fc-row .fc-helper-skeleton{z-index:5}.fc-row .fc-content-skeleton td,.fc-row .fc-helper-skeleton td{background:0 0;border-color:transparent;border-bottom:0}.fc-row .fc-content-skeleton tbody td,.fc-row .fc-helper-skeleton tbody td{border-top:0}.fc-scroller{overflow-y:scroll;overflow-x:hidden}.fc-scroller>*{position:relative;width:100%;overflow:hidden}.fc-event{position:relative;display:block;font-size:.85em;line-height:1.3;border-radius:3px;border:1px solid #3a87ad;background-color:#3a87ad;font-weight:400}.fc-event,.fc-event:hover,.ui-widget .fc-event{color:#fff;text-decoration:none}.fc-event.fc-draggable,.fc-event[href]{cursor:pointer}.fc-not-allowed,.fc-not-allowed .fc-event{cursor:not-allowed}.fc-event .fc-bg{z-index:1;background:#fff;opacity:.25;filter:alpha(opacity=25)}.fc-event .fc-content{position:relative;z-index:2}.fc-event .fc-resizer{position:absolute;z-index:3}.fc-ltr .fc-h-event.fc-not-start,.fc-rtl .fc-h-event.fc-not-end{margin-left:0;border-left-width:0;padding-left:1px;border-top-left-radius:0;border-bottom-left-radius:0}.fc-ltr .fc-h-event.fc-not-end,.fc-rtl .fc-h-event.fc-not-start{margin-right:0;border-right-width:0;padding-right:1px;border-top-right-radius:0;border-bottom-right-radius:0}.fc-h-event .fc-resizer{top:-1px;bottom:-1px;left:-1px;right:-1px;width:5px}.fc-ltr .fc-h-event .fc-start-resizer,.fc-ltr .fc-h-event .fc-start-resizer:after,.fc-ltr .fc-h-event .fc-start-resizer:before,.fc-rtl .fc-h-event .fc-end-resizer,.fc-rtl .fc-h-event .fc-end-resizer:after,.fc-rtl .fc-h-event .fc-end-resizer:before{right:auto;cursor:w-resize}.fc-ltr .fc-h-event .fc-end-resizer,.fc-ltr .fc-h-event .fc-end-resizer:after,.fc-ltr .fc-h-event .fc-end-resizer:before,.fc-rtl .fc-h-event .fc-start-resizer,.fc-rtl .fc-h-event .fc-start-resizer:after,.fc-rtl .fc-h-event .fc-start-resizer:before{left:auto;cursor:e-resize}.fc-day-grid-event{margin:1px 2px 0;padding:0 1px}.fc-day-grid-event .fc-content{white-space:nowrap;overflow:hidden}.fc-day-grid-event .fc-time{font-weight:700}.fc-day-grid-event .fc-resizer{left:-3px;right:-3px;width:7px}a.fc-more{margin:1px 3px;font-size:.85em;cursor:pointer;text-decoration:none}a.fc-more:hover{text-decoration:underline}.fc-limited{display:none}.fc-day-grid .fc-row{z-index:1}.fc-more-popover{z-index:2;width:220px}.fc-more-popover .fc-event-container{padding:10px}.fc-toolbar{text-align:center;margin-bottom:1em}.fc-toolbar .fc-left{float:left}.fc-toolbar .fc-right{float:right}.fc-toolbar .fc-center{display:inline-block}.fc .fc-toolbar>*>*{float:left;margin-left:.75em}.fc .fc-toolbar>*>:first-child{margin-left:0}.fc-toolbar h2{margin:0}.fc-toolbar button{position:relative}.fc-toolbar .fc-state-hover,.fc-toolbar .ui-state-hover{z-index:2}.fc-toolbar .fc-state-down{z-index:3}.fc-toolbar .fc-state-active,.fc-toolbar .ui-state-active{z-index:4}.fc-toolbar button:focus{z-index:5}.fc-view-container *,.fc-view-container :after,.fc-view-container :before{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}.fc-view,.fc-view>table{position:relative;z-index:1}.fc-basicDay-view .fc-content-skeleton,.fc-basicWeek-view .fc-content-skeleton{padding-top:1px;padding-bottom:1em}.fc-basic-view .fc-body .fc-row{min-height:4em}.fc-row.fc-rigid{overflow:hidden}.fc-row.fc-rigid .fc-content-skeleton{position:absolute;top:0;left:0;right:0}.fc-basic-view .fc-day-number,.fc-basic-view .fc-week-number{padding:0 2px}.fc-basic-view td.fc-day-number,.fc-basic-view td.fc-week-number span{padding-top:2px;padding-bottom:2px}.fc-basic-view .fc-week-number{text-align:center}.fc-basic-view .fc-week-number span{display:inline-block;min-width:1.25em}.fc-ltr .fc-basic-view .fc-day-number{text-align:right}.fc-rtl .fc-basic-view .fc-day-number{text-align:left}.fc-day-number.fc-other-month{opacity:.3;filter:alpha(opacity=30)}.fc-agenda-view .fc-day-grid{position:relative;z-index:2}.fc-agenda-view .fc-day-grid .fc-row{min-height:3em}.fc-agenda-view .fc-day-grid .fc-row .fc-content-skeleton{padding-top:1px;padding-bottom:1em}.fc .fc-axis{vertical-align:middle;padding:0 4px;white-space:nowrap}.fc-ltr .fc-axis{text-align:right}.fc-rtl .fc-axis{text-align:left}.ui-widget td.fc-axis{font-weight:400}.fc-time-grid,.fc-time-grid-container{position:relative;z-index:1}.fc-time-grid{min-height:100%}.fc-time-grid table{border:0 hidden transparent}.fc-time-grid>.fc-bg{z-index:1}.fc-time-grid .fc-slats,.fc-time-grid>hr{position:relative;z-index:2}.fc-time-grid .fc-bgevent-skeleton,.fc-time-grid .fc-content-skeleton{position:absolute;top:0;left:0;right:0}.fc-time-grid .fc-bgevent-skeleton{z-index:3}.fc-time-grid .fc-highlight-skeleton{z-index:4}.fc-time-grid .fc-content-skeleton{z-index:5}.fc-time-grid .fc-helper-skeleton{z-index:6}.fc-time-grid .fc-slats td{height:1.5em;border-bottom:0}.fc-time-grid .fc-slats .fc-minor td{border-top-style:dotted}.fc-time-grid .fc-slats .ui-widget-content{background:0 0}.fc-time-grid .fc-highlight-container{position:relative}.fc-time-grid .fc-highlight{position:absolute;left:0;right:0}.fc-time-grid .fc-bgevent-container,.fc-time-grid .fc-event-container{position:relative}.fc-ltr .fc-time-grid .fc-event-container{margin:0 2.5% 0 2px}.fc-rtl .fc-time-grid .fc-event-container{margin:0 2px 0 2.5%}.fc-time-grid .fc-bgevent,.fc-time-grid .fc-event{position:absolute;z-index:1}.fc-time-grid .fc-bgevent{left:0;right:0}.fc-v-event.fc-not-start{border-top-width:0;padding-top:1px;border-top-left-radius:0;border-top-right-radius:0}.fc-v-event.fc-not-end{border-bottom-width:0;padding-bottom:1px;border-bottom-left-radius:0;border-bottom-right-radius:0}.fc-time-grid-event{overflow:hidden}.fc-time-grid-event .fc-time,.fc-time-grid-event .fc-title{padding:0 1px}.fc-time-grid-event .fc-time{font-size:.85em;white-space:nowrap}.fc-time-grid-event.fc-short .fc-content{white-space:nowrap}.fc-time-grid-event.fc-short .fc-time,.fc-time-grid-event.fc-short .fc-title{display:inline-block;vertical-align:top}.fc-time-grid-event.fc-short .fc-time span{display:none}.fc-time-grid-event.fc-short .fc-time:before{content:attr(data-start)}.fc-time-grid-event.fc-short .fc-time:after{content:"\000A0-\000A0"}.fc-time-grid-event.fc-short .fc-title{font-size:.85em;padding:0}.fc-time-grid-event .fc-resizer{left:0;right:0;bottom:0;height:8px;overflow:hidden;line-height:8px;font-size:11px;font-family:monospace;text-align:center;cursor:s-resize}.fc-time-grid-event .fc-resizer:after{content:"="}
|
1 |
/*!
|
2 |
+
* FullCalendar v2.4.0 Stylesheet
|
3 |
* Docs & License: http://fullcalendar.io/
|
4 |
* (c) 2015 Adam Shaw
|
5 |
*/.fc{direction:ltr;text-align:left}.fc-rtl{text-align:right}body .fc{font-size:1em}.fc-unthemed .fc-divider,.fc-unthemed .fc-popover,.fc-unthemed .fc-row,.fc-unthemed tbody,.fc-unthemed td,.fc-unthemed th,.fc-unthemed thead{border-color:#ddd}.fc-unthemed .fc-popover{background-color:#fff}.fc-unthemed .fc-divider,.fc-unthemed .fc-popover .fc-header{background:#eee}.fc-unthemed .fc-popover .fc-header .fc-close{color:#666}.fc-unthemed .fc-today{background:#fcf8e3}.fc-highlight{background:#bce8f1;opacity:.3;filter:alpha(opacity=30)}.fc-bgevent{background:#8fdf82;opacity:.3;filter:alpha(opacity=30)}.fc-nonbusiness{background:#d7d7d7}.fc-icon{display:inline-block;width:1em;height:1em;line-height:1em;font-size:1em;text-align:center;overflow:hidden;font-family:"Courier New",Courier,monospace;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.fc-icon:after{position:relative;margin:0 -1em}.fc-icon-left-single-arrow:after{content:"\02039";font-weight:700;font-size:200%;top:-7%;left:3%}.fc-icon-right-single-arrow:after{content:"\0203A";font-weight:700;font-size:200%;top:-7%;left:-3%}.fc-icon-left-double-arrow:after{content:"\000AB";font-size:160%;top:-7%}.fc-icon-right-double-arrow:after{content:"\000BB";font-size:160%;top:-7%}.fc-icon-left-triangle:after{content:"\25C4";font-size:125%;top:3%;left:-2%}.fc-icon-right-triangle:after{content:"\25BA";font-size:125%;top:3%;left:2%}.fc-icon-down-triangle:after{content:"\25BC";font-size:125%;top:2%}.fc-icon-x:after{content:"\000D7";font-size:200%;top:6%}.fc button{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box;margin:0;height:2.1em;padding:0 .6em;font-size:1em;white-space:nowrap;cursor:pointer}.fc button::-moz-focus-inner{margin:0;padding:0}.fc-state-default{border:1px solid}.fc-state-default.fc-corner-left{border-top-left-radius:4px;border-bottom-left-radius:4px}.fc-state-default.fc-corner-right{border-top-right-radius:4px;border-bottom-right-radius:4px}.fc button .fc-icon{position:relative;top:-.05em;margin:0 .2em;vertical-align:middle}.fc-state-default{background-color:#f5f5f5;background-image:-moz-linear-gradient(top,#fff,#e6e6e6);background-image:-webkit-gradient(linear,0 0,0 100%,from(#fff),to(#e6e6e6));background-image:-webkit-linear-gradient(top,#fff,#e6e6e6);background-image:-o-linear-gradient(top,#fff,#e6e6e6);background-image:linear-gradient(to bottom,#fff,#e6e6e6);background-repeat:repeat-x;border-color:#e6e6e6 #e6e6e6 #bfbfbf;border-color:rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);color:#333;text-shadow:0 1px 1px rgba(255,255,255,.75);box-shadow:inset 0 1px 0 rgba(255,255,255,.2),0 1px 2px rgba(0,0,0,.05)}.fc-state-active,.fc-state-disabled,.fc-state-down,.fc-state-hover{color:#333;background-color:#e6e6e6}.fc-state-hover{color:#333;text-decoration:none;background-position:0 -15px;-webkit-transition:background-position .1s linear;-moz-transition:background-position .1s linear;-o-transition:background-position .1s linear;transition:background-position .1s linear}.fc-state-active,.fc-state-down{background-color:#ccc;background-image:none;box-shadow:inset 0 2px 4px rgba(0,0,0,.15),0 1px 2px rgba(0,0,0,.05)}.fc-state-disabled{cursor:default;background-image:none;opacity:.65;filter:alpha(opacity=65);box-shadow:none}.fc-button-group{display:inline-block}.fc .fc-button-group>*{float:left;margin:0 0 0 -1px}.fc .fc-button-group>:first-child{margin-left:0}.fc-popover{position:absolute;box-shadow:0 2px 6px rgba(0,0,0,.15)}.fc-popover .fc-header{padding:2px 4px}.fc-popover .fc-header .fc-title{margin:0 2px}.fc-popover .fc-header .fc-close{cursor:pointer}.fc-ltr .fc-popover .fc-header .fc-title,.fc-rtl .fc-popover .fc-header .fc-close{float:left}.fc-ltr .fc-popover .fc-header .fc-close,.fc-rtl .fc-popover .fc-header .fc-title{float:right}.fc-unthemed .fc-popover{border-width:1px;border-style:solid}.fc-unthemed .fc-popover .fc-header .fc-close{font-size:.9em;margin-top:2px}.fc-popover>.ui-widget-header+.ui-widget-content{border-top:0}.fc-divider{border-style:solid;border-width:1px}hr.fc-divider{height:0;margin:0;padding:0 0 2px;border-width:1px 0}.fc-clear{clear:both}.fc-bg,.fc-bgevent-skeleton,.fc-helper-skeleton,.fc-highlight-skeleton{position:absolute;top:0;left:0;right:0}.fc-bg{bottom:0}.fc-bg table{height:100%}.fc table{width:100%;table-layout:fixed;border-collapse:collapse;border-spacing:0;font-size:1em}.fc th{text-align:center}.fc td,.fc th{border-style:solid;border-width:1px;padding:0;vertical-align:top}.fc td.fc-today{border-style:double}.fc .fc-row{border-style:solid;border-width:0}.fc-row table{border-left:0 hidden transparent;border-right:0 hidden transparent;border-bottom:0 hidden transparent}.fc-row:first-child table{border-top:0 hidden transparent}.fc-row{position:relative}.fc-row .fc-bg{z-index:1}.fc-row .fc-bgevent-skeleton,.fc-row .fc-highlight-skeleton{bottom:0}.fc-row .fc-bgevent-skeleton table,.fc-row .fc-highlight-skeleton table{height:100%}.fc-row .fc-bgevent-skeleton td,.fc-row .fc-highlight-skeleton td{border-color:transparent}.fc-row .fc-bgevent-skeleton{z-index:2}.fc-row .fc-highlight-skeleton{z-index:3}.fc-row .fc-content-skeleton{position:relative;z-index:4;padding-bottom:2px}.fc-row .fc-helper-skeleton{z-index:5}.fc-row .fc-content-skeleton td,.fc-row .fc-helper-skeleton td{background:0 0;border-color:transparent;border-bottom:0}.fc-row .fc-content-skeleton tbody td,.fc-row .fc-helper-skeleton tbody td{border-top:0}.fc-scroller{overflow-y:scroll;overflow-x:hidden}.fc-scroller>*{position:relative;width:100%;overflow:hidden}.fc-event{position:relative;display:block;font-size:.85em;line-height:1.3;border-radius:3px;border:1px solid #3a87ad;background-color:#3a87ad;font-weight:400}.fc-event,.fc-event:hover,.ui-widget .fc-event{color:#fff;text-decoration:none}.fc-event.fc-draggable,.fc-event[href]{cursor:pointer}.fc-not-allowed,.fc-not-allowed .fc-event{cursor:not-allowed}.fc-event .fc-bg{z-index:1;background:#fff;opacity:.25;filter:alpha(opacity=25)}.fc-event .fc-content{position:relative;z-index:2}.fc-event .fc-resizer{position:absolute;z-index:3}.fc-ltr .fc-h-event.fc-not-start,.fc-rtl .fc-h-event.fc-not-end{margin-left:0;border-left-width:0;padding-left:1px;border-top-left-radius:0;border-bottom-left-radius:0}.fc-ltr .fc-h-event.fc-not-end,.fc-rtl .fc-h-event.fc-not-start{margin-right:0;border-right-width:0;padding-right:1px;border-top-right-radius:0;border-bottom-right-radius:0}.fc-h-event .fc-resizer{top:-1px;bottom:-1px;left:-1px;right:-1px;width:5px}.fc-ltr .fc-h-event .fc-start-resizer,.fc-ltr .fc-h-event .fc-start-resizer:after,.fc-ltr .fc-h-event .fc-start-resizer:before,.fc-rtl .fc-h-event .fc-end-resizer,.fc-rtl .fc-h-event .fc-end-resizer:after,.fc-rtl .fc-h-event .fc-end-resizer:before{right:auto;cursor:w-resize}.fc-ltr .fc-h-event .fc-end-resizer,.fc-ltr .fc-h-event .fc-end-resizer:after,.fc-ltr .fc-h-event .fc-end-resizer:before,.fc-rtl .fc-h-event .fc-start-resizer,.fc-rtl .fc-h-event .fc-start-resizer:after,.fc-rtl .fc-h-event .fc-start-resizer:before{left:auto;cursor:e-resize}.fc-day-grid-event{margin:1px 2px 0;padding:0 1px}.fc-day-grid-event .fc-content{white-space:nowrap;overflow:hidden}.fc-day-grid-event .fc-time{font-weight:700}.fc-day-grid-event .fc-resizer{left:-3px;right:-3px;width:7px}a.fc-more{margin:1px 3px;font-size:.85em;cursor:pointer;text-decoration:none}a.fc-more:hover{text-decoration:underline}.fc-limited{display:none}.fc-day-grid .fc-row{z-index:1}.fc-more-popover{z-index:2;width:220px}.fc-more-popover .fc-event-container{padding:10px}.fc-toolbar{text-align:center;margin-bottom:1em}.fc-toolbar .fc-left{float:left}.fc-toolbar .fc-right{float:right}.fc-toolbar .fc-center{display:inline-block}.fc .fc-toolbar>*>*{float:left;margin-left:.75em}.fc .fc-toolbar>*>:first-child{margin-left:0}.fc-toolbar h2{margin:0}.fc-toolbar button{position:relative}.fc-toolbar .fc-state-hover,.fc-toolbar .ui-state-hover{z-index:2}.fc-toolbar .fc-state-down{z-index:3}.fc-toolbar .fc-state-active,.fc-toolbar .ui-state-active{z-index:4}.fc-toolbar button:focus{z-index:5}.fc-view-container *,.fc-view-container :after,.fc-view-container :before{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}.fc-view,.fc-view>table{position:relative;z-index:1}.fc-basicDay-view .fc-content-skeleton,.fc-basicWeek-view .fc-content-skeleton{padding-top:1px;padding-bottom:1em}.fc-basic-view .fc-body .fc-row{min-height:4em}.fc-row.fc-rigid{overflow:hidden}.fc-row.fc-rigid .fc-content-skeleton{position:absolute;top:0;left:0;right:0}.fc-basic-view .fc-day-number,.fc-basic-view .fc-week-number{padding:0 2px}.fc-basic-view td.fc-day-number,.fc-basic-view td.fc-week-number span{padding-top:2px;padding-bottom:2px}.fc-basic-view .fc-week-number{text-align:center}.fc-basic-view .fc-week-number span{display:inline-block;min-width:1.25em}.fc-ltr .fc-basic-view .fc-day-number{text-align:right}.fc-rtl .fc-basic-view .fc-day-number{text-align:left}.fc-day-number.fc-other-month{opacity:.3;filter:alpha(opacity=30)}.fc-agenda-view .fc-day-grid{position:relative;z-index:2}.fc-agenda-view .fc-day-grid .fc-row{min-height:3em}.fc-agenda-view .fc-day-grid .fc-row .fc-content-skeleton{padding-top:1px;padding-bottom:1em}.fc .fc-axis{vertical-align:middle;padding:0 4px;white-space:nowrap}.fc-ltr .fc-axis{text-align:right}.fc-rtl .fc-axis{text-align:left}.ui-widget td.fc-axis{font-weight:400}.fc-time-grid,.fc-time-grid-container{position:relative;z-index:1}.fc-time-grid{min-height:100%}.fc-time-grid table{border:0 hidden transparent}.fc-time-grid>.fc-bg{z-index:1}.fc-time-grid .fc-slats,.fc-time-grid>hr{position:relative;z-index:2}.fc-time-grid .fc-bgevent-skeleton,.fc-time-grid .fc-content-skeleton{position:absolute;top:0;left:0;right:0}.fc-time-grid .fc-bgevent-skeleton{z-index:3}.fc-time-grid .fc-highlight-skeleton{z-index:4}.fc-time-grid .fc-content-skeleton{z-index:5}.fc-time-grid .fc-helper-skeleton{z-index:6}.fc-time-grid .fc-slats td{height:1.5em;border-bottom:0}.fc-time-grid .fc-slats .fc-minor td{border-top-style:dotted}.fc-time-grid .fc-slats .ui-widget-content{background:0 0}.fc-time-grid .fc-highlight-container{position:relative}.fc-time-grid .fc-highlight{position:absolute;left:0;right:0}.fc-time-grid .fc-bgevent-container,.fc-time-grid .fc-event-container{position:relative}.fc-ltr .fc-time-grid .fc-event-container{margin:0 2.5% 0 2px}.fc-rtl .fc-time-grid .fc-event-container{margin:0 2px 0 2.5%}.fc-time-grid .fc-bgevent,.fc-time-grid .fc-event{position:absolute;z-index:1}.fc-time-grid .fc-bgevent{left:0;right:0}.fc-v-event.fc-not-start{border-top-width:0;padding-top:1px;border-top-left-radius:0;border-top-right-radius:0}.fc-v-event.fc-not-end{border-bottom-width:0;padding-bottom:1px;border-bottom-left-radius:0;border-bottom-right-radius:0}.fc-time-grid-event{overflow:hidden}.fc-time-grid-event .fc-time,.fc-time-grid-event .fc-title{padding:0 1px}.fc-time-grid-event .fc-time{font-size:.85em;white-space:nowrap}.fc-time-grid-event.fc-short .fc-content{white-space:nowrap}.fc-time-grid-event.fc-short .fc-time,.fc-time-grid-event.fc-short .fc-title{display:inline-block;vertical-align:top}.fc-time-grid-event.fc-short .fc-time span{display:none}.fc-time-grid-event.fc-short .fc-time:before{content:attr(data-start)}.fc-time-grid-event.fc-short .fc-time:after{content:"\000A0-\000A0"}.fc-time-grid-event.fc-short .fc-title{font-size:.85em;padding:0}.fc-time-grid-event .fc-resizer{left:0;right:0;bottom:0;height:8px;overflow:hidden;line-height:8px;font-size:11px;font-family:monospace;text-align:center;cursor:s-resize}.fc-time-grid-event .fc-resizer:after{content:"="}
|
backend/modules/calendar/resources/js/calendar.js
CHANGED
@@ -1,9 +1,9 @@
|
|
1 |
jQuery(function ($) {
|
2 |
|
3 |
-
var $fullCalendar = $('#
|
4 |
-
$tabs = $('
|
5 |
-
$staff = $('input.
|
6 |
-
$showAll = $('input#
|
7 |
firstHour = new Date().getHours(),
|
8 |
$staffButton = $('#ab-staff-button'),
|
9 |
staffMembers = [],
|
@@ -17,12 +17,12 @@ jQuery(function ($) {
|
|
17 |
}
|
18 |
// Init tabs and staff member filters.
|
19 |
if (staffIds === null) {
|
20 |
-
$staff.each(function(index, value){
|
21 |
this.checked = true;
|
22 |
$tabs.filter('[data-staff_id=' + this.value + ']').show();
|
23 |
});
|
24 |
} else if (staffIds != '') {
|
25 |
-
$.each(staffIds.split(','), function (index, value){
|
26 |
$staff.filter('[value=' + value + ']').prop('checked', true);
|
27 |
$tabs.filter('[data-staff_id=' + value + ']').show();
|
28 |
});
|
@@ -49,7 +49,7 @@ jQuery(function ($) {
|
|
49 |
height: heightFC(),
|
50 |
// Views.
|
51 |
defaultView: lastView,
|
52 |
-
scrollTime: firstHour+':00:00',
|
53 |
views: {
|
54 |
agendaWeek: {
|
55 |
columnFormat: 'ddd, D'
|
@@ -67,16 +67,16 @@ jQuery(function ($) {
|
|
67 |
// Text/Time Customization.
|
68 |
timeFormat: BooklyL10n.mjsTimeFormat,
|
69 |
displayEventEnd: true,
|
70 |
-
buttonText
|
71 |
today: BooklyL10n.today,
|
72 |
month: BooklyL10n.month,
|
73 |
week: BooklyL10n.week,
|
74 |
day: BooklyL10n.day
|
75 |
},
|
76 |
-
monthNames: BooklyL10n.longMonths,
|
77 |
-
monthNamesShort: BooklyL10n.shortMonths,
|
78 |
-
dayNames: BooklyL10n.longDays,
|
79 |
-
dayNamesShort: BooklyL10n.shortDays,
|
80 |
// Event Dragging & Resizing.
|
81 |
editable: false,
|
82 |
// Event Data.
|
@@ -84,7 +84,7 @@ jQuery(function ($) {
|
|
84 |
url: ajaxurl,
|
85 |
data: {
|
86 |
action : 'ab_get_staff_appointments',
|
87 |
-
staff_ids : function() {
|
88 |
var ids = [];
|
89 |
if ($tabs.filter('.active').data('staff_id') == 0) {
|
90 |
for (var i = 0; i < staffMembers.length; ++i) {
|
@@ -98,8 +98,8 @@ jQuery(function ($) {
|
|
98 |
}
|
99 |
}],
|
100 |
// Event Rendering.
|
101 |
-
eventRender : function(calEvent, $event) {
|
102 |
-
var body =
|
103 |
|
104 |
if (calEvent.desc) {
|
105 |
body += calEvent.desc;
|
@@ -107,7 +107,10 @@ jQuery(function ($) {
|
|
107 |
|
108 |
$event.find('.fc-title').html(body);
|
109 |
|
110 |
-
$event.find('
|
|
|
|
|
|
|
111 |
e.stopPropagation();
|
112 |
if (confirm(BooklyL10n.are_you_sure)) {
|
113 |
$.post(ajaxurl, {'action' : 'ab_delete_appointment', 'appointment_id' : calEvent.id }, function () {
|
@@ -116,39 +119,25 @@ jQuery(function ($) {
|
|
116 |
}
|
117 |
});
|
118 |
},
|
119 |
-
eventAfterRender : function(calEvent, $calEventList, calendar) {
|
120 |
$calEventList.each(function () {
|
121 |
var $calEvent = $(this);
|
122 |
-
var titleHeight = $calEvent.find('.fc-title').height()
|
123 |
-
|
124 |
-
|
125 |
-
if ( origHeight < titleHeight ) {
|
126 |
-
var $info = $('<i class="icon-rt glyphicon glyphicon-info-sign"></i>');
|
127 |
-
var $delete = $('.delete-event', $calEvent);
|
128 |
-
|
129 |
-
$delete.hide();
|
130 |
-
$('.fc-content', $calEvent).append($info);
|
131 |
-
$('.fc-content', $calEvent).append($delete);
|
132 |
-
|
133 |
var z_index = $calEvent.zIndex();
|
134 |
// Mouse handlers.
|
135 |
-
$
|
136 |
-
$calEvent.
|
137 |
-
|
138 |
-
|
139 |
-
$calEvent.
|
140 |
-
});
|
141 |
-
$calEvent.on('mouseleave', function () {
|
142 |
-
$calEvent.css({height: origHeight, 'z-index': z_index});
|
143 |
-
$delete.hide();
|
144 |
-
$info.show();
|
145 |
-
$calEvent.addClass('fc-short');
|
146 |
});
|
147 |
}
|
148 |
});
|
149 |
},
|
150 |
// Clicking & Hovering.
|
151 |
-
dayClick: function(date, jsEvent, view) {
|
152 |
var staff_id, visible_staff_id;
|
153 |
if (view.type == 'multiStaffDay') {
|
154 |
var cell = view.coordMap.getCell(jsEvent.pageX, jsEvent.pageY);
|
@@ -163,11 +152,10 @@ jQuery(function ($) {
|
|
163 |
null,
|
164 |
staff_id,
|
165 |
date,
|
166 |
-
|
167 |
-
function(event) {
|
168 |
if (visible_staff_id == event.staffId || visible_staff_id == 0) {
|
169 |
// Create event in calendar.
|
170 |
-
$fullCalendar.fullCalendar('renderEvent', event
|
171 |
} else {
|
172 |
// Switch to the event owner tab.
|
173 |
jQuery('li[data-staff_id=' + event.staffId + ']').click();
|
@@ -175,7 +163,7 @@ jQuery(function ($) {
|
|
175 |
}
|
176 |
);
|
177 |
},
|
178 |
-
eventClick: function(calEvent, jsEvent, view) {
|
179 |
var visible_staff_id;
|
180 |
if (view.type == 'multiStaffDay') {
|
181 |
visible_staff_id = 0;
|
@@ -185,10 +173,9 @@ jQuery(function ($) {
|
|
185 |
|
186 |
showAppointmentDialog(
|
187 |
calEvent.id,
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
function(event) {
|
192 |
if (visible_staff_id == event.staffId || visible_staff_id == 0) {
|
193 |
// Update event in calendar.
|
194 |
jQuery.extend(calEvent, event);
|
@@ -201,10 +188,10 @@ jQuery(function ($) {
|
|
201 |
);
|
202 |
},
|
203 |
loading: function (bool) {
|
204 |
-
|
205 |
},
|
206 |
-
viewRender: function(view, element){
|
207 |
-
setCookie('bookly_cal_view',view.type);
|
208 |
}
|
209 |
});
|
210 |
|
@@ -217,43 +204,44 @@ jQuery(function ($) {
|
|
217 |
|
218 |
// Init date picker for fast navigation in FullCalendar.
|
219 |
var $fcDatePicker = $('<input type=hidden />');
|
220 |
-
$('.fc-toolbar .fc-center h2').before($fcDatePicker).on('click', function() {
|
221 |
$fcDatePicker.datepicker('setDate', $fullCalendar.fullCalendar('getDate').toDate()).datepicker('show');
|
222 |
});
|
223 |
$fcDatePicker.datepicker({
|
224 |
-
dayNamesMin : BooklyL10n.shortDays,
|
225 |
-
monthNames : BooklyL10n.longMonths,
|
226 |
-
monthNamesShort : BooklyL10n.shortMonths,
|
227 |
firstDay : BooklyL10n.startOfWeek,
|
228 |
-
beforeShow: function(input, inst){
|
229 |
-
inst.dpDiv.queue(function() {
|
230 |
inst.dpDiv.css({marginTop: '35px'});
|
231 |
inst.dpDiv.dequeue();
|
232 |
});
|
233 |
},
|
234 |
-
onSelect: function(dateText, inst) {
|
235 |
var d = new Date(dateText);
|
236 |
$fullCalendar.fullCalendar('gotoDate', d);
|
237 |
-
if(
|
238 |
-
$fullCalendar.fullCalendar('getView').type != 'multiStaffDay'
|
239 |
-
|
240 |
-
$fullCalendar.find('.fc-day
|
|
|
241 |
}
|
242 |
},
|
243 |
-
onClose: function(dateText, inst) {
|
244 |
-
inst.dpDiv.queue(function() {
|
245 |
inst.dpDiv.css({marginTop: '0'});
|
246 |
inst.dpDiv.dequeue();
|
247 |
});
|
248 |
}
|
249 |
});
|
250 |
|
251 |
-
$(window).on('resize', function() {
|
252 |
$fullCalendar.fullCalendar('option', 'height', heightFC());
|
253 |
});
|
254 |
|
255 |
// Click on tabs.
|
256 |
-
$tabs.on('click', function(e) {
|
257 |
e.preventDefault();
|
258 |
$tabs.removeClass('active');
|
259 |
$(this).addClass('active');
|
@@ -298,7 +286,7 @@ jQuery(function ($) {
|
|
298 |
updateStaffButton();
|
299 |
|
300 |
$tabs.filter('[data-staff_id=' + this.value + ']').toggle(this.checked);
|
301 |
-
if ($tabs.filter(':visible.active').length == 0
|
302 |
$tabs.filter(':visible:first').triggerHandler('click');
|
303 |
} else if ($tabs.filter('.active').data('staff_id') == 0) {
|
304 |
var view = $fullCalendar.fullCalendar('getView');
|
@@ -315,7 +303,7 @@ jQuery(function ($) {
|
|
315 |
// Update staffMembers array.
|
316 |
var ids = [];
|
317 |
staffMembers.length = 0;
|
318 |
-
$staff.filter(':checked').each(function() {
|
319 |
staffMembers.push({id: this.value, name: this.getAttribute('data-staff_name')});
|
320 |
ids.push(this.value);
|
321 |
});
|
@@ -340,7 +328,7 @@ jQuery(function ($) {
|
|
340 |
*/
|
341 |
function setCookie(key, value) {
|
342 |
var expires = new Date();
|
343 |
-
expires.setTime(expires.getTime() +
|
344 |
document.cookie = key + '=' + value + ';expires=' + expires.toUTCString();
|
345 |
}
|
346 |
|
@@ -363,7 +351,7 @@ jQuery(function ($) {
|
|
363 |
function heightFC() {
|
364 |
var window_height = $(window).height(),
|
365 |
wp_admin_bar_height = $('#wpadminbar').height(),
|
366 |
-
ab_calendar_tabs_height = $('#
|
367 |
height_to_reduce = wp_admin_bar_height + ab_calendar_tabs_height,
|
368 |
$wrap = $('#wpbody-content .wrap');
|
369 |
|
1 |
jQuery(function ($) {
|
2 |
|
3 |
+
var $fullCalendar = $('#bookly-fc-wrapper .bookly-js-calendar-element'),
|
4 |
+
$tabs = $('.bookly-js-calendar-tab'),
|
5 |
+
$staff = $('input.bookly-js-check-entity'),
|
6 |
+
$showAll = $('input#bookly-check-all-entities'),
|
7 |
firstHour = new Date().getHours(),
|
8 |
$staffButton = $('#ab-staff-button'),
|
9 |
staffMembers = [],
|
17 |
}
|
18 |
// Init tabs and staff member filters.
|
19 |
if (staffIds === null) {
|
20 |
+
$staff.each(function (index, value) {
|
21 |
this.checked = true;
|
22 |
$tabs.filter('[data-staff_id=' + this.value + ']').show();
|
23 |
});
|
24 |
} else if (staffIds != '') {
|
25 |
+
$.each(staffIds.split(','), function (index, value) {
|
26 |
$staff.filter('[value=' + value + ']').prop('checked', true);
|
27 |
$tabs.filter('[data-staff_id=' + value + ']').show();
|
28 |
});
|
49 |
height: heightFC(),
|
50 |
// Views.
|
51 |
defaultView: lastView,
|
52 |
+
scrollTime: firstHour + ':00:00',
|
53 |
views: {
|
54 |
agendaWeek: {
|
55 |
columnFormat: 'ddd, D'
|
67 |
// Text/Time Customization.
|
68 |
timeFormat: BooklyL10n.mjsTimeFormat,
|
69 |
displayEventEnd: true,
|
70 |
+
buttonText: {
|
71 |
today: BooklyL10n.today,
|
72 |
month: BooklyL10n.month,
|
73 |
week: BooklyL10n.week,
|
74 |
day: BooklyL10n.day
|
75 |
},
|
76 |
+
monthNames: BooklyL10n.calendar.longMonths,
|
77 |
+
monthNamesShort: BooklyL10n.calendar.shortMonths,
|
78 |
+
dayNames: BooklyL10n.calendar.longDays,
|
79 |
+
dayNamesShort: BooklyL10n.calendar.shortDays,
|
80 |
// Event Dragging & Resizing.
|
81 |
editable: false,
|
82 |
// Event Data.
|
84 |
url: ajaxurl,
|
85 |
data: {
|
86 |
action : 'ab_get_staff_appointments',
|
87 |
+
staff_ids : function () {
|
88 |
var ids = [];
|
89 |
if ($tabs.filter('.active').data('staff_id') == 0) {
|
90 |
for (var i = 0; i < staffMembers.length; ++i) {
|
98 |
}
|
99 |
}],
|
100 |
// Event Rendering.
|
101 |
+
eventRender : function (calEvent, $event) {
|
102 |
+
var body = calEvent.title + '<a class="delete-event dashicons dashicons-trash" title="' + BooklyL10n.delete + '"></a>';
|
103 |
|
104 |
if (calEvent.desc) {
|
105 |
body += calEvent.desc;
|
107 |
|
108 |
$event.find('.fc-title').html(body);
|
109 |
|
110 |
+
var $time = $event.find('.fc-time');
|
111 |
+
$time.attr('data-start', $time.find('span').text());
|
112 |
+
|
113 |
+
$event.find('.delete-event').on('click', function(e) {
|
114 |
e.stopPropagation();
|
115 |
if (confirm(BooklyL10n.are_you_sure)) {
|
116 |
$.post(ajaxurl, {'action' : 'ab_delete_appointment', 'appointment_id' : calEvent.id }, function () {
|
119 |
}
|
120 |
});
|
121 |
},
|
122 |
+
eventAfterRender : function (calEvent, $calEventList, calendar) {
|
123 |
$calEventList.each(function () {
|
124 |
var $calEvent = $(this);
|
125 |
+
var titleHeight = $calEvent.find('.fc-title').height(),
|
126 |
+
origHeight = $calEvent.outerHeight();
|
127 |
+
if (origHeight < titleHeight) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
128 |
var z_index = $calEvent.zIndex();
|
129 |
// Mouse handlers.
|
130 |
+
$calEvent.on('mouseenter', function () {
|
131 |
+
$calEvent.removeClass('fc-short')
|
132 |
+
.css({'z-index': 64, bottom: '', height: ''});
|
133 |
+
}).on('mouseleave', function () {
|
134 |
+
$calEvent.css({'z-index': z_index, height: origHeight});
|
|
|
|
|
|
|
|
|
|
|
|
|
135 |
});
|
136 |
}
|
137 |
});
|
138 |
},
|
139 |
// Clicking & Hovering.
|
140 |
+
dayClick: function (date, jsEvent, view) {
|
141 |
var staff_id, visible_staff_id;
|
142 |
if (view.type == 'multiStaffDay') {
|
143 |
var cell = view.coordMap.getCell(jsEvent.pageX, jsEvent.pageY);
|
152 |
null,
|
153 |
staff_id,
|
154 |
date,
|
155 |
+
function (event) {
|
|
|
156 |
if (visible_staff_id == event.staffId || visible_staff_id == 0) {
|
157 |
// Create event in calendar.
|
158 |
+
$fullCalendar.fullCalendar('renderEvent', event);
|
159 |
} else {
|
160 |
// Switch to the event owner tab.
|
161 |
jQuery('li[data-staff_id=' + event.staffId + ']').click();
|
163 |
}
|
164 |
);
|
165 |
},
|
166 |
+
eventClick: function (calEvent, jsEvent, view) {
|
167 |
var visible_staff_id;
|
168 |
if (view.type == 'multiStaffDay') {
|
169 |
visible_staff_id = 0;
|
173 |
|
174 |
showAppointmentDialog(
|
175 |
calEvent.id,
|
176 |
+
null,
|
177 |
+
null,
|
178 |
+
function (event) {
|
|
|
179 |
if (visible_staff_id == event.staffId || visible_staff_id == 0) {
|
180 |
// Update event in calendar.
|
181 |
jQuery.extend(calEvent, event);
|
188 |
);
|
189 |
},
|
190 |
loading: function (bool) {
|
191 |
+
$('.fc-loading-inner').toggle(bool);
|
192 |
},
|
193 |
+
viewRender: function (view, element) {
|
194 |
+
setCookie('bookly_cal_view', view.type);
|
195 |
}
|
196 |
});
|
197 |
|
204 |
|
205 |
// Init date picker for fast navigation in FullCalendar.
|
206 |
var $fcDatePicker = $('<input type=hidden />');
|
207 |
+
$('.fc-toolbar .fc-center h2').before($fcDatePicker).on('click', function () {
|
208 |
$fcDatePicker.datepicker('setDate', $fullCalendar.fullCalendar('getDate').toDate()).datepicker('show');
|
209 |
});
|
210 |
$fcDatePicker.datepicker({
|
211 |
+
dayNamesMin : BooklyL10n.calendar.shortDays,
|
212 |
+
monthNames : BooklyL10n.calendar.longMonths,
|
213 |
+
monthNamesShort : BooklyL10n.calendar.shortMonths,
|
214 |
firstDay : BooklyL10n.startOfWeek,
|
215 |
+
beforeShow: function (input, inst) {
|
216 |
+
inst.dpDiv.queue(function () {
|
217 |
inst.dpDiv.css({marginTop: '35px'});
|
218 |
inst.dpDiv.dequeue();
|
219 |
});
|
220 |
},
|
221 |
+
onSelect: function (dateText, inst) {
|
222 |
var d = new Date(dateText);
|
223 |
$fullCalendar.fullCalendar('gotoDate', d);
|
224 |
+
if ($fullCalendar.fullCalendar('getView').type != 'agendaDay' &&
|
225 |
+
$fullCalendar.fullCalendar('getView').type != 'multiStaffDay')
|
226 |
+
{
|
227 |
+
$fullCalendar.find('.fc-day').removeClass('bookly-fc-day-active');
|
228 |
+
$fullCalendar.find('.fc-day[data-date="' + moment(d).format('YYYY-MM-DD') + '"]').addClass('bookly-fc-day-active');
|
229 |
}
|
230 |
},
|
231 |
+
onClose: function (dateText, inst) {
|
232 |
+
inst.dpDiv.queue(function () {
|
233 |
inst.dpDiv.css({marginTop: '0'});
|
234 |
inst.dpDiv.dequeue();
|
235 |
});
|
236 |
}
|
237 |
});
|
238 |
|
239 |
+
$(window).on('resize', function () {
|
240 |
$fullCalendar.fullCalendar('option', 'height', heightFC());
|
241 |
});
|
242 |
|
243 |
// Click on tabs.
|
244 |
+
$tabs.on('click', function (e) {
|
245 |
e.preventDefault();
|
246 |
$tabs.removeClass('active');
|
247 |
$(this).addClass('active');
|
286 |
updateStaffButton();
|
287 |
|
288 |
$tabs.filter('[data-staff_id=' + this.value + ']').toggle(this.checked);
|
289 |
+
if ($tabs.filter(':visible.active').length == 0) {
|
290 |
$tabs.filter(':visible:first').triggerHandler('click');
|
291 |
} else if ($tabs.filter('.active').data('staff_id') == 0) {
|
292 |
var view = $fullCalendar.fullCalendar('getView');
|
303 |
// Update staffMembers array.
|
304 |
var ids = [];
|
305 |
staffMembers.length = 0;
|
306 |
+
$staff.filter(':checked').each(function () {
|
307 |
staffMembers.push({id: this.value, name: this.getAttribute('data-staff_name')});
|
308 |
ids.push(this.value);
|
309 |
});
|
328 |
*/
|
329 |
function setCookie(key, value) {
|
330 |
var expires = new Date();
|
331 |
+
expires.setTime(expires.getTime() + 86400000); // 60 × 60 × 24 × 1000
|
332 |
document.cookie = key + '=' + value + ';expires=' + expires.toUTCString();
|
333 |
}
|
334 |
|
351 |
function heightFC() {
|
352 |
var window_height = $(window).height(),
|
353 |
wp_admin_bar_height = $('#wpadminbar').height(),
|
354 |
+
ab_calendar_tabs_height = $('#bookly-fc-wrapper .tabbable').outerHeight(true),
|
355 |
height_to_reduce = wp_admin_bar_height + ab_calendar_tabs_height,
|
356 |
$wrap = $('#wpbody-content .wrap');
|
357 |
|
backend/modules/calendar/resources/js/fullcalendar.min.js
CHANGED
@@ -1,8 +1,9 @@
|
|
1 |
/*!
|
2 |
-
* FullCalendar v2.
|
3 |
* Docs & License: http://fullcalendar.io/
|
4 |
* (c) 2015 Adam Shaw
|
5 |
*/
|
6 |
-
!function(a){"function"==typeof define&&define.amd?define(["jquery","moment"],a):"object"==typeof exports?module.exports=a(require("jquery"),require("moment")):a(jQuery,moment)}(function(a,b){function c(a){return J(a,La)}function d(b){var c,d={views:b.views||{}};return a.each(b,function(b,e){"views"!=b&&(a.isPlainObject(e)&&!/(time|duration|interval)$/i.test(b)&&-1==a.inArray(b,La)?(c=null,a.each(e,function(a,e){/^(month|week|day|default|basic(Week|Day)?|agenda(Week|Day)?)$/.test(a)?(d.views[a]||(d.views[a]={}),d.views[a][b]=e):(c||(c={}),c[a]=e)}),c&&(d[b]=c)):d[b]=e)}),d}function e(a,b){b.left&&a.css({"border-left-width":1,"margin-left":b.left-1}),b.right&&a.css({"border-right-width":1,"margin-right":b.right-1})}function f(a){a.css({"margin-left":"","margin-right":"","border-left-width":"","border-right-width":""})}function g(){a("body").addClass("fc-not-allowed")}function h(){a("body").removeClass("fc-not-allowed")}function i(b,c,d){var e=Math.floor(c/b.length),f=Math.floor(c-e*(b.length-1)),g=[],h=[],i=[],k=0;j(b),b.each(function(c,d){var j=c===b.length-1?f:e,l=a(d).outerHeight(!0);j>l?(g.push(d),h.push(l),i.push(a(d).height())):k+=l}),d&&(c-=k,e=Math.floor(c/g.length),f=Math.floor(c-e*(g.length-1))),a(g).each(function(b,c){var d=b===g.length-1?f:e,j=h[b],k=i[b],l=d-(j-k);d>j&&a(c).height(l)})}function j(a){a.height("")}function k(b){var c=0;return b.find("> *").each(function(b,d){var e=a(d).outerWidth();e>c&&(c=e)}),c++,b.width(c),c}function l(a,b){return a.height(b).addClass("fc-scroller"),a[0].scrollHeight-1>a[0].clientHeight?!0:(m(a),!1)}function m(a){a.height("").removeClass("fc-scroller")}function n(b){var c=b.css("position"),d=b.parents().filter(function(){var b=a(this);return/(auto|scroll)/.test(b.css("overflow")+b.css("overflow-y")+b.css("overflow-x"))}).eq(0);return"fixed"!==c&&d.length?d:a(b[0].ownerDocument||document)}function o(a){var b=a.offset();return{left:b.left,right:b.left+a.outerWidth(),top:b.top,bottom:b.top+a.outerHeight()}}function p(a){var b=a.offset(),c=r(a),d=b.left+u(a,"border-left-width")+c.left,e=b.top+u(a,"border-top-width")+c.top;return{left:d,right:d+a[0].clientWidth,top:e,bottom:e+a[0].clientHeight}}function q(a){var b=a.offset(),c=b.left+u(a,"border-left-width")+u(a,"padding-left"),d=b.top+u(a,"border-top-width")+u(a,"padding-top");return{left:c,right:c+a.width(),top:d,bottom:d+a.height()}}function r(a){var b=a.innerWidth()-a[0].clientWidth,c={left:0,right:0,top:0,bottom:a.innerHeight()-a[0].clientHeight};return s()&&"rtl"==a.css("direction")?c.left=b:c.right=b,c}function s(){return null===Ma&&(Ma=t()),Ma}function t(){var b=a("<div><div/></div>").css({position:"absolute",top:-1e3,left:0,border:0,padding:0,overflow:"scroll",direction:"rtl"}).appendTo("body"),c=b.children(),d=c.offset().left>b.offset().left;return b.remove(),d}function u(a,b){return parseFloat(a.css(b))||0}function v(a){return 1==a.which&&!a.ctrlKey}function w(a,b){var c={left:Math.max(a.left,b.left),right:Math.min(a.right,b.right),top:Math.max(a.top,b.top),bottom:Math.min(a.bottom,b.bottom)};return c.left<c.right&&c.top<c.bottom?c:!1}function x(a,b){return{left:Math.min(Math.max(a.left,b.left),b.right),top:Math.min(Math.max(a.top,b.top),b.bottom)}}function y(a){return{left:(a.left+a.right)/2,top:(a.top+a.bottom)/2}}function z(a,b){return{left:a.left-b.left,top:a.top-b.top}}function A(a,b){var c,d,e,f,g=a.start,h=a.end,i=b.start,j=b.end;return h>i&&j>g?(g>=i?(c=g.clone(),e=!0):(c=i.clone(),e=!1),j>=h?(d=h.clone(),f=!0):(d=j.clone(),f=!1),{start:c,end:d,isStart:e,isEnd:f}):void 0}function B(a,c){return b.duration({days:a.clone().stripTime().diff(c.clone().stripTime(),"days"),ms:a.time()-c.time()})}function C(a,c){return b.duration({days:a.clone().stripTime().diff(c.clone().stripTime(),"days")})}function D(a,c,d){return b.duration(Math.round(a.diff(c,d,!0)),d)}function E(a,b){var c,d,e;for(c=0;c<Ra.length&&(d=Ra[c],e=F(d,a,b),!(e>=1&&W(e)));c++);return d}function F(a,c,d){return null!=d?d.diff(c,a,!0):b.isDuration(c)?c.as(a):c.end.diff(c.start,a,!0)}function G(a){return Boolean(a.hours()||a.minutes()||a.seconds()||a.milliseconds())}function H(a){return"[object Date]"===Object.prototype.toString.call(a)||a instanceof Date}function I(a){return/^\d+\:\d+(?:\:\d+\.?(?:\d{3})?)?$/.test(a)}function J(a,b){var c,d,e,f,g,h,i={};if(b)for(c=0;c<b.length;c++){for(d=b[c],e=[],f=a.length-1;f>=0;f--)if(g=a[f][d],"object"==typeof g)e.unshift(g);else if(void 0!==g){i[d]=g;break}e.length&&(i[d]=J(e))}for(c=a.length-1;c>=0;c--){h=a[c];for(d in h)d in i||(i[d]=h[d])}return i}function K(a){var b=function(){};return b.prototype=a,new b}function L(a,b){for(var c in a)N(a,c)&&(b[c]=a[c])}function M(a,b){var c,d,e=["constructor","toString","valueOf"];for(c=0;c<e.length;c++)d=e[c],a[d]!==Object.prototype[d]&&(b[d]=a[d])}function N(a,b){return Sa.call(a,b)}function O(b){return/undefined|null|boolean|number|string/.test(a.type(b))}function P(b,c,d){if(a.isFunction(b)&&(b=[b]),b){var e,f;for(e=0;e<b.length;e++)f=b[e].apply(c,d)||f;return f}}function Q(){for(var a=0;a<arguments.length;a++)if(void 0!==arguments[a])return arguments[a]}function R(a){return(a+"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/'/g,"'").replace(/"/g,""").replace(/\n/g,"<br />")}function S(a){return a.replace(/&.*?;/g,"")}function T(b){var c=[];return a.each(b,function(a,b){null!=b&&c.push(a+":"+b)}),c.join(";")}function U(a){return a.charAt(0).toUpperCase()+a.slice(1)}function V(a,b){return a-b}function W(a){return a%1===0}function X(a,b){var c=a[b];return function(){return c.apply(a,arguments)}}function Y(a,b){var c,d,e,f,g=function(){var h=+new Date-f;b>h&&h>0?c=setTimeout(g,b-h):(c=null,a.apply(e,d),c||(e=d=null))};return function(){e=this,d=arguments,f=+new Date,c||(c=setTimeout(g,b))}}function Z(c,d,e){var f,g,h,i,j=c[0],k=1==c.length&&"string"==typeof j;return b.isMoment(j)?(i=b.apply(null,c),_(j,i)):H(j)||void 0===j?i=b.apply(null,c):(f=!1,g=!1,k?Ta.test(j)?(j+="-01",c=[j],f=!0,g=!0):(h=Ua.exec(j))&&(f=!h[5],g=!0):a.isArray(j)&&(g=!0),i=d||f?b.utc.apply(b,c):b.apply(null,c),f?(i._ambigTime=!0,i._ambigZone=!0):e&&(g?i._ambigZone=!0:k&&(i.utcOffset?i.utcOffset(j):i.zone(j)))),i._fullCalendar=!0,i}function $(a,c){var d,e,f=!1,g=!1,h=a.length,i=[];for(d=0;h>d;d++)e=a[d],b.isMoment(e)||(e=Ja.moment.parseZone(e)),f=f||e._ambigTime,g=g||e._ambigZone,i.push(e);for(d=0;h>d;d++)e=i[d],c||!f||e._ambigTime?g&&!e._ambigZone&&(i[d]=e.clone().stripZone()):i[d]=e.clone().stripTime();return i}function _(a,b){a._ambigTime?b._ambigTime=!0:b._ambigTime&&(b._ambigTime=!1),a._ambigZone?b._ambigZone=!0:b._ambigZone&&(b._ambigZone=!1)}function aa(a,b){a.year(b[0]||0).month(b[1]||0).date(b[2]||0).hours(b[3]||0).minutes(b[4]||0).seconds(b[5]||0).milliseconds(b[6]||0)}function ba(a,b){return Wa.format.call(a,b)}function ca(a,b){return da(a,ia(b))}function da(a,b){var c,d="";for(c=0;c<b.length;c++)d+=ea(a,b[c]);return d}function ea(a,b){var c,d;return"string"==typeof b?b:(c=b.token)?Xa[c]?Xa[c](a):ba(a,c):b.maybe&&(d=da(a,b.maybe),d.match(/[1-9]/))?d:""}function fa(a,b,c,d,e){var f;return a=Ja.moment.parseZone(a),b=Ja.moment.parseZone(b),f=(a.localeData||a.lang).call(a),c=f.longDateFormat(c)||c,d=d||" - ",ga(a,b,ia(c),d,e)}function ga(a,b,c,d,e){var f,g,h,i,j="",k="",l="",m="",n="";for(g=0;g<c.length&&(f=ha(a,b,c[g]),f!==!1);g++)j+=f;for(h=c.length-1;h>g&&(f=ha(a,b,c[h]),f!==!1);h--)k=f+k;for(i=g;h>=i;i++)l+=ea(a,c[i]),m+=ea(b,c[i]);return(l||m)&&(n=e?m+d+l:l+d+m),j+n+k}function ha(a,b,c){var d,e;return"string"==typeof c?c:(d=c.token)&&(e=Ya[d.charAt(0)],e&&a.isSame(b,e))?ba(a,d):!1}function ia(a){return a in Za?Za[a]:Za[a]=ja(a)}function ja(a){for(var b,c=[],d=/\[([^\]]*)\]|\(([^\)]*)\)|(LTS|LT|(\w)\4*o?)|([^\w\[\(]+)/g;b=d.exec(a);)b[1]?c.push(b[1]):b[2]?c.push({maybe:ja(b[2])}):b[3]?c.push({token:b[3]}):b[5]&&c.push(b[5]);return c}function ka(){}function la(a,b){return a||b?a&&b?a.grid===b.grid&&a.row===b.row&&a.col===b.col:!1:!0}function ma(a){var b=oa(a);return"background"===b||"inverse-background"===b}function na(a){return"inverse-background"===oa(a)}function oa(a){return Q((a.source||{}).rendering,a.rendering)}function pa(a){var b,c,d={};for(b=0;b<a.length;b++)c=a[b],(d[c._id]||(d[c._id]=[])).push(c);return d}function qa(a,b){return a.eventStartMS-b.eventStartMS}function ra(a,b){return a.eventStartMS-b.eventStartMS||b.eventDurationMS-a.eventDurationMS||b.event.allDay-a.event.allDay||(a.event.title||"").localeCompare(b.event.title)}function sa(c){var d,e,f,g,h=Ja.dataAttrPrefix;return h&&(h+="-"),d=c.data(h+"event")||null,d&&(d="object"==typeof d?a.extend({},d):{},e=d.start,null==e&&(e=d.time),f=d.duration,g=d.stick,delete d.start,delete d.time,delete d.duration,delete d.stick),null==e&&(e=c.data(h+"start")),null==e&&(e=c.data(h+"time")),null==f&&(f=c.data(h+"duration")),null==g&&(g=c.data(h+"stick")),e=null!=e?b.duration(e):null,f=null!=f?b.duration(f):null,g=Boolean(g),{eventProps:d,startTime:e,duration:f,stick:g}}function ta(a,b){var c,d;for(c=0;c<b.length;c++)if(d=b[c],d.leftCol<=a.rightCol&&d.rightCol>=a.leftCol)return!0;return!1}function ua(a,b){return a.leftCol-b.leftCol}function va(a){var b,c,d;if(a.sort(ra),b=wa(a),xa(b),c=b[0]){for(d=0;d<c.length;d++)ya(c[d]);for(d=0;d<c.length;d++)za(c[d],0,0)}}function wa(a){var b,c,d,e=[];for(b=0;b<a.length;b++){for(c=a[b],d=0;d<e.length&&Aa(c,e[d]).length;d++);c.level=d,(e[d]||(e[d]=[])).push(c)}return e}function xa(a){var b,c,d,e,f;for(b=0;b<a.length;b++)for(c=a[b],d=0;d<c.length;d++)for(e=c[d],e.forwardSegs=[],f=b+1;f<a.length;f++)Aa(e,a[f],e.forwardSegs)}function ya(a){var b,c,d=a.forwardSegs,e=0;if(void 0===a.forwardPressure){for(b=0;b<d.length;b++)c=d[b],ya(c),e=Math.max(e,1+c.forwardPressure);a.forwardPressure=e}}function za(a,b,c){var d,e=a.forwardSegs;if(void 0===a.forwardCoord)for(e.length?(e.sort(Ca),za(e[0],b+1,c),a.forwardCoord=e[0].backwardCoord):a.forwardCoord=1,a.backwardCoord=a.forwardCoord-(a.forwardCoord-c)/(b+1),d=0;d<e.length;d++)za(e[d],0,a.forwardCoord)}function Aa(a,b,c){c=c||[];for(var d=0;d<b.length;d++)Ba(a,b[d])&&c.push(b[d]);return c}function Ba(a,b){return a.bottom>b.top&&a.top<b.bottom}function Ca(a,b){return b.forwardPressure-a.forwardPressure||(a.backwardCoord||0)-(b.backwardCoord||0)||ra(a,b)}function Da(c,d){function e(){U?h()&&(k(),i()):f()}function f(){V=P.theme?"ui":"fc",c.addClass("fc"),P.isRTL?c.addClass("fc-rtl"):c.addClass("fc-ltr"),P.theme?c.addClass("ui-widget"):c.addClass("fc-unthemed"),U=a("<div class='fc-view-container'/>").prependTo(c),S=O.header=new Ga(O,P),T=S.render(),T&&c.prepend(T),i(P.defaultView),P.handleWindowResize&&(Z=Y(m,P.windowResizeDelay),a(window).resize(Z))}function g(){W&&W.removeElement(),S.removeElement(),U.remove(),c.removeClass("fc fc-ltr fc-rtl fc-unthemed ui-widget"),Z&&a(window).unbind("resize",Z)}function h(){return c.is(":visible")}function i(b){da++,W&&b&&W.type!==b&&(S.deactivateButton(W.type),H(),W.removeElement(),W=O.view=null),!W&&b&&(W=O.view=ca[b]||(ca[b]=O.instantiateView(b)),W.setElement(a("<div class='fc-view fc-"+b+"-view' />").appendTo(U)),S.activateButton(b)),W&&($=W.massageCurrentDate($),W.displaying&&$.isWithin(W.intervalStart,W.intervalEnd)||h()&&(H(),W.display($),I(),u(),v(),q())),I(),da--}function j(a){return h()?(a&&l(),da++,W.updateSize(!0),da--,!0):void 0}function k(){h()&&l()}function l(){X="number"==typeof P.contentHeight?P.contentHeight:"number"==typeof P.height?P.height-(T?T.outerHeight(!0):0):Math.round(U.width()/Math.max(P.aspectRatio,.5))}function m(a){!da&&a.target===window&&W.start&&j(!0)&&W.trigger("windowResize",ba)}function n(){p(),r()}function o(){h()&&(H(),W.displayEvents(ea),I())}function p(){H(),W.clearEvents(),I()}function q(){!P.lazyFetching||_(W.start,W.end)?r():o()}function r(){aa(W.start,W.end)}function s(a){ea=a,o()}function t(){o()}function u(){S.updateTitle(W.title)}function v(){var a=O.getNow();a.isWithin(W.intervalStart,W.intervalEnd)?S.disableButton("today"):S.enableButton("today")}function w(a,b){W.select(O.buildSelectRange.apply(O,arguments))}function x(){W&&W.unselect()}function y(){$=W.computePrevDate($),i()}function z(){$=W.computeNextDate($),i()}function A(){$.add(-1,"years"),i()}function B(){$.add(1,"years"),i()}function C(){$=O.getNow(),i()}function D(a){$=O.moment(a),i()}function E(a){$.add(b.duration(a)),i()}function F(a,b){var c;b=b||"day",c=O.getViewSpec(b)||O.getUnitViewSpec(b),$=a,i(c?c.type:null)}function G(){return $.clone()}function H(){U.css({width:"100%",height:U.height(),overflow:"hidden"})}function I(){U.css({width:"",height:"",overflow:""})}function J(){return O}function L(){return W}function M(a,b){return void 0===b?P[a]:void(("height"==a||"contentHeight"==a||"aspectRatio"==a)&&(P[a]=b,j(!0)))}function N(a,b){return P[a]?P[a].apply(b||ba,Array.prototype.slice.call(arguments,2)):void 0}var O=this;O.initOptions(d||{});var P=this.options;O.render=e,O.destroy=g,O.refetchEvents=n,O.reportEvents=s,O.reportEventChange=t,O.rerenderEvents=o,O.changeView=i,O.select=w,O.unselect=x,O.prev=y,O.next=z,O.prevYear=A,O.nextYear=B,O.today=C,O.gotoDate=D,O.incrementDate=E,O.zoomTo=F,O.getDate=G,O.getCalendar=J,O.getView=L,O.option=M,O.trigger=N;var Q=K(Fa(P.lang));if(P.monthNames&&(Q._months=P.monthNames),P.monthNamesShort&&(Q._monthsShort=P.monthNamesShort),P.dayNames&&(Q._weekdays=P.dayNames),P.dayNamesShort&&(Q._weekdaysShort=P.dayNamesShort),null!=P.firstDay){var R=K(Q._week);R.dow=P.firstDay,Q._week=R}Q._fullCalendar_weekCalc=function(a){return"function"==typeof a?a:"local"===a?a:"iso"===a||"ISO"===a?"ISO":void 0}(P.weekNumberCalculation),O.defaultAllDayEventDuration=b.duration(P.defaultAllDayEventDuration),O.defaultTimedEventDuration=b.duration(P.defaultTimedEventDuration),O.moment=function(){var a;return"local"===P.timezone?(a=Ja.moment.apply(null,arguments),a.hasTime()&&a.local()):a="UTC"===P.timezone?Ja.moment.utc.apply(null,arguments):Ja.moment.parseZone.apply(null,arguments),"_locale"in a?a._locale=Q:a._lang=Q,a},O.getIsAmbigTimezone=function(){return"local"!==P.timezone&&"UTC"!==P.timezone},O.rezoneDate=function(a){return O.moment(a.toArray())},O.getNow=function(){var a=P.now;return"function"==typeof a&&(a=a()),O.moment(a)},O.getEventEnd=function(a){return a.end?a.end.clone():O.getDefaultEventEnd(a.allDay,a.start)},O.getDefaultEventEnd=function(a,b){var c=b.clone();return a?c.stripTime().add(O.defaultAllDayEventDuration):c.add(O.defaultTimedEventDuration),O.getIsAmbigTimezone()&&c.stripZone(),c},O.humanizeDuration=function(a){return(a.locale||a.lang).call(a,P.lang).humanize()},Ha.call(O,P);var S,T,U,V,W,X,Z,$,_=O.isFetchNeeded,aa=O.fetchEvents,ba=c[0],ca={},da=0,ea=[];$=null!=P.defaultDate?O.moment(P.defaultDate):O.getNow(),O.getSuggestedViewHeight=function(){return void 0===X&&k(),X},O.isHeightAuto=function(){return"auto"===P.contentHeight||"auto"===P.height},O.initialize()}function Ea(b){a.each(nb,function(a,c){null==b[a]&&(b[a]=c(b))})}function Fa(a){var c=b.localeData||b.langData;return c.call(b,a)||c.call(b,"en")}function Ga(b,c){function d(){var b=c.header;return n=c.theme?"ui":"fc",b?o=a("<div class='fc-toolbar'/>").append(f("left")).append(f("right")).append(f("center")).append('<div class="fc-clear"/>'):void 0}function e(){o.remove(),o=a()}function f(d){var e=a('<div class="fc-'+d+'"/>'),f=c.header[d];return f&&a.each(f.split(" "),function(d){var f,g=a(),h=!0;a.each(this.split(","),function(d,e){var f,i,j,k,l,m,o,q,r;"title"==e?(g=g.add(a("<h2> </h2>")),h=!1):(f=b.getViewSpec(e),f?(i=function(){b.changeView(e)},p.push(e),j=f.buttonTextOverride,k=f.buttonTextDefault):b[e]&&(i=function(){b[e]()},j=(b.overrides.buttonText||{})[e],k=c.buttonText[e]),i&&(l=c.themeButtonIcons[e],m=c.buttonIcons[e],o=j?R(j):l&&c.theme?"<span class='ui-icon ui-icon-"+l+"'></span>":m&&!c.theme?"<span class='fc-icon fc-icon-"+m+"'></span>":R(k),q=["fc-"+e+"-button",n+"-button",n+"-state-default"],r=a('<button type="button" class="'+q.join(" ")+'">'+o+"</button>").click(function(){r.hasClass(n+"-state-disabled")||(i(),(r.hasClass(n+"-state-active")||r.hasClass(n+"-state-disabled"))&&r.removeClass(n+"-state-hover"))}).mousedown(function(){r.not("."+n+"-state-active").not("."+n+"-state-disabled").addClass(n+"-state-down")}).mouseup(function(){r.removeClass(n+"-state-down")}).hover(function(){r.not("."+n+"-state-active").not("."+n+"-state-disabled").addClass(n+"-state-hover")},function(){r.removeClass(n+"-state-hover").removeClass(n+"-state-down")}),g=g.add(r)))}),h&&g.first().addClass(n+"-corner-left").end().last().addClass(n+"-corner-right").end(),g.length>1?(f=a("<div/>"),h&&f.addClass("fc-button-group"),f.append(g),e.append(f)):e.append(g)}),e}function g(a){o.find("h2").text(a)}function h(a){o.find(".fc-"+a+"-button").addClass(n+"-state-active")}function i(a){o.find(".fc-"+a+"-button").removeClass(n+"-state-active")}function j(a){o.find(".fc-"+a+"-button").attr("disabled","disabled").addClass(n+"-state-disabled")}function k(a){o.find(".fc-"+a+"-button").removeAttr("disabled").removeClass(n+"-state-disabled")}function l(){return p}var m=this;m.render=d,m.removeElement=e,m.updateTitle=g,m.activateButton=h,m.deactivateButton=i,m.disableButton=j,m.enableButton=k,m.getViewsWithButtons=l;var n,o=a(),p=[]}function Ha(c){function d(a,b){return!N||a.clone().stripZone()<N.clone().stripZone()||b.clone().stripZone()>R.clone().stripZone()}function e(a,b){N=a,R=b,X=[];var c=++V,d=U.length;W=d;for(var e=0;d>e;e++)f(U[e],c)}function f(b,c){g(b,function(d){var e,f,g,h=a.isArray(b.events);if(c==V){if(d)for(e=0;e<d.length;e++)f=d[e],g=h?f:s(f,b),g&&X.push.apply(X,x(g));W--,W||S(X)}})}function g(b,d){var e,f,h=Ja.sourceFetchers;for(e=0;e<h.length;e++){if(f=h[e].call(M,b,N.clone(),R.clone(),c.timezone,d),f===!0)return;if("object"==typeof f)return void g(f,d)}var i=b.events;if(i)a.isFunction(i)?(M.pushLoading(),i.call(M,N.clone(),R.clone(),c.timezone,function(a){d(a),M.popLoading()})):a.isArray(i)?d(i):d();else{var j=b.url;if(j){var k,l=b.success,m=b.error,n=b.complete;k=a.isFunction(b.data)?b.data():b.data;var o=a.extend({},k||{}),p=Q(b.startParam,c.startParam),q=Q(b.endParam,c.endParam),r=Q(b.timezoneParam,c.timezoneParam);p&&(o[p]=N.format()),q&&(o[q]=R.format()),c.timezone&&"local"!=c.timezone&&(o[r]=c.timezone),M.pushLoading(),a.ajax(a.extend({},ob,b,{data:o,success:function(b){b=b||[];var c=P(l,this,arguments);a.isArray(c)&&(b=c),d(b)},error:function(){P(m,this,arguments),d()},complete:function(){P(n,this,arguments),M.popLoading()}}))}else d()}}function h(a){var b=i(a);b&&(U.push(b),W++,f(b,V))}function i(b){var c,d,e=Ja.sourceNormalizers;if(a.isFunction(b)||a.isArray(b)?c={events:b}:"string"==typeof b?c={url:b}:"object"==typeof b&&(c=a.extend({},b)),c){for(c.className?"string"==typeof c.className&&(c.className=c.className.split(/\s+/)):c.className=[],a.isArray(c.events)&&(c.origArray=c.events,c.events=a.map(c.events,function(a){return s(a,c)})),d=0;d<e.length;d++)e[d].call(M,c);return c}}function j(b){U=a.grep(U,function(a){return!k(a,b)}),X=a.grep(X,function(a){return!k(a.source,b)}),S(X)}function k(a,b){return a&&b&&l(a)==l(b)}function l(a){return("object"==typeof a?a.origArray||a.googleCalendarId||a.url||a.events:null)||a}function m(a){a.start=M.moment(a.start),a.end?a.end=M.moment(a.end):a.end=null,y(a,n(a)),S(X)}function n(b){var c={};return a.each(b,function(a,b){o(a)&&void 0!==b&&O(b)&&(c[a]=b)}),c}function o(a){return!/^_|^(id|allDay|start|end)$/.test(a)}function p(a,b){var c,d,e,f=s(a);if(f){for(c=x(f),d=0;d<c.length;d++)e=c[d],e.source||(b&&(T.events.push(e),e.source=T),X.push(e));return S(X),c}return[]}function q(b){var c,d;for(null==b?b=function(){return!0}:a.isFunction(b)||(c=b+"",b=function(a){return a._id==c}),X=a.grep(X,b,!0),d=0;d<U.length;d++)a.isArray(U[d].events)&&(U[d].events=a.grep(U[d].events,b,!0));S(X)}function r(b){return a.isFunction(b)?a.grep(X,b):null!=b?(b+="",a.grep(X,function(a){return a._id==b})):X}function s(d,e){var f,g,h,i={};if(c.eventDataTransform&&(d=c.eventDataTransform(d)),e&&e.eventDataTransform&&(d=e.eventDataTransform(d)),a.extend(i,d),e&&(i.source=e),i._id=d._id||(void 0===d.id?"_fc"+pb++:d.id+""),d.className?"string"==typeof d.className?i.className=d.className.split(/\s+/):i.className=d.className:i.className=[],f=d.start||d.date,g=d.end,I(f)&&(f=b.duration(f)),I(g)&&(g=b.duration(g)),d.dow||b.isDuration(f)||b.isDuration(g))i.start=f?b.duration(f):null,i.end=g?b.duration(g):null,i._recurring=!0;else{if(f&&(f=M.moment(f),!f.isValid()))return!1;g&&(g=M.moment(g),g.isValid()||(g=null)),h=d.allDay,void 0===h&&(h=Q(e?e.allDayDefault:void 0,c.allDayDefault)),t(f,g,h,i)}return i}function t(a,b,c,d){d.start=a,d.end=b,d.allDay=c,u(d),Ia(d)}function u(a){v(a),a.end&&!a.end.isAfter(a.start)&&(a.end=null),a.end||(c.forceEventDuration?a.end=M.getDefaultEventEnd(a.allDay,a.start):a.end=null)}function v(a){null==a.allDay&&(a.allDay=!(a.start.hasTime()||a.end&&a.end.hasTime())),a.allDay?(a.start.stripTime(),a.end&&a.end.stripTime()):(a.start.hasTime()||(a.start=M.rezoneDate(a.start)),a.end&&!a.end.hasTime()&&(a.end=M.rezoneDate(a.end)))}function w(b){var c;return b.end||(c=b.allDay,null==c&&(c=!b.start.hasTime()),b=a.extend({},b),b.end=M.getDefaultEventEnd(c,b.start)),b}function x(b,c,d){var e,f,g,h,i,j,k,l,m,n=[];if(c=c||N,d=d||R,b)if(b._recurring){if(f=b.dow)for(e={},g=0;g<f.length;g++)e[f[g]]=!0;for(h=c.clone().stripTime();h.isBefore(d);)(!e||e[h.day()])&&(i=b.start,j=b.end,k=h.clone(),l=null,i&&(k=k.time(i)),j&&(l=h.clone().time(j)),m=a.extend({},b),t(k,l,!i&&!j,m),n.push(m)),h.add(1,"days")}else n.push(b);return n}function y(b,c,d){function e(a,b){return d?D(a,b,d):c.allDay?C(a,b):B(a,b)}var f,g,h,i,j,k,l={};return c=c||{},c.start||(c.start=b.start.clone()),void 0===c.end&&(c.end=b.end?b.end.clone():null),null==c.allDay&&(c.allDay=b.allDay),u(c),f={start:b._start.clone(),end:b._end?b._end.clone():M.getDefaultEventEnd(b._allDay,b._start),allDay:c.allDay},u(f),g=null!==b._end&&null===c.end,h=e(c.start,f.start),c.end?(i=e(c.end,f.end),j=i.subtract(h)):j=null,a.each(c,function(a,b){o(a)&&void 0!==b&&(l[a]=b)}),k=z(r(b._id),g,c.allDay,h,j,l),{dateDelta:h,durationDelta:j,undo:k}}function z(b,c,d,e,f,g){var h=M.getIsAmbigTimezone(),i=[];return e&&!e.valueOf()&&(e=null),f&&!f.valueOf()&&(f=null),a.each(b,function(b,j){var k,l;k={start:j.start.clone(),end:j.end?j.end.clone():null,allDay:j.allDay},a.each(g,function(a){k[a]=j[a]}),l={start:j._start,end:j._end,allDay:d},u(l),c?l.end=null:f&&!l.end&&(l.end=M.getDefaultEventEnd(l.allDay,l.start)),e&&(l.start.add(e),l.end&&l.end.add(e)),f&&l.end.add(f),h&&!l.allDay&&(e||f)&&(l.start.stripZone(),l.end&&l.end.stripZone()),a.extend(j,g,l),Ia(j),i.push(function(){a.extend(j,k),Ia(j)})}),function(){for(var a=0;a<i.length;a++)i[a]()}}function A(b){var d,e=c.businessHours,f={className:"fc-nonbusiness",start:"09:00",end:"17:00",dow:[1,2,3,4,5],rendering:"inverse-background"},g=M.getView();return e&&(d=a.extend({},f,"object"==typeof e?e:{})),d?(b&&(d.start=null,d.end=null),x(s(d),g.start,g.end)):[]}function E(a,b){var d=b.source||{},e=Q(b.constraint,d.constraint,c.eventConstraint),f=Q(b.overlap,d.overlap,c.eventOverlap);return a=w(a),H(a,e,f,b)}function F(a){return H(a,c.selectConstraint,c.selectOverlap)}function G(b,c){var d,e;return c&&(d=a.extend({},c,b),e=x(s(d))[0]),e?E(b,e):(b=w(b),F(b))}function H(b,c,d,e){var f,g,h,i,j,k;if(b=a.extend({},b),b.start=b.start.clone().stripZone(),b.end=b.end.clone().stripZone(),null!=c){for(f=J(c),g=!1,i=0;i<f.length;i++)if(K(f[i],b)){g=!0;break}if(!g)return!1}for(h=M.getPeerEvents(e,b),i=0;i<h.length;i++)if(j=h[i],L(j,b)){if(d===!1)return!1;if("function"==typeof d&&!d(j,e))return!1;if(e){if(k=Q(j.overlap,(j.source||{}).overlap),k===!1)return!1;if("function"==typeof k&&!k(e,j))return!1}}return!0}function J(a){return"businessHours"===a?A():"object"==typeof a?x(s(a)):r(a)}function K(a,b){var c=a.start.clone().stripZone(),d=M.getEventEnd(a).stripZone();return b.start>=c&&b.end<=d}function L(a,b){var c=a.start.clone().stripZone(),d=M.getEventEnd(a).stripZone();return b.start<d&&b.end>c}var M=this;M.isFetchNeeded=d,M.fetchEvents=e,M.addEventSource=h,M.removeEventSource=j,M.updateEvent=m,M.renderEvent=p,M.removeEvents=q,M.clientEvents=r,M.mutateEvent=y,M.normalizeEventRange=u,M.normalizeEventRangeTimes=v,M.ensureVisibleEventRange=w;var N,R,S=M.reportEvents,T={events:[]},U=[T],V=0,W=0,X=[];a.each((c.events?[c.events]:[]).concat(c.eventSources||[]),function(a,b){var c=i(b);c&&U.push(c)}),M.getBusinessHoursEvents=A,M.isEventRangeAllowed=E,M.isSelectionRangeAllowed=F,M.isExternalDropRangeAllowed=G,M.getEventCache=function(){return X}}function Ia(a){a._allDay=a.allDay,a._start=a.start.clone(),a._end=a.end?a.end.clone():null}var Ja=a.fullCalendar={version:"2.3.2"},Ka=Ja.views={};a.fn.fullCalendar=function(b){var c=Array.prototype.slice.call(arguments,1),d=this;return this.each(function(e,f){var g,h=a(f),i=h.data("fullCalendar");"string"==typeof b?i&&a.isFunction(i[b])&&(g=i[b].apply(i,c),e||(d=g),"destroy"===b&&h.removeData("fullCalendar")):i||(i=new jb(h,b),h.data("fullCalendar",i),i.render())}),d};var La=["header","buttonText","buttonIcons","themeButtonIcons"];Ja.intersectionToSeg=A,Ja.applyAll=P,Ja.debounce=Y,Ja.isInt=W,Ja.htmlEscape=R,Ja.cssToStr=T,Ja.proxy=X,Ja.capitaliseFirstLetter=U,Ja.getClientRect=p,Ja.getContentRect=q,Ja.getScrollbarWidths=r;var Ma=null;Ja.computeIntervalUnit=E,Ja.durationHasTime=G;var Na,Oa,Pa,Qa=["sun","mon","tue","wed","thu","fri","sat"],Ra=["year","month","week","day","hour","minute","second","millisecond"],Sa={}.hasOwnProperty,Ta=/^\s*\d{4}-\d\d$/,Ua=/^\s*\d{4}-(?:(\d\d-\d\d)|(W\d\d$)|(W\d\d-\d)|(\d\d\d))((T| )(\d\d(:\d\d(:\d\d(\.\d+)?)?)?)?)?$/,Va=b.fn,Wa=a.extend({},Va);Ja.moment=function(){return Z(arguments)},Ja.moment.utc=function(){var a=Z(arguments,!0);return a.hasTime()&&a.utc(),a},Ja.moment.parseZone=function(){return Z(arguments,!0,!0)},Va.clone=function(){var a=Wa.clone.apply(this,arguments);return _(this,a),this._fullCalendar&&(a._fullCalendar=!0),a},Va.week=Va.weeks=function(a){var b=(this._locale||this._lang)._fullCalendar_weekCalc;return null==a&&"function"==typeof b?b(this):"ISO"===b?Wa.isoWeek.apply(this,arguments):Wa.week.apply(this,arguments)},Va.time=function(a){if(!this._fullCalendar)return Wa.time.apply(this,arguments);if(null==a)return b.duration({hours:this.hours(),minutes:this.minutes(),seconds:this.seconds(),milliseconds:this.milliseconds()});this._ambigTime=!1,b.isDuration(a)||b.isMoment(a)||(a=b.duration(a));var c=0;return b.isDuration(a)&&(c=24*Math.floor(a.asDays())),this.hours(c+a.hours()).minutes(a.minutes()).seconds(a.seconds()).milliseconds(a.milliseconds())},Va.stripTime=function(){var a;return this._ambigTime||(a=this.toArray(),this.utc(),Oa(this,a.slice(0,3)),this._ambigTime=!0,this._ambigZone=!0),this},Va.hasTime=function(){return!this._ambigTime},Va.stripZone=function(){var a,b;return this._ambigZone||(a=this.toArray(),b=this._ambigTime,this.utc(),Oa(this,a),this._ambigTime=b||!1,this._ambigZone=!0),this},Va.hasZone=function(){return!this._ambigZone},Va.local=function(){var a=this.toArray(),b=this._ambigZone;return Wa.local.apply(this,arguments),this._ambigTime=!1,this._ambigZone=!1,b&&Pa(this,a),this},Va.utc=function(){return Wa.utc.apply(this,arguments),this._ambigTime=!1,this._ambigZone=!1,this},a.each(["zone","utcOffset"],function(a,b){Wa[b]&&(Va[b]=function(a){return null!=a&&(this._ambigTime=!1,this._ambigZone=!1),Wa[b].apply(this,arguments)})}),Va.format=function(){return this._fullCalendar&&arguments[0]?ca(this,arguments[0]):this._ambigTime?ba(this,"YYYY-MM-DD"):this._ambigZone?ba(this,"YYYY-MM-DD[T]HH:mm:ss"):Wa.format.apply(this,arguments)},Va.toISOString=function(){return this._ambigTime?ba(this,"YYYY-MM-DD"):this._ambigZone?ba(this,"YYYY-MM-DD[T]HH:mm:ss"):Wa.toISOString.apply(this,arguments)},Va.isWithin=function(a,b){var c=$([this,a,b]);return c[0]>=c[1]&&c[0]<c[2]},Va.isSame=function(a,b){var c;return this._fullCalendar?b?(c=$([this,a],!0),Wa.isSame.call(c[0],c[1],b)):(a=Ja.moment.parseZone(a),Wa.isSame.call(this,a)&&Boolean(this._ambigTime)===Boolean(a._ambigTime)&&Boolean(this._ambigZone)===Boolean(a._ambigZone)):Wa.isSame.apply(this,arguments)},a.each(["isBefore","isAfter"],function(a,b){Va[b]=function(a,c){var d;return this._fullCalendar?(d=$([this,a]),Wa[b].call(d[0],d[1],c)):Wa[b].apply(this,arguments)}}),Na="_d"in b()&&"updateOffset"in b,Oa=Na?function(a,c){a._d.setTime(Date.UTC.apply(Date,c)),b.updateOffset(a,!1)}:aa,Pa=Na?function(a,c){a._d.setTime(+new Date(c[0]||0,c[1]||0,c[2]||0,c[3]||0,c[4]||0,c[5]||0,c[6]||0)),b.updateOffset(a,!1)}:aa;var Xa={t:function(a){return ba(a,"a").charAt(0)},T:function(a){return ba(a,"A").charAt(0)}};Ja.formatRange=fa;var Ya={Y:"year",M:"month",D:"day",d:"day",A:"second",a:"second",T:"second",t:"second",H:"second",h:"second",m:"second",s:"second"},Za={};Ja.Class=ka,ka.extend=function(a){var b,c=this;return a=a||{},N(a,"constructor")&&(b=a.constructor),"function"!=typeof b&&(b=a.constructor=function(){c.apply(this,arguments)}),b.prototype=K(c.prototype),L(a,b.prototype),M(a,b.prototype),L(c,b),b},ka.mixin=function(a){L(a.prototype||a,this.prototype)};var $a=ka.extend({isHidden:!0,options:null,el:null,documentMousedownProxy:null,margin:10,constructor:function(a){this.options=a||{}},show:function(){this.isHidden&&(this.el||this.render(),this.el.show(),this.position(),this.isHidden=!1,this.trigger("show"))},hide:function(){this.isHidden||(this.el.hide(),this.isHidden=!0,this.trigger("hide"))},render:function(){var b=this,c=this.options;this.el=a('<div class="fc-popover"/>').addClass(c.className||"").css({top:0,left:0}).append(c.content).appendTo(c.parentEl),this.el.on("click",".fc-close",function(){b.hide()}),c.autoHide&&a(document).on("mousedown",this.documentMousedownProxy=X(this,"documentMousedown"))},documentMousedown:function(b){this.el&&!a(b.target).closest(this.el).length&&this.hide()},removeElement:function(){this.hide(),this.el&&(this.el.remove(),this.el=null),a(document).off("mousedown",this.documentMousedownProxy)},position:function(){var b,c,d,e,f,g=this.options,h=this.el.offsetParent().offset(),i=this.el.outerWidth(),j=this.el.outerHeight(),k=a(window),l=n(this.el);e=g.top||0,f=void 0!==g.left?g.left:void 0!==g.right?g.right-i:0,l.is(window)||l.is(document)?(l=k,b=0,c=0):(d=l.offset(),b=d.top,c=d.left),b+=k.scrollTop(),c+=k.scrollLeft(),g.viewportConstrain!==!1&&(e=Math.min(e,b+l.outerHeight()-j-this.margin),e=Math.max(e,b+this.margin),f=Math.min(f,c+l.outerWidth()-i-this.margin),f=Math.max(f,c+this.margin)),this.el.css({top:e-h.top,left:f-h.left})},trigger:function(a){this.options[a]&&this.options[a].apply(this,Array.prototype.slice.call(arguments,1))}}),_a=ka.extend({grid:null,rowCoords:null,colCoords:null,containerEl:null,bounds:null,constructor:function(a){this.grid=a},build:function(){this.grid.build(),this.rowCoords=this.grid.computeRowCoords(),this.colCoords=this.grid.computeColCoords(),this.computeBounds()},clear:function(){this.grid.clear(),this.rowCoords=null,this.colCoords=null},getCell:function(b,c){var d,e,f,g=this.rowCoords,h=g.length,i=this.colCoords,j=i.length,k=null,l=null;if(this.inBounds(b,c)){for(d=0;h>d;d++)if(e=g[d],c>=e.top&&c<e.bottom){k=d;break}for(d=0;j>d;d++)if(e=i[d],b>=e.left&&b<e.right){l=d;break}if(null!==k&&null!==l)return f=this.grid.getCell(k,l),f.grid=this.grid,a.extend(f,g[k],i[l]),f}return null},computeBounds:function(){this.bounds=this.containerEl?p(this.containerEl):null},inBounds:function(a,b){var c=this.bounds;return c?a>=c.left&&a<c.right&&b>=c.top&&b<c.bottom:!0}}),ab=ka.extend({coordMaps:null,constructor:function(a){this.coordMaps=a},build:function(){var a,b=this.coordMaps;for(a=0;a<b.length;a++)b[a].build()},getCell:function(a,b){var c,d=this.coordMaps,e=null;for(c=0;c<d.length&&!e;c++)e=d[c].getCell(a,b);return e},clear:function(){var a,b=this.coordMaps;for(a=0;a<b.length;a++)b[a].clear()}}),bb=Ja.DragListener=ka.extend({options:null,isListening:!1,isDragging:!1,originX:null,originY:null,mousemoveProxy:null,mouseupProxy:null,subjectEl:null,subjectHref:null,scrollEl:null,scrollBounds:null,scrollTopVel:null,
|
7 |
-
scrollLeftVel:null,scrollIntervalId:null,scrollHandlerProxy:null,scrollSensitivity:30,scrollSpeed:200,scrollIntervalMs:50,constructor:function(a){a=a||{},this.options=a,this.subjectEl=a.subjectEl},mousedown:function(a){v(a)&&(a.preventDefault(),this.startListening(a),this.options.distance||this.startDrag(a))},startListening:function(b){var c;this.isListening||(b&&this.options.scroll&&(c=n(a(b.target)),c.is(window)||c.is(document)||(this.scrollEl=c,this.scrollHandlerProxy=Y(X(this,"scrollHandler"),100),this.scrollEl.on("scroll",this.scrollHandlerProxy))),a(document).on("mousemove",this.mousemoveProxy=X(this,"mousemove")).on("mouseup",this.mouseupProxy=X(this,"mouseup")).on("selectstart",this.preventDefault),b?(this.originX=b.pageX,this.originY=b.pageY):(this.originX=0,this.originY=0),this.isListening=!0,this.listenStart(b))},listenStart:function(a){this.trigger("listenStart",a)},mousemove:function(a){var b,c,d=a.pageX-this.originX,e=a.pageY-this.originY;this.isDragging||(b=this.options.distance||1,c=d*d+e*e,c>=b*b&&this.startDrag(a)),this.isDragging&&this.drag(d,e,a)},startDrag:function(a){this.isListening||this.startListening(),this.isDragging||(this.isDragging=!0,this.dragStart(a))},dragStart:function(a){var b=this.subjectEl;this.trigger("dragStart",a),(this.subjectHref=b?b.attr("href"):null)&&b.removeAttr("href")},drag:function(a,b,c){this.trigger("drag",a,b,c),this.updateScroll(c)},mouseup:function(a){this.stopListening(a)},stopDrag:function(a){this.isDragging&&(this.stopScrolling(),this.dragStop(a),this.isDragging=!1)},dragStop:function(a){var b=this;this.trigger("dragStop",a),setTimeout(function(){b.subjectHref&&b.subjectEl.attr("href",b.subjectHref)},0)},stopListening:function(b){this.stopDrag(b),this.isListening&&(this.scrollEl&&(this.scrollEl.off("scroll",this.scrollHandlerProxy),this.scrollHandlerProxy=null),a(document).off("mousemove",this.mousemoveProxy).off("mouseup",this.mouseupProxy).off("selectstart",this.preventDefault),this.mousemoveProxy=null,this.mouseupProxy=null,this.isListening=!1,this.listenStop(b))},listenStop:function(a){this.trigger("listenStop",a)},trigger:function(a){this.options[a]&&this.options[a].apply(this,Array.prototype.slice.call(arguments,1))},preventDefault:function(a){a.preventDefault()},computeScrollBounds:function(){var a=this.scrollEl;this.scrollBounds=a?o(a):null},updateScroll:function(a){var b,c,d,e,f=this.scrollSensitivity,g=this.scrollBounds,h=0,i=0;g&&(b=(f-(a.pageY-g.top))/f,c=(f-(g.bottom-a.pageY))/f,d=(f-(a.pageX-g.left))/f,e=(f-(g.right-a.pageX))/f,b>=0&&1>=b?h=b*this.scrollSpeed*-1:c>=0&&1>=c&&(h=c*this.scrollSpeed),d>=0&&1>=d?i=d*this.scrollSpeed*-1:e>=0&&1>=e&&(i=e*this.scrollSpeed)),this.setScrollVel(h,i)},setScrollVel:function(a,b){this.scrollTopVel=a,this.scrollLeftVel=b,this.constrainScrollVel(),!this.scrollTopVel&&!this.scrollLeftVel||this.scrollIntervalId||(this.scrollIntervalId=setInterval(X(this,"scrollIntervalFunc"),this.scrollIntervalMs))},constrainScrollVel:function(){var a=this.scrollEl;this.scrollTopVel<0?a.scrollTop()<=0&&(this.scrollTopVel=0):this.scrollTopVel>0&&a.scrollTop()+a[0].clientHeight>=a[0].scrollHeight&&(this.scrollTopVel=0),this.scrollLeftVel<0?a.scrollLeft()<=0&&(this.scrollLeftVel=0):this.scrollLeftVel>0&&a.scrollLeft()+a[0].clientWidth>=a[0].scrollWidth&&(this.scrollLeftVel=0)},scrollIntervalFunc:function(){var a=this.scrollEl,b=this.scrollIntervalMs/1e3;this.scrollTopVel&&a.scrollTop(a.scrollTop()+this.scrollTopVel*b),this.scrollLeftVel&&a.scrollLeft(a.scrollLeft()+this.scrollLeftVel*b),this.constrainScrollVel(),this.scrollTopVel||this.scrollLeftVel||this.stopScrolling()},stopScrolling:function(){this.scrollIntervalId&&(clearInterval(this.scrollIntervalId),this.scrollIntervalId=null,this.scrollStop())},scrollHandler:function(){this.scrollIntervalId||this.scrollStop()},scrollStop:function(){}}),cb=bb.extend({coordMap:null,origCell:null,cell:null,coordAdjust:null,constructor:function(a,b){bb.prototype.constructor.call(this,b),this.coordMap=a},listenStart:function(a){var b,c,d,e=this.subjectEl;bb.prototype.listenStart.apply(this,arguments),this.computeCoords(),a?(c={left:a.pageX,top:a.pageY},d=c,e&&(b=o(e),d=x(d,b)),this.origCell=this.getCell(d.left,d.top),e&&this.options.subjectCenter&&(this.origCell&&(b=w(this.origCell,b)||b),d=y(b)),this.coordAdjust=z(d,c)):(this.origCell=null,this.coordAdjust=null)},computeCoords:function(){this.coordMap.build(),this.computeScrollBounds()},dragStart:function(a){var b;bb.prototype.dragStart.apply(this,arguments),b=this.getCell(a.pageX,a.pageY),b&&this.cellOver(b)},drag:function(a,b,c){var d;bb.prototype.drag.apply(this,arguments),d=this.getCell(c.pageX,c.pageY),la(d,this.cell)||(this.cell&&this.cellOut(),d&&this.cellOver(d))},dragStop:function(){this.cellDone(),bb.prototype.dragStop.apply(this,arguments)},cellOver:function(a){this.cell=a,this.trigger("cellOver",a,la(a,this.origCell),this.origCell)},cellOut:function(){this.cell&&(this.trigger("cellOut",this.cell),this.cellDone(),this.cell=null)},cellDone:function(){this.cell&&this.trigger("cellDone",this.cell)},listenStop:function(){bb.prototype.listenStop.apply(this,arguments),this.origCell=this.cell=null,this.coordMap.clear()},scrollStop:function(){bb.prototype.scrollStop.apply(this,arguments),this.computeCoords()},getCell:function(a,b){return this.coordAdjust&&(a+=this.coordAdjust.left,b+=this.coordAdjust.top),this.coordMap.getCell(a,b)}}),db=ka.extend({options:null,sourceEl:null,el:null,parentEl:null,top0:null,left0:null,mouseY0:null,mouseX0:null,topDelta:null,leftDelta:null,mousemoveProxy:null,isFollowing:!1,isHidden:!1,isAnimating:!1,constructor:function(b,c){this.options=c=c||{},this.sourceEl=b,this.parentEl=c.parentEl?a(c.parentEl):b.parent()},start:function(b){this.isFollowing||(this.isFollowing=!0,this.mouseY0=b.pageY,this.mouseX0=b.pageX,this.topDelta=0,this.leftDelta=0,this.isHidden||this.updatePosition(),a(document).on("mousemove",this.mousemoveProxy=X(this,"mousemove")))},stop:function(b,c){function d(){this.isAnimating=!1,e.removeElement(),this.top0=this.left0=null,c&&c()}var e=this,f=this.options.revertDuration;this.isFollowing&&!this.isAnimating&&(this.isFollowing=!1,a(document).off("mousemove",this.mousemoveProxy),b&&f&&!this.isHidden?(this.isAnimating=!0,this.el.animate({top:this.top0,left:this.left0},{duration:f,complete:d})):d())},getEl:function(){var a=this.el;return a||(this.sourceEl.width(),a=this.el=this.sourceEl.clone().css({position:"absolute",visibility:"",display:this.isHidden?"none":"",margin:0,right:"auto",bottom:"auto",width:this.sourceEl.width(),height:this.sourceEl.height(),opacity:this.options.opacity||"",zIndex:this.options.zIndex}).appendTo(this.parentEl)),a},removeElement:function(){this.el&&(this.el.remove(),this.el=null)},updatePosition:function(){var a,b;this.getEl(),null===this.top0&&(this.sourceEl.width(),a=this.sourceEl.offset(),b=this.el.offsetParent().offset(),this.top0=a.top-b.top,this.left0=a.left-b.left),this.el.css({top:this.top0+this.topDelta,left:this.left0+this.leftDelta})},mousemove:function(a){this.topDelta=a.pageY-this.mouseY0,this.leftDelta=a.pageX-this.mouseX0,this.isHidden||this.updatePosition()},hide:function(){this.isHidden||(this.isHidden=!0,this.el&&this.el.hide())},show:function(){this.isHidden&&(this.isHidden=!1,this.updatePosition(),this.getEl().show())}}),eb=ka.extend({view:null,isRTL:null,cellHtml:"<td/>",constructor:function(a){this.view=a,this.isRTL=a.opt("isRTL")},rowHtml:function(a,b){var c,d,e=this.getHtmlRenderer("cell",a),f="";for(b=b||0,c=0;c<this.colCnt;c++)d=this.getCell(b,c),f+=e(d);return f=this.bookendCells(f,a,b),"<tr>"+f+"</tr>"},bookendCells:function(a,b,c){var d=this.getHtmlRenderer("intro",b)(c||0),e=this.getHtmlRenderer("outro",b)(c||0),f=this.isRTL?e:d,g=this.isRTL?d:e;return"string"==typeof a?f+a+g:a.prepend(f).append(g)},getHtmlRenderer:function(a,b){var c,d,e,f,g=this.view;return c=a+"Html",b&&(d=b+U(a)+"Html"),d&&(f=g[d])?e=g:d&&(f=this[d])?e=this:(f=g[c])?e=g:(f=this[c])&&(e=this),"function"==typeof f?function(){return f.apply(e,arguments)||""}:function(){return f||""}}}),fb=Ja.Grid=eb.extend({start:null,end:null,rowCnt:0,colCnt:0,el:null,coordMap:null,elsByFill:null,externalDragStartProxy:null,colHeadFormat:null,eventTimeFormat:null,displayEventTime:null,displayEventEnd:null,cellDuration:null,largeUnit:null,constructor:function(){eb.apply(this,arguments),this.coordMap=new _a(this),this.elsByFill={},this.externalDragStartProxy=X(this,"externalDragStart")},computeColHeadFormat:function(){},computeEventTimeFormat:function(){return this.view.opt("smallTimeFormat")},computeDisplayEventTime:function(){return!0},computeDisplayEventEnd:function(){return!0},setRange:function(a){this.start=a.start.clone(),this.end=a.end.clone(),this.rangeUpdated(),this.processRangeOptions()},rangeUpdated:function(){},processRangeOptions:function(){var a,b,c=this.view;this.colHeadFormat=c.opt("columnFormat")||this.computeColHeadFormat(),this.eventTimeFormat=c.opt("eventTimeFormat")||c.opt("timeFormat")||this.computeEventTimeFormat(),a=c.opt("displayEventTime"),null==a&&(a=this.computeDisplayEventTime()),b=c.opt("displayEventEnd"),null==b&&(b=this.computeDisplayEventEnd()),this.displayEventTime=a,this.displayEventEnd=b},build:function(){},clear:function(){},rangeToSegs:function(a){},diffDates:function(a,b){return this.largeUnit?D(a,b,this.largeUnit):B(a,b)},getCell:function(b,c){var d;return null==c&&("number"==typeof b?(c=b%this.colCnt,b=Math.floor(b/this.colCnt)):(c=b.col,b=b.row)),d={row:b,col:c},a.extend(d,this.getRowData(b),this.getColData(c)),a.extend(d,this.computeCellRange(d)),d},computeCellRange:function(a){var b=this.computeCellDate(a);return{start:b,end:b.clone().add(this.cellDuration)}},computeCellDate:function(a){},getRowData:function(a){return{}},getColData:function(a){return{}},getRowEl:function(a){},getColEl:function(a){},getCellDayEl:function(a){return this.getColEl(a.col)||this.getRowEl(a.row)},computeRowCoords:function(){var a,b,c,d=[];for(a=0;a<this.rowCnt;a++)b=this.getRowEl(a),c=b.offset().top,d.push({top:c,bottom:c+b.outerHeight()});return d},computeColCoords:function(){var a,b,c,d=[];for(a=0;a<this.colCnt;a++)b=this.getColEl(a),c=b.offset().left,d.push({left:c,right:c+b.outerWidth()});return d},setElement:function(b){var c=this;this.el=b,b.on("mousedown",function(b){a(b.target).is(".fc-event-container *, .fc-more")||a(b.target).closest(".fc-popover").length||c.dayMousedown(b)}),this.bindSegHandlers(),this.bindGlobalHandlers()},removeElement:function(){this.unbindGlobalHandlers(),this.el.remove()},renderSkeleton:function(){},renderDates:function(){},unrenderDates:function(){},bindGlobalHandlers:function(){a(document).on("dragstart sortstart",this.externalDragStartProxy)},unbindGlobalHandlers:function(){a(document).off("dragstart sortstart",this.externalDragStartProxy)},dayMousedown:function(a){var b,c,d=this,e=this.view,f=e.opt("selectable"),i=new cb(this.coordMap,{scroll:e.opt("dragScroll"),dragStart:function(){e.unselect()},cellOver:function(a,e,h){h&&(b=e?a:null,f&&(c=d.computeSelection(h,a),c?d.renderSelection(c):g()))},cellOut:function(a){b=null,c=null,d.unrenderSelection(),h()},listenStop:function(a){b&&e.triggerDayClick(b,d.getCellDayEl(b),a),c&&e.reportSelection(c,a),h()}});i.mousedown(a)},renderRangeHelper:function(a,b){var c=this.fabricateHelperEvent(a,b);this.renderHelper(c,b)},fabricateHelperEvent:function(a,b){var c=b?K(b.event):{};return c.start=a.start.clone(),c.end=a.end?a.end.clone():null,c.allDay=null,this.view.calendar.normalizeEventRange(c),c.className=(c.className||[]).concat("fc-helper"),b||(c.editable=!1),c},renderHelper:function(a,b){},unrenderHelper:function(){},renderSelection:function(a){this.renderHighlight(this.selectionRangeToSegs(a))},unrenderSelection:function(){this.unrenderHighlight()},computeSelection:function(a,b){var c,d=[a.start,a.end,b.start,b.end];return d.sort(V),c={start:d[0].clone(),end:d[3].clone()},this.view.calendar.isSelectionRangeAllowed(c)?c:null},selectionRangeToSegs:function(a){return this.rangeToSegs(a)},renderHighlight:function(a){this.renderFill("highlight",a)},unrenderHighlight:function(){this.unrenderFill("highlight")},highlightSegClasses:function(){return["fc-highlight"]},renderFill:function(a,b){},unrenderFill:function(a){var b=this.elsByFill[a];b&&(b.remove(),delete this.elsByFill[a])},renderFillSegEls:function(b,c){var d,e=this,f=this[b+"SegEl"],g="",h=[];if(c.length){for(d=0;d<c.length;d++)g+=this.fillSegHtml(b,c[d]);a(g).each(function(b,d){var g=c[b],i=a(d);f&&(i=f.call(e,g,i)),i&&(i=a(i),i.is(e.fillSegTag)&&(g.el=i,h.push(g)))})}return h},fillSegTag:"div",fillSegHtml:function(a,b){var c=this[a+"SegClasses"],d=this[a+"SegCss"],e=c?c.call(this,b):[],f=T(d?d.call(this,b):{});return"<"+this.fillSegTag+(e.length?' class="'+e.join(" ")+'"':"")+(f?' style="'+f+'"':"")+" />"},headHtml:function(){return'<div class="fc-row '+this.view.widgetHeaderClass+'"><table><thead>'+this.rowHtml("head")+"</thead></table></div>"},headCellHtml:function(a){var b=this.view,c=a.start;return'<th class="fc-day-header '+b.widgetHeaderClass+" fc-"+Qa[c.day()]+'">'+R(c.format(this.colHeadFormat))+"</th>"},bgCellHtml:function(a){var b=this.view,c=a.start,d=this.getDayClasses(c);return d.unshift("fc-day",b.widgetContentClass),'<td class="'+d.join(" ")+'" data-date="'+c.format("YYYY-MM-DD")+'"></td>'},getDayClasses:function(a){var b=this.view,c=b.calendar.getNow().stripTime(),d=["fc-"+Qa[a.day()]];return 1==b.intervalDuration.as("months")&&a.month()!=b.intervalStart.month()&&d.push("fc-other-month"),a.isSame(c,"day")?d.push("fc-today",b.highlightStateClass):c>a?d.push("fc-past"):d.push("fc-future"),d}});fb.mixin({mousedOverSeg:null,isDraggingSeg:!1,isResizingSeg:!1,isDraggingExternal:!1,segs:null,renderEvents:function(a){var b,c,d=this.eventsToSegs(a),e=[],f=[];for(b=0;b<d.length;b++)c=d[b],ma(c.event)?e.push(c):f.push(c);e=this.renderBgSegs(e)||e,f=this.renderFgSegs(f)||f,this.segs=e.concat(f)},unrenderEvents:function(){this.triggerSegMouseout(),this.unrenderFgSegs(),this.unrenderBgSegs(),this.segs=null},getEventSegs:function(){return this.segs||[]},renderFgSegs:function(a){},unrenderFgSegs:function(){},renderFgSegEls:function(b,c){var d,e=this.view,f="",g=[];if(b.length){for(d=0;d<b.length;d++)f+=this.fgSegHtml(b[d],c);a(f).each(function(c,d){var f=b[c],h=e.resolveEventEl(f.event,a(d));h&&(h.data("fc-seg",f),f.el=h,g.push(f))})}return g},fgSegHtml:function(a,b){},renderBgSegs:function(a){return this.renderFill("bgEvent",a)},unrenderBgSegs:function(){this.unrenderFill("bgEvent")},bgEventSegEl:function(a,b){return this.view.resolveEventEl(a.event,b)},bgEventSegClasses:function(a){var b=a.event,c=b.source||{};return["fc-bgevent"].concat(b.className,c.className||[])},bgEventSegCss:function(a){var b=this.view,c=a.event,d=c.source||{};return{"background-color":c.backgroundColor||c.color||d.backgroundColor||d.color||b.opt("eventBackgroundColor")||b.opt("eventColor")}},businessHoursSegClasses:function(a){return["fc-nonbusiness","fc-bgevent"]},bindSegHandlers:function(){var b=this,c=this.view;a.each({mouseenter:function(a,c){b.triggerSegMouseover(a,c)},mouseleave:function(a,c){b.triggerSegMouseout(a,c)},click:function(a,b){return c.trigger("eventClick",this,a.event,b)},mousedown:function(d,e){a(e.target).is(".fc-resizer")&&c.isEventResizable(d.event)?b.segResizeMousedown(d,e,a(e.target).is(".fc-start-resizer")):c.isEventDraggable(d.event)&&b.segDragMousedown(d,e)}},function(c,d){b.el.on(c,".fc-event-container > *",function(c){var e=a(this).data("fc-seg");return!e||b.isDraggingSeg||b.isResizingSeg?void 0:d.call(this,e,c)})})},triggerSegMouseover:function(a,b){this.mousedOverSeg||(this.mousedOverSeg=a,this.view.trigger("eventMouseover",a.el[0],a.event,b))},triggerSegMouseout:function(a,b){b=b||{},this.mousedOverSeg&&(a=a||this.mousedOverSeg,this.mousedOverSeg=null,this.view.trigger("eventMouseout",a.el[0],a.event,b))},segDragMousedown:function(a,b){var c,d=this,e=this.view,f=e.calendar,i=a.el,j=a.event,k=new db(a.el,{parentEl:e.el,opacity:e.opt("dragOpacity"),revertDuration:e.opt("dragRevertDuration"),zIndex:2}),l=new cb(e.coordMap,{distance:5,scroll:e.opt("dragScroll"),subjectEl:i,subjectCenter:!0,listenStart:function(a){k.hide(),k.start(a)},dragStart:function(b){d.triggerSegMouseout(a,b),d.segDragStart(a,b),e.hideEvent(j)},cellOver:function(b,h,i){a.cell&&(i=a.cell),c=d.computeEventDrop(i,b,j),c&&!f.isEventRangeAllowed(c,j)&&(g(),c=null),c&&e.renderDrag(c,a)?k.hide():k.show(),h&&(c=null)},cellOut:function(){e.unrenderDrag(),k.show(),c=null},cellDone:function(){h()},dragStop:function(b){k.stop(!c,function(){e.unrenderDrag(),e.showEvent(j),d.segDragStop(a,b),c&&e.reportEventDrop(j,c,this.largeUnit,i,b)})},listenStop:function(){k.stop()}});l.mousedown(b)},segDragStart:function(a,b){this.isDraggingSeg=!0,this.view.trigger("eventDragStart",a.el[0],a.event,b,{})},segDragStop:function(a,b){this.isDraggingSeg=!1,this.view.trigger("eventDragStop",a.el[0],a.event,b,{})},computeEventDrop:function(a,b,c){var d,e,f=this.view.calendar,g=a.start,h=b.start;return g.hasTime()===h.hasTime()?(d=this.diffDates(h,g),c.allDay&&G(d)?(e={start:c.start.clone(),end:f.getEventEnd(c),allDay:!1},f.normalizeEventRangeTimes(e)):e={start:c.start.clone(),end:c.end?c.end.clone():null,allDay:c.allDay},e.start.add(d),e.end&&e.end.add(d)):e={start:h.clone(),end:null,allDay:!h.hasTime()},e},applyDragOpacity:function(a){var b=this.view.opt("dragOpacity");null!=b&&a.each(function(a,c){c.style.opacity=b})},externalDragStart:function(b,c){var d,e,f=this.view;f.opt("droppable")&&(d=a((c?c.item:null)||b.target),e=f.opt("dropAccept"),(a.isFunction(e)?e.call(d[0],d):d.is(e))&&(this.isDraggingExternal||this.listenToExternalDrag(d,b,c)))},listenToExternalDrag:function(a,b,c){var d,e,f=this,i=sa(a);d=new cb(this.coordMap,{listenStart:function(){f.isDraggingExternal=!0},cellOver:function(a){e=f.computeExternalDrop(a,i),e?f.renderDrag(e):g()},cellOut:function(){e=null,f.unrenderDrag(),h()},dragStop:function(){f.unrenderDrag(),h(),e&&f.view.reportExternalDrop(i,e,a,b,c)},listenStop:function(){f.isDraggingExternal=!1}}),d.startDrag(b)},computeExternalDrop:function(a,b){var c={start:a.start.clone(),end:null};return b.startTime&&!c.start.hasTime()&&c.start.time(b.startTime),b.duration&&(c.end=c.start.clone().add(b.duration)),this.view.calendar.isExternalDropRangeAllowed(c,b.eventProps)?c:null},renderDrag:function(a,b){},unrenderDrag:function(){},segResizeMousedown:function(a,b,c){var d,e,f=this,i=this.view,j=i.calendar,k=a.el,l=a.event,m=j.getEventEnd(l);d=new cb(this.coordMap,{distance:5,scroll:i.opt("dragScroll"),subjectEl:k,dragStart:function(b){f.triggerSegMouseout(a,b),f.segResizeStart(a,b)},cellOver:function(b,d,h){e=c?f.computeEventStartResize(h,b,l):f.computeEventEndResize(h,b,l),e&&(j.isEventRangeAllowed(e,l)?e.start.isSame(l.start)&&e.end.isSame(m)&&(e=null):(g(),e=null)),e&&(i.hideEvent(l),f.renderEventResize(e,a))},cellOut:function(){e=null},cellDone:function(){f.unrenderEventResize(),i.showEvent(l),h()},dragStop:function(b){f.segResizeStop(a,b),e&&i.reportEventResize(l,e,this.largeUnit,k,b)}}),d.mousedown(b)},segResizeStart:function(a,b){this.isResizingSeg=!0,this.view.trigger("eventResizeStart",a.el[0],a.event,b,{})},segResizeStop:function(a,b){this.isResizingSeg=!1,this.view.trigger("eventResizeStop",a.el[0],a.event,b,{})},computeEventStartResize:function(a,b,c){return this.computeEventResize("start",a,b,c)},computeEventEndResize:function(a,b,c){return this.computeEventResize("end",a,b,c)},computeEventResize:function(a,b,c,d){var e,f,g=this.view.calendar,h=this.diffDates(c[a],b[a]);return e={start:d.start.clone(),end:g.getEventEnd(d),allDay:d.allDay},e.allDay&&G(h)&&(e.allDay=!1,g.normalizeEventRangeTimes(e)),e[a].add(h),e.start.isBefore(e.end)||(f=d.allDay?g.defaultAllDayEventDuration:g.defaultTimedEventDuration,this.cellDuration&&this.cellDuration<f&&(f=this.cellDuration),"start"==a?e.start=e.end.clone().subtract(f):e.end=e.start.clone().add(f)),e},renderEventResize:function(a,b){},unrenderEventResize:function(){},getEventTimeText:function(a,b,c){return null==b&&(b=this.eventTimeFormat),null==c&&(c=this.displayEventEnd),this.displayEventTime&&a.start.hasTime()?c&&a.end?this.view.formatRange(a,b):a.start.format(b):""},getSegClasses:function(a,b,c){var d=a.event,e=["fc-event",a.isStart?"fc-start":"fc-not-start",a.isEnd?"fc-end":"fc-not-end"].concat(d.className,d.source?d.source.className:[]);return b&&e.push("fc-draggable"),c&&e.push("fc-resizable"),e},getEventSkinCss:function(a){var b=this.view,c=a.source||{},d=a.color,e=c.color,f=b.opt("eventColor");return{"background-color":a.backgroundColor||d||c.backgroundColor||e||b.opt("eventBackgroundColor")||f,"border-color":a.borderColor||d||c.borderColor||e||b.opt("eventBorderColor")||f,color:a.textColor||c.textColor||b.opt("eventTextColor")}},eventsToSegs:function(a,b){var c,d=this.eventsToRanges(a),e=[];for(c=0;c<d.length;c++)e.push.apply(e,this.eventRangeToSegs(d[c],b));return e},eventsToRanges:function(b){var c=this,d=pa(b),e=[];return a.each(d,function(a,b){b.length&&e.push.apply(e,na(b[0])?c.eventsToInverseRanges(b):c.eventsToNormalRanges(b))}),e},eventsToNormalRanges:function(a){var b,c,d,e,f=this.view.calendar,g=[];for(b=0;b<a.length;b++)c=a[b],d=c.start.clone().stripZone(),e=f.getEventEnd(c).stripZone(),g.push({event:c,start:d,end:e,eventStartMS:+d,eventDurationMS:e-d});return g},eventsToInverseRanges:function(a){var b,c,d=this.view,e=d.start.clone().stripZone(),f=d.end.clone().stripZone(),g=this.eventsToNormalRanges(a),h=[],i=a[0],j=e;for(g.sort(qa),b=0;b<g.length;b++)c=g[b],c.start>j&&h.push({event:i,start:j,end:c.start}),j=c.end;return f>j&&h.push({event:i,start:j,end:f}),h},eventRangeToSegs:function(a,b){var c,d,e;for(a=this.view.calendar.ensureVisibleEventRange(a),c=b?b(a):this.rangeToSegs(a),d=0;d<c.length;d++)e=c[d],e.event=a.event,e.eventStartMS=a.eventStartMS,e.eventDurationMS=a.eventDurationMS;return c}}),Ja.compareSegs=ra,Ja.dataAttrPrefix="";var gb=fb.extend({numbersVisible:!1,bottomCoordPadding:0,breakOnWeeks:null,cellDates:null,dayToCellOffsets:null,rowEls:null,dayEls:null,helperEls:null,constructor:function(){fb.apply(this,arguments),this.cellDuration=b.duration(1,"day")},renderDates:function(a){var b,c,d,e=this.view,f=this.rowCnt,g=this.colCnt,h=f*g,i="";for(b=0;f>b;b++)i+=this.dayRowHtml(b,a);for(this.el.html(i),this.rowEls=this.el.find(".fc-row"),this.dayEls=this.el.find(".fc-day"),c=0;h>c;c++)d=this.getCell(c),e.trigger("dayRender",null,d.start,this.dayEls.eq(c))},unrenderDates:function(){this.removeSegPopover()},renderBusinessHours:function(){var a=this.view.calendar.getBusinessHoursEvents(!0),b=this.eventsToSegs(a);this.renderFill("businessHours",b,"bgevent")},dayRowHtml:function(a,b){var c=this.view,d=["fc-row","fc-week",c.widgetContentClass];return b&&d.push("fc-rigid"),'<div class="'+d.join(" ")+'"><div class="fc-bg"><table>'+this.rowHtml("day",a)+'</table></div><div class="fc-content-skeleton"><table>'+(this.numbersVisible?"<thead>"+this.rowHtml("number",a)+"</thead>":"")+"</table></div></div>"},dayCellHtml:function(a){return this.bgCellHtml(a)},computeColHeadFormat:function(){return this.rowCnt>1?"ddd":this.colCnt>1?this.view.opt("dayOfMonthFormat"):"dddd"},computeEventTimeFormat:function(){return this.view.opt("extraSmallTimeFormat")},computeDisplayEventEnd:function(){return 1==this.colCnt},rangeUpdated:function(){var a,b,c,d;if(this.updateCellDates(),a=this.cellDates,this.breakOnWeeks){for(b=a[0].day(),d=1;d<a.length&&a[d].day()!=b;d++);c=Math.ceil(a.length/d)}else c=1,d=a.length;this.rowCnt=c,this.colCnt=d},updateCellDates:function(){for(var a=this.view,b=this.start.clone(),c=[],d=-1,e=[];b.isBefore(this.end);)a.isHiddenDay(b)?e.push(d+.5):(d++,e.push(d),c.push(b.clone())),b.add(1,"days");this.cellDates=c,this.dayToCellOffsets=e},computeCellDate:function(a){var b=this.colCnt,c=a.row*b+(this.isRTL?b-a.col-1:a.col);return this.cellDates[c].clone()},getRowEl:function(a){return this.rowEls.eq(a)},getColEl:function(a){return this.dayEls.eq(a)},getCellDayEl:function(a){return this.dayEls.eq(a.row*this.colCnt+a.col)},computeRowCoords:function(){var a=fb.prototype.computeRowCoords.call(this);return a[a.length-1].bottom+=this.bottomCoordPadding,a},rangeToSegs:function(a){var b,c,d,e,f,g,h,i,j,k,l=this.isRTL,m=this.rowCnt,n=this.colCnt,o=[];for(a=this.view.computeDayRange(a),b=this.dateToCellOffset(a.start),c=this.dateToCellOffset(a.end.subtract(1,"days")),d=0;m>d;d++)e=d*n,f=e+n-1,i=Math.max(e,b),j=Math.min(f,c),i=Math.ceil(i),j=Math.floor(j),j>=i&&(g=i===b,h=j===c,i-=e,j-=e,k={row:d,isStart:g,isEnd:h},l?(k.leftCol=n-j-1,k.rightCol=n-i-1):(k.leftCol=i,k.rightCol=j),o.push(k));return o},dateToCellOffset:function(a){var b=this.dayToCellOffsets,c=a.diff(this.start,"days");return 0>c?b[0]-1:c>=b.length?b[b.length-1]+1:b[c]},renderDrag:function(a,b){return this.renderHighlight(this.eventRangeToSegs(a)),b&&!b.el.closest(this.el).length?(this.renderRangeHelper(a,b),this.applyDragOpacity(this.helperEls),!0):void 0},unrenderDrag:function(){this.unrenderHighlight(),this.unrenderHelper()},renderEventResize:function(a,b){this.renderHighlight(this.eventRangeToSegs(a)),this.renderRangeHelper(a,b)},unrenderEventResize:function(){this.unrenderHighlight(),this.unrenderHelper()},renderHelper:function(b,c){var d,e=[],f=this.eventsToSegs([b]);f=this.renderFgSegEls(f),d=this.renderSegRows(f),this.rowEls.each(function(b,f){var g,h=a(f),i=a('<div class="fc-helper-skeleton"><table/></div>');g=c&&c.row===b?c.el.position().top:h.find(".fc-content-skeleton tbody").position().top,i.css("top",g).find("table").append(d[b].tbodyEl),h.append(i),e.push(i[0])}),this.helperEls=a(e)},unrenderHelper:function(){this.helperEls&&(this.helperEls.remove(),this.helperEls=null)},fillSegTag:"td",renderFill:function(b,c,d){var e,f,g,h=[];for(c=this.renderFillSegEls(b,c),e=0;e<c.length;e++)f=c[e],g=this.renderFillRow(b,f,d),this.rowEls.eq(f.row).append(g),h.push(g[0]);return this.elsByFill[b]=a(h),c},renderFillRow:function(b,c,d){var e,f,g=this.colCnt,h=c.leftCol,i=c.rightCol+1;return d=d||b.toLowerCase(),e=a('<div class="fc-'+d+'-skeleton"><table><tr/></table></div>'),f=e.find("tr"),h>0&&f.append('<td colspan="'+h+'"/>'),f.append(c.el.attr("colspan",i-h)),g>i&&f.append('<td colspan="'+(g-i)+'"/>'),this.bookendCells(f,b),e}});gb.mixin({rowStructs:null,unrenderEvents:function(){this.removeSegPopover(),fb.prototype.unrenderEvents.apply(this,arguments)},getEventSegs:function(){return fb.prototype.getEventSegs.call(this).concat(this.popoverSegs||[])},renderBgSegs:function(b){var c=a.grep(b,function(a){return a.event.allDay});return fb.prototype.renderBgSegs.call(this,c)},renderFgSegs:function(b){var c;return b=this.renderFgSegEls(b),c=this.rowStructs=this.renderSegRows(b),this.rowEls.each(function(b,d){a(d).find(".fc-content-skeleton > table").append(c[b].tbodyEl)}),b},unrenderFgSegs:function(){for(var a,b=this.rowStructs||[];a=b.pop();)a.tbodyEl.remove();this.rowStructs=null},renderSegRows:function(a){var b,c,d=[];for(b=this.groupSegRows(a),c=0;c<b.length;c++)d.push(this.renderSegRow(c,b[c]));return d},fgSegHtml:function(a,b){var c,d,e=this.view,f=a.event,g=e.isEventDraggable(f),h=!b&&f.allDay&&a.isStart&&e.isEventResizableFromStart(f),i=!b&&f.allDay&&a.isEnd&&e.isEventResizableFromEnd(f),j=this.getSegClasses(a,g,h||i),k=T(this.getEventSkinCss(f)),l="";return j.unshift("fc-day-grid-event","fc-h-event"),a.isStart&&(c=this.getEventTimeText(f),c&&(l='<span class="fc-time">'+R(c)+"</span>")),d='<span class="fc-title">'+(R(f.title||"")||" ")+"</span>",'<a class="'+j.join(" ")+'"'+(f.url?' href="'+R(f.url)+'"':"")+(k?' style="'+k+'"':"")+'><div class="fc-content">'+(this.isRTL?d+" "+l:l+" "+d)+"</div>"+(h?'<div class="fc-resizer fc-start-resizer" />':"")+(i?'<div class="fc-resizer fc-end-resizer" />':"")+"</a>"},renderSegRow:function(b,c){function d(b){for(;b>g;)k=(r[e-1]||[])[g],k?k.attr("rowspan",parseInt(k.attr("rowspan")||1,10)+1):(k=a("<td/>"),h.append(k)),q[e][g]=k,r[e][g]=k,g++}var e,f,g,h,i,j,k,l=this.colCnt,m=this.buildSegLevels(c),n=Math.max(1,m.length),o=a("<tbody/>"),p=[],q=[],r=[];for(e=0;n>e;e++){if(f=m[e],g=0,h=a("<tr/>"),p.push([]),q.push([]),r.push([]),f)for(i=0;i<f.length;i++){for(j=f[i],d(j.leftCol),k=a('<td class="fc-event-container"/>').append(j.el),j.leftCol!=j.rightCol?k.attr("colspan",j.rightCol-j.leftCol+1):r[e][g]=k;g<=j.rightCol;)q[e][g]=k,p[e][g]=j,g++;h.append(k)}d(l),this.bookendCells(h,"eventSkeleton"),o.append(h)}return{row:b,tbodyEl:o,cellMatrix:q,segMatrix:p,segLevels:m,segs:c}},buildSegLevels:function(a){var b,c,d,e=[];for(a.sort(ra),b=0;b<a.length;b++){for(c=a[b],d=0;d<e.length&&ta(c,e[d]);d++);c.level=d,(e[d]||(e[d]=[])).push(c)}for(d=0;d<e.length;d++)e[d].sort(ua);return e},groupSegRows:function(a){var b,c=[];for(b=0;b<this.rowCnt;b++)c.push([]);for(b=0;b<a.length;b++)c[a[b].row].push(a[b]);return c}}),gb.mixin({segPopover:null,popoverSegs:null,removeSegPopover:function(){this.segPopover&&this.segPopover.hide()},limitRows:function(a){var b,c,d=this.rowStructs||[];for(b=0;b<d.length;b++)this.unlimitRow(b),c=a?"number"==typeof a?a:this.computeRowLevelLimit(b):!1,c!==!1&&this.limitRow(b,c)},computeRowLevelLimit:function(b){function c(b,c){f=Math.max(f,a(c).outerHeight())}var d,e,f,g=this.rowEls.eq(b),h=g.height(),i=this.rowStructs[b].tbodyEl.children();for(d=0;d<i.length;d++)if(e=i.eq(d).removeClass("fc-limited"),f=0,e.find("> td > :first-child").each(c),e.position().top+f>h)return d;return!1},limitRow:function(b,c){function d(d){for(;d>x;)e=u.getCell(b,x),k=u.getCellSegs(e,c),k.length&&(n=g[c-1][x],t=u.renderMoreLink(e,k),s=a("<div/>").append(t),n.append(s),w.push(s[0])),x++}var e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u=this,v=this.rowStructs[b],w=[],x=0;if(c&&c<v.segLevels.length){for(f=v.segLevels[c-1],g=v.cellMatrix,h=v.tbodyEl.children().slice(c).addClass("fc-limited").get(),i=0;i<f.length;i++){for(j=f[i],d(j.leftCol),m=[],l=0;x<=j.rightCol;)e=this.getCell(b,x),k=this.getCellSegs(e,c),m.push(k),l+=k.length,x++;if(l){for(n=g[c-1][j.leftCol],o=n.attr("rowspan")||1,p=[],q=0;q<m.length;q++)r=a('<td class="fc-more-cell"/>').attr("rowspan",o),k=m[q],e=this.getCell(b,j.leftCol+q),t=this.renderMoreLink(e,[j].concat(k)),s=a("<div/>").append(t),r.append(s),p.push(r[0]),w.push(r[0]);n.addClass("fc-limited").after(a(p)),h.push(n[0])}}d(this.colCnt),v.moreEls=a(w),v.limitedEls=a(h)}},unlimitRow:function(a){var b=this.rowStructs[a];b.moreEls&&(b.moreEls.remove(),b.moreEls=null),b.limitedEls&&(b.limitedEls.removeClass("fc-limited"),b.limitedEls=null)},renderMoreLink:function(b,c){var d=this,e=this.view;return a('<a class="fc-more"/>').text(this.getMoreLinkText(c.length)).on("click",function(f){var g=e.opt("eventLimitClick"),h=b.start,i=a(this),j=d.getCellDayEl(b),k=d.getCellSegs(b),l=d.resliceDaySegs(k,h),m=d.resliceDaySegs(c,h);"function"==typeof g&&(g=e.trigger("eventLimitClick",null,{date:h,dayEl:j,moreEl:i,segs:l,hiddenSegs:m},f)),"popover"===g?d.showSegPopover(b,i,l):"string"==typeof g&&e.calendar.zoomTo(h,g)})},showSegPopover:function(a,b,c){var d,e,f=this,g=this.view,h=b.parent();d=1==this.rowCnt?g.el:this.rowEls.eq(a.row),e={className:"fc-more-popover",content:this.renderSegPopoverContent(a,c),parentEl:this.el,top:d.offset().top,autoHide:!0,viewportConstrain:g.opt("popoverViewportConstrain"),hide:function(){f.segPopover.removeElement(),f.segPopover=null,f.popoverSegs=null}},this.isRTL?e.right=h.offset().left+h.outerWidth()+1:e.left=h.offset().left-1,this.segPopover=new $a(e),this.segPopover.show()},renderSegPopoverContent:function(b,c){var d,e=this.view,f=e.opt("theme"),g=b.start.format(e.opt("dayPopoverFormat")),h=a('<div class="fc-header '+e.widgetHeaderClass+'"><span class="fc-close '+(f?"ui-icon ui-icon-closethick":"fc-icon fc-icon-x")+'"></span><span class="fc-title">'+R(g)+'</span><div class="fc-clear"/></div><div class="fc-body '+e.widgetContentClass+'"><div class="fc-event-container"></div></div>'),i=h.find(".fc-event-container");for(c=this.renderFgSegEls(c,!0),this.popoverSegs=c,d=0;d<c.length;d++)c[d].cell=b,
|
8 |
-
i.append(c[d].el);return h},resliceDaySegs:function(b,c){var d=a.map(b,function(a){return a.event}),e=c.clone().stripTime(),f=e.clone().add(1,"days"),g={start:e,end:f};return b=this.eventsToSegs(d,function(a){var b=A(a,g);return b?[b]:[]}),b.sort(ra),b},getMoreLinkText:function(a){var b=this.view.opt("eventLimitText");return"function"==typeof b?b(a):"+"+a+" "+b},getCellSegs:function(a,b){for(var c,d=this.rowStructs[a.row].segMatrix,e=b||0,f=[];e<d.length;)c=d[e][a.col],c&&f.push(c),e++;return f}});var hb=fb.extend({slotDuration:null,snapDuration:null,minTime:null,maxTime:null,colDates:null,axisFormat:null,dayEls:null,slatEls:null,slatTops:null,helperEl:null,businessHourSegs:null,constructor:function(){fb.apply(this,arguments),this.processOptions()},renderDates:function(){this.el.html(this.renderHtml()),this.dayEls=this.el.find(".fc-day"),this.slatEls=this.el.find(".fc-slats tr")},renderBusinessHours:function(){var a=this.view.calendar.getBusinessHoursEvents();this.businessHourSegs=this.renderFill("businessHours",this.eventsToSegs(a),"bgevent")},renderHtml:function(){return'<div class="fc-bg"><table>'+this.rowHtml("slotBg")+'</table></div><div class="fc-slats"><table>'+this.slatRowHtml()+"</table></div>"},slotBgCellHtml:function(a){return this.bgCellHtml(a)},slatRowHtml:function(){for(var a,c,d,e=this.view,f=this.isRTL,g="",h=this.slotDuration.asMinutes()%15===0,i=b.duration(+this.minTime);i<this.maxTime;)a=this.start.clone().time(i),c=a.minutes(),d='<td class="fc-axis fc-time '+e.widgetContentClass+'" '+e.axisStyleAttr()+">"+(h&&c?"":"<span>"+R(a.format(this.axisFormat))+"</span>")+"</td>",g+="<tr "+(c?'class="fc-minor"':"")+">"+(f?"":d)+'<td class="'+e.widgetContentClass+'"/>'+(f?d:"")+"</tr>",i.add(this.slotDuration);return g},processOptions:function(){var a=this.view,c=a.opt("slotDuration"),d=a.opt("snapDuration");c=b.duration(c),d=d?b.duration(d):c,this.slotDuration=c,this.snapDuration=d,this.cellDuration=d,this.minTime=b.duration(a.opt("minTime")),this.maxTime=b.duration(a.opt("maxTime")),this.axisFormat=a.opt("axisFormat")||a.opt("smallTimeFormat")},computeColHeadFormat:function(){return this.colCnt>1?this.view.opt("dayOfMonthFormat"):"dddd"},computeEventTimeFormat:function(){return this.view.opt("noMeridiemTimeFormat")},computeDisplayEventEnd:function(){return!0},rangeUpdated:function(){var a,b=this.view,c=[];for(a=this.start.clone();a.isBefore(this.end);)c.push(a.clone()),a.add(1,"day"),a=b.skipHiddenDays(a);this.isRTL&&c.reverse(),this.colDates=c,this.colCnt=c.length,this.rowCnt=Math.ceil((this.maxTime-this.minTime)/this.snapDuration)},computeCellDate:function(a){var b=this.colDates[a.col],c=this.computeSnapTime(a.row);return b=this.view.calendar.rezoneDate(b),b.time(c),b},getColEl:function(a){return this.dayEls.eq(a)},computeSnapTime:function(a){return b.duration(this.minTime+this.snapDuration*a)},rangeToSegs:function(a){var b,c,d,e,f=this.colCnt,g=[];for(a={start:a.start.clone().stripZone(),end:a.end.clone().stripZone()},c=0;f>c;c++)d=this.colDates[c],e={start:d.clone().time(this.minTime),end:d.clone().time(this.maxTime)},b=A(a,e),b&&(b.col=c,g.push(b));return g},updateSize:function(a){this.computeSlatTops(),a&&this.updateSegVerticals()},computeRowCoords:function(){var a,b,c=this.el.offset().top,d=[];for(a=0;a<this.rowCnt;a++)b={top:c+this.computeTimeTop(this.computeSnapTime(a))},a>0&&(d[a-1].bottom=b.top),d.push(b);return b.bottom=b.top+this.computeTimeTop(this.computeSnapTime(a)),d},computeDateTop:function(a,c){return this.computeTimeTop(b.duration(a.clone().stripZone()-c.clone().stripTime()))},computeTimeTop:function(a){var b,c,d,e,f=(a-this.minTime)/this.slotDuration;return f=Math.max(0,f),f=Math.min(this.slatEls.length,f),b=Math.floor(f),c=f-b,d=this.slatTops[b],c?(e=this.slatTops[b+1],d+(e-d)*c):d},computeSlatTops:function(){var b,c=[];this.slatEls.each(function(d,e){b=a(e).position().top,c.push(b)}),c.push(b+this.slatEls.last().outerHeight()),this.slatTops=c},renderDrag:function(a,b){return b?(this.renderRangeHelper(a,b),this.applyDragOpacity(this.helperEl),!0):void this.renderHighlight(this.eventRangeToSegs(a))},unrenderDrag:function(){this.unrenderHelper(),this.unrenderHighlight()},renderEventResize:function(a,b){this.renderRangeHelper(a,b)},unrenderEventResize:function(){this.unrenderHelper()},renderHelper:function(b,c){var d,e,f,g,h=this.eventsToSegs([b]);for(h=this.renderFgSegEls(h),d=this.renderSegTable(h),e=0;e<h.length;e++)f=h[e],c&&c.col===f.col&&(g=c.el,f.el.css({left:g.css("left"),right:g.css("right"),"margin-left":g.css("margin-left"),"margin-right":g.css("margin-right")}));this.helperEl=a('<div class="fc-helper-skeleton"/>').append(d).appendTo(this.el)},unrenderHelper:function(){this.helperEl&&(this.helperEl.remove(),this.helperEl=null)},renderSelection:function(a){this.view.opt("selectHelper")?this.renderRangeHelper(a):this.renderHighlight(this.selectionRangeToSegs(a))},unrenderSelection:function(){this.unrenderHelper(),this.unrenderHighlight()},renderFill:function(b,c,d){var e,f,g,h,i,j,k,l,m,n;if(c.length){for(c=this.renderFillSegEls(b,c),e=this.groupSegCols(c),d=d||b.toLowerCase(),f=a('<div class="fc-'+d+'-skeleton"><table><tr/></table></div>'),g=f.find("tr"),h=0;h<e.length;h++)if(i=e[h],j=a("<td/>").appendTo(g),i.length)for(k=a('<div class="fc-'+d+'-container"/>').appendTo(j),l=this.colDates[h],m=0;m<i.length;m++)n=i[m],k.append(n.el.css({top:this.computeDateTop(n.start,l),bottom:-this.computeDateTop(n.end,l)}));this.bookendCells(g,b),this.el.append(f),this.elsByFill[b]=f}return c}});hb.mixin({eventSkeletonEl:null,renderFgSegs:function(b){return b=this.renderFgSegEls(b),this.el.append(this.eventSkeletonEl=a('<div class="fc-content-skeleton"/>').append(this.renderSegTable(b))),b},unrenderFgSegs:function(a){this.eventSkeletonEl&&(this.eventSkeletonEl.remove(),this.eventSkeletonEl=null)},renderSegTable:function(b){var c,d,e,f,g,h,i=a("<table><tr/></table>"),j=i.find("tr");for(c=this.groupSegCols(b),this.computeSegVerticals(b),f=0;f<c.length;f++){for(g=c[f],va(g),h=a('<div class="fc-event-container"/>'),d=0;d<g.length;d++)e=g[d],e.el.css(this.generateSegPositionCss(e)),e.bottom-e.top<30&&e.el.addClass("fc-short"),h.append(e.el);j.append(a("<td/>").append(h))}return this.bookendCells(j,"eventSkeleton"),i},updateSegVerticals:function(){var a,b=(this.segs||[]).concat(this.businessHourSegs||[]);for(this.computeSegVerticals(b),a=0;a<b.length;a++)b[a].el.css(this.generateSegVerticalCss(b[a]))},computeSegVerticals:function(a){var b,c;for(b=0;b<a.length;b++)c=a[b],c.top=this.computeDateTop(c.start,c.start),c.bottom=this.computeDateTop(c.end,c.start)},fgSegHtml:function(a,b){var c,d,e,f=this.view,g=a.event,h=f.isEventDraggable(g),i=!b&&a.isStart&&f.isEventResizableFromStart(g),j=!b&&a.isEnd&&f.isEventResizableFromEnd(g),k=this.getSegClasses(a,h,i||j),l=T(this.getEventSkinCss(g));return k.unshift("fc-time-grid-event","fc-v-event"),f.isMultiDayEvent(g)?(a.isStart||a.isEnd)&&(c=this.getEventTimeText(a),d=this.getEventTimeText(a,"LT"),e=this.getEventTimeText(a,null,!1)):(c=this.getEventTimeText(g),d=this.getEventTimeText(g,"LT"),e=this.getEventTimeText(g,null,!1)),'<a class="'+k.join(" ")+'"'+(g.url?' href="'+R(g.url)+'"':"")+(l?' style="'+l+'"':"")+'><div class="fc-content">'+(c?'<div class="fc-time" data-start="'+R(e)+'" data-full="'+R(d)+'"><span>'+R(c)+"</span></div>":"")+(g.title?'<div class="fc-title">'+R(g.title)+"</div>":"")+'</div><div class="fc-bg"/>'+(j?'<div class="fc-resizer fc-end-resizer" />':"")+"</a>"},generateSegPositionCss:function(a){var b,c,d=this.view.opt("slotEventOverlap"),e=a.backwardCoord,f=a.forwardCoord,g=this.generateSegVerticalCss(a);return d&&(f=Math.min(1,e+2*(f-e))),this.isRTL?(b=1-f,c=e):(b=e,c=1-f),g.zIndex=a.level+1,g.left=100*b+"%",g.right=100*c+"%",d&&a.forwardPressure&&(g[this.isRTL?"marginLeft":"marginRight"]=20),g},generateSegVerticalCss:function(a){return{top:a.top,bottom:-a.bottom}},groupSegCols:function(a){var b,c=[];for(b=0;b<this.colCnt;b++)c.push([]);for(b=0;b<a.length;b++)c[a[b].col].push(a[b]);return c}});var ib=Ja.View=ka.extend({type:null,name:null,title:null,calendar:null,options:null,coordMap:null,el:null,displaying:null,isSkeletonRendered:!1,isEventsRendered:!1,start:null,end:null,intervalStart:null,intervalEnd:null,intervalDuration:null,intervalUnit:null,isRTL:!1,isSelected:!1,scrollerEl:null,scrollTop:null,widgetHeaderClass:null,widgetContentClass:null,highlightStateClass:null,nextDayThreshold:null,isHiddenDayHash:null,documentMousedownProxy:null,constructor:function(a,c,d,e){this.calendar=a,this.type=this.name=c,this.options=d,this.intervalDuration=e||b.duration(1,"day"),this.nextDayThreshold=b.duration(this.opt("nextDayThreshold")),this.initThemingProps(),this.initHiddenDays(),this.isRTL=this.opt("isRTL"),this.documentMousedownProxy=X(this,"documentMousedown"),this.initialize()},initialize:function(){},opt:function(a){return this.options[a]},trigger:function(a,b){var c=this.calendar;return c.trigger.apply(c,[a,b||this].concat(Array.prototype.slice.call(arguments,2),[this]))},setDate:function(a){this.setRange(this.computeRange(a))},setRange:function(b){a.extend(this,b),this.updateTitle()},computeRange:function(a){var b,c,d=E(this.intervalDuration),e=a.clone().startOf(d),f=e.clone().add(this.intervalDuration);return/year|month|week|day/.test(d)?(e.stripTime(),f.stripTime()):(e.hasTime()||(e=this.calendar.rezoneDate(e)),f.hasTime()||(f=this.calendar.rezoneDate(f))),b=e.clone(),b=this.skipHiddenDays(b),c=f.clone(),c=this.skipHiddenDays(c,-1,!0),{intervalUnit:d,intervalStart:e,intervalEnd:f,start:b,end:c}},computePrevDate:function(a){return this.massageCurrentDate(a.clone().startOf(this.intervalUnit).subtract(this.intervalDuration),-1)},computeNextDate:function(a){return this.massageCurrentDate(a.clone().startOf(this.intervalUnit).add(this.intervalDuration))},massageCurrentDate:function(a,b){return this.intervalDuration.as("days")<=1&&this.isHiddenDay(a)&&(a=this.skipHiddenDays(a,b),a.startOf("day")),a},updateTitle:function(){this.title=this.computeTitle()},computeTitle:function(){return this.formatRange({start:this.intervalStart,end:this.intervalEnd},this.opt("titleFormat")||this.computeTitleFormat(),this.opt("titleRangeSeparator"))},computeTitleFormat:function(){return"year"==this.intervalUnit?"YYYY":"month"==this.intervalUnit?this.opt("monthYearFormat"):this.intervalDuration.as("days")>1?"ll":"LL"},formatRange:function(a,b,c){var d=a.end;return d.hasTime()||(d=d.clone().subtract(1)),fa(a.start,d,b,c,this.opt("isRTL"))},setElement:function(a){this.el=a,this.bindGlobalHandlers()},removeElement:function(){this.clear(),this.isSkeletonRendered&&(this.unrenderSkeleton(),this.isSkeletonRendered=!1),this.unbindGlobalHandlers(),this.el.remove()},display:function(b){var c=this,d=null;return this.displaying&&(d=this.queryScroll()),this.clear().then(function(){return c.displaying=a.when(c.displayView(b)).then(function(){c.forceScroll(c.computeInitialScroll(d)),c.triggerRender()})})},clear:function(){var b=this,c=this.displaying;return c?c.then(function(){return b.displaying=null,b.clearEvents(),b.clearView()}):a.when()},displayView:function(a){this.isSkeletonRendered||(this.renderSkeleton(),this.isSkeletonRendered=!0),this.setDate(a),this.render&&this.render(),this.renderDates(),this.updateSize(),this.renderBusinessHours()},clearView:function(){this.unselect(),this.triggerUnrender(),this.unrenderBusinessHours(),this.unrenderDates(),this.destroy&&this.destroy()},renderSkeleton:function(){},unrenderSkeleton:function(){},renderDates:function(){},unrenderDates:function(){},renderBusinessHours:function(){},unrenderBusinessHours:function(){},triggerRender:function(){this.trigger("viewRender",this,this,this.el)},triggerUnrender:function(){this.trigger("viewDestroy",this,this,this.el)},bindGlobalHandlers:function(){a(document).on("mousedown",this.documentMousedownProxy)},unbindGlobalHandlers:function(){a(document).off("mousedown",this.documentMousedownProxy)},initThemingProps:function(){var a=this.opt("theme")?"ui":"fc";this.widgetHeaderClass=a+"-widget-header",this.widgetContentClass=a+"-widget-content",this.highlightStateClass=a+"-state-highlight"},updateSize:function(a){var b;a&&(b=this.queryScroll()),this.updateHeight(a),this.updateWidth(a),a&&this.setScroll(b)},updateWidth:function(a){},updateHeight:function(a){var b=this.calendar;this.setHeight(b.getSuggestedViewHeight(),b.isHeightAuto())},setHeight:function(a,b){},computeScrollerHeight:function(a){var b,c,d=this.scrollerEl;return b=this.el.add(d),b.css({position:"relative",left:-1}),c=this.el.outerHeight()-d.height(),b.css({position:"",left:""}),a-c},computeInitialScroll:function(a){return 0},queryScroll:function(){return this.scrollerEl?this.scrollerEl.scrollTop():void 0},setScroll:function(a){return this.scrollerEl?this.scrollerEl.scrollTop(a):void 0},forceScroll:function(a){var b=this;this.setScroll(a),setTimeout(function(){b.setScroll(a)},0)},displayEvents:function(a){var b=this.queryScroll();this.clearEvents(),this.renderEvents(a),this.isEventsRendered=!0,this.setScroll(b),this.triggerEventRender()},clearEvents:function(){this.isEventsRendered&&(this.triggerEventUnrender(),this.destroyEvents&&this.destroyEvents(),this.unrenderEvents(),this.isEventsRendered=!1)},renderEvents:function(a){},unrenderEvents:function(){},triggerEventRender:function(){this.renderedEventSegEach(function(a){this.trigger("eventAfterRender",a.event,a.event,a.el)}),this.trigger("eventAfterAllRender")},triggerEventUnrender:function(){this.renderedEventSegEach(function(a){this.trigger("eventDestroy",a.event,a.event,a.el)})},resolveEventEl:function(b,c){var d=this.trigger("eventRender",b,b,c);return d===!1?c=null:d&&d!==!0&&(c=a(d)),c},showEvent:function(a){this.renderedEventSegEach(function(a){a.el.css("visibility","")},a)},hideEvent:function(a){this.renderedEventSegEach(function(a){a.el.css("visibility","hidden")},a)},renderedEventSegEach:function(a,b){var c,d=this.getEventSegs();for(c=0;c<d.length;c++)b&&d[c].event._id!==b._id||d[c].el&&a.call(this,d[c])},getEventSegs:function(){return[]},isEventDraggable:function(a){var b=a.source||{};return Q(a.startEditable,b.startEditable,this.opt("eventStartEditable"),a.editable,b.editable,this.opt("editable"))},reportEventDrop:function(a,b,c,d,e){var f=this.calendar,g=f.mutateEvent(a,b,c),h=function(){g.undo(),f.reportEventChange()};this.triggerEventDrop(a,g.dateDelta,h,d,e),f.reportEventChange()},triggerEventDrop:function(a,b,c,d,e){this.trigger("eventDrop",d[0],a,b,c,e,{})},reportExternalDrop:function(b,c,d,e,f){var g,h,i=b.eventProps;i&&(g=a.extend({},i,c),h=this.calendar.renderEvent(g,b.stick)[0]),this.triggerExternalDrop(h,c,d,e,f)},triggerExternalDrop:function(a,b,c,d,e){this.trigger("drop",c[0],b.start,d,e),a&&this.trigger("eventReceive",null,a)},renderDrag:function(a,b){},unrenderDrag:function(){},isEventResizableFromStart:function(a){return this.opt("eventResizableFromStart")&&this.isEventResizable(a)},isEventResizableFromEnd:function(a){return this.isEventResizable(a)},isEventResizable:function(a){var b=a.source||{};return Q(a.durationEditable,b.durationEditable,this.opt("eventDurationEditable"),a.editable,b.editable,this.opt("editable"))},reportEventResize:function(a,b,c,d,e){var f=this.calendar,g=f.mutateEvent(a,b,c),h=function(){g.undo(),f.reportEventChange()};this.triggerEventResize(a,g.durationDelta,h,d,e),f.reportEventChange()},triggerEventResize:function(a,b,c,d,e){this.trigger("eventResize",d[0],a,b,c,e,{})},select:function(a,b){this.unselect(b),this.renderSelection(a),this.reportSelection(a,b)},renderSelection:function(a){},reportSelection:function(a,b){this.isSelected=!0,this.triggerSelect(a,b)},triggerSelect:function(a,b){this.trigger("select",null,a.start,a.end,b)},unselect:function(a){this.isSelected&&(this.isSelected=!1,this.destroySelection&&this.destroySelection(),this.unrenderSelection(),this.trigger("unselect",null,a))},unrenderSelection:function(){},documentMousedown:function(b){var c;this.isSelected&&this.opt("unselectAuto")&&v(b)&&(c=this.opt("unselectCancel"),c&&a(b.target).closest(c).length||this.unselect(b))},triggerDayClick:function(a,b,c){this.trigger("dayClick",b,a.start,c)},initHiddenDays:function(){var b,c=this.opt("hiddenDays")||[],d=[],e=0;for(this.opt("weekends")===!1&&c.push(0,6),b=0;7>b;b++)(d[b]=-1!==a.inArray(b,c))||e++;if(!e)throw"invalid hiddenDays";this.isHiddenDayHash=d},isHiddenDay:function(a){return b.isMoment(a)&&(a=a.day()),this.isHiddenDayHash[a]},skipHiddenDays:function(a,b,c){var d=a.clone();for(b=b||1;this.isHiddenDayHash[(d.day()+(c?b:0)+7)%7];)d.add(b,"days");return d},computeDayRange:function(a){var b,c=a.start.clone().stripTime(),d=a.end,e=null;return d&&(e=d.clone().stripTime(),b=+d.time(),b&&b>=this.nextDayThreshold&&e.add(1,"days")),(!d||c>=e)&&(e=c.clone().add(1,"days")),{start:c,end:e}},isMultiDayEvent:function(a){var b=this.computeDayRange(a);return b.end.diff(b.start,"days")>1}}),jb=Ja.Calendar=ka.extend({dirDefaults:null,langDefaults:null,overrides:null,options:null,viewSpecCache:null,view:null,header:null,loadingLevel:0,constructor:Da,initialize:function(){},initOptions:function(a){var b,e,f,g;a=d(a),b=a.lang,e=kb[b],e||(b=jb.defaults.lang,e=kb[b]||{}),f=Q(a.isRTL,e.isRTL,jb.defaults.isRTL),g=f?jb.rtlDefaults:{},this.dirDefaults=g,this.langDefaults=e,this.overrides=a,this.options=c([jb.defaults,g,e,a]),Ea(this.options),this.viewSpecCache={}},getViewSpec:function(a){var b=this.viewSpecCache;return b[a]||(b[a]=this.buildViewSpec(a))},getUnitViewSpec:function(b){var c,d,e;if(-1!=a.inArray(b,Ra))for(c=this.header.getViewsWithButtons(),a.each(Ja.views,function(a){c.push(a)}),d=0;d<c.length;d++)if(e=this.getViewSpec(c[d]),e&&e.singleUnit==b)return e},buildViewSpec:function(a){for(var d,e,f,g,h=this.overrides.views||{},i=[],j=[],k=[],l=a;l;)d=Ka[l],e=h[l],l=null,"function"==typeof d&&(d={"class":d}),d&&(i.unshift(d),j.unshift(d.defaults||{}),f=f||d.duration,l=l||d.type),e&&(k.unshift(e),f=f||e.duration,l=l||e.type);return d=J(i),d.type=a,d["class"]?(f&&(f=b.duration(f),f.valueOf()&&(d.duration=f,g=E(f),1===f.as(g)&&(d.singleUnit=g,k.unshift(h[g]||{})))),d.defaults=c(j),d.overrides=c(k),this.buildViewSpecOptions(d),this.buildViewSpecButtonText(d,a),d):!1},buildViewSpecOptions:function(a){a.options=c([jb.defaults,a.defaults,this.dirDefaults,this.langDefaults,this.overrides,a.overrides]),Ea(a.options)},buildViewSpecButtonText:function(a,b){function c(c){var d=c.buttonText||{};return d[b]||(a.singleUnit?d[a.singleUnit]:null)}a.buttonTextOverride=c(this.overrides)||a.overrides.buttonText,a.buttonTextDefault=c(this.langDefaults)||c(this.dirDefaults)||a.defaults.buttonText||c(jb.defaults)||(a.duration?this.humanizeDuration(a.duration):null)||b},instantiateView:function(a){var b=this.getViewSpec(a);return new b["class"](this,a,b.options,b.duration)},isValidViewType:function(a){return Boolean(this.getViewSpec(a))},pushLoading:function(){this.loadingLevel++||this.trigger("loading",null,!0,this.view)},popLoading:function(){--this.loadingLevel||this.trigger("loading",null,!1,this.view)},buildSelectRange:function(a,b){return a=this.moment(a),b=b?this.moment(b):a.hasTime()?a.clone().add(this.defaultTimedEventDuration):a.clone().add(this.defaultAllDayEventDuration),{start:a,end:b}}});jb.defaults={titleRangeSeparator:" — ",monthYearFormat:"MMMM YYYY",defaultTimedEventDuration:"02:00:00",defaultAllDayEventDuration:{days:1},forceEventDuration:!1,nextDayThreshold:"09:00:00",defaultView:"month",aspectRatio:1.35,header:{left:"title",center:"",right:"today prev,next"},weekends:!0,weekNumbers:!1,weekNumberTitle:"W",weekNumberCalculation:"local",scrollTime:"06:00:00",lazyFetching:!0,startParam:"start",endParam:"end",timezoneParam:"timezone",timezone:!1,isRTL:!1,buttonText:{prev:"prev",next:"next",prevYear:"prev year",nextYear:"next year",year:"year",today:"today",month:"month",week:"week",day:"day"},buttonIcons:{prev:"left-single-arrow",next:"right-single-arrow",prevYear:"left-double-arrow",nextYear:"right-double-arrow"},theme:!1,themeButtonIcons:{prev:"circle-triangle-w",next:"circle-triangle-e",prevYear:"seek-prev",nextYear:"seek-next"},dragOpacity:.75,dragRevertDuration:500,dragScroll:!0,unselectAuto:!0,dropAccept:"*",eventLimit:!1,eventLimitText:"more",eventLimitClick:"popover",dayPopoverFormat:"LL",handleWindowResize:!0,windowResizeDelay:200},jb.englishDefaults={dayPopoverFormat:"dddd, MMMM D"},jb.rtlDefaults={header:{left:"next,prev today",center:"",right:"title"},buttonIcons:{prev:"right-single-arrow",next:"left-single-arrow",prevYear:"right-double-arrow",nextYear:"left-double-arrow"},themeButtonIcons:{prev:"circle-triangle-e",next:"circle-triangle-w",nextYear:"seek-prev",prevYear:"seek-next"}};var kb=Ja.langs={};Ja.datepickerLang=function(b,c,d){var e=kb[b]||(kb[b]={});e.isRTL=d.isRTL,e.weekNumberTitle=d.weekHeader,a.each(lb,function(a,b){e[a]=b(d)}),a.datepicker&&(a.datepicker.regional[c]=a.datepicker.regional[b]=d,a.datepicker.regional.en=a.datepicker.regional[""],a.datepicker.setDefaults(d))},Ja.lang=function(b,d){var e,f;e=kb[b]||(kb[b]={}),d&&(e=kb[b]=c([e,d])),f=Fa(b),a.each(mb,function(a,b){null==e[a]&&(e[a]=b(f,e))}),jb.defaults.lang=b};var lb={buttonText:function(a){return{prev:S(a.prevText),next:S(a.nextText),today:S(a.currentText)}},monthYearFormat:function(a){return a.showMonthAfterYear?"YYYY["+a.yearSuffix+"] MMMM":"MMMM YYYY["+a.yearSuffix+"]"}},mb={dayOfMonthFormat:function(a,b){var c=a.longDateFormat("l");return c=c.replace(/^Y+[^\w\s]*|[^\w\s]*Y+$/g,""),b.isRTL?c+=" ddd":c="ddd "+c,c},mediumTimeFormat:function(a){return a.longDateFormat("LT").replace(/\s*a$/i,"a")},smallTimeFormat:function(a){return a.longDateFormat("LT").replace(":mm","(:mm)").replace(/(\Wmm)$/,"($1)").replace(/\s*a$/i,"a")},extraSmallTimeFormat:function(a){return a.longDateFormat("LT").replace(":mm","(:mm)").replace(/(\Wmm)$/,"($1)").replace(/\s*a$/i,"t")},hourFormat:function(a){return a.longDateFormat("LT").replace(":mm","").replace(/(\Wmm)$/,"").replace(/\s*a$/i,"a")},noMeridiemTimeFormat:function(a){return a.longDateFormat("LT").replace(/\s*a$/i,"")}},nb={smallDayDateFormat:function(a){return a.isRTL?"D dd":"dd D"},weekFormat:function(a){return a.isRTL?"w[ "+a.weekNumberTitle+"]":"["+a.weekNumberTitle+" ]w"},smallWeekFormat:function(a){return a.isRTL?"w["+a.weekNumberTitle+"]":"["+a.weekNumberTitle+"]w"}};Ja.lang("en",jb.englishDefaults),Ja.sourceNormalizers=[],Ja.sourceFetchers=[];var ob={dataType:"json",cache:!1},pb=1;jb.prototype.getPeerEvents=function(a,b){var c,d,e=this.getEventCache(),f=[];for(c=0;c<e.length;c++)d=e[c],a&&a._id===d._id||f.push(d);return f};var qb=ib.extend({dayGrid:null,dayNumbersVisible:!1,weekNumbersVisible:!1,weekNumberWidth:null,headRowEl:null,initialize:function(){this.dayGrid=new gb(this),this.coordMap=this.dayGrid.coordMap},setRange:function(a){ib.prototype.setRange.call(this,a),this.dayGrid.breakOnWeeks=/year|month|week/.test(this.intervalUnit),this.dayGrid.setRange(a)},computeRange:function(a){var b=ib.prototype.computeRange.call(this,a);return/year|month/.test(b.intervalUnit)&&(b.start.startOf("week"),b.start=this.skipHiddenDays(b.start),b.end.weekday()&&(b.end.add(1,"week").startOf("week"),b.end=this.skipHiddenDays(b.end,-1,!0))),b},renderDates:function(){this.dayNumbersVisible=this.dayGrid.rowCnt>1,this.weekNumbersVisible=this.opt("weekNumbers"),this.dayGrid.numbersVisible=this.dayNumbersVisible||this.weekNumbersVisible,this.el.addClass("fc-basic-view").html(this.renderHtml()),this.headRowEl=this.el.find("thead .fc-row"),this.scrollerEl=this.el.find(".fc-day-grid-container"),this.dayGrid.coordMap.containerEl=this.scrollerEl,this.dayGrid.setElement(this.el.find(".fc-day-grid")),this.dayGrid.renderDates(this.hasRigidRows())},unrenderDates:function(){this.dayGrid.unrenderDates(),this.dayGrid.removeElement()},renderBusinessHours:function(){this.dayGrid.renderBusinessHours()},renderHtml:function(){return'<table><thead class="fc-head"><tr><td class="'+this.widgetHeaderClass+'">'+this.dayGrid.headHtml()+'</td></tr></thead><tbody class="fc-body"><tr><td class="'+this.widgetContentClass+'"><div class="fc-day-grid-container"><div class="fc-day-grid"/></div></td></tr></tbody></table>'},headIntroHtml:function(){return this.weekNumbersVisible?'<th class="fc-week-number '+this.widgetHeaderClass+'" '+this.weekNumberStyleAttr()+"><span>"+R(this.opt("weekNumberTitle"))+"</span></th>":void 0},numberIntroHtml:function(a){return this.weekNumbersVisible?'<td class="fc-week-number" '+this.weekNumberStyleAttr()+"><span>"+this.dayGrid.getCell(a,0).start.format("w")+"</span></td>":void 0},dayIntroHtml:function(){return this.weekNumbersVisible?'<td class="fc-week-number '+this.widgetContentClass+'" '+this.weekNumberStyleAttr()+"></td>":void 0},introHtml:function(){return this.weekNumbersVisible?'<td class="fc-week-number" '+this.weekNumberStyleAttr()+"></td>":void 0},numberCellHtml:function(a){var b,c=a.start;return this.dayNumbersVisible?(b=this.dayGrid.getDayClasses(c),b.unshift("fc-day-number"),'<td class="'+b.join(" ")+'" data-date="'+c.format()+'">'+c.date()+"</td>"):"<td/>"},weekNumberStyleAttr:function(){return null!==this.weekNumberWidth?'style="width:'+this.weekNumberWidth+'px"':""},hasRigidRows:function(){var a=this.opt("eventLimit");return a&&"number"!=typeof a},updateWidth:function(){this.weekNumbersVisible&&(this.weekNumberWidth=k(this.el.find(".fc-week-number")))},setHeight:function(a,b){var c,d=this.opt("eventLimit");m(this.scrollerEl),f(this.headRowEl),this.dayGrid.removeSegPopover(),d&&"number"==typeof d&&this.dayGrid.limitRows(d),c=this.computeScrollerHeight(a),this.setGridHeight(c,b),d&&"number"!=typeof d&&this.dayGrid.limitRows(d),!b&&l(this.scrollerEl,c)&&(e(this.headRowEl,r(this.scrollerEl)),c=this.computeScrollerHeight(a),this.scrollerEl.height(c))},setGridHeight:function(a,b){b?j(this.dayGrid.rowEls):i(this.dayGrid.rowEls,a,!0)},renderEvents:function(a){this.dayGrid.renderEvents(a),this.updateHeight()},getEventSegs:function(){return this.dayGrid.getEventSegs()},unrenderEvents:function(){this.dayGrid.unrenderEvents()},renderDrag:function(a,b){return this.dayGrid.renderDrag(a,b)},unrenderDrag:function(){this.dayGrid.unrenderDrag()},renderSelection:function(a){this.dayGrid.renderSelection(a)},unrenderSelection:function(){this.dayGrid.unrenderSelection()}}),rb=qb.extend({computeRange:function(a){var b,c=qb.prototype.computeRange.call(this,a);return this.isFixedWeeks()&&(b=Math.ceil(c.end.diff(c.start,"weeks",!0)),c.end.add(6-b,"weeks")),c},setGridHeight:function(a,b){b=b||"variable"===this.opt("weekMode"),b&&(a*=this.rowCnt/6),i(this.dayGrid.rowEls,a,!b)},isFixedWeeks:function(){var a=this.opt("weekMode");return a?"fixed"===a:this.opt("fixedWeekCount")}});Ka.basic={"class":qb},Ka.basicDay={type:"basic",duration:{days:1}},Ka.basicWeek={type:"basic",duration:{weeks:1}},Ka.month={"class":rb,duration:{months:1},defaults:{fixedWeekCount:!0}};var sb=ib.extend({timeGrid:null,dayGrid:null,axisWidth:null,noScrollRowEls:null,bottomRuleEl:null,bottomRuleHeight:null,initialize:function(){this.timeGrid=new hb(this),this.opt("allDaySlot")?(this.dayGrid=new gb(this),this.coordMap=new ab([this.dayGrid.coordMap,this.timeGrid.coordMap])):this.coordMap=this.timeGrid.coordMap},setRange:function(a){ib.prototype.setRange.call(this,a),this.timeGrid.setRange(a),this.dayGrid&&this.dayGrid.setRange(a)},renderDates:function(){this.el.addClass("fc-agenda-view").html(this.renderHtml()),this.scrollerEl=this.el.find(".fc-time-grid-container"),this.timeGrid.coordMap.containerEl=this.scrollerEl,this.timeGrid.setElement(this.el.find(".fc-time-grid")),this.timeGrid.renderDates(),this.bottomRuleEl=a('<hr class="fc-divider '+this.widgetHeaderClass+'"/>').appendTo(this.timeGrid.el),this.dayGrid&&(this.dayGrid.setElement(this.el.find(".fc-day-grid")),this.dayGrid.renderDates(),this.dayGrid.bottomCoordPadding=this.dayGrid.el.next("hr").outerHeight()),this.noScrollRowEls=this.el.find(".fc-row:not(.fc-scroller *)")},unrenderDates:function(){this.timeGrid.unrenderDates(),this.timeGrid.removeElement(),this.dayGrid&&(this.dayGrid.unrenderDates(),this.dayGrid.removeElement())},renderBusinessHours:function(){this.timeGrid.renderBusinessHours(),this.dayGrid&&this.dayGrid.renderBusinessHours()},renderHtml:function(){return'<table><thead class="fc-head"><tr><td class="'+this.widgetHeaderClass+'">'+this.timeGrid.headHtml()+'</td></tr></thead><tbody class="fc-body"><tr><td class="'+this.widgetContentClass+'">'+(this.dayGrid?'<div class="fc-day-grid"/><hr class="fc-divider '+this.widgetHeaderClass+'"/>':"")+'<div class="fc-time-grid-container"><div class="fc-time-grid"/></div></td></tr></tbody></table>'},headIntroHtml:function(){var a,b;return this.opt("weekNumbers")?(a=this.timeGrid.getCell(0).start,b=a.format(this.opt("smallWeekFormat")),'<th class="fc-axis fc-week-number '+this.widgetHeaderClass+'" '+this.axisStyleAttr()+"><span>"+R(b)+"</span></th>"):'<th class="fc-axis '+this.widgetHeaderClass+'" '+this.axisStyleAttr()+"></th>"},dayIntroHtml:function(){return'<td class="fc-axis '+this.widgetContentClass+'" '+this.axisStyleAttr()+"><span>"+(this.opt("allDayHtml")||R(this.opt("allDayText")))+"</span></td>"},slotBgIntroHtml:function(){return'<td class="fc-axis '+this.widgetContentClass+'" '+this.axisStyleAttr()+"></td>"},introHtml:function(){return'<td class="fc-axis" '+this.axisStyleAttr()+"></td>"},axisStyleAttr:function(){return null!==this.axisWidth?'style="width:'+this.axisWidth+'px"':""},updateSize:function(a){this.timeGrid.updateSize(a),ib.prototype.updateSize.call(this,a)},updateWidth:function(){this.axisWidth=k(this.el.find(".fc-axis"))},setHeight:function(a,b){var c,d;null===this.bottomRuleHeight&&(this.bottomRuleHeight=this.bottomRuleEl.outerHeight()),this.bottomRuleEl.hide(),this.scrollerEl.css("overflow",""),m(this.scrollerEl),f(this.noScrollRowEls),this.dayGrid&&(this.dayGrid.removeSegPopover(),c=this.opt("eventLimit"),c&&"number"!=typeof c&&(c=tb),c&&this.dayGrid.limitRows(c)),b||(d=this.computeScrollerHeight(a),l(this.scrollerEl,d)?(e(this.noScrollRowEls,r(this.scrollerEl)),d=this.computeScrollerHeight(a),this.scrollerEl.height(d)):(this.scrollerEl.height(d).css("overflow","hidden"),this.bottomRuleEl.show()))},computeInitialScroll:function(){var a=b.duration(this.opt("scrollTime")),c=this.timeGrid.computeTimeTop(a);return c=Math.ceil(c),c&&c++,c},renderEvents:function(a){var b,c,d=[],e=[],f=[];for(c=0;c<a.length;c++)a[c].allDay?d.push(a[c]):e.push(a[c]);b=this.timeGrid.renderEvents(e),this.dayGrid&&(f=this.dayGrid.renderEvents(d)),this.updateHeight()},getEventSegs:function(){return this.timeGrid.getEventSegs().concat(this.dayGrid?this.dayGrid.getEventSegs():[])},unrenderEvents:function(){this.timeGrid.unrenderEvents(),this.dayGrid&&this.dayGrid.unrenderEvents()},renderDrag:function(a,b){return a.start.hasTime()?this.timeGrid.renderDrag(a,b):this.dayGrid?this.dayGrid.renderDrag(a,b):void 0},unrenderDrag:function(){this.timeGrid.unrenderDrag(),this.dayGrid&&this.dayGrid.unrenderDrag()},renderSelection:function(a){a.start.hasTime()||a.end.hasTime()?this.timeGrid.renderSelection(a):this.dayGrid&&this.dayGrid.renderSelection(a)},unrenderSelection:function(){this.timeGrid.unrenderSelection(),this.dayGrid&&this.dayGrid.unrenderSelection()}}),tb=5;return Ka.agenda={"class":sb,defaults:{allDaySlot:!0,allDayText:"all-day",slotDuration:"00:30:00",minTime:"00:00:00",maxTime:"24:00:00",slotEventOverlap:!0}},Ka.agendaDay={type:"agenda",duration:{days:1}},Ka.agendaWeek={type:"agenda",duration:{weeks:1}},Ja});
|
|
1 |
/*!
|
2 |
+
* FullCalendar v2.4.0
|
3 |
* Docs & License: http://fullcalendar.io/
|
4 |
* (c) 2015 Adam Shaw
|
5 |
*/
|
6 |
+
!function(a){"function"==typeof define&&define.amd?define(["jquery","moment"],a):"object"==typeof exports?module.exports=a(require("jquery"),require("moment")):a(jQuery,moment)}(function(a,b){function c(a){return Q(a,Oa)}function d(b){var c,d={views:b.views||{}};return a.each(b,function(b,e){"views"!=b&&(a.isPlainObject(e)&&!/(time|duration|interval)$/i.test(b)&&-1==a.inArray(b,Oa)?(c=null,a.each(e,function(a,e){/^(month|week|day|default|basic(Week|Day)?|agenda(Week|Day)?)$/.test(a)?(d.views[a]||(d.views[a]={}),d.views[a][b]=e):(c||(c={}),c[a]=e)}),c&&(d[b]=c)):d[b]=e)}),d}function e(a,b){b.left&&a.css({"border-left-width":1,"margin-left":b.left-1}),b.right&&a.css({"border-right-width":1,"margin-right":b.right-1})}function f(a){a.css({"margin-left":"","margin-right":"","border-left-width":"","border-right-width":""})}function g(){a("body").addClass("fc-not-allowed")}function h(){a("body").removeClass("fc-not-allowed")}function i(b,c,d){var e=Math.floor(c/b.length),f=Math.floor(c-e*(b.length-1)),g=[],h=[],i=[],k=0;j(b),b.each(function(c,d){var j=c===b.length-1?f:e,l=a(d).outerHeight(!0);j>l?(g.push(d),h.push(l),i.push(a(d).height())):k+=l}),d&&(c-=k,e=Math.floor(c/g.length),f=Math.floor(c-e*(g.length-1))),a(g).each(function(b,c){var d=b===g.length-1?f:e,j=h[b],k=i[b],l=d-(j-k);d>j&&a(c).height(l)})}function j(a){a.height("")}function k(b){var c=0;return b.find("> *").each(function(b,d){var e=a(d).outerWidth();e>c&&(c=e)}),c++,b.width(c),c}function l(a,b){return a.height(b).addClass("fc-scroller"),a[0].scrollHeight-1>a[0].clientHeight?!0:(m(a),!1)}function m(a){a.height("").removeClass("fc-scroller")}function n(b){var c=b.css("position"),d=b.parents().filter(function(){var b=a(this);return/(auto|scroll)/.test(b.css("overflow")+b.css("overflow-y")+b.css("overflow-x"))}).eq(0);return"fixed"!==c&&d.length?d:a(b[0].ownerDocument||document)}function o(a){var b=a.offset();return{left:b.left,right:b.left+a.outerWidth(),top:b.top,bottom:b.top+a.outerHeight()}}function p(a){var b=a.offset(),c=r(a),d=b.left+u(a,"border-left-width")+c.left,e=b.top+u(a,"border-top-width")+c.top;return{left:d,right:d+a[0].clientWidth,top:e,bottom:e+a[0].clientHeight}}function q(a){var b=a.offset(),c=b.left+u(a,"border-left-width")+u(a,"padding-left"),d=b.top+u(a,"border-top-width")+u(a,"padding-top");return{left:c,right:c+a.width(),top:d,bottom:d+a.height()}}function r(a){var b=a.innerWidth()-a[0].clientWidth,c={left:0,right:0,top:0,bottom:a.innerHeight()-a[0].clientHeight};return s()&&"rtl"==a.css("direction")?c.left=b:c.right=b,c}function s(){return null===Pa&&(Pa=t()),Pa}function t(){var b=a("<div><div/></div>").css({position:"absolute",top:-1e3,left:0,border:0,padding:0,overflow:"scroll",direction:"rtl"}).appendTo("body"),c=b.children(),d=c.offset().left>b.offset().left;return b.remove(),d}function u(a,b){return parseFloat(a.css(b))||0}function v(a){return 1==a.which&&!a.ctrlKey}function w(a,b){var c={left:Math.max(a.left,b.left),right:Math.min(a.right,b.right),top:Math.max(a.top,b.top),bottom:Math.min(a.bottom,b.bottom)};return c.left<c.right&&c.top<c.bottom?c:!1}function x(a,b){return{left:Math.min(Math.max(a.left,b.left),b.right),top:Math.min(Math.max(a.top,b.top),b.bottom)}}function y(a){return{left:(a.left+a.right)/2,top:(a.top+a.bottom)/2}}function z(a,b){return{left:a.left-b.left,top:a.top-b.top}}function A(b){var c,d,e=[],f=[];for("string"==typeof b?f=b.split(/\s*,\s*/):"function"==typeof b?f=[b]:a.isArray(b)&&(f=b),c=0;c<f.length;c++)d=f[c],"string"==typeof d?e.push("-"==d.charAt(0)?{field:d.substring(1),order:-1}:{field:d,order:1}):"function"==typeof d&&e.push({func:d});return e}function B(a,b,c){var d,e;for(d=0;d<c.length;d++)if(e=C(a,b,c[d]))return e;return 0}function C(a,b,c){return c.func?c.func(a,b):D(a[c.field],b[c.field])*(c.order||1)}function D(b,c){return b||c?null==c?-1:null==b?1:"string"===a.type(b)||"string"===a.type(c)?String(b).localeCompare(String(c)):b-c:0}function E(a,b){var c,d,e,f,g=a.start,h=a.end,i=b.start,j=b.end;return h>i&&j>g?(g>=i?(c=g.clone(),e=!0):(c=i.clone(),e=!1),j>=h?(d=h.clone(),f=!0):(d=j.clone(),f=!1),{start:c,end:d,isStart:e,isEnd:f}):void 0}function F(a,c){return b.duration({days:a.clone().stripTime().diff(c.clone().stripTime(),"days"),ms:a.time()-c.time()})}function G(a,c){return b.duration({days:a.clone().stripTime().diff(c.clone().stripTime(),"days")})}function H(a,c,d){return b.duration(Math.round(a.diff(c,d,!0)),d)}function I(a,b){var c,d,e;for(c=0;c<Ra.length&&(d=Ra[c],e=J(d,a,b),!(e>=1&&ba(e)));c++);return d}function J(a,c,d){return null!=d?d.diff(c,a,!0):b.isDuration(c)?c.as(a):c.end.diff(c.start,a,!0)}function K(a,b,c){var d;return N(c)?(b-a)/c:(d=c.asMonths(),Math.abs(d)>=1&&ba(d)?b.diff(a,"months",!0)/d:b.diff(a,"days",!0)/c.asDays())}function L(a,b){var c,d;return N(a)||N(b)?a/b:(c=a.asMonths(),d=b.asMonths(),Math.abs(c)>=1&&ba(c)&&Math.abs(d)>=1&&ba(d)?c/d:a.asDays()/b.asDays())}function M(a,c){var d;return N(a)?b.duration(a*c):(d=a.asMonths(),Math.abs(d)>=1&&ba(d)?b.duration({months:d*c}):b.duration({days:a.asDays()*c}))}function N(a){return Boolean(a.hours()||a.minutes()||a.seconds()||a.milliseconds())}function O(a){return"[object Date]"===Object.prototype.toString.call(a)||a instanceof Date}function P(a){return/^\d+\:\d+(?:\:\d+\.?(?:\d{3})?)?$/.test(a)}function Q(a,b){var c,d,e,f,g,h,i={};if(b)for(c=0;c<b.length;c++){for(d=b[c],e=[],f=a.length-1;f>=0;f--)if(g=a[f][d],"object"==typeof g)e.unshift(g);else if(void 0!==g){i[d]=g;break}e.length&&(i[d]=Q(e))}for(c=a.length-1;c>=0;c--){h=a[c];for(d in h)d in i||(i[d]=h[d])}return i}function R(a){var b=function(){};return b.prototype=a,new b}function S(a,b){for(var c in a)U(a,c)&&(b[c]=a[c])}function T(a,b){var c,d,e=["constructor","toString","valueOf"];for(c=0;c<e.length;c++)d=e[c],a[d]!==Object.prototype[d]&&(b[d]=a[d])}function U(a,b){return Va.call(a,b)}function V(b){return/undefined|null|boolean|number|string/.test(a.type(b))}function W(b,c,d){if(a.isFunction(b)&&(b=[b]),b){var e,f;for(e=0;e<b.length;e++)f=b[e].apply(c,d)||f;return f}}function X(){for(var a=0;a<arguments.length;a++)if(void 0!==arguments[a])return arguments[a]}function Y(a){return(a+"").replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/'/g,"'").replace(/"/g,""").replace(/\n/g,"<br />")}function Z(a){return a.replace(/&.*?;/g,"")}function $(b){var c=[];return a.each(b,function(a,b){null!=b&&c.push(a+":"+b)}),c.join(";")}function _(a){return a.charAt(0).toUpperCase()+a.slice(1)}function aa(a,b){return a-b}function ba(a){return a%1===0}function ca(a,b){var c=a[b];return function(){return c.apply(a,arguments)}}function da(a,b){var c,d,e,f,g=function(){var h=+new Date-f;b>h&&h>0?c=setTimeout(g,b-h):(c=null,a.apply(e,d),c||(e=d=null))};return function(){e=this,d=arguments,f=+new Date,c||(c=setTimeout(g,b))}}function ea(c,d,e){var f,g,h,i,j=c[0],k=1==c.length&&"string"==typeof j;return b.isMoment(j)?(i=b.apply(null,c),ga(j,i)):O(j)||void 0===j?i=b.apply(null,c):(f=!1,g=!1,k?Wa.test(j)?(j+="-01",c=[j],f=!0,g=!0):(h=Xa.exec(j))&&(f=!h[5],g=!0):a.isArray(j)&&(g=!0),i=d||f?b.utc.apply(b,c):b.apply(null,c),f?(i._ambigTime=!0,i._ambigZone=!0):e&&(g?i._ambigZone=!0:k&&(i.utcOffset?i.utcOffset(j):i.zone(j)))),i._fullCalendar=!0,i}function fa(a,c){var d,e,f=!1,g=!1,h=a.length,i=[];for(d=0;h>d;d++)e=a[d],b.isMoment(e)||(e=Ma.moment.parseZone(e)),f=f||e._ambigTime,g=g||e._ambigZone,i.push(e);for(d=0;h>d;d++)e=i[d],c||!f||e._ambigTime?g&&!e._ambigZone&&(i[d]=e.clone().stripZone()):i[d]=e.clone().stripTime();return i}function ga(a,b){a._ambigTime?b._ambigTime=!0:b._ambigTime&&(b._ambigTime=!1),a._ambigZone?b._ambigZone=!0:b._ambigZone&&(b._ambigZone=!1)}function ha(a,b){a.year(b[0]||0).month(b[1]||0).date(b[2]||0).hours(b[3]||0).minutes(b[4]||0).seconds(b[5]||0).milliseconds(b[6]||0)}function ia(a,b){return Za.format.call(a,b)}function ja(a,b){return ka(a,pa(b))}function ka(a,b){var c,d="";for(c=0;c<b.length;c++)d+=la(a,b[c]);return d}function la(a,b){var c,d;return"string"==typeof b?b:(c=b.token)?$a[c]?$a[c](a):ia(a,c):b.maybe&&(d=ka(a,b.maybe),d.match(/[1-9]/))?d:""}function ma(a,b,c,d,e){var f;return a=Ma.moment.parseZone(a),b=Ma.moment.parseZone(b),f=(a.localeData||a.lang).call(a),c=f.longDateFormat(c)||c,d=d||" - ",na(a,b,pa(c),d,e)}function na(a,b,c,d,e){var f,g,h,i,j="",k="",l="",m="",n="";for(g=0;g<c.length&&(f=oa(a,b,c[g]),f!==!1);g++)j+=f;for(h=c.length-1;h>g&&(f=oa(a,b,c[h]),f!==!1);h--)k=f+k;for(i=g;h>=i;i++)l+=la(a,c[i]),m+=la(b,c[i]);return(l||m)&&(n=e?m+d+l:l+d+m),j+n+k}function oa(a,b,c){var d,e;return"string"==typeof c?c:(d=c.token)&&(e=_a[d.charAt(0)],e&&a.isSame(b,e))?ia(a,d):!1}function pa(a){return a in ab?ab[a]:ab[a]=qa(a)}function qa(a){for(var b,c=[],d=/\[([^\]]*)\]|\(([^\)]*)\)|(LTS|LT|(\w)\4*o?)|([^\w\[\(]+)/g;b=d.exec(a);)b[1]?c.push(b[1]):b[2]?c.push({maybe:qa(b[2])}):b[3]?c.push({token:b[3]}):b[5]&&c.push(b[5]);return c}function ra(){}function sa(a,b){return a||b?a&&b?a.grid===b.grid&&a.row===b.row&&a.col===b.col:!1:!0}function ta(a){var b=va(a);return"background"===b||"inverse-background"===b}function ua(a){return"inverse-background"===va(a)}function va(a){return X((a.source||{}).rendering,a.rendering)}function wa(a){var b,c,d={};for(b=0;b<a.length;b++)c=a[b],(d[c._id]||(d[c._id]=[])).push(c);return d}function xa(a,b){return a.eventStartMS-b.eventStartMS}function ya(c){var d,e,f,g,h=Ma.dataAttrPrefix;return h&&(h+="-"),d=c.data(h+"event")||null,d&&(d="object"==typeof d?a.extend({},d):{},e=d.start,null==e&&(e=d.time),f=d.duration,g=d.stick,delete d.start,delete d.time,delete d.duration,delete d.stick),null==e&&(e=c.data(h+"start")),null==e&&(e=c.data(h+"time")),null==f&&(f=c.data(h+"duration")),null==g&&(g=c.data(h+"stick")),e=null!=e?b.duration(e):null,f=null!=f?b.duration(f):null,g=Boolean(g),{eventProps:d,startTime:e,duration:f,stick:g}}function za(a,b){var c,d;for(c=0;c<b.length;c++)if(d=b[c],d.leftCol<=a.rightCol&&d.rightCol>=a.leftCol)return!0;return!1}function Aa(a,b){return a.leftCol-b.leftCol}function Ba(a){var b,c,d,e=[];for(b=0;b<a.length;b++){for(c=a[b],d=0;d<e.length&&Ea(c,e[d]).length;d++);c.level=d,(e[d]||(e[d]=[])).push(c)}return e}function Ca(a){var b,c,d,e,f;for(b=0;b<a.length;b++)for(c=a[b],d=0;d<c.length;d++)for(e=c[d],e.forwardSegs=[],f=b+1;f<a.length;f++)Ea(e,a[f],e.forwardSegs)}function Da(a){var b,c,d=a.forwardSegs,e=0;if(void 0===a.forwardPressure){for(b=0;b<d.length;b++)c=d[b],Da(c),e=Math.max(e,1+c.forwardPressure);a.forwardPressure=e}}function Ea(a,b,c){c=c||[];for(var d=0;d<b.length;d++)Fa(a,b[d])&&c.push(b[d]);return c}function Fa(a,b){return a.bottom>b.top&&a.top<b.bottom}function Ga(c,d){function e(){U?h()&&(k(),i()):f()}function f(){V=O.theme?"ui":"fc",c.addClass("fc"),O.isRTL?c.addClass("fc-rtl"):c.addClass("fc-ltr"),O.theme?c.addClass("ui-widget"):c.addClass("fc-unthemed"),U=a("<div class='fc-view-container'/>").prependTo(c),S=N.header=new Ja(N,O),T=S.render(),T&&c.prepend(T),i(O.defaultView),O.handleWindowResize&&(Y=da(m,O.windowResizeDelay),a(window).resize(Y))}function g(){W&&W.removeElement(),S.removeElement(),U.remove(),c.removeClass("fc fc-ltr fc-rtl fc-unthemed ui-widget"),Y&&a(window).unbind("resize",Y)}function h(){return c.is(":visible")}function i(b){ca++,W&&b&&W.type!==b&&(S.deactivateButton(W.type),H(),W.removeElement(),W=N.view=null),!W&&b&&(W=N.view=ba[b]||(ba[b]=N.instantiateView(b)),W.setElement(a("<div class='fc-view fc-"+b+"-view' />").appendTo(U)),S.activateButton(b)),W&&(Z=W.massageCurrentDate(Z),W.displaying&&Z.isWithin(W.intervalStart,W.intervalEnd)||h()&&(H(),W.display(Z),I(),u(),v(),q())),I(),ca--}function j(a){return h()?(a&&l(),ca++,W.updateSize(!0),ca--,!0):void 0}function k(){h()&&l()}function l(){X="number"==typeof O.contentHeight?O.contentHeight:"number"==typeof O.height?O.height-(T?T.outerHeight(!0):0):Math.round(U.width()/Math.max(O.aspectRatio,.5))}function m(a){!ca&&a.target===window&&W.start&&j(!0)&&W.trigger("windowResize",aa)}function n(){p(),r()}function o(){h()&&(H(),W.displayEvents(ea),I())}function p(){H(),W.clearEvents(),I()}function q(){!O.lazyFetching||$(W.start,W.end)?r():o()}function r(){_(W.start,W.end)}function s(a){ea=a,o()}function t(){o()}function u(){S.updateTitle(W.title)}function v(){var a=N.getNow();a.isWithin(W.intervalStart,W.intervalEnd)?S.disableButton("today"):S.enableButton("today")}function w(a,b){W.select(N.buildSelectRange.apply(N,arguments))}function x(){W&&W.unselect()}function y(){Z=W.computePrevDate(Z),i()}function z(){Z=W.computeNextDate(Z),i()}function A(){Z.add(-1,"years"),i()}function B(){Z.add(1,"years"),i()}function C(){Z=N.getNow(),i()}function D(a){Z=N.moment(a),i()}function E(a){Z.add(b.duration(a)),i()}function F(a,b){var c;b=b||"day",c=N.getViewSpec(b)||N.getUnitViewSpec(b),Z=a,i(c?c.type:null)}function G(){return Z.clone()}function H(){U.css({width:"100%",height:U.height(),overflow:"hidden"})}function I(){U.css({width:"",height:"",overflow:""})}function J(){return N}function K(){return W}function L(a,b){return void 0===b?O[a]:void(("height"==a||"contentHeight"==a||"aspectRatio"==a)&&(O[a]=b,j(!0)))}function M(a,b){var c=Array.prototype.slice.call(arguments,2);return b=b||aa,this.triggerWith(a,b,c),O[a]?O[a].apply(b,c):void 0}var N=this;N.initOptions(d||{});var O=this.options;N.render=e,N.destroy=g,N.refetchEvents=n,N.reportEvents=s,N.reportEventChange=t,N.rerenderEvents=o,N.changeView=i,N.select=w,N.unselect=x,N.prev=y,N.next=z,N.prevYear=A,N.nextYear=B,N.today=C,N.gotoDate=D,N.incrementDate=E,N.zoomTo=F,N.getDate=G,N.getCalendar=J,N.getView=K,N.option=L,N.trigger=M;var P=R(Ia(O.lang));if(O.monthNames&&(P._months=O.monthNames),O.monthNamesShort&&(P._monthsShort=O.monthNamesShort),O.dayNames&&(P._weekdays=O.dayNames),O.dayNamesShort&&(P._weekdaysShort=O.dayNamesShort),null!=O.firstDay){var Q=R(P._week);Q.dow=O.firstDay,P._week=Q}P._fullCalendar_weekCalc=function(a){return"function"==typeof a?a:"local"===a?a:"iso"===a||"ISO"===a?"ISO":void 0}(O.weekNumberCalculation),N.defaultAllDayEventDuration=b.duration(O.defaultAllDayEventDuration),N.defaultTimedEventDuration=b.duration(O.defaultTimedEventDuration),N.moment=function(){var a;return"local"===O.timezone?(a=Ma.moment.apply(null,arguments),a.hasTime()&&a.local()):a="UTC"===O.timezone?Ma.moment.utc.apply(null,arguments):Ma.moment.parseZone.apply(null,arguments),"_locale"in a?a._locale=P:a._lang=P,a},N.getIsAmbigTimezone=function(){return"local"!==O.timezone&&"UTC"!==O.timezone},N.rezoneDate=function(a){return N.moment(a.toArray())},N.getNow=function(){var a=O.now;return"function"==typeof a&&(a=a()),N.moment(a)},N.getEventEnd=function(a){return a.end?a.end.clone():N.getDefaultEventEnd(a.allDay,a.start)},N.getDefaultEventEnd=function(a,b){var c=b.clone();return a?c.stripTime().add(N.defaultAllDayEventDuration):c.add(N.defaultTimedEventDuration),N.getIsAmbigTimezone()&&c.stripZone(),c},N.humanizeDuration=function(a){return(a.locale||a.lang).call(a,O.lang).humanize()},Ka.call(N,O);var S,T,U,V,W,X,Y,Z,$=N.isFetchNeeded,_=N.fetchEvents,aa=c[0],ba={},ca=0,ea=[];Z=null!=O.defaultDate?N.moment(O.defaultDate):N.getNow(),N.getSuggestedViewHeight=function(){return void 0===X&&k(),X},N.isHeightAuto=function(){return"auto"===O.contentHeight||"auto"===O.height},N.initialize()}function Ha(b){a.each(rb,function(a,c){null==b[a]&&(b[a]=c(b))})}function Ia(a){var c=b.localeData||b.langData;return c.call(b,a)||c.call(b,"en")}function Ja(b,c){function d(){var b=c.header;return n=c.theme?"ui":"fc",b?o=a("<div class='fc-toolbar'/>").append(f("left")).append(f("right")).append(f("center")).append('<div class="fc-clear"/>'):void 0}function e(){o.remove(),o=a()}function f(d){var e=a('<div class="fc-'+d+'"/>'),f=c.header[d];return f&&a.each(f.split(" "),function(d){var f,g=a(),h=!0;a.each(this.split(","),function(d,e){var f,i,j,k,l,m,o,q,r,s;"title"==e?(g=g.add(a("<h2> </h2>")),h=!1):((f=(b.options.customButtons||{})[e])?(j=function(a){f.click&&f.click.call(s[0],a)},k="",l=f.text):(i=b.getViewSpec(e))?(j=function(){b.changeView(e)},p.push(e),k=i.buttonTextOverride,l=i.buttonTextDefault):b[e]&&(j=function(){b[e]()},k=(b.overrides.buttonText||{})[e],l=c.buttonText[e]),j&&(m=f?f.themeIcon:c.themeButtonIcons[e],o=f?f.icon:c.buttonIcons[e],q=k?Y(k):m&&c.theme?"<span class='ui-icon ui-icon-"+m+"'></span>":o&&!c.theme?"<span class='fc-icon fc-icon-"+o+"'></span>":Y(l),r=["fc-"+e+"-button",n+"-button",n+"-state-default"],s=a('<button type="button" class="'+r.join(" ")+'">'+q+"</button>").click(function(a){s.hasClass(n+"-state-disabled")||(j(a),(s.hasClass(n+"-state-active")||s.hasClass(n+"-state-disabled"))&&s.removeClass(n+"-state-hover"))}).mousedown(function(){s.not("."+n+"-state-active").not("."+n+"-state-disabled").addClass(n+"-state-down")}).mouseup(function(){s.removeClass(n+"-state-down")}).hover(function(){s.not("."+n+"-state-active").not("."+n+"-state-disabled").addClass(n+"-state-hover")},function(){s.removeClass(n+"-state-hover").removeClass(n+"-state-down")}),g=g.add(s)))}),h&&g.first().addClass(n+"-corner-left").end().last().addClass(n+"-corner-right").end(),g.length>1?(f=a("<div/>"),h&&f.addClass("fc-button-group"),f.append(g),e.append(f)):e.append(g)}),e}function g(a){o.find("h2").text(a)}function h(a){o.find(".fc-"+a+"-button").addClass(n+"-state-active")}function i(a){o.find(".fc-"+a+"-button").removeClass(n+"-state-active")}function j(a){o.find(".fc-"+a+"-button").attr("disabled","disabled").addClass(n+"-state-disabled")}function k(a){o.find(".fc-"+a+"-button").removeAttr("disabled").removeClass(n+"-state-disabled")}function l(){return p}var m=this;m.render=d,m.removeElement=e,m.updateTitle=g,m.activateButton=h,m.deactivateButton=i,m.disableButton=j,m.enableButton=k,m.getViewsWithButtons=l;var n,o=a(),p=[]}function Ka(c){function d(a,b){return!M||a.clone().stripZone()<M.clone().stripZone()||b.clone().stripZone()>N.clone().stripZone()}function e(a,b){M=a,N=b,U=[];var c=++S,d=R.length;T=d;for(var e=0;d>e;e++)f(R[e],c)}function f(b,c){g(b,function(d){var e,f,g,h=a.isArray(b.events);if(c==S){if(d)for(e=0;e<d.length;e++)f=d[e],g=h?f:s(f,b),g&&U.push.apply(U,x(g));T--,T||O(U)}})}function g(b,d){var e,f,h=Ma.sourceFetchers;for(e=0;e<h.length;e++){if(f=h[e].call(L,b,M.clone(),N.clone(),c.timezone,d),f===!0)return;if("object"==typeof f)return void g(f,d)}var i=b.events;if(i)a.isFunction(i)?(L.pushLoading(),i.call(L,M.clone(),N.clone(),c.timezone,function(a){d(a),L.popLoading()})):a.isArray(i)?d(i):d();else{var j=b.url;if(j){var k,l=b.success,m=b.error,n=b.complete;k=a.isFunction(b.data)?b.data():b.data;var o=a.extend({},k||{}),p=X(b.startParam,c.startParam),q=X(b.endParam,c.endParam),r=X(b.timezoneParam,c.timezoneParam);p&&(o[p]=M.format()),q&&(o[q]=N.format()),c.timezone&&"local"!=c.timezone&&(o[r]=c.timezone),L.pushLoading(),a.ajax(a.extend({},sb,b,{data:o,success:function(b){b=b||[];var c=W(l,this,arguments);a.isArray(c)&&(b=c),d(b)},error:function(){W(m,this,arguments),d()},complete:function(){W(n,this,arguments),L.popLoading()}}))}else d()}}function h(a){var b=i(a);b&&(R.push(b),T++,f(b,S))}function i(b){var c,d,e=Ma.sourceNormalizers;if(a.isFunction(b)||a.isArray(b)?c={events:b}:"string"==typeof b?c={url:b}:"object"==typeof b&&(c=a.extend({},b)),c){for(c.className?"string"==typeof c.className&&(c.className=c.className.split(/\s+/)):c.className=[],a.isArray(c.events)&&(c.origArray=c.events,c.events=a.map(c.events,function(a){return s(a,c)})),d=0;d<e.length;d++)e[d].call(L,c);return c}}function j(b){R=a.grep(R,function(a){return!k(a,b)}),U=a.grep(U,function(a){return!k(a.source,b)}),O(U)}function k(a,b){return a&&b&&l(a)==l(b)}function l(a){return("object"==typeof a?a.origArray||a.googleCalendarId||a.url||a.events:null)||a}function m(a){a.start=L.moment(a.start),a.end?a.end=L.moment(a.end):a.end=null,y(a,n(a)),O(U)}function n(b){var c={};return a.each(b,function(a,b){o(a)&&void 0!==b&&V(b)&&(c[a]=b)}),c}function o(a){return!/^_|^(id|allDay|start|end)$/.test(a)}function p(a,b){var c,d,e,f=s(a);if(f){for(c=x(f),d=0;d<c.length;d++)e=c[d],e.source||(b&&(Q.events.push(e),e.source=Q),U.push(e));return O(U),c}return[]}function q(b){var c,d;for(null==b?b=function(){return!0}:a.isFunction(b)||(c=b+"",b=function(a){return a._id==c}),U=a.grep(U,b,!0),d=0;d<R.length;d++)a.isArray(R[d].events)&&(R[d].events=a.grep(R[d].events,b,!0));O(U)}function r(b){return a.isFunction(b)?a.grep(U,b):null!=b?(b+="",a.grep(U,function(a){return a._id==b})):U}function s(d,e){var f,g,h,i={};if(c.eventDataTransform&&(d=c.eventDataTransform(d)),e&&e.eventDataTransform&&(d=e.eventDataTransform(d)),a.extend(i,d),e&&(i.source=e),i._id=d._id||(void 0===d.id?"_fc"+tb++:d.id+""),d.className?"string"==typeof d.className?i.className=d.className.split(/\s+/):i.className=d.className:i.className=[],f=d.start||d.date,g=d.end,P(f)&&(f=b.duration(f)),P(g)&&(g=b.duration(g)),d.dow||b.isDuration(f)||b.isDuration(g))i.start=f?b.duration(f):null,i.end=g?b.duration(g):null,i._recurring=!0;else{if(f&&(f=L.moment(f),!f.isValid()))return!1;g&&(g=L.moment(g),g.isValid()||(g=null)),h=d.allDay,void 0===h&&(h=X(e?e.allDayDefault:void 0,c.allDayDefault)),t(f,g,h,i)}return i}function t(a,b,c,d){d.start=a,d.end=b,d.allDay=c,u(d),La(d)}function u(a){v(a),a.end&&!a.end.isAfter(a.start)&&(a.end=null),a.end||(c.forceEventDuration?a.end=L.getDefaultEventEnd(a.allDay,a.start):a.end=null)}function v(a){null==a.allDay&&(a.allDay=!(a.start.hasTime()||a.end&&a.end.hasTime())),a.allDay?(a.start.stripTime(),a.end&&a.end.stripTime()):(a.start.hasTime()||(a.start=L.rezoneDate(a.start)),a.end&&!a.end.hasTime()&&(a.end=L.rezoneDate(a.end)))}function w(b){var c;return b.end||(c=b.allDay,null==c&&(c=!b.start.hasTime()),b=a.extend({},b),b.end=L.getDefaultEventEnd(c,b.start)),b}function x(b,c,d){var e,f,g,h,i,j,k,l,m,n=[];if(c=c||M,d=d||N,b)if(b._recurring){if(f=b.dow)for(e={},g=0;g<f.length;g++)e[f[g]]=!0;for(h=c.clone().stripTime();h.isBefore(d);)(!e||e[h.day()])&&(i=b.start,j=b.end,k=h.clone(),l=null,i&&(k=k.time(i)),j&&(l=h.clone().time(j)),m=a.extend({},b),t(k,l,!i&&!j,m),n.push(m)),h.add(1,"days")}else n.push(b);return n}function y(b,c,d){function e(a,b){return d?H(a,b,d):c.allDay?G(a,b):F(a,b)}var f,g,h,i,j,k,l={};return c=c||{},c.start||(c.start=b.start.clone()),void 0===c.end&&(c.end=b.end?b.end.clone():null),null==c.allDay&&(c.allDay=b.allDay),u(c),f={start:b._start.clone(),end:b._end?b._end.clone():L.getDefaultEventEnd(b._allDay,b._start),allDay:c.allDay},u(f),g=null!==b._end&&null===c.end,h=e(c.start,f.start),c.end?(i=e(c.end,f.end),j=i.subtract(h)):j=null,a.each(c,function(a,b){o(a)&&void 0!==b&&(l[a]=b)}),k=z(r(b._id),g,c.allDay,h,j,l),{dateDelta:h,durationDelta:j,undo:k}}function z(b,c,d,e,f,g){var h=L.getIsAmbigTimezone(),i=[];return e&&!e.valueOf()&&(e=null),f&&!f.valueOf()&&(f=null),a.each(b,function(b,j){var k,l;k={start:j.start.clone(),end:j.end?j.end.clone():null,allDay:j.allDay},a.each(g,function(a){k[a]=j[a]}),l={start:j._start,end:j._end,allDay:d},u(l),c?l.end=null:f&&!l.end&&(l.end=L.getDefaultEventEnd(l.allDay,l.start)),e&&(l.start.add(e),l.end&&l.end.add(e)),f&&l.end.add(f),h&&!l.allDay&&(e||f)&&(l.start.stripZone(),l.end&&l.end.stripZone()),a.extend(j,g,l),La(j),i.push(function(){a.extend(j,k),La(j)})}),function(){for(var a=0;a<i.length;a++)i[a]()}}function A(b){var d,e=c.businessHours,f={className:"fc-nonbusiness",start:"09:00",end:"17:00",dow:[1,2,3,4,5],rendering:"inverse-background"},g=L.getView();return e&&(d=a.extend({},f,"object"==typeof e?e:{})),d?(b&&(d.start=null,d.end=null),x(s(d),g.start,g.end)):[]}function B(a,b){var d=b.source||{},e=X(b.constraint,d.constraint,c.eventConstraint),f=X(b.overlap,d.overlap,c.eventOverlap);return a=w(a),E(a,e,f,b)}function C(a){return E(a,c.selectConstraint,c.selectOverlap)}function D(b,c){var d,e;return c&&(d=a.extend({},c,b),e=x(s(d))[0]),e?B(b,e):(b=w(b),C(b))}function E(b,c,d,e){var f,g,h,i,j,k;if(b=a.extend({},b),b.start=b.start.clone().stripZone(),b.end=b.end.clone().stripZone(),null!=c){for(f=I(c),g=!1,i=0;i<f.length;i++)if(J(f[i],b)){g=!0;break}if(!g)return!1}for(h=L.getPeerEvents(e,b),i=0;i<h.length;i++)if(j=h[i],K(j,b)){if(d===!1)return!1;if("function"==typeof d&&!d(j,e))return!1;if(e){if(k=X(j.overlap,(j.source||{}).overlap),k===!1)return!1;if("function"==typeof k&&!k(e,j))return!1}}return!0}function I(a){return"businessHours"===a?A():"object"==typeof a?x(s(a)):r(a)}function J(a,b){var c=a.start.clone().stripZone(),d=L.getEventEnd(a).stripZone();return b.start>=c&&b.end<=d}function K(a,b){var c=a.start.clone().stripZone(),d=L.getEventEnd(a).stripZone();return b.start<d&&b.end>c}var L=this;L.isFetchNeeded=d,L.fetchEvents=e,L.addEventSource=h,L.removeEventSource=j,L.updateEvent=m,L.renderEvent=p,L.removeEvents=q,L.clientEvents=r,L.mutateEvent=y,L.normalizeEventRange=u,L.normalizeEventRangeTimes=v,L.ensureVisibleEventRange=w;var M,N,O=L.reportEvents,Q={events:[]},R=[Q],S=0,T=0,U=[];a.each((c.events?[c.events]:[]).concat(c.eventSources||[]),function(a,b){var c=i(b);c&&R.push(c)}),L.getBusinessHoursEvents=A,L.isEventRangeAllowed=B,L.isSelectionRangeAllowed=C,L.isExternalDropRangeAllowed=D,L.getEventCache=function(){return U}}function La(a){a._allDay=a.allDay,a._start=a.start.clone(),a._end=a.end?a.end.clone():null}var Ma=a.fullCalendar={version:"2.4.0"},Na=Ma.views={};a.fn.fullCalendar=function(b){var c=Array.prototype.slice.call(arguments,1),d=this;return this.each(function(e,f){var g,h=a(f),i=h.data("fullCalendar");"string"==typeof b?i&&a.isFunction(i[b])&&(g=i[b].apply(i,c),e||(d=g),"destroy"===b&&h.removeData("fullCalendar")):i||(i=new nb(h,b),h.data("fullCalendar",i),i.render())}),d};var Oa=["header","buttonText","buttonIcons","themeButtonIcons"];Ma.intersectionToSeg=E,Ma.applyAll=W,Ma.debounce=da,Ma.isInt=ba,Ma.htmlEscape=Y,Ma.cssToStr=$,Ma.proxy=ca,Ma.capitaliseFirstLetter=_,Ma.getClientRect=p,Ma.getContentRect=q,Ma.getScrollbarWidths=r;var Pa=null;Ma.intersectRects=w,Ma.parseFieldSpecs=A,Ma.compareByFieldSpecs=B,Ma.compareByFieldSpec=C,Ma.flexibleCompare=D,Ma.computeIntervalUnit=I,Ma.divideRangeByDuration=K,Ma.divideDurationByDuration=L,Ma.multiplyDuration=M,Ma.durationHasTime=N;var Qa=["sun","mon","tue","wed","thu","fri","sat"],Ra=["year","month","week","day","hour","minute","second","millisecond"];Ma.log=function(){var a=window.console;return a&&a.log?a.log.apply(a,arguments):void 0},Ma.warn=function(){var a=window.console;return a&&a.warn?a.warn.apply(a,arguments):Ma.log.apply(Ma,arguments)};var Sa,Ta,Ua,Va={}.hasOwnProperty,Wa=/^\s*\d{4}-\d\d$/,Xa=/^\s*\d{4}-(?:(\d\d-\d\d)|(W\d\d$)|(W\d\d-\d)|(\d\d\d))((T| )(\d\d(:\d\d(:\d\d(\.\d+)?)?)?)?)?$/,Ya=b.fn,Za=a.extend({},Ya);Ma.moment=function(){return ea(arguments)},Ma.moment.utc=function(){var a=ea(arguments,!0);return a.hasTime()&&a.utc(),a},Ma.moment.parseZone=function(){return ea(arguments,!0,!0)},Ya.clone=function(){var a=Za.clone.apply(this,arguments);return ga(this,a),this._fullCalendar&&(a._fullCalendar=!0),a},Ya.week=Ya.weeks=function(a){var b=(this._locale||this._lang)._fullCalendar_weekCalc;return null==a&&"function"==typeof b?b(this):"ISO"===b?Za.isoWeek.apply(this,arguments):Za.week.apply(this,arguments)},Ya.time=function(a){if(!this._fullCalendar)return Za.time.apply(this,arguments);if(null==a)return b.duration({hours:this.hours(),minutes:this.minutes(),seconds:this.seconds(),milliseconds:this.milliseconds()});this._ambigTime=!1,b.isDuration(a)||b.isMoment(a)||(a=b.duration(a));var c=0;return b.isDuration(a)&&(c=24*Math.floor(a.asDays())),this.hours(c+a.hours()).minutes(a.minutes()).seconds(a.seconds()).milliseconds(a.milliseconds())},Ya.stripTime=function(){var a;return this._ambigTime||(a=this.toArray(),this.utc(),Ta(this,a.slice(0,3)),this._ambigTime=!0,this._ambigZone=!0),this},Ya.hasTime=function(){return!this._ambigTime},Ya.stripZone=function(){var a,b;return this._ambigZone||(a=this.toArray(),b=this._ambigTime,this.utc(),Ta(this,a),this._ambigTime=b||!1,this._ambigZone=!0),this},Ya.hasZone=function(){return!this._ambigZone},Ya.local=function(){var a=this.toArray(),b=this._ambigZone;return Za.local.apply(this,arguments),this._ambigTime=!1,this._ambigZone=!1,b&&Ua(this,a),this},Ya.utc=function(){return Za.utc.apply(this,arguments),this._ambigTime=!1,this._ambigZone=!1,this},a.each(["zone","utcOffset"],function(a,b){Za[b]&&(Ya[b]=function(a){return null!=a&&(this._ambigTime=!1,this._ambigZone=!1),Za[b].apply(this,arguments)})}),Ya.format=function(){return this._fullCalendar&&arguments[0]?ja(this,arguments[0]):this._ambigTime?ia(this,"YYYY-MM-DD"):this._ambigZone?ia(this,"YYYY-MM-DD[T]HH:mm:ss"):Za.format.apply(this,arguments)},Ya.toISOString=function(){return this._ambigTime?ia(this,"YYYY-MM-DD"):this._ambigZone?ia(this,"YYYY-MM-DD[T]HH:mm:ss"):Za.toISOString.apply(this,arguments)},Ya.isWithin=function(a,b){var c=fa([this,a,b]);return c[0]>=c[1]&&c[0]<c[2]},Ya.isSame=function(a,b){var c;return this._fullCalendar?b?(c=fa([this,a],!0),Za.isSame.call(c[0],c[1],b)):(a=Ma.moment.parseZone(a),Za.isSame.call(this,a)&&Boolean(this._ambigTime)===Boolean(a._ambigTime)&&Boolean(this._ambigZone)===Boolean(a._ambigZone)):Za.isSame.apply(this,arguments)},a.each(["isBefore","isAfter"],function(a,b){Ya[b]=function(a,c){var d;return this._fullCalendar?(d=fa([this,a]),Za[b].call(d[0],d[1],c)):Za[b].apply(this,arguments)}}),Sa="_d"in b()&&"updateOffset"in b,Ta=Sa?function(a,c){a._d.setTime(Date.UTC.apply(Date,c)),b.updateOffset(a,!1)}:ha,Ua=Sa?function(a,c){a._d.setTime(+new Date(c[0]||0,c[1]||0,c[2]||0,c[3]||0,c[4]||0,c[5]||0,c[6]||0)),b.updateOffset(a,!1)}:ha;var $a={t:function(a){return ia(a,"a").charAt(0)},T:function(a){return ia(a,"A").charAt(0)}};Ma.formatRange=ma;var _a={Y:"year",M:"month",D:"day",d:"day",A:"second",a:"second",T:"second",t:"second",H:"second",h:"second",m:"second",s:"second"},ab={};Ma.Class=ra,ra.extend=function(a){var b,c=this;return a=a||{},U(a,"constructor")&&(b=a.constructor),"function"!=typeof b&&(b=a.constructor=function(){c.apply(this,arguments)}),b.prototype=R(c.prototype),S(a,b.prototype),T(a,b.prototype),S(c,b),b},ra.mixin=function(a){S(a.prototype||a,this.prototype)};var bb=Ma.Emitter=ra.extend({callbackHash:null,on:function(a,b){return this.getCallbacks(a).add(b),this},off:function(a,b){return this.getCallbacks(a).remove(b),this},trigger:function(a){var b=Array.prototype.slice.call(arguments,1);return this.triggerWith(a,this,b),this},triggerWith:function(a,b,c){var d=this.getCallbacks(a);return d.fireWith(b,c),this},getCallbacks:function(b){var c;return this.callbackHash||(this.callbackHash={}),c=this.callbackHash[b],c||(c=this.callbackHash[b]=a.Callbacks()),c}}),cb=ra.extend({isHidden:!0,options:null,el:null,documentMousedownProxy:null,margin:10,constructor:function(a){this.options=a||{}},show:function(){this.isHidden&&(this.el||this.render(),this.el.show(),this.position(),this.isHidden=!1,this.trigger("show"))},hide:function(){this.isHidden||(this.el.hide(),this.isHidden=!0,this.trigger("hide"))},render:function(){var b=this,c=this.options;this.el=a('<div class="fc-popover"/>').addClass(c.className||"").css({top:0,left:0}).append(c.content).appendTo(c.parentEl),this.el.on("click",".fc-close",function(){b.hide()}),c.autoHide&&a(document).on("mousedown",this.documentMousedownProxy=ca(this,"documentMousedown"))},documentMousedown:function(b){this.el&&!a(b.target).closest(this.el).length&&this.hide()},removeElement:function(){this.hide(),this.el&&(this.el.remove(),this.el=null),a(document).off("mousedown",this.documentMousedownProxy)},position:function(){var b,c,d,e,f,g=this.options,h=this.el.offsetParent().offset(),i=this.el.outerWidth(),j=this.el.outerHeight(),k=a(window),l=n(this.el);e=g.top||0,f=void 0!==g.left?g.left:void 0!==g.right?g.right-i:0,l.is(window)||l.is(document)?(l=k,b=0,c=0):(d=l.offset(),b=d.top,c=d.left),b+=k.scrollTop(),c+=k.scrollLeft(),g.viewportConstrain!==!1&&(e=Math.min(e,b+l.outerHeight()-j-this.margin),e=Math.max(e,b+this.margin),f=Math.min(f,c+l.outerWidth()-i-this.margin),f=Math.max(f,c+this.margin)),this.el.css({top:e-h.top,left:f-h.left})},trigger:function(a){this.options[a]&&this.options[a].apply(this,Array.prototype.slice.call(arguments,1));
|
7 |
+
}}),db=ra.extend({grid:null,rowCoords:null,colCoords:null,containerEl:null,bounds:null,constructor:function(a){this.grid=a},build:function(){this.grid.build(),this.rowCoords=this.grid.computeRowCoords(),this.colCoords=this.grid.computeColCoords(),this.computeBounds()},clear:function(){this.grid.clear(),this.rowCoords=null,this.colCoords=null},getCell:function(b,c){var d,e,f,g=this.rowCoords,h=g.length,i=this.colCoords,j=i.length,k=null,l=null;if(this.inBounds(b,c)){for(d=0;h>d;d++)if(e=g[d],c>=e.top&&c<e.bottom){k=d;break}for(d=0;j>d;d++)if(e=i[d],b>=e.left&&b<e.right){l=d;break}if(null!==k&&null!==l)return f=this.grid.getCell(k,l),f.grid=this.grid,a.extend(f,g[k],i[l]),f}return null},computeBounds:function(){this.bounds=this.containerEl?p(this.containerEl):null},inBounds:function(a,b){var c=this.bounds;return c?a>=c.left&&a<c.right&&b>=c.top&&b<c.bottom:!0}}),eb=ra.extend({coordMaps:null,constructor:function(a){this.coordMaps=a},build:function(){var a,b=this.coordMaps;for(a=0;a<b.length;a++)b[a].build()},getCell:function(a,b){var c,d=this.coordMaps,e=null;for(c=0;c<d.length&&!e;c++)e=d[c].getCell(a,b);return e},clear:function(){var a,b=this.coordMaps;for(a=0;a<b.length;a++)b[a].clear()}}),fb=Ma.DragListener=ra.extend({options:null,isListening:!1,isDragging:!1,originX:null,originY:null,mousemoveProxy:null,mouseupProxy:null,subjectEl:null,subjectHref:null,scrollEl:null,scrollBounds:null,scrollTopVel:null,scrollLeftVel:null,scrollIntervalId:null,scrollHandlerProxy:null,scrollSensitivity:30,scrollSpeed:200,scrollIntervalMs:50,constructor:function(a){a=a||{},this.options=a,this.subjectEl=a.subjectEl},mousedown:function(a){v(a)&&(a.preventDefault(),this.startListening(a),this.options.distance||this.startDrag(a))},startListening:function(b){var c;this.isListening||(b&&this.options.scroll&&(c=n(a(b.target)),c.is(window)||c.is(document)||(this.scrollEl=c,this.scrollHandlerProxy=da(ca(this,"scrollHandler"),100),this.scrollEl.on("scroll",this.scrollHandlerProxy))),a(document).on("mousemove",this.mousemoveProxy=ca(this,"mousemove")).on("mouseup",this.mouseupProxy=ca(this,"mouseup")).on("selectstart",this.preventDefault),b?(this.originX=b.pageX,this.originY=b.pageY):(this.originX=0,this.originY=0),this.isListening=!0,this.listenStart(b))},listenStart:function(a){this.trigger("listenStart",a)},mousemove:function(a){var b,c,d=a.pageX-this.originX,e=a.pageY-this.originY;this.isDragging||(b=this.options.distance||1,c=d*d+e*e,c>=b*b&&this.startDrag(a)),this.isDragging&&this.drag(d,e,a)},startDrag:function(a){this.isListening||this.startListening(),this.isDragging||(this.isDragging=!0,this.dragStart(a))},dragStart:function(a){var b=this.subjectEl;this.trigger("dragStart",a),(this.subjectHref=b?b.attr("href"):null)&&b.removeAttr("href")},drag:function(a,b,c){this.trigger("drag",a,b,c),this.updateScroll(c)},mouseup:function(a){this.stopListening(a)},stopDrag:function(a){this.isDragging&&(this.stopScrolling(),this.dragStop(a),this.isDragging=!1)},dragStop:function(a){var b=this;this.trigger("dragStop",a),setTimeout(function(){b.subjectHref&&b.subjectEl.attr("href",b.subjectHref)},0)},stopListening:function(b){this.stopDrag(b),this.isListening&&(this.scrollEl&&(this.scrollEl.off("scroll",this.scrollHandlerProxy),this.scrollHandlerProxy=null),a(document).off("mousemove",this.mousemoveProxy).off("mouseup",this.mouseupProxy).off("selectstart",this.preventDefault),this.mousemoveProxy=null,this.mouseupProxy=null,this.isListening=!1,this.listenStop(b))},listenStop:function(a){this.trigger("listenStop",a)},trigger:function(a){this.options[a]&&this.options[a].apply(this,Array.prototype.slice.call(arguments,1))},preventDefault:function(a){a.preventDefault()},computeScrollBounds:function(){var a=this.scrollEl;this.scrollBounds=a?o(a):null},updateScroll:function(a){var b,c,d,e,f=this.scrollSensitivity,g=this.scrollBounds,h=0,i=0;g&&(b=(f-(a.pageY-g.top))/f,c=(f-(g.bottom-a.pageY))/f,d=(f-(a.pageX-g.left))/f,e=(f-(g.right-a.pageX))/f,b>=0&&1>=b?h=b*this.scrollSpeed*-1:c>=0&&1>=c&&(h=c*this.scrollSpeed),d>=0&&1>=d?i=d*this.scrollSpeed*-1:e>=0&&1>=e&&(i=e*this.scrollSpeed)),this.setScrollVel(h,i)},setScrollVel:function(a,b){this.scrollTopVel=a,this.scrollLeftVel=b,this.constrainScrollVel(),!this.scrollTopVel&&!this.scrollLeftVel||this.scrollIntervalId||(this.scrollIntervalId=setInterval(ca(this,"scrollIntervalFunc"),this.scrollIntervalMs))},constrainScrollVel:function(){var a=this.scrollEl;this.scrollTopVel<0?a.scrollTop()<=0&&(this.scrollTopVel=0):this.scrollTopVel>0&&a.scrollTop()+a[0].clientHeight>=a[0].scrollHeight&&(this.scrollTopVel=0),this.scrollLeftVel<0?a.scrollLeft()<=0&&(this.scrollLeftVel=0):this.scrollLeftVel>0&&a.scrollLeft()+a[0].clientWidth>=a[0].scrollWidth&&(this.scrollLeftVel=0)},scrollIntervalFunc:function(){var a=this.scrollEl,b=this.scrollIntervalMs/1e3;this.scrollTopVel&&a.scrollTop(a.scrollTop()+this.scrollTopVel*b),this.scrollLeftVel&&a.scrollLeft(a.scrollLeft()+this.scrollLeftVel*b),this.constrainScrollVel(),this.scrollTopVel||this.scrollLeftVel||this.stopScrolling()},stopScrolling:function(){this.scrollIntervalId&&(clearInterval(this.scrollIntervalId),this.scrollIntervalId=null,this.scrollStop())},scrollHandler:function(){this.scrollIntervalId||this.scrollStop()},scrollStop:function(){}}),gb=fb.extend({coordMap:null,origCell:null,cell:null,coordAdjust:null,constructor:function(a,b){fb.prototype.constructor.call(this,b),this.coordMap=a},listenStart:function(a){var b,c,d,e=this.subjectEl;fb.prototype.listenStart.apply(this,arguments),this.computeCoords(),a?(c={left:a.pageX,top:a.pageY},d=c,e&&(b=o(e),d=x(d,b)),this.origCell=this.getCell(d.left,d.top),e&&this.options.subjectCenter&&(this.origCell&&(b=w(this.origCell,b)||b),d=y(b)),this.coordAdjust=z(d,c)):(this.origCell=null,this.coordAdjust=null)},computeCoords:function(){this.coordMap.build(),this.computeScrollBounds()},dragStart:function(a){var b;fb.prototype.dragStart.apply(this,arguments),b=this.getCell(a.pageX,a.pageY),b&&this.cellOver(b)},drag:function(a,b,c){var d;fb.prototype.drag.apply(this,arguments),d=this.getCell(c.pageX,c.pageY),sa(d,this.cell)||(this.cell&&this.cellOut(),d&&this.cellOver(d))},dragStop:function(){this.cellDone(),fb.prototype.dragStop.apply(this,arguments)},cellOver:function(a){this.cell=a,this.trigger("cellOver",a,sa(a,this.origCell),this.origCell)},cellOut:function(){this.cell&&(this.trigger("cellOut",this.cell),this.cellDone(),this.cell=null)},cellDone:function(){this.cell&&this.trigger("cellDone",this.cell)},listenStop:function(){fb.prototype.listenStop.apply(this,arguments),this.origCell=this.cell=null,this.coordMap.clear()},scrollStop:function(){fb.prototype.scrollStop.apply(this,arguments),this.computeCoords()},getCell:function(a,b){return this.coordAdjust&&(a+=this.coordAdjust.left,b+=this.coordAdjust.top),this.coordMap.getCell(a,b)}}),hb=ra.extend({options:null,sourceEl:null,el:null,parentEl:null,top0:null,left0:null,mouseY0:null,mouseX0:null,topDelta:null,leftDelta:null,mousemoveProxy:null,isFollowing:!1,isHidden:!1,isAnimating:!1,constructor:function(b,c){this.options=c=c||{},this.sourceEl=b,this.parentEl=c.parentEl?a(c.parentEl):b.parent()},start:function(b){this.isFollowing||(this.isFollowing=!0,this.mouseY0=b.pageY,this.mouseX0=b.pageX,this.topDelta=0,this.leftDelta=0,this.isHidden||this.updatePosition(),a(document).on("mousemove",this.mousemoveProxy=ca(this,"mousemove")))},stop:function(b,c){function d(){this.isAnimating=!1,e.removeElement(),this.top0=this.left0=null,c&&c()}var e=this,f=this.options.revertDuration;this.isFollowing&&!this.isAnimating&&(this.isFollowing=!1,a(document).off("mousemove",this.mousemoveProxy),b&&f&&!this.isHidden?(this.isAnimating=!0,this.el.animate({top:this.top0,left:this.left0},{duration:f,complete:d})):d())},getEl:function(){var a=this.el;return a||(this.sourceEl.width(),a=this.el=this.sourceEl.clone().css({position:"absolute",visibility:"",display:this.isHidden?"none":"",margin:0,right:"auto",bottom:"auto",width:this.sourceEl.width(),height:this.sourceEl.height(),opacity:this.options.opacity||"",zIndex:this.options.zIndex}).appendTo(this.parentEl)),a},removeElement:function(){this.el&&(this.el.remove(),this.el=null)},updatePosition:function(){var a,b;this.getEl(),null===this.top0&&(this.sourceEl.width(),a=this.sourceEl.offset(),b=this.el.offsetParent().offset(),this.top0=a.top-b.top,this.left0=a.left-b.left),this.el.css({top:this.top0+this.topDelta,left:this.left0+this.leftDelta})},mousemove:function(a){this.topDelta=a.pageY-this.mouseY0,this.leftDelta=a.pageX-this.mouseX0,this.isHidden||this.updatePosition()},hide:function(){this.isHidden||(this.isHidden=!0,this.el&&this.el.hide())},show:function(){this.isHidden&&(this.isHidden=!1,this.updatePosition(),this.getEl().show())}}),ib=ra.extend({view:null,isRTL:null,cellHtml:"<td/>",constructor:function(a){this.view=a,this.isRTL=a.opt("isRTL")},rowHtml:function(a,b){var c,d,e=this.getHtmlRenderer("cell",a),f="";for(b=b||0,c=0;c<this.colCnt;c++)d=this.getCell(b,c),f+=e(d);return f=this.bookendCells(f,a,b),"<tr>"+f+"</tr>"},bookendCells:function(a,b,c){var d=this.getHtmlRenderer("intro",b)(c||0),e=this.getHtmlRenderer("outro",b)(c||0),f=this.isRTL?e:d,g=this.isRTL?d:e;return"string"==typeof a?f+a+g:a.prepend(f).append(g)},getHtmlRenderer:function(a,b){var c,d,e,f,g=this.view;return c=a+"Html",b&&(d=b+_(a)+"Html"),d&&(f=g[d])?e=g:d&&(f=this[d])?e=this:(f=g[c])?e=g:(f=this[c])&&(e=this),"function"==typeof f?function(){return f.apply(e,arguments)||""}:function(){return f||""}}}),jb=Ma.Grid=ib.extend({start:null,end:null,rowCnt:0,colCnt:0,el:null,coordMap:null,elsByFill:null,externalDragStartProxy:null,colHeadFormat:null,eventTimeFormat:null,displayEventTime:null,displayEventEnd:null,cellDuration:null,largeUnit:null,constructor:function(){ib.apply(this,arguments),this.coordMap=new db(this),this.elsByFill={},this.externalDragStartProxy=ca(this,"externalDragStart")},computeColHeadFormat:function(){},computeEventTimeFormat:function(){return this.view.opt("smallTimeFormat")},computeDisplayEventTime:function(){return!0},computeDisplayEventEnd:function(){return!0},setRange:function(a){this.start=a.start.clone(),this.end=a.end.clone(),this.rangeUpdated(),this.processRangeOptions()},rangeUpdated:function(){},processRangeOptions:function(){var a,b,c=this.view;this.colHeadFormat=c.opt("columnFormat")||this.computeColHeadFormat(),this.eventTimeFormat=c.opt("eventTimeFormat")||c.opt("timeFormat")||this.computeEventTimeFormat(),a=c.opt("displayEventTime"),null==a&&(a=this.computeDisplayEventTime()),b=c.opt("displayEventEnd"),null==b&&(b=this.computeDisplayEventEnd()),this.displayEventTime=a,this.displayEventEnd=b},build:function(){},clear:function(){},rangeToSegs:function(a){},diffDates:function(a,b){return this.largeUnit?H(a,b,this.largeUnit):F(a,b)},getCell:function(b,c){var d;return null==c&&("number"==typeof b?(c=b%this.colCnt,b=Math.floor(b/this.colCnt)):(c=b.col,b=b.row)),d={row:b,col:c},a.extend(d,this.getRowData(b),this.getColData(c)),a.extend(d,this.computeCellRange(d)),d},computeCellRange:function(a){var b=this.computeCellDate(a);return{start:b,end:b.clone().add(this.cellDuration)}},computeCellDate:function(a){},getRowData:function(a){return{}},getColData:function(a){return{}},getRowEl:function(a){},getColEl:function(a){},getCellDayEl:function(a){return this.getColEl(a.col)||this.getRowEl(a.row)},computeRowCoords:function(){var a,b,c,d=[];for(a=0;a<this.rowCnt;a++)b=this.getRowEl(a),c=b.offset().top,d.push({top:c,bottom:c+b.outerHeight()});return d},computeColCoords:function(){var a,b,c,d=[];for(a=0;a<this.colCnt;a++)b=this.getColEl(a),c=b.offset().left,d.push({left:c,right:c+b.outerWidth()});return d},setElement:function(b){var c=this;this.el=b,b.on("mousedown",function(b){a(b.target).is(".fc-event-container *, .fc-more")||a(b.target).closest(".fc-popover").length||c.dayMousedown(b)}),this.bindSegHandlers(),this.bindGlobalHandlers()},removeElement:function(){this.unbindGlobalHandlers(),this.el.remove()},renderSkeleton:function(){},renderDates:function(){},unrenderDates:function(){},bindGlobalHandlers:function(){a(document).on("dragstart sortstart",this.externalDragStartProxy)},unbindGlobalHandlers:function(){a(document).off("dragstart sortstart",this.externalDragStartProxy)},dayMousedown:function(a){var b,c,d=this,e=this.view,f=e.opt("selectable"),i=new gb(this.coordMap,{scroll:e.opt("dragScroll"),dragStart:function(){e.unselect()},cellOver:function(a,e,h){h&&(b=e?a:null,f&&(c=d.computeSelection(h,a),c?d.renderSelection(c):g()))},cellOut:function(a){b=null,c=null,d.unrenderSelection(),h()},listenStop:function(a){b&&e.triggerDayClick(b,d.getCellDayEl(b),a),c&&e.reportSelection(c,a),h()}});i.mousedown(a)},renderRangeHelper:function(a,b){var c=this.fabricateHelperEvent(a,b);this.renderHelper(c,b)},fabricateHelperEvent:function(a,b){var c=b?R(b.event):{};return c.start=a.start.clone(),c.end=a.end?a.end.clone():null,c.allDay=null,this.view.calendar.normalizeEventRange(c),c.className=(c.className||[]).concat("fc-helper"),b||(c.editable=!1),c},renderHelper:function(a,b){},unrenderHelper:function(){},renderSelection:function(a){this.renderHighlight(this.selectionRangeToSegs(a))},unrenderSelection:function(){this.unrenderHighlight()},computeSelection:function(a,b){var c,d=[a.start,a.end,b.start,b.end];return d.sort(aa),c={start:d[0].clone(),end:d[3].clone()},this.view.calendar.isSelectionRangeAllowed(c)?c:null},selectionRangeToSegs:function(a){return this.rangeToSegs(a)},renderHighlight:function(a){this.renderFill("highlight",a)},unrenderHighlight:function(){this.unrenderFill("highlight")},highlightSegClasses:function(){return["fc-highlight"]},renderFill:function(a,b){},unrenderFill:function(a){var b=this.elsByFill[a];b&&(b.remove(),delete this.elsByFill[a])},renderFillSegEls:function(b,c){var d,e=this,f=this[b+"SegEl"],g="",h=[];if(c.length){for(d=0;d<c.length;d++)g+=this.fillSegHtml(b,c[d]);a(g).each(function(b,d){var g=c[b],i=a(d);f&&(i=f.call(e,g,i)),i&&(i=a(i),i.is(e.fillSegTag)&&(g.el=i,h.push(g)))})}return h},fillSegTag:"div",fillSegHtml:function(a,b){var c=this[a+"SegClasses"],d=this[a+"SegCss"],e=c?c.call(this,b):[],f=$(d?d.call(this,b):{});return"<"+this.fillSegTag+(e.length?' class="'+e.join(" ")+'"':"")+(f?' style="'+f+'"':"")+" />"},headHtml:function(){return'<div class="fc-row '+this.view.widgetHeaderClass+'"><table><thead>'+this.rowHtml("head")+"</thead></table></div>"},headCellHtml:function(a){var b=this.view,c=a.start;return'<th class="fc-day-header '+b.widgetHeaderClass+" fc-"+Qa[c.day()]+'">'+Y(c.format(this.colHeadFormat))+"</th>"},bgCellHtml:function(a){var b=this.view,c=a.start,d=this.getDayClasses(c);return d.unshift("fc-day",b.widgetContentClass),'<td class="'+d.join(" ")+'" data-date="'+c.format("YYYY-MM-DD")+'"></td>'},getDayClasses:function(a){var b=this.view,c=b.calendar.getNow().stripTime(),d=["fc-"+Qa[a.day()]];return 1==b.intervalDuration.as("months")&&a.month()!=b.intervalStart.month()&&d.push("fc-other-month"),a.isSame(c,"day")?d.push("fc-today",b.highlightStateClass):c>a?d.push("fc-past"):d.push("fc-future"),d}});jb.mixin({mousedOverSeg:null,isDraggingSeg:!1,isResizingSeg:!1,isDraggingExternal:!1,segs:null,renderEvents:function(a){var b,c,d=this.eventsToSegs(a),e=[],f=[];for(b=0;b<d.length;b++)c=d[b],ta(c.event)?e.push(c):f.push(c);e=this.renderBgSegs(e)||e,f=this.renderFgSegs(f)||f,this.segs=e.concat(f)},unrenderEvents:function(){this.triggerSegMouseout(),this.unrenderFgSegs(),this.unrenderBgSegs(),this.segs=null},getEventSegs:function(){return this.segs||[]},renderFgSegs:function(a){},unrenderFgSegs:function(){},renderFgSegEls:function(b,c){var d,e=this.view,f="",g=[];if(b.length){for(d=0;d<b.length;d++)f+=this.fgSegHtml(b[d],c);a(f).each(function(c,d){var f=b[c],h=e.resolveEventEl(f.event,a(d));h&&(h.data("fc-seg",f),f.el=h,g.push(f))})}return g},fgSegHtml:function(a,b){},renderBgSegs:function(a){return this.renderFill("bgEvent",a)},unrenderBgSegs:function(){this.unrenderFill("bgEvent")},bgEventSegEl:function(a,b){return this.view.resolveEventEl(a.event,b)},bgEventSegClasses:function(a){var b=a.event,c=b.source||{};return["fc-bgevent"].concat(b.className,c.className||[])},bgEventSegCss:function(a){var b=this.view,c=a.event,d=c.source||{};return{"background-color":c.backgroundColor||c.color||d.backgroundColor||d.color||b.opt("eventBackgroundColor")||b.opt("eventColor")}},businessHoursSegClasses:function(a){return["fc-nonbusiness","fc-bgevent"]},bindSegHandlers:function(){var b=this,c=this.view;a.each({mouseenter:function(a,c){b.triggerSegMouseover(a,c)},mouseleave:function(a,c){b.triggerSegMouseout(a,c)},click:function(a,b){return c.trigger("eventClick",this,a.event,b)},mousedown:function(d,e){a(e.target).is(".fc-resizer")&&c.isEventResizable(d.event)?b.segResizeMousedown(d,e,a(e.target).is(".fc-start-resizer")):c.isEventDraggable(d.event)&&b.segDragMousedown(d,e)}},function(c,d){b.el.on(c,".fc-event-container > *",function(c){var e=a(this).data("fc-seg");return!e||b.isDraggingSeg||b.isResizingSeg?void 0:d.call(this,e,c)})})},triggerSegMouseover:function(a,b){this.mousedOverSeg||(this.mousedOverSeg=a,this.view.trigger("eventMouseover",a.el[0],a.event,b))},triggerSegMouseout:function(a,b){b=b||{},this.mousedOverSeg&&(a=a||this.mousedOverSeg,this.mousedOverSeg=null,this.view.trigger("eventMouseout",a.el[0],a.event,b))},segDragMousedown:function(a,b){var c,d=this,e=this.view,f=e.calendar,i=a.el,j=a.event,k=new hb(a.el,{parentEl:e.el,opacity:e.opt("dragOpacity"),revertDuration:e.opt("dragRevertDuration"),zIndex:2}),l=new gb(e.coordMap,{distance:5,scroll:e.opt("dragScroll"),subjectEl:i,subjectCenter:!0,listenStart:function(a){k.hide(),k.start(a)},dragStart:function(b){d.triggerSegMouseout(a,b),d.segDragStart(a,b),e.hideEvent(j)},cellOver:function(b,h,i){a.cell&&(i=a.cell),c=d.computeEventDrop(i,b,j),c&&!f.isEventRangeAllowed(c,j)&&(g(),c=null),c&&e.renderDrag(c,a)?k.hide():k.show(),h&&(c=null)},cellOut:function(){e.unrenderDrag(),k.show(),c=null},cellDone:function(){h()},dragStop:function(b){k.stop(!c,function(){e.unrenderDrag(),e.showEvent(j),d.segDragStop(a,b),c&&e.reportEventDrop(j,c,this.largeUnit,i,b)})},listenStop:function(){k.stop()}});l.mousedown(b)},segDragStart:function(a,b){this.isDraggingSeg=!0,this.view.trigger("eventDragStart",a.el[0],a.event,b,{})},segDragStop:function(a,b){this.isDraggingSeg=!1,this.view.trigger("eventDragStop",a.el[0],a.event,b,{})},computeEventDrop:function(a,b,c){var d,e,f=this.view.calendar,g=a.start,h=b.start;return g.hasTime()===h.hasTime()?(d=this.diffDates(h,g),c.allDay&&N(d)?(e={start:c.start.clone(),end:f.getEventEnd(c),allDay:!1},f.normalizeEventRangeTimes(e)):e={start:c.start.clone(),end:c.end?c.end.clone():null,allDay:c.allDay},e.start.add(d),e.end&&e.end.add(d)):e={start:h.clone(),end:null,allDay:!h.hasTime()},e},applyDragOpacity:function(a){var b=this.view.opt("dragOpacity");null!=b&&a.each(function(a,c){c.style.opacity=b})},externalDragStart:function(b,c){var d,e,f=this.view;f.opt("droppable")&&(d=a((c?c.item:null)||b.target),e=f.opt("dropAccept"),(a.isFunction(e)?e.call(d[0],d):d.is(e))&&(this.isDraggingExternal||this.listenToExternalDrag(d,b,c)))},listenToExternalDrag:function(a,b,c){var d,e,f=this,i=ya(a);d=new gb(this.coordMap,{listenStart:function(){f.isDraggingExternal=!0},cellOver:function(a){e=f.computeExternalDrop(a,i),e?f.renderDrag(e):g()},cellOut:function(){e=null,f.unrenderDrag(),h()},dragStop:function(){f.unrenderDrag(),h(),e&&f.view.reportExternalDrop(i,e,a,b,c)},listenStop:function(){f.isDraggingExternal=!1}}),d.startDrag(b)},computeExternalDrop:function(a,b){var c={start:a.start.clone(),end:null};return b.startTime&&!c.start.hasTime()&&c.start.time(b.startTime),b.duration&&(c.end=c.start.clone().add(b.duration)),this.view.calendar.isExternalDropRangeAllowed(c,b.eventProps)?c:null},renderDrag:function(a,b){},unrenderDrag:function(){},segResizeMousedown:function(a,b,c){var d,e,f=this,i=this.view,j=i.calendar,k=a.el,l=a.event,m=j.getEventEnd(l);d=new gb(this.coordMap,{distance:5,scroll:i.opt("dragScroll"),subjectEl:k,dragStart:function(b){f.triggerSegMouseout(a,b),f.segResizeStart(a,b)},cellOver:function(b,d,h){e=c?f.computeEventStartResize(h,b,l):f.computeEventEndResize(h,b,l),e&&(j.isEventRangeAllowed(e,l)?e.start.isSame(l.start)&&e.end.isSame(m)&&(e=null):(g(),e=null)),e&&(i.hideEvent(l),f.renderEventResize(e,a))},cellOut:function(){e=null},cellDone:function(){f.unrenderEventResize(),i.showEvent(l),h()},dragStop:function(b){f.segResizeStop(a,b),e&&i.reportEventResize(l,e,this.largeUnit,k,b)}}),d.mousedown(b)},segResizeStart:function(a,b){this.isResizingSeg=!0,this.view.trigger("eventResizeStart",a.el[0],a.event,b,{})},segResizeStop:function(a,b){this.isResizingSeg=!1,this.view.trigger("eventResizeStop",a.el[0],a.event,b,{})},computeEventStartResize:function(a,b,c){return this.computeEventResize("start",a,b,c)},computeEventEndResize:function(a,b,c){return this.computeEventResize("end",a,b,c)},computeEventResize:function(a,b,c,d){var e,f,g=this.view.calendar,h=this.diffDates(c[a],b[a]);return e={start:d.start.clone(),end:g.getEventEnd(d),allDay:d.allDay},e.allDay&&N(h)&&(e.allDay=!1,g.normalizeEventRangeTimes(e)),e[a].add(h),e.start.isBefore(e.end)||(f=d.allDay?g.defaultAllDayEventDuration:g.defaultTimedEventDuration,this.cellDuration&&this.cellDuration<f&&(f=this.cellDuration),"start"==a?e.start=e.end.clone().subtract(f):e.end=e.start.clone().add(f)),e},renderEventResize:function(a,b){},unrenderEventResize:function(){},getEventTimeText:function(a,b,c){return null==b&&(b=this.eventTimeFormat),null==c&&(c=this.displayEventEnd),this.displayEventTime&&a.start.hasTime()?c&&a.end?this.view.formatRange(a,b):a.start.format(b):""},getSegClasses:function(a,b,c){var d=a.event,e=["fc-event",a.isStart?"fc-start":"fc-not-start",a.isEnd?"fc-end":"fc-not-end"].concat(d.className,d.source?d.source.className:[]);return b&&e.push("fc-draggable"),c&&e.push("fc-resizable"),e},getEventSkinCss:function(a){var b=this.view,c=a.source||{},d=a.color,e=c.color,f=b.opt("eventColor");return{"background-color":a.backgroundColor||d||c.backgroundColor||e||b.opt("eventBackgroundColor")||f,"border-color":a.borderColor||d||c.borderColor||e||b.opt("eventBorderColor")||f,color:a.textColor||c.textColor||b.opt("eventTextColor")}},eventsToSegs:function(a,b){var c,d=this.eventsToRanges(a),e=[];for(c=0;c<d.length;c++)e.push.apply(e,this.eventRangeToSegs(d[c],b));return e},eventsToRanges:function(b){var c=this,d=wa(b),e=[];return a.each(d,function(a,b){b.length&&e.push.apply(e,ua(b[0])?c.eventsToInverseRanges(b):c.eventsToNormalRanges(b))}),e},eventsToNormalRanges:function(a){var b,c,d,e,f=this.view.calendar,g=[];for(b=0;b<a.length;b++)c=a[b],d=c.start.clone().stripZone(),e=f.getEventEnd(c).stripZone(),g.push({event:c,start:d,end:e,eventStartMS:+d,eventDurationMS:e-d});return g},eventsToInverseRanges:function(a){var b,c,d=this.view,e=d.start.clone().stripZone(),f=d.end.clone().stripZone(),g=this.eventsToNormalRanges(a),h=[],i=a[0],j=e;for(g.sort(xa),b=0;b<g.length;b++)c=g[b],c.start>j&&h.push({event:i,start:j,end:c.start}),j=c.end;return f>j&&h.push({event:i,start:j,end:f}),h},eventRangeToSegs:function(a,b){var c,d,e;for(a=this.view.calendar.ensureVisibleEventRange(a),c=b?b(a):this.rangeToSegs(a),d=0;d<c.length;d++)e=c[d],e.event=a.event,e.eventStartMS=a.eventStartMS,e.eventDurationMS=a.eventDurationMS;return c},sortSegs:function(a){a.sort(ca(this,"compareSegs"))},compareSegs:function(a,b){return a.eventStartMS-b.eventStartMS||b.eventDurationMS-a.eventDurationMS||b.event.allDay-a.event.allDay||B(a.event,b.event,this.view.eventOrderSpecs)}}),Ma.dataAttrPrefix="";var kb=jb.extend({numbersVisible:!1,bottomCoordPadding:0,breakOnWeeks:null,cellDates:null,dayToCellOffsets:null,rowEls:null,dayEls:null,helperEls:null,constructor:function(){jb.apply(this,arguments),this.cellDuration=b.duration(1,"day")},renderDates:function(a){var b,c,d,e=this.view,f=this.rowCnt,g=this.colCnt,h=f*g,i="";for(b=0;f>b;b++)i+=this.dayRowHtml(b,a);for(this.el.html(i),this.rowEls=this.el.find(".fc-row"),this.dayEls=this.el.find(".fc-day"),c=0;h>c;c++)d=this.getCell(c),e.trigger("dayRender",null,d.start,this.dayEls.eq(c))},unrenderDates:function(){this.removeSegPopover()},renderBusinessHours:function(){var a=this.view.calendar.getBusinessHoursEvents(!0),b=this.eventsToSegs(a);this.renderFill("businessHours",b,"bgevent")},dayRowHtml:function(a,b){var c=this.view,d=["fc-row","fc-week",c.widgetContentClass];return b&&d.push("fc-rigid"),'<div class="'+d.join(" ")+'"><div class="fc-bg"><table>'+this.rowHtml("day",a)+'</table></div><div class="fc-content-skeleton"><table>'+(this.numbersVisible?"<thead>"+this.rowHtml("number",a)+"</thead>":"")+"</table></div></div>"},dayCellHtml:function(a){return this.bgCellHtml(a)},computeColHeadFormat:function(){return this.rowCnt>1?"ddd":this.colCnt>1?this.view.opt("dayOfMonthFormat"):"dddd"},computeEventTimeFormat:function(){return this.view.opt("extraSmallTimeFormat")},computeDisplayEventEnd:function(){return 1==this.colCnt},rangeUpdated:function(){var a,b,c,d;if(this.updateCellDates(),a=this.cellDates,this.breakOnWeeks){for(b=a[0].day(),d=1;d<a.length&&a[d].day()!=b;d++);c=Math.ceil(a.length/d)}else c=1,d=a.length;this.rowCnt=c,this.colCnt=d},updateCellDates:function(){for(var a=this.view,b=this.start.clone(),c=[],d=-1,e=[];b.isBefore(this.end);)a.isHiddenDay(b)?e.push(d+.5):(d++,e.push(d),c.push(b.clone())),b.add(1,"days");this.cellDates=c,this.dayToCellOffsets=e},computeCellDate:function(a){var b=this.colCnt,c=a.row*b+(this.isRTL?b-a.col-1:a.col);return this.cellDates[c].clone()},getRowEl:function(a){return this.rowEls.eq(a)},getColEl:function(a){return this.dayEls.eq(a)},getCellDayEl:function(a){return this.dayEls.eq(a.row*this.colCnt+a.col)},computeRowCoords:function(){var a=jb.prototype.computeRowCoords.call(this);return a[a.length-1].bottom+=this.bottomCoordPadding,a},rangeToSegs:function(a){var b,c,d,e,f,g,h,i,j,k,l=this.isRTL,m=this.rowCnt,n=this.colCnt,o=[];for(a=this.view.computeDayRange(a),b=this.dateToCellOffset(a.start),c=this.dateToCellOffset(a.end.subtract(1,"days")),d=0;m>d;d++)e=d*n,f=e+n-1,i=Math.max(e,b),j=Math.min(f,c),i=Math.ceil(i),j=Math.floor(j),j>=i&&(g=i===b,h=j===c,i-=e,j-=e,k={row:d,isStart:g,isEnd:h},l?(k.leftCol=n-j-1,k.rightCol=n-i-1):(k.leftCol=i,k.rightCol=j),o.push(k));return o},dateToCellOffset:function(a){var b=this.dayToCellOffsets,c=a.diff(this.start,"days");return 0>c?b[0]-1:c>=b.length?b[b.length-1]+1:b[c]},renderDrag:function(a,b){return this.renderHighlight(this.eventRangeToSegs(a)),b&&!b.el.closest(this.el).length?(this.renderRangeHelper(a,b),this.applyDragOpacity(this.helperEls),!0):void 0},unrenderDrag:function(){this.unrenderHighlight(),this.unrenderHelper()},renderEventResize:function(a,b){this.renderHighlight(this.eventRangeToSegs(a)),this.renderRangeHelper(a,b)},unrenderEventResize:function(){this.unrenderHighlight(),this.unrenderHelper()},renderHelper:function(b,c){var d,e=[],f=this.eventsToSegs([b]);f=this.renderFgSegEls(f),d=this.renderSegRows(f),this.rowEls.each(function(b,f){var g,h=a(f),i=a('<div class="fc-helper-skeleton"><table/></div>');g=c&&c.row===b?c.el.position().top:h.find(".fc-content-skeleton tbody").position().top,i.css("top",g).find("table").append(d[b].tbodyEl),h.append(i),e.push(i[0])}),this.helperEls=a(e)},unrenderHelper:function(){this.helperEls&&(this.helperEls.remove(),this.helperEls=null)},fillSegTag:"td",renderFill:function(b,c,d){var e,f,g,h=[];for(c=this.renderFillSegEls(b,c),e=0;e<c.length;e++)f=c[e],g=this.renderFillRow(b,f,d),this.rowEls.eq(f.row).append(g),h.push(g[0]);return this.elsByFill[b]=a(h),c},renderFillRow:function(b,c,d){var e,f,g=this.colCnt,h=c.leftCol,i=c.rightCol+1;return d=d||b.toLowerCase(),e=a('<div class="fc-'+d+'-skeleton"><table><tr/></table></div>'),f=e.find("tr"),h>0&&f.append('<td colspan="'+h+'"/>'),f.append(c.el.attr("colspan",i-h)),g>i&&f.append('<td colspan="'+(g-i)+'"/>'),this.bookendCells(f,b),e}});kb.mixin({rowStructs:null,unrenderEvents:function(){this.removeSegPopover(),jb.prototype.unrenderEvents.apply(this,arguments)},getEventSegs:function(){return jb.prototype.getEventSegs.call(this).concat(this.popoverSegs||[])},renderBgSegs:function(b){var c=a.grep(b,function(a){return a.event.allDay});return jb.prototype.renderBgSegs.call(this,c)},renderFgSegs:function(b){var c;return b=this.renderFgSegEls(b),c=this.rowStructs=this.renderSegRows(b),this.rowEls.each(function(b,d){a(d).find(".fc-content-skeleton > table").append(c[b].tbodyEl)}),b},unrenderFgSegs:function(){for(var a,b=this.rowStructs||[];a=b.pop();)a.tbodyEl.remove();this.rowStructs=null},renderSegRows:function(a){var b,c,d=[];for(b=this.groupSegRows(a),c=0;c<b.length;c++)d.push(this.renderSegRow(c,b[c]));return d},fgSegHtml:function(a,b){var c,d,e=this.view,f=a.event,g=e.isEventDraggable(f),h=!b&&f.allDay&&a.isStart&&e.isEventResizableFromStart(f),i=!b&&f.allDay&&a.isEnd&&e.isEventResizableFromEnd(f),j=this.getSegClasses(a,g,h||i),k=$(this.getEventSkinCss(f)),l="";return j.unshift("fc-day-grid-event","fc-h-event"),a.isStart&&(c=this.getEventTimeText(f),c&&(l='<span class="fc-time">'+Y(c)+"</span>")),d='<span class="fc-title">'+(Y(f.title||"")||" ")+"</span>",'<a class="'+j.join(" ")+'"'+(f.url?' href="'+Y(f.url)+'"':"")+(k?' style="'+k+'"':"")+'><div class="fc-content">'+(this.isRTL?d+" "+l:l+" "+d)+"</div>"+(h?'<div class="fc-resizer fc-start-resizer" />':"")+(i?'<div class="fc-resizer fc-end-resizer" />':"")+"</a>"},renderSegRow:function(b,c){function d(b){for(;b>g;)k=(r[e-1]||[])[g],k?k.attr("rowspan",parseInt(k.attr("rowspan")||1,10)+1):(k=a("<td/>"),h.append(k)),q[e][g]=k,r[e][g]=k,g++}var e,f,g,h,i,j,k,l=this.colCnt,m=this.buildSegLevels(c),n=Math.max(1,m.length),o=a("<tbody/>"),p=[],q=[],r=[];for(e=0;n>e;e++){if(f=m[e],g=0,h=a("<tr/>"),p.push([]),q.push([]),r.push([]),f)for(i=0;i<f.length;i++){for(j=f[i],d(j.leftCol),k=a('<td class="fc-event-container"/>').append(j.el),j.leftCol!=j.rightCol?k.attr("colspan",j.rightCol-j.leftCol+1):r[e][g]=k;g<=j.rightCol;)q[e][g]=k,p[e][g]=j,g++;h.append(k)}d(l),this.bookendCells(h,"eventSkeleton"),o.append(h)}return{row:b,tbodyEl:o,cellMatrix:q,segMatrix:p,segLevels:m,segs:c}},buildSegLevels:function(a){var b,c,d,e=[];for(this.sortSegs(a),b=0;b<a.length;b++){for(c=a[b],d=0;d<e.length&&za(c,e[d]);d++);c.level=d,(e[d]||(e[d]=[])).push(c)}for(d=0;d<e.length;d++)e[d].sort(Aa);return e},groupSegRows:function(a){var b,c=[];for(b=0;b<this.rowCnt;b++)c.push([]);for(b=0;b<a.length;b++)c[a[b].row].push(a[b]);return c}}),kb.mixin({segPopover:null,popoverSegs:null,removeSegPopover:function(){this.segPopover&&this.segPopover.hide()},limitRows:function(a){var b,c,d=this.rowStructs||[];for(b=0;b<d.length;b++)this.unlimitRow(b),c=a?"number"==typeof a?a:this.computeRowLevelLimit(b):!1,c!==!1&&this.limitRow(b,c)},computeRowLevelLimit:function(b){function c(b,c){f=Math.max(f,a(c).outerHeight())}var d,e,f,g=this.rowEls.eq(b),h=g.height(),i=this.rowStructs[b].tbodyEl.children();for(d=0;d<i.length;d++)if(e=i.eq(d).removeClass("fc-limited"),f=0,e.find("> td > :first-child").each(c),e.position().top+f>h)return d;return!1},limitRow:function(b,c){function d(d){for(;d>x;)e=u.getCell(b,x),k=u.getCellSegs(e,c),k.length&&(n=g[c-1][x],t=u.renderMoreLink(e,k),s=a("<div/>").append(t),n.append(s),w.push(s[0])),x++}var e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u=this,v=this.rowStructs[b],w=[],x=0;if(c&&c<v.segLevels.length){for(f=v.segLevels[c-1],g=v.cellMatrix,h=v.tbodyEl.children().slice(c).addClass("fc-limited").get(),i=0;i<f.length;i++){for(j=f[i],d(j.leftCol),m=[],l=0;x<=j.rightCol;)e=this.getCell(b,x),k=this.getCellSegs(e,c),m.push(k),l+=k.length,x++;if(l){for(n=g[c-1][j.leftCol],o=n.attr("rowspan")||1,p=[],q=0;q<m.length;q++)r=a('<td class="fc-more-cell"/>').attr("rowspan",o),k=m[q],e=this.getCell(b,j.leftCol+q),t=this.renderMoreLink(e,[j].concat(k)),s=a("<div/>").append(t),r.append(s),p.push(r[0]),w.push(r[0]);n.addClass("fc-limited").after(a(p)),h.push(n[0])}}d(this.colCnt),v.moreEls=a(w),v.limitedEls=a(h)}},
|
8 |
+
unlimitRow:function(a){var b=this.rowStructs[a];b.moreEls&&(b.moreEls.remove(),b.moreEls=null),b.limitedEls&&(b.limitedEls.removeClass("fc-limited"),b.limitedEls=null)},renderMoreLink:function(b,c){var d=this,e=this.view;return a('<a class="fc-more"/>').text(this.getMoreLinkText(c.length)).on("click",function(f){var g=e.opt("eventLimitClick"),h=b.start,i=a(this),j=d.getCellDayEl(b),k=d.getCellSegs(b),l=d.resliceDaySegs(k,h),m=d.resliceDaySegs(c,h);"function"==typeof g&&(g=e.trigger("eventLimitClick",null,{date:h,dayEl:j,moreEl:i,segs:l,hiddenSegs:m},f)),"popover"===g?d.showSegPopover(b,i,l):"string"==typeof g&&e.calendar.zoomTo(h,g)})},showSegPopover:function(a,b,c){var d,e,f=this,g=this.view,h=b.parent();d=1==this.rowCnt?g.el:this.rowEls.eq(a.row),e={className:"fc-more-popover",content:this.renderSegPopoverContent(a,c),parentEl:this.el,top:d.offset().top,autoHide:!0,viewportConstrain:g.opt("popoverViewportConstrain"),hide:function(){f.segPopover.removeElement(),f.segPopover=null,f.popoverSegs=null}},this.isRTL?e.right=h.offset().left+h.outerWidth()+1:e.left=h.offset().left-1,this.segPopover=new cb(e),this.segPopover.show()},renderSegPopoverContent:function(b,c){var d,e=this.view,f=e.opt("theme"),g=b.start.format(e.opt("dayPopoverFormat")),h=a('<div class="fc-header '+e.widgetHeaderClass+'"><span class="fc-close '+(f?"ui-icon ui-icon-closethick":"fc-icon fc-icon-x")+'"></span><span class="fc-title">'+Y(g)+'</span><div class="fc-clear"/></div><div class="fc-body '+e.widgetContentClass+'"><div class="fc-event-container"></div></div>'),i=h.find(".fc-event-container");for(c=this.renderFgSegEls(c,!0),this.popoverSegs=c,d=0;d<c.length;d++)c[d].cell=b,i.append(c[d].el);return h},resliceDaySegs:function(b,c){var d=a.map(b,function(a){return a.event}),e=c.clone().stripTime(),f=e.clone().add(1,"days"),g={start:e,end:f};return b=this.eventsToSegs(d,function(a){var b=E(a,g);return b?[b]:[]}),this.sortSegs(b),b},getMoreLinkText:function(a){var b=this.view.opt("eventLimitText");return"function"==typeof b?b(a):"+"+a+" "+b},getCellSegs:function(a,b){for(var c,d=this.rowStructs[a.row].segMatrix,e=b||0,f=[];e<d.length;)c=d[e][a.col],c&&f.push(c),e++;return f}});var lb=jb.extend({slotDuration:null,snapDuration:null,minTime:null,maxTime:null,colDates:null,labelFormat:null,labelInterval:null,dayEls:null,slatEls:null,slatTops:null,helperEl:null,businessHourSegs:null,constructor:function(){jb.apply(this,arguments),this.processOptions()},renderDates:function(){this.el.html(this.renderHtml()),this.dayEls=this.el.find(".fc-day"),this.slatEls=this.el.find(".fc-slats tr")},renderBusinessHours:function(){var a=this.view.calendar.getBusinessHoursEvents();this.businessHourSegs=this.renderFill("businessHours",this.eventsToSegs(a),"bgevent")},renderHtml:function(){return'<div class="fc-bg"><table>'+this.rowHtml("slotBg")+'</table></div><div class="fc-slats"><table>'+this.slatRowHtml()+"</table></div>"},slotBgCellHtml:function(a){return this.bgCellHtml(a)},slatRowHtml:function(){for(var a,c,d,e=this.view,f=this.isRTL,g="",h=b.duration(+this.minTime);h<this.maxTime;)a=this.start.clone().time(h),c=ba(L(h,this.labelInterval)),d='<td class="fc-axis fc-time '+e.widgetContentClass+'" '+e.axisStyleAttr()+">"+(c?"<span>"+Y(a.format(this.labelFormat))+"</span>":"")+"</td>",g+="<tr "+(c?"":'class="fc-minor"')+">"+(f?"":d)+'<td class="'+e.widgetContentClass+'"/>'+(f?d:"")+"</tr>",h.add(this.slotDuration);return g},processOptions:function(){var c,d=this.view,e=d.opt("slotDuration"),f=d.opt("snapDuration");e=b.duration(e),f=f?b.duration(f):e,this.slotDuration=e,this.snapDuration=f,this.cellDuration=f,this.minTime=b.duration(d.opt("minTime")),this.maxTime=b.duration(d.opt("maxTime")),c=d.opt("slotLabelFormat"),a.isArray(c)&&(c=c[c.length-1]),this.labelFormat=c||d.opt("axisFormat")||d.opt("smallTimeFormat"),c=d.opt("slotLabelInterval"),this.labelInterval=c?b.duration(c):this.computeLabelInterval(e)},computeLabelInterval:function(a){var c,d,e;for(c=yb.length-1;c>=0;c--)if(d=b.duration(yb[c]),e=L(d,a),ba(e)&&e>1)return d;return b.duration(a)},computeColHeadFormat:function(){return this.colCnt>1?this.view.opt("dayOfMonthFormat"):"dddd"},computeEventTimeFormat:function(){return this.view.opt("noMeridiemTimeFormat")},computeDisplayEventEnd:function(){return!0},rangeUpdated:function(){var a,b=this.view,c=[];for(a=this.start.clone();a.isBefore(this.end);)c.push(a.clone()),a.add(1,"day"),a=b.skipHiddenDays(a);this.isRTL&&c.reverse(),this.colDates=c,this.colCnt=c.length,this.rowCnt=Math.ceil((this.maxTime-this.minTime)/this.snapDuration)},computeCellDate:function(a){var b=this.colDates[a.col],c=this.computeSnapTime(a.row);return b=this.view.calendar.rezoneDate(b),b.time(c),b},getColEl:function(a){return this.dayEls.eq(a)},computeSnapTime:function(a){return b.duration(this.minTime+this.snapDuration*a)},rangeToSegs:function(a){var b,c,d,e,f=this.colCnt,g=[];for(a={start:a.start.clone().stripZone(),end:a.end.clone().stripZone()},c=0;f>c;c++)d=this.colDates[c],e={start:d.clone().time(this.minTime),end:d.clone().time(this.maxTime)},b=E(a,e),b&&(b.col=c,g.push(b));return g},updateSize:function(a){this.computeSlatTops(),a&&this.updateSegVerticals()},computeRowCoords:function(){var a,b,c=this.el.offset().top,d=[];for(a=0;a<this.rowCnt;a++)b={top:c+this.computeTimeTop(this.computeSnapTime(a))},a>0&&(d[a-1].bottom=b.top),d.push(b);return b.bottom=b.top+this.computeTimeTop(this.computeSnapTime(a)),d},computeDateTop:function(a,c){return this.computeTimeTop(b.duration(a.clone().stripZone()-c.clone().stripTime()))},computeTimeTop:function(a){var b,c,d,e,f=(a-this.minTime)/this.slotDuration;return f=Math.max(0,f),f=Math.min(this.slatEls.length,f),b=Math.floor(f),c=f-b,d=this.slatTops[b],c?(e=this.slatTops[b+1],d+(e-d)*c):d},computeSlatTops:function(){var b,c=[];this.slatEls.each(function(d,e){b=a(e).position().top,c.push(b)}),c.push(b+this.slatEls.last().outerHeight()),this.slatTops=c},renderDrag:function(a,b){return b?(this.renderRangeHelper(a,b),this.applyDragOpacity(this.helperEl),!0):void this.renderHighlight(this.eventRangeToSegs(a))},unrenderDrag:function(){this.unrenderHelper(),this.unrenderHighlight()},renderEventResize:function(a,b){this.renderRangeHelper(a,b)},unrenderEventResize:function(){this.unrenderHelper()},renderHelper:function(b,c){var d,e,f,g,h=this.eventsToSegs([b]);for(h=this.renderFgSegEls(h),d=this.renderSegTable(h),e=0;e<h.length;e++)f=h[e],c&&c.col===f.col&&(g=c.el,f.el.css({left:g.css("left"),right:g.css("right"),"margin-left":g.css("margin-left"),"margin-right":g.css("margin-right")}));this.helperEl=a('<div class="fc-helper-skeleton"/>').append(d).appendTo(this.el)},unrenderHelper:function(){this.helperEl&&(this.helperEl.remove(),this.helperEl=null)},renderSelection:function(a){this.view.opt("selectHelper")?this.renderRangeHelper(a):this.renderHighlight(this.selectionRangeToSegs(a))},unrenderSelection:function(){this.unrenderHelper(),this.unrenderHighlight()},renderFill:function(b,c,d){var e,f,g,h,i,j,k,l,m,n;if(c.length){for(c=this.renderFillSegEls(b,c),e=this.groupSegCols(c),d=d||b.toLowerCase(),f=a('<div class="fc-'+d+'-skeleton"><table><tr/></table></div>'),g=f.find("tr"),h=0;h<e.length;h++)if(i=e[h],j=a("<td/>").appendTo(g),i.length)for(k=a('<div class="fc-'+d+'-container"/>').appendTo(j),l=this.colDates[h],m=0;m<i.length;m++)n=i[m],k.append(n.el.css({top:this.computeDateTop(n.start,l),bottom:-this.computeDateTop(n.end,l)}));this.bookendCells(g,b),this.el.append(f),this.elsByFill[b]=f}return c}});lb.mixin({eventSkeletonEl:null,renderFgSegs:function(b){return b=this.renderFgSegEls(b),this.el.append(this.eventSkeletonEl=a('<div class="fc-content-skeleton"/>').append(this.renderSegTable(b))),b},unrenderFgSegs:function(a){this.eventSkeletonEl&&(this.eventSkeletonEl.remove(),this.eventSkeletonEl=null)},renderSegTable:function(b){var c,d,e,f,g,h,i=a("<table><tr/></table>"),j=i.find("tr");for(c=this.groupSegCols(b),this.computeSegVerticals(b),f=0;f<c.length;f++){for(g=c[f],this.placeSlotSegs(g),h=a('<div class="fc-event-container"/>'),d=0;d<g.length;d++)e=g[d],e.el.css(this.generateSegPositionCss(e)),e.bottom-e.top<30&&e.el.addClass("fc-short"),h.append(e.el);j.append(a("<td/>").append(h))}return this.bookendCells(j,"eventSkeleton"),i},placeSlotSegs:function(a){var b,c,d;if(this.sortSegs(a),b=Ba(a),Ca(b),c=b[0]){for(d=0;d<c.length;d++)Da(c[d]);for(d=0;d<c.length;d++)this.computeSlotSegCoords(c[d],0,0)}},computeSlotSegCoords:function(a,b,c){var d,e=a.forwardSegs;if(void 0===a.forwardCoord)for(e.length?(this.sortForwardSlotSegs(e),this.computeSlotSegCoords(e[0],b+1,c),a.forwardCoord=e[0].backwardCoord):a.forwardCoord=1,a.backwardCoord=a.forwardCoord-(a.forwardCoord-c)/(b+1),d=0;d<e.length;d++)this.computeSlotSegCoords(e[d],0,a.forwardCoord)},updateSegVerticals:function(){var a,b=(this.segs||[]).concat(this.businessHourSegs||[]);for(this.computeSegVerticals(b),a=0;a<b.length;a++)b[a].el.css(this.generateSegVerticalCss(b[a]))},computeSegVerticals:function(a){var b,c;for(b=0;b<a.length;b++)c=a[b],c.top=this.computeDateTop(c.start,c.start),c.bottom=this.computeDateTop(c.end,c.start)},fgSegHtml:function(a,b){var c,d,e,f=this.view,g=a.event,h=f.isEventDraggable(g),i=!b&&a.isStart&&f.isEventResizableFromStart(g),j=!b&&a.isEnd&&f.isEventResizableFromEnd(g),k=this.getSegClasses(a,h,i||j),l=$(this.getEventSkinCss(g));return k.unshift("fc-time-grid-event","fc-v-event"),f.isMultiDayEvent(g)?(a.isStart||a.isEnd)&&(c=this.getEventTimeText(a),d=this.getEventTimeText(a,"LT"),e=this.getEventTimeText(a,null,!1)):(c=this.getEventTimeText(g),d=this.getEventTimeText(g,"LT"),e=this.getEventTimeText(g,null,!1)),'<a class="'+k.join(" ")+'"'+(g.url?' href="'+Y(g.url)+'"':"")+(l?' style="'+l+'"':"")+'><div class="fc-content">'+(c?'<div class="fc-time" data-start="'+Y(e)+'" data-full="'+Y(d)+'"><span>'+Y(c)+"</span></div>":"")+(g.title?'<div class="fc-title">'+Y(g.title)+"</div>":"")+'</div><div class="fc-bg"/>'+(j?'<div class="fc-resizer fc-end-resizer" />':"")+"</a>"},generateSegPositionCss:function(a){var b,c,d=this.view.opt("slotEventOverlap"),e=a.backwardCoord,f=a.forwardCoord,g=this.generateSegVerticalCss(a);return d&&(f=Math.min(1,e+2*(f-e))),this.isRTL?(b=1-f,c=e):(b=e,c=1-f),g.zIndex=a.level+1,g.left=100*b+"%",g.right=100*c+"%",d&&a.forwardPressure&&(g[this.isRTL?"marginLeft":"marginRight"]=20),g},generateSegVerticalCss:function(a){return{top:a.top,bottom:-a.bottom}},groupSegCols:function(a){var b,c=[];for(b=0;b<this.colCnt;b++)c.push([]);for(b=0;b<a.length;b++)c[a[b].col].push(a[b]);return c},sortForwardSlotSegs:function(a){a.sort(ca(this,"compareForwardSlotSegs"))},compareForwardSlotSegs:function(a,b){return b.forwardPressure-a.forwardPressure||(a.backwardCoord||0)-(b.backwardCoord||0)||this.compareSegs(a,b)}});var mb=Ma.View=ra.extend({type:null,name:null,title:null,calendar:null,options:null,coordMap:null,el:null,displaying:null,isSkeletonRendered:!1,isEventsRendered:!1,start:null,end:null,intervalStart:null,intervalEnd:null,intervalDuration:null,intervalUnit:null,isRTL:!1,isSelected:!1,eventOrderSpecs:null,scrollerEl:null,scrollTop:null,widgetHeaderClass:null,widgetContentClass:null,highlightStateClass:null,nextDayThreshold:null,isHiddenDayHash:null,documentMousedownProxy:null,constructor:function(a,c,d,e){this.calendar=a,this.type=this.name=c,this.options=d,this.intervalDuration=e||b.duration(1,"day"),this.nextDayThreshold=b.duration(this.opt("nextDayThreshold")),this.initThemingProps(),this.initHiddenDays(),this.isRTL=this.opt("isRTL"),this.eventOrderSpecs=A(this.opt("eventOrder")),this.documentMousedownProxy=ca(this,"documentMousedown"),this.initialize()},initialize:function(){},opt:function(a){return this.options[a]},trigger:function(a,b){var c=this.calendar;return c.trigger.apply(c,[a,b||this].concat(Array.prototype.slice.call(arguments,2),[this]))},setDate:function(a){this.setRange(this.computeRange(a))},setRange:function(b){a.extend(this,b),this.updateTitle()},computeRange:function(a){var b,c,d=I(this.intervalDuration),e=a.clone().startOf(d),f=e.clone().add(this.intervalDuration);return/year|month|week|day/.test(d)?(e.stripTime(),f.stripTime()):(e.hasTime()||(e=this.calendar.rezoneDate(e)),f.hasTime()||(f=this.calendar.rezoneDate(f))),b=e.clone(),b=this.skipHiddenDays(b),c=f.clone(),c=this.skipHiddenDays(c,-1,!0),{intervalUnit:d,intervalStart:e,intervalEnd:f,start:b,end:c}},computePrevDate:function(a){return this.massageCurrentDate(a.clone().startOf(this.intervalUnit).subtract(this.intervalDuration),-1)},computeNextDate:function(a){return this.massageCurrentDate(a.clone().startOf(this.intervalUnit).add(this.intervalDuration))},massageCurrentDate:function(a,b){return this.intervalDuration.as("days")<=1&&this.isHiddenDay(a)&&(a=this.skipHiddenDays(a,b),a.startOf("day")),a},updateTitle:function(){this.title=this.computeTitle()},computeTitle:function(){return this.formatRange({start:this.intervalStart,end:this.intervalEnd},this.opt("titleFormat")||this.computeTitleFormat(),this.opt("titleRangeSeparator"))},computeTitleFormat:function(){return"year"==this.intervalUnit?"YYYY":"month"==this.intervalUnit?this.opt("monthYearFormat"):this.intervalDuration.as("days")>1?"ll":"LL"},formatRange:function(a,b,c){var d=a.end;return d.hasTime()||(d=d.clone().subtract(1)),ma(a.start,d,b,c,this.opt("isRTL"))},setElement:function(a){this.el=a,this.bindGlobalHandlers()},removeElement:function(){this.clear(),this.isSkeletonRendered&&(this.unrenderSkeleton(),this.isSkeletonRendered=!1),this.unbindGlobalHandlers(),this.el.remove()},display:function(b){var c=this,d=null;return this.displaying&&(d=this.queryScroll()),this.clear().then(function(){return c.displaying=a.when(c.displayView(b)).then(function(){c.forceScroll(c.computeInitialScroll(d)),c.triggerRender()})})},clear:function(){var b=this,c=this.displaying;return c?c.then(function(){return b.displaying=null,b.clearEvents(),b.clearView()}):a.when()},displayView:function(a){this.isSkeletonRendered||(this.renderSkeleton(),this.isSkeletonRendered=!0),this.setDate(a),this.render&&this.render(),this.renderDates(),this.updateSize(),this.renderBusinessHours()},clearView:function(){this.unselect(),this.triggerUnrender(),this.unrenderBusinessHours(),this.unrenderDates(),this.destroy&&this.destroy()},renderSkeleton:function(){},unrenderSkeleton:function(){},renderDates:function(){},unrenderDates:function(){},renderBusinessHours:function(){},unrenderBusinessHours:function(){},triggerRender:function(){this.trigger("viewRender",this,this,this.el)},triggerUnrender:function(){this.trigger("viewDestroy",this,this,this.el)},bindGlobalHandlers:function(){a(document).on("mousedown",this.documentMousedownProxy)},unbindGlobalHandlers:function(){a(document).off("mousedown",this.documentMousedownProxy)},initThemingProps:function(){var a=this.opt("theme")?"ui":"fc";this.widgetHeaderClass=a+"-widget-header",this.widgetContentClass=a+"-widget-content",this.highlightStateClass=a+"-state-highlight"},updateSize:function(a){var b;a&&(b=this.queryScroll()),this.updateHeight(a),this.updateWidth(a),a&&this.setScroll(b)},updateWidth:function(a){},updateHeight:function(a){var b=this.calendar;this.setHeight(b.getSuggestedViewHeight(),b.isHeightAuto())},setHeight:function(a,b){},computeScrollerHeight:function(a){var b,c,d=this.scrollerEl;return b=this.el.add(d),b.css({position:"relative",left:-1}),c=this.el.outerHeight()-d.height(),b.css({position:"",left:""}),a-c},computeInitialScroll:function(a){return 0},queryScroll:function(){return this.scrollerEl?this.scrollerEl.scrollTop():void 0},setScroll:function(a){return this.scrollerEl?this.scrollerEl.scrollTop(a):void 0},forceScroll:function(a){var b=this;this.setScroll(a),setTimeout(function(){b.setScroll(a)},0)},displayEvents:function(a){var b=this.queryScroll();this.clearEvents(),this.renderEvents(a),this.isEventsRendered=!0,this.setScroll(b),this.triggerEventRender()},clearEvents:function(){this.isEventsRendered&&(this.triggerEventUnrender(),this.destroyEvents&&this.destroyEvents(),this.unrenderEvents(),this.isEventsRendered=!1)},renderEvents:function(a){},unrenderEvents:function(){},triggerEventRender:function(){this.renderedEventSegEach(function(a){this.trigger("eventAfterRender",a.event,a.event,a.el)}),this.trigger("eventAfterAllRender")},triggerEventUnrender:function(){this.renderedEventSegEach(function(a){this.trigger("eventDestroy",a.event,a.event,a.el)})},resolveEventEl:function(b,c){var d=this.trigger("eventRender",b,b,c);return d===!1?c=null:d&&d!==!0&&(c=a(d)),c},showEvent:function(a){this.renderedEventSegEach(function(a){a.el.css("visibility","")},a)},hideEvent:function(a){this.renderedEventSegEach(function(a){a.el.css("visibility","hidden")},a)},renderedEventSegEach:function(a,b){var c,d=this.getEventSegs();for(c=0;c<d.length;c++)b&&d[c].event._id!==b._id||d[c].el&&a.call(this,d[c])},getEventSegs:function(){return[]},isEventDraggable:function(a){var b=a.source||{};return X(a.startEditable,b.startEditable,this.opt("eventStartEditable"),a.editable,b.editable,this.opt("editable"))},reportEventDrop:function(a,b,c,d,e){var f=this.calendar,g=f.mutateEvent(a,b,c),h=function(){g.undo(),f.reportEventChange()};this.triggerEventDrop(a,g.dateDelta,h,d,e),f.reportEventChange()},triggerEventDrop:function(a,b,c,d,e){this.trigger("eventDrop",d[0],a,b,c,e,{})},reportExternalDrop:function(b,c,d,e,f){var g,h,i=b.eventProps;i&&(g=a.extend({},i,c),h=this.calendar.renderEvent(g,b.stick)[0]),this.triggerExternalDrop(h,c,d,e,f)},triggerExternalDrop:function(a,b,c,d,e){this.trigger("drop",c[0],b.start,d,e),a&&this.trigger("eventReceive",null,a)},renderDrag:function(a,b){},unrenderDrag:function(){},isEventResizableFromStart:function(a){return this.opt("eventResizableFromStart")&&this.isEventResizable(a)},isEventResizableFromEnd:function(a){return this.isEventResizable(a)},isEventResizable:function(a){var b=a.source||{};return X(a.durationEditable,b.durationEditable,this.opt("eventDurationEditable"),a.editable,b.editable,this.opt("editable"))},reportEventResize:function(a,b,c,d,e){var f=this.calendar,g=f.mutateEvent(a,b,c),h=function(){g.undo(),f.reportEventChange()};this.triggerEventResize(a,g.durationDelta,h,d,e),f.reportEventChange()},triggerEventResize:function(a,b,c,d,e){this.trigger("eventResize",d[0],a,b,c,e,{})},select:function(a,b){this.unselect(b),this.renderSelection(a),this.reportSelection(a,b)},renderSelection:function(a){},reportSelection:function(a,b){this.isSelected=!0,this.triggerSelect(a,b)},triggerSelect:function(a,b){this.trigger("select",null,a.start,a.end,b)},unselect:function(a){this.isSelected&&(this.isSelected=!1,this.destroySelection&&this.destroySelection(),this.unrenderSelection(),this.trigger("unselect",null,a))},unrenderSelection:function(){},documentMousedown:function(b){var c;this.isSelected&&this.opt("unselectAuto")&&v(b)&&(c=this.opt("unselectCancel"),c&&a(b.target).closest(c).length||this.unselect(b))},triggerDayClick:function(a,b,c){this.trigger("dayClick",b,a.start,c)},initHiddenDays:function(){var b,c=this.opt("hiddenDays")||[],d=[],e=0;for(this.opt("weekends")===!1&&c.push(0,6),b=0;7>b;b++)(d[b]=-1!==a.inArray(b,c))||e++;if(!e)throw"invalid hiddenDays";this.isHiddenDayHash=d},isHiddenDay:function(a){return b.isMoment(a)&&(a=a.day()),this.isHiddenDayHash[a]},skipHiddenDays:function(a,b,c){var d=a.clone();for(b=b||1;this.isHiddenDayHash[(d.day()+(c?b:0)+7)%7];)d.add(b,"days");return d},computeDayRange:function(a){var b,c=a.start.clone().stripTime(),d=a.end,e=null;return d&&(e=d.clone().stripTime(),b=+d.time(),b&&b>=this.nextDayThreshold&&e.add(1,"days")),(!d||c>=e)&&(e=c.clone().add(1,"days")),{start:c,end:e}},isMultiDayEvent:function(a){var b=this.computeDayRange(a);return b.end.diff(b.start,"days")>1}}),nb=Ma.Calendar=ra.extend({dirDefaults:null,langDefaults:null,overrides:null,options:null,viewSpecCache:null,view:null,header:null,loadingLevel:0,constructor:Ga,initialize:function(){},initOptions:function(a){var b,e,f,g;a=d(a),b=a.lang,e=ob[b],e||(b=nb.defaults.lang,e=ob[b]||{}),f=X(a.isRTL,e.isRTL,nb.defaults.isRTL),g=f?nb.rtlDefaults:{},this.dirDefaults=g,this.langDefaults=e,this.overrides=a,this.options=c([nb.defaults,g,e,a]),Ha(this.options),this.viewSpecCache={}},getViewSpec:function(a){var b=this.viewSpecCache;return b[a]||(b[a]=this.buildViewSpec(a))},getUnitViewSpec:function(b){var c,d,e;if(-1!=a.inArray(b,Ra))for(c=this.header.getViewsWithButtons(),a.each(Ma.views,function(a){c.push(a)}),d=0;d<c.length;d++)if(e=this.getViewSpec(c[d]),e&&e.singleUnit==b)return e},buildViewSpec:function(a){for(var d,e,f,g,h=this.overrides.views||{},i=[],j=[],k=[],l=a;l;)d=Na[l],e=h[l],l=null,"function"==typeof d&&(d={"class":d}),d&&(i.unshift(d),j.unshift(d.defaults||{}),f=f||d.duration,l=l||d.type),e&&(k.unshift(e),f=f||e.duration,l=l||e.type);return d=Q(i),d.type=a,d["class"]?(f&&(f=b.duration(f),f.valueOf()&&(d.duration=f,g=I(f),1===f.as(g)&&(d.singleUnit=g,k.unshift(h[g]||{})))),d.defaults=c(j),d.overrides=c(k),this.buildViewSpecOptions(d),this.buildViewSpecButtonText(d,a),d):!1},buildViewSpecOptions:function(a){a.options=c([nb.defaults,a.defaults,this.dirDefaults,this.langDefaults,this.overrides,a.overrides]),Ha(a.options)},buildViewSpecButtonText:function(a,b){function c(c){var d=c.buttonText||{};return d[b]||(a.singleUnit?d[a.singleUnit]:null)}a.buttonTextOverride=c(this.overrides)||a.overrides.buttonText,a.buttonTextDefault=c(this.langDefaults)||c(this.dirDefaults)||a.defaults.buttonText||c(nb.defaults)||(a.duration?this.humanizeDuration(a.duration):null)||b},instantiateView:function(a){var b=this.getViewSpec(a);return new b["class"](this,a,b.options,b.duration)},isValidViewType:function(a){return Boolean(this.getViewSpec(a))},pushLoading:function(){this.loadingLevel++||this.trigger("loading",null,!0,this.view)},popLoading:function(){--this.loadingLevel||this.trigger("loading",null,!1,this.view)},buildSelectRange:function(a,b){return a=this.moment(a),b=b?this.moment(b):a.hasTime()?a.clone().add(this.defaultTimedEventDuration):a.clone().add(this.defaultAllDayEventDuration),{start:a,end:b}}});nb.mixin(bb),nb.defaults={titleRangeSeparator:" — ",monthYearFormat:"MMMM YYYY",defaultTimedEventDuration:"02:00:00",defaultAllDayEventDuration:{days:1},forceEventDuration:!1,nextDayThreshold:"09:00:00",defaultView:"month",aspectRatio:1.35,header:{left:"title",center:"",right:"today prev,next"},weekends:!0,weekNumbers:!1,weekNumberTitle:"W",weekNumberCalculation:"local",scrollTime:"06:00:00",lazyFetching:!0,startParam:"start",endParam:"end",timezoneParam:"timezone",timezone:!1,isRTL:!1,buttonText:{prev:"prev",next:"next",prevYear:"prev year",nextYear:"next year",year:"year",today:"today",month:"month",week:"week",day:"day"},buttonIcons:{prev:"left-single-arrow",next:"right-single-arrow",prevYear:"left-double-arrow",nextYear:"right-double-arrow"},theme:!1,themeButtonIcons:{prev:"circle-triangle-w",next:"circle-triangle-e",prevYear:"seek-prev",nextYear:"seek-next"},dragOpacity:.75,dragRevertDuration:500,dragScroll:!0,unselectAuto:!0,dropAccept:"*",eventOrder:"title",eventLimit:!1,eventLimitText:"more",eventLimitClick:"popover",dayPopoverFormat:"LL",handleWindowResize:!0,windowResizeDelay:200},nb.englishDefaults={dayPopoverFormat:"dddd, MMMM D"},nb.rtlDefaults={header:{left:"next,prev today",center:"",right:"title"},buttonIcons:{prev:"right-single-arrow",next:"left-single-arrow",prevYear:"right-double-arrow",nextYear:"left-double-arrow"},themeButtonIcons:{prev:"circle-triangle-e",next:"circle-triangle-w",nextYear:"seek-prev",prevYear:"seek-next"}};var ob=Ma.langs={};Ma.datepickerLang=function(b,c,d){var e=ob[b]||(ob[b]={});e.isRTL=d.isRTL,e.weekNumberTitle=d.weekHeader,a.each(pb,function(a,b){e[a]=b(d)}),a.datepicker&&(a.datepicker.regional[c]=a.datepicker.regional[b]=d,a.datepicker.regional.en=a.datepicker.regional[""],a.datepicker.setDefaults(d))},Ma.lang=function(b,d){var e,f;e=ob[b]||(ob[b]={}),d&&(e=ob[b]=c([e,d])),f=Ia(b),a.each(qb,function(a,b){null==e[a]&&(e[a]=b(f,e))}),nb.defaults.lang=b};var pb={buttonText:function(a){return{prev:Z(a.prevText),next:Z(a.nextText),today:Z(a.currentText)}},monthYearFormat:function(a){return a.showMonthAfterYear?"YYYY["+a.yearSuffix+"] MMMM":"MMMM YYYY["+a.yearSuffix+"]"}},qb={dayOfMonthFormat:function(a,b){var c=a.longDateFormat("l");return c=c.replace(/^Y+[^\w\s]*|[^\w\s]*Y+$/g,""),b.isRTL?c+=" ddd":c="ddd "+c,c},mediumTimeFormat:function(a){return a.longDateFormat("LT").replace(/\s*a$/i,"a")},smallTimeFormat:function(a){return a.longDateFormat("LT").replace(":mm","(:mm)").replace(/(\Wmm)$/,"($1)").replace(/\s*a$/i,"a")},extraSmallTimeFormat:function(a){return a.longDateFormat("LT").replace(":mm","(:mm)").replace(/(\Wmm)$/,"($1)").replace(/\s*a$/i,"t")},hourFormat:function(a){return a.longDateFormat("LT").replace(":mm","").replace(/(\Wmm)$/,"").replace(/\s*a$/i,"a")},noMeridiemTimeFormat:function(a){return a.longDateFormat("LT").replace(/\s*a$/i,"")}},rb={smallDayDateFormat:function(a){return a.isRTL?"D dd":"dd D"},weekFormat:function(a){return a.isRTL?"w[ "+a.weekNumberTitle+"]":"["+a.weekNumberTitle+" ]w"},smallWeekFormat:function(a){return a.isRTL?"w["+a.weekNumberTitle+"]":"["+a.weekNumberTitle+"]w"}};Ma.lang("en",nb.englishDefaults),Ma.sourceNormalizers=[],Ma.sourceFetchers=[];var sb={dataType:"json",cache:!1},tb=1;nb.prototype.getPeerEvents=function(a,b){var c,d,e=this.getEventCache(),f=[];for(c=0;c<e.length;c++)d=e[c],a&&a._id===d._id||f.push(d);return f};var ub=mb.extend({dayGrid:null,dayNumbersVisible:!1,weekNumbersVisible:!1,weekNumberWidth:null,headRowEl:null,initialize:function(){this.dayGrid=new kb(this),this.coordMap=this.dayGrid.coordMap},setRange:function(a){mb.prototype.setRange.call(this,a),this.dayGrid.breakOnWeeks=/year|month|week/.test(this.intervalUnit),this.dayGrid.setRange(a)},computeRange:function(a){var b=mb.prototype.computeRange.call(this,a);return/year|month/.test(b.intervalUnit)&&(b.start.startOf("week"),b.start=this.skipHiddenDays(b.start),b.end.weekday()&&(b.end.add(1,"week").startOf("week"),b.end=this.skipHiddenDays(b.end,-1,!0))),b},renderDates:function(){this.dayNumbersVisible=this.dayGrid.rowCnt>1,this.weekNumbersVisible=this.opt("weekNumbers"),this.dayGrid.numbersVisible=this.dayNumbersVisible||this.weekNumbersVisible,this.el.addClass("fc-basic-view").html(this.renderHtml()),this.headRowEl=this.el.find("thead .fc-row"),this.scrollerEl=this.el.find(".fc-day-grid-container"),this.dayGrid.coordMap.containerEl=this.scrollerEl,this.dayGrid.setElement(this.el.find(".fc-day-grid")),this.dayGrid.renderDates(this.hasRigidRows())},unrenderDates:function(){this.dayGrid.unrenderDates(),this.dayGrid.removeElement()},renderBusinessHours:function(){this.dayGrid.renderBusinessHours()},renderHtml:function(){return'<table><thead class="fc-head"><tr><td class="'+this.widgetHeaderClass+'">'+this.dayGrid.headHtml()+'</td></tr></thead><tbody class="fc-body"><tr><td class="'+this.widgetContentClass+'"><div class="fc-day-grid-container"><div class="fc-day-grid"/></div></td></tr></tbody></table>'},headIntroHtml:function(){return this.weekNumbersVisible?'<th class="fc-week-number '+this.widgetHeaderClass+'" '+this.weekNumberStyleAttr()+"><span>"+Y(this.opt("weekNumberTitle"))+"</span></th>":void 0},numberIntroHtml:function(a){return this.weekNumbersVisible?'<td class="fc-week-number" '+this.weekNumberStyleAttr()+"><span>"+this.dayGrid.getCell(a,0).start.format("w")+"</span></td>":void 0},dayIntroHtml:function(){return this.weekNumbersVisible?'<td class="fc-week-number '+this.widgetContentClass+'" '+this.weekNumberStyleAttr()+"></td>":void 0},introHtml:function(){return this.weekNumbersVisible?'<td class="fc-week-number" '+this.weekNumberStyleAttr()+"></td>":void 0},numberCellHtml:function(a){var b,c=a.start;return this.dayNumbersVisible?(b=this.dayGrid.getDayClasses(c),b.unshift("fc-day-number"),'<td class="'+b.join(" ")+'" data-date="'+c.format()+'">'+c.date()+"</td>"):"<td/>"},weekNumberStyleAttr:function(){return null!==this.weekNumberWidth?'style="width:'+this.weekNumberWidth+'px"':""},hasRigidRows:function(){var a=this.opt("eventLimit");return a&&"number"!=typeof a},updateWidth:function(){this.weekNumbersVisible&&(this.weekNumberWidth=k(this.el.find(".fc-week-number")))},setHeight:function(a,b){var c,d=this.opt("eventLimit");m(this.scrollerEl),f(this.headRowEl),this.dayGrid.removeSegPopover(),d&&"number"==typeof d&&this.dayGrid.limitRows(d),c=this.computeScrollerHeight(a),this.setGridHeight(c,b),d&&"number"!=typeof d&&this.dayGrid.limitRows(d),!b&&l(this.scrollerEl,c)&&(e(this.headRowEl,r(this.scrollerEl)),c=this.computeScrollerHeight(a),this.scrollerEl.height(c))},setGridHeight:function(a,b){b?j(this.dayGrid.rowEls):i(this.dayGrid.rowEls,a,!0)},renderEvents:function(a){this.dayGrid.renderEvents(a),this.updateHeight()},getEventSegs:function(){return this.dayGrid.getEventSegs()},unrenderEvents:function(){this.dayGrid.unrenderEvents()},renderDrag:function(a,b){return this.dayGrid.renderDrag(a,b)},unrenderDrag:function(){this.dayGrid.unrenderDrag()},renderSelection:function(a){this.dayGrid.renderSelection(a)},unrenderSelection:function(){this.dayGrid.unrenderSelection()}}),vb=ub.extend({computeRange:function(a){var b,c=ub.prototype.computeRange.call(this,a);return this.isFixedWeeks()&&(b=Math.ceil(c.end.diff(c.start,"weeks",!0)),c.end.add(6-b,"weeks")),c},setGridHeight:function(a,b){b=b||"variable"===this.opt("weekMode"),b&&(a*=this.rowCnt/6),i(this.dayGrid.rowEls,a,!b)},isFixedWeeks:function(){var a=this.opt("weekMode");return a?"fixed"===a:this.opt("fixedWeekCount")}});Na.basic={"class":ub},Na.basicDay={type:"basic",duration:{days:1}},Na.basicWeek={type:"basic",duration:{weeks:1}},Na.month={"class":vb,duration:{months:1},defaults:{fixedWeekCount:!0}};var wb=mb.extend({timeGrid:null,dayGrid:null,axisWidth:null,noScrollRowEls:null,bottomRuleEl:null,bottomRuleHeight:null,initialize:function(){this.timeGrid=new lb(this),this.opt("allDaySlot")?(this.dayGrid=new kb(this),this.coordMap=new eb([this.dayGrid.coordMap,this.timeGrid.coordMap])):this.coordMap=this.timeGrid.coordMap},setRange:function(a){mb.prototype.setRange.call(this,a),this.timeGrid.setRange(a),this.dayGrid&&this.dayGrid.setRange(a)},renderDates:function(){this.el.addClass("fc-agenda-view").html(this.renderHtml()),this.scrollerEl=this.el.find(".fc-time-grid-container"),this.timeGrid.coordMap.containerEl=this.scrollerEl,this.timeGrid.setElement(this.el.find(".fc-time-grid")),this.timeGrid.renderDates(),this.bottomRuleEl=a('<hr class="fc-divider '+this.widgetHeaderClass+'"/>').appendTo(this.timeGrid.el),this.dayGrid&&(this.dayGrid.setElement(this.el.find(".fc-day-grid")),this.dayGrid.renderDates(),this.dayGrid.bottomCoordPadding=this.dayGrid.el.next("hr").outerHeight()),this.noScrollRowEls=this.el.find(".fc-row:not(.fc-scroller *)")},unrenderDates:function(){this.timeGrid.unrenderDates(),this.timeGrid.removeElement(),this.dayGrid&&(this.dayGrid.unrenderDates(),this.dayGrid.removeElement())},renderBusinessHours:function(){this.timeGrid.renderBusinessHours(),this.dayGrid&&this.dayGrid.renderBusinessHours()},renderHtml:function(){return'<table><thead class="fc-head"><tr><td class="'+this.widgetHeaderClass+'">'+this.timeGrid.headHtml()+'</td></tr></thead><tbody class="fc-body"><tr><td class="'+this.widgetContentClass+'">'+(this.dayGrid?'<div class="fc-day-grid"/><hr class="fc-divider '+this.widgetHeaderClass+'"/>':"")+'<div class="fc-time-grid-container"><div class="fc-time-grid"/></div></td></tr></tbody></table>'},headIntroHtml:function(){var a,b;return this.opt("weekNumbers")?(a=this.timeGrid.getCell(0).start,b=a.format(this.opt("smallWeekFormat")),'<th class="fc-axis fc-week-number '+this.widgetHeaderClass+'" '+this.axisStyleAttr()+"><span>"+Y(b)+"</span></th>"):'<th class="fc-axis '+this.widgetHeaderClass+'" '+this.axisStyleAttr()+"></th>"},dayIntroHtml:function(){return'<td class="fc-axis '+this.widgetContentClass+'" '+this.axisStyleAttr()+"><span>"+(this.opt("allDayHtml")||Y(this.opt("allDayText")))+"</span></td>"},slotBgIntroHtml:function(){return'<td class="fc-axis '+this.widgetContentClass+'" '+this.axisStyleAttr()+"></td>";
|
9 |
+
},introHtml:function(){return'<td class="fc-axis" '+this.axisStyleAttr()+"></td>"},axisStyleAttr:function(){return null!==this.axisWidth?'style="width:'+this.axisWidth+'px"':""},updateSize:function(a){this.timeGrid.updateSize(a),mb.prototype.updateSize.call(this,a)},updateWidth:function(){this.axisWidth=k(this.el.find(".fc-axis"))},setHeight:function(a,b){var c,d;null===this.bottomRuleHeight&&(this.bottomRuleHeight=this.bottomRuleEl.outerHeight()),this.bottomRuleEl.hide(),this.scrollerEl.css("overflow",""),m(this.scrollerEl),f(this.noScrollRowEls),this.dayGrid&&(this.dayGrid.removeSegPopover(),c=this.opt("eventLimit"),c&&"number"!=typeof c&&(c=xb),c&&this.dayGrid.limitRows(c)),b||(d=this.computeScrollerHeight(a),l(this.scrollerEl,d)?(e(this.noScrollRowEls,r(this.scrollerEl)),d=this.computeScrollerHeight(a),this.scrollerEl.height(d)):(this.scrollerEl.height(d).css("overflow","hidden"),this.bottomRuleEl.show()))},computeInitialScroll:function(){var a=b.duration(this.opt("scrollTime")),c=this.timeGrid.computeTimeTop(a);return c=Math.ceil(c),c&&c++,c},renderEvents:function(a){var b,c,d=[],e=[],f=[];for(c=0;c<a.length;c++)a[c].allDay?d.push(a[c]):e.push(a[c]);b=this.timeGrid.renderEvents(e),this.dayGrid&&(f=this.dayGrid.renderEvents(d)),this.updateHeight()},getEventSegs:function(){return this.timeGrid.getEventSegs().concat(this.dayGrid?this.dayGrid.getEventSegs():[])},unrenderEvents:function(){this.timeGrid.unrenderEvents(),this.dayGrid&&this.dayGrid.unrenderEvents()},renderDrag:function(a,b){return a.start.hasTime()?this.timeGrid.renderDrag(a,b):this.dayGrid?this.dayGrid.renderDrag(a,b):void 0},unrenderDrag:function(){this.timeGrid.unrenderDrag(),this.dayGrid&&this.dayGrid.unrenderDrag()},renderSelection:function(a){a.start.hasTime()||a.end.hasTime()?this.timeGrid.renderSelection(a):this.dayGrid&&this.dayGrid.renderSelection(a)},unrenderSelection:function(){this.timeGrid.unrenderSelection(),this.dayGrid&&this.dayGrid.unrenderSelection()}}),xb=5,yb=[{hours:1},{minutes:30},{minutes:15},{seconds:30},{seconds:15}];return Na.agenda={"class":wb,defaults:{allDaySlot:!0,allDayText:"all-day",slotDuration:"00:30:00",minTime:"00:00:00",maxTime:"24:00:00",slotEventOverlap:!0}},Na.agendaDay={type:"agenda",duration:{days:1}},Na.agendaWeek={type:"agenda",duration:{weeks:1}},Ma});
|
backend/{resources/js/ng-edit_appointment_dialog.js → modules/calendar/resources/js/ng-appointment_dialog.js}
RENAMED
@@ -1,18 +1,23 @@
|
|
1 |
;(function() {
|
2 |
|
3 |
-
var module = angular.module('
|
4 |
|
5 |
/**
|
6 |
* DataSource service.
|
7 |
*/
|
8 |
module.factory('dataSource', function($q, $rootScope, $filter) {
|
9 |
var ds = {
|
|
|
10 |
data : {
|
11 |
staff : [],
|
12 |
customers : [],
|
13 |
start_time : [],
|
14 |
end_time : [],
|
15 |
-
time_interval : 900
|
|
|
|
|
|
|
|
|
16 |
},
|
17 |
form : {
|
18 |
id : null,
|
@@ -22,27 +27,33 @@
|
|
22 |
start_time : null,
|
23 |
end_time : null,
|
24 |
customers : [],
|
25 |
-
|
26 |
},
|
27 |
loadData : function() {
|
28 |
var deferred = $q.defer();
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
46 |
return deferred.promise;
|
47 |
},
|
48 |
findStaff : function(id) {
|
@@ -94,8 +105,15 @@
|
|
94 |
},
|
95 |
resetCustomers : function() {
|
96 |
ds.data.customers.forEach(function(customer) {
|
97 |
-
customer.custom_fields
|
|
|
|
|
98 |
customer.number_of_persons = 1;
|
|
|
|
|
|
|
|
|
|
|
99 |
});
|
100 |
},
|
101 |
getDataForEndTime : function() {
|
@@ -143,16 +161,26 @@
|
|
143 |
end_date.setMinutes(end_time[1]);
|
144 |
|
145 |
return {
|
146 |
-
start_date : $filter('date')(start_date, 'yyyy-MM-dd HH:mm:
|
147 |
-
end_date : $filter('date')(end_date, 'yyyy-MM-dd HH:mm:
|
148 |
};
|
149 |
},
|
150 |
getTotalNumberOfPersons : function () {
|
151 |
var result = 0;
|
152 |
-
ds.form.customers.forEach(function(item) {
|
153 |
result += parseInt(item.number_of_persons);
|
154 |
});
|
155 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
156 |
return result;
|
157 |
}
|
158 |
};
|
@@ -161,19 +189,14 @@
|
|
161 |
});
|
162 |
|
163 |
/**
|
164 |
-
* Controller for
|
165 |
*/
|
166 |
module.controller('appointmentDialogCtrl', function($scope, $element, dataSource) {
|
167 |
// Set up initial data.
|
168 |
-
$scope.loading = true;
|
169 |
$scope.$calendar = null;
|
170 |
// Set up data source.
|
171 |
$scope.dataSource = dataSource;
|
172 |
$scope.form = dataSource.form; // shortcut
|
173 |
-
// Populate data source.
|
174 |
-
dataSource.loadData().then(function() {
|
175 |
-
$scope.loading = false;
|
176 |
-
});
|
177 |
// Error messages.
|
178 |
$scope.errors = {};
|
179 |
// Callback to be called after editing appointment.
|
@@ -187,6 +210,7 @@
|
|
187 |
* @param function _callback
|
188 |
*/
|
189 |
$scope.configureNewForm = function(staff_id, start_date, _callback) {
|
|
|
190 |
jQuery.extend($scope.form, {
|
191 |
id : null,
|
192 |
staff : dataSource.findStaff(staff_id),
|
@@ -195,47 +219,74 @@
|
|
195 |
start_time : dataSource.findTime('start', start_date.format('HH:mm')),
|
196 |
end_time : null,
|
197 |
customers : [],
|
198 |
-
|
|
|
199 |
});
|
200 |
$scope.errors = {};
|
201 |
dataSource.setEndTimeBasedOnService();
|
202 |
callback = _callback;
|
203 |
|
204 |
$scope.reInitChosen();
|
|
|
|
|
205 |
$scope.dataSource.resetCustomers();
|
206 |
};
|
207 |
|
208 |
/**
|
209 |
-
* Prepare the form for editing event.
|
210 |
*/
|
211 |
-
$scope.configureEditForm = function(appointment_id,
|
212 |
$scope.loading = true;
|
213 |
jQuery.post(
|
214 |
ajaxurl,
|
215 |
-
{
|
216 |
function(response) {
|
217 |
$scope.$apply(function($scope) {
|
218 |
if (response.success) {
|
|
|
|
|
219 |
jQuery.extend($scope.form, {
|
220 |
id : appointment_id,
|
221 |
-
staff : $scope.dataSource.findStaff(staff_id),
|
222 |
-
service : $scope.dataSource.findService(staff_id, response.data.service_id),
|
223 |
date : start_date.clone().local().toDate(),
|
224 |
start_time : $scope.dataSource.findTime('start', start_date.format('HH:mm')),
|
225 |
end_time : start_date.format('YYYY-MM-DD') == end_date.format('YYYY-MM-DD')
|
226 |
? $scope.dataSource.findTime('end', end_date.format('HH:mm'))
|
227 |
: $scope.dataSource.findTime('end', (24 + end_date.hour()) + end_date.format(':mm')),
|
228 |
-
customers : []
|
|
|
|
|
229 |
});
|
230 |
|
231 |
$scope.reInitChosen();
|
|
|
|
|
232 |
$scope.dataSource.resetCustomers();
|
233 |
|
234 |
-
|
235 |
-
|
236 |
-
customer
|
237 |
-
|
238 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
239 |
});
|
240 |
}
|
241 |
$scope.loading = false;
|
@@ -259,8 +310,8 @@
|
|
259 |
staff_id : $scope.form.staff ? $scope.form.staff.id : null,
|
260 |
service_id : $scope.form.service ? $scope.form.service.id : null
|
261 |
},
|
262 |
-
function(response){
|
263 |
-
$scope.$apply(function($scope) {
|
264 |
$scope.errors = response;
|
265 |
});
|
266 |
},
|
@@ -271,6 +322,8 @@
|
|
271 |
$scope.onServiceChange = function() {
|
272 |
$scope.dataSource.setEndTimeBasedOnService();
|
273 |
$scope.reInitChosen();
|
|
|
|
|
274 |
checkTimeInterval();
|
275 |
};
|
276 |
|
@@ -291,13 +344,26 @@
|
|
291 |
$scope.loading = true;
|
292 |
|
293 |
var dates = $scope.dataSource.getStartAndEndDates(),
|
294 |
-
customers = []
|
295 |
-
|
296 |
-
$scope.form.customers.forEach(function(item, i, arr){
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
297 |
customers.push({
|
298 |
id : item.id,
|
|
|
299 |
custom_fields : item.custom_fields,
|
300 |
-
|
|
|
|
|
|
|
301 |
});
|
302 |
});
|
303 |
|
@@ -311,7 +377,8 @@
|
|
311 |
start_date : dates.start_date,
|
312 |
end_date : dates.end_date,
|
313 |
customers : JSON.stringify(customers),
|
314 |
-
|
|
|
315 |
},
|
316 |
function (response) {
|
317 |
$scope.$apply(function($scope) {
|
@@ -333,12 +400,12 @@
|
|
333 |
};
|
334 |
|
335 |
// On 'Cancel' button click.
|
336 |
-
$scope.closeDialog = function() {
|
337 |
// Close the dialog.
|
338 |
$element.children().modal('hide');
|
339 |
};
|
340 |
|
341 |
-
$scope.reInitChosen = function(){
|
342 |
jQuery('#chosen')
|
343 |
.chosen('destroy')
|
344 |
.chosen({
|
@@ -348,6 +415,10 @@
|
|
348 |
});
|
349 |
};
|
350 |
|
|
|
|
|
|
|
|
|
351 |
/**************************************************************************************************************
|
352 |
* New customer *
|
353 |
**************************************************************************************************************/
|
@@ -362,7 +433,14 @@
|
|
362 |
id : customer.id.toString(),
|
363 |
name : customer.name,
|
364 |
custom_fields : customer.custom_fields,
|
365 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
366 |
};
|
367 |
|
368 |
if (customer.email || customer.phone){
|
@@ -372,109 +450,186 @@
|
|
372 |
dataSource.data.customers.push(new_customer);
|
373 |
|
374 |
// Make it selected.
|
375 |
-
if (!dataSource.form.service || dataSource.form.customers.length < dataSource.form.service.capacity){
|
376 |
dataSource.form.customers.push(new_customer);
|
377 |
}
|
378 |
|
379 |
-
setTimeout(function() { jQuery(
|
380 |
};
|
381 |
|
382 |
$scope.removeCustomer = function(customer) {
|
383 |
-
customer.custom_fields = [];
|
384 |
-
customer.number_of_persons = 1;
|
385 |
$scope.form.customers.splice($scope.form.customers.indexOf(customer), 1);
|
386 |
};
|
387 |
|
388 |
/**************************************************************************************************************
|
389 |
-
*
|
390 |
**************************************************************************************************************/
|
391 |
|
392 |
-
$scope.
|
393 |
-
var $
|
394 |
-
$
|
395 |
-
$
|
|
|
396 |
|
397 |
-
customer.custom_fields.forEach(function(field) {
|
398 |
-
var $
|
399 |
-
switch ($
|
400 |
case 'checkboxes':
|
401 |
-
field.value.forEach(function(value) {
|
402 |
-
$
|
403 |
return this.value == value;
|
404 |
}).prop('checked', true);
|
405 |
});
|
406 |
break;
|
407 |
case 'radio-buttons':
|
408 |
-
$
|
409 |
return this.value == field.value;
|
410 |
}).prop('checked', true);
|
411 |
break;
|
412 |
default:
|
413 |
-
$
|
414 |
break;
|
415 |
}
|
416 |
});
|
417 |
|
|
|
|
|
|
|
|
|
418 |
// Prepare select for number of persons.
|
419 |
-
var $number_of_persons = $
|
|
|
420 |
var max = $scope.form.service
|
421 |
-
? parseInt($scope.form.service.capacity) - $scope.dataSource.
|
422 |
: 1;
|
423 |
$number_of_persons.empty();
|
424 |
-
for (var i = 1; i <= max; ++
|
425 |
-
$number_of_persons.append('<option value="' + i +'">' + i + '</option>');
|
426 |
}
|
427 |
if (customer.number_of_persons > max) {
|
428 |
-
$number_of_persons.append('<option value="' + customer.number_of_persons +'">' + customer.number_of_persons + '</option>');
|
429 |
}
|
430 |
$number_of_persons.val(customer.number_of_persons);
|
431 |
-
|
432 |
-
|
|
|
433 |
$scope.edit_customer = customer;
|
434 |
|
435 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
436 |
};
|
437 |
|
438 |
$scope.saveCustomFields = function() {
|
439 |
var result = [],
|
440 |
-
|
441 |
-
$
|
442 |
-
|
443 |
-
|
444 |
-
|
445 |
-
|
446 |
-
|
447 |
-
|
448 |
-
|
449 |
-
|
450 |
-
|
451 |
-
|
452 |
-
|
453 |
-
|
454 |
-
|
455 |
-
|
456 |
-
|
457 |
-
|
458 |
-
|
|
|
|
|
|
|
|
|
|
|
459 |
}
|
460 |
-
result.push({ id: $this.data('id'), value: value });
|
461 |
});
|
462 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
463 |
$scope.edit_customer.custom_fields = result;
|
464 |
$scope.edit_customer.number_of_persons = $number_of_persons.val();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
465 |
|
466 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
467 |
};
|
468 |
|
469 |
/**
|
470 |
* Datepicker options.
|
471 |
*/
|
472 |
$scope.dateOptions = {
|
473 |
-
dateFormat :
|
474 |
-
dayNamesMin :
|
475 |
-
monthNames :
|
476 |
-
monthNamesShort :
|
477 |
-
firstDay :
|
478 |
};
|
479 |
});
|
480 |
|
@@ -501,12 +656,12 @@
|
|
501 |
module.directive('chosen',function($timeout) {
|
502 |
var linker = function(scope,element,attrs) {
|
503 |
scope.$watch(attrs['chosen'], function() {
|
504 |
-
element.trigger(
|
505 |
});
|
506 |
|
507 |
scope.$watchCollection(attrs['ngModel'], function() {
|
508 |
$timeout(function() {
|
509 |
-
element.trigger(
|
510 |
});
|
511 |
});
|
512 |
|
@@ -520,53 +675,55 @@
|
|
520 |
});
|
521 |
|
522 |
/**
|
523 |
-
* Directive for Popover jQuery plugin
|
524 |
*/
|
525 |
-
module.directive('
|
526 |
return function(scope, element, attrs) {
|
527 |
element.popover({
|
528 |
trigger : 'hover',
|
529 |
-
content :
|
530 |
-
html : true
|
|
|
|
|
531 |
});
|
532 |
};
|
533 |
});
|
534 |
-
|
535 |
})();
|
536 |
|
537 |
/**
|
538 |
* @param int appointment_id
|
539 |
* @param int staff_id
|
540 |
* @param moment start_date
|
541 |
-
* @param moment end_date
|
542 |
* @param function callback
|
543 |
*/
|
544 |
-
var showAppointmentDialog = function (appointment_id, staff_id, start_date,
|
545 |
-
var $
|
546 |
-
|
547 |
$scope.$apply(function ($scope) {
|
548 |
-
|
549 |
-
|
550 |
-
|
551 |
-
title
|
552 |
-
|
553 |
-
|
554 |
-
$scope.
|
555 |
-
|
556 |
-
|
557 |
-
|
|
|
|
|
|
|
558 |
});
|
559 |
|
560 |
-
// hide
|
561 |
-
if (jQuery('#
|
562 |
-
jQuery('#
|
563 |
}
|
564 |
|
565 |
// hide new customer dialog, if it remained opened.
|
566 |
-
if (jQuery('#
|
567 |
-
jQuery('#
|
568 |
}
|
569 |
|
570 |
-
|
571 |
-
|
572 |
-
}
|
1 |
;(function() {
|
2 |
|
3 |
+
var module = angular.module('appointmentDialog', ['ui.date', 'customerDialog', 'paymentDetailsDialog']);
|
4 |
|
5 |
/**
|
6 |
* DataSource service.
|
7 |
*/
|
8 |
module.factory('dataSource', function($q, $rootScope, $filter) {
|
9 |
var ds = {
|
10 |
+
loaded : false,
|
11 |
data : {
|
12 |
staff : [],
|
13 |
customers : [],
|
14 |
start_time : [],
|
15 |
end_time : [],
|
16 |
+
time_interval : 900,
|
17 |
+
status : {
|
18 |
+
items: [],
|
19 |
+
default: null
|
20 |
+
}
|
21 |
},
|
22 |
form : {
|
23 |
id : null,
|
27 |
start_time : null,
|
28 |
end_time : null,
|
29 |
customers : [],
|
30 |
+
notification : null
|
31 |
},
|
32 |
loadData : function() {
|
33 |
var deferred = $q.defer();
|
34 |
+
if (!ds.loaded) {
|
35 |
+
jQuery.get(
|
36 |
+
ajaxurl,
|
37 |
+
{ action : 'ab_get_data_for_appointment_form' },
|
38 |
+
function(data) {
|
39 |
+
ds.loaded = true;
|
40 |
+
ds.data = data;
|
41 |
+
// Add empty element to beginning of array for single-select customer form
|
42 |
+
ds.data.customers.unshift({name: ''});
|
43 |
+
|
44 |
+
if (data.staff.length) {
|
45 |
+
ds.form.staff = data.staff[0];
|
46 |
+
}
|
47 |
+
ds.form.start_time = data.start_time[0];
|
48 |
+
ds.form.end_time = data.end_time[1];
|
49 |
+
deferred.resolve();
|
50 |
+
},
|
51 |
+
'json'
|
52 |
+
);
|
53 |
+
} else {
|
54 |
+
deferred.resolve();
|
55 |
+
}
|
56 |
+
|
57 |
return deferred.promise;
|
58 |
},
|
59 |
findStaff : function(id) {
|
105 |
},
|
106 |
resetCustomers : function() {
|
107 |
ds.data.customers.forEach(function(customer) {
|
108 |
+
customer.custom_fields = [];
|
109 |
+
customer.extras = [];
|
110 |
+
customer.status = ds.data.status.default;
|
111 |
customer.number_of_persons = 1;
|
112 |
+
customer.compound_token = null;
|
113 |
+
customer.location_id = null;
|
114 |
+
customer.payment_id = null;
|
115 |
+
customer.payment_type = null;
|
116 |
+
customer.payment_title = null;
|
117 |
});
|
118 |
},
|
119 |
getDataForEndTime : function() {
|
161 |
end_date.setMinutes(end_time[1]);
|
162 |
|
163 |
return {
|
164 |
+
start_date : $filter('date')(start_date, 'yyyy-MM-dd HH:mm:00'),
|
165 |
+
end_date : $filter('date')(end_date, 'yyyy-MM-dd HH:mm:00')
|
166 |
};
|
167 |
},
|
168 |
getTotalNumberOfPersons : function () {
|
169 |
var result = 0;
|
170 |
+
ds.form.customers.forEach(function (item) {
|
171 |
result += parseInt(item.number_of_persons);
|
172 |
});
|
173 |
|
174 |
+
return result;
|
175 |
+
},
|
176 |
+
getTotalNotCancelledNumberOfPersons: function () {
|
177 |
+
var result = 0;
|
178 |
+
ds.form.customers.forEach(function (item) {
|
179 |
+
if (item.status != 'cancelled') {
|
180 |
+
result += parseInt(item.number_of_persons);
|
181 |
+
}
|
182 |
+
});
|
183 |
+
|
184 |
return result;
|
185 |
}
|
186 |
};
|
189 |
});
|
190 |
|
191 |
/**
|
192 |
+
* Controller for 'create/edit appointment' dialog form.
|
193 |
*/
|
194 |
module.controller('appointmentDialogCtrl', function($scope, $element, dataSource) {
|
195 |
// Set up initial data.
|
|
|
196 |
$scope.$calendar = null;
|
197 |
// Set up data source.
|
198 |
$scope.dataSource = dataSource;
|
199 |
$scope.form = dataSource.form; // shortcut
|
|
|
|
|
|
|
|
|
200 |
// Error messages.
|
201 |
$scope.errors = {};
|
202 |
// Callback to be called after editing appointment.
|
210 |
* @param function _callback
|
211 |
*/
|
212 |
$scope.configureNewForm = function(staff_id, start_date, _callback) {
|
213 |
+
|
214 |
jQuery.extend($scope.form, {
|
215 |
id : null,
|
216 |
staff : dataSource.findStaff(staff_id),
|
219 |
start_time : dataSource.findTime('start', start_date.format('HH:mm')),
|
220 |
end_time : null,
|
221 |
customers : [],
|
222 |
+
notification : 'no',
|
223 |
+
internal_note : null
|
224 |
});
|
225 |
$scope.errors = {};
|
226 |
dataSource.setEndTimeBasedOnService();
|
227 |
callback = _callback;
|
228 |
|
229 |
$scope.reInitChosen();
|
230 |
+
$scope.prepareExtras();
|
231 |
+
$scope.prepareCustomFields();
|
232 |
$scope.dataSource.resetCustomers();
|
233 |
};
|
234 |
|
235 |
/**
|
236 |
+
* Prepare the form for editing an event.
|
237 |
*/
|
238 |
+
$scope.configureEditForm = function(appointment_id, _callback) {
|
239 |
$scope.loading = true;
|
240 |
jQuery.post(
|
241 |
ajaxurl,
|
242 |
+
{action: 'ab_get_data_for_appointment', id: appointment_id},
|
243 |
function(response) {
|
244 |
$scope.$apply(function($scope) {
|
245 |
if (response.success) {
|
246 |
+
var start_date = moment(response.data.start_date),
|
247 |
+
end_date = moment(response.data.end_date);
|
248 |
jQuery.extend($scope.form, {
|
249 |
id : appointment_id,
|
250 |
+
staff : $scope.dataSource.findStaff(response.data.staff_id),
|
251 |
+
service : $scope.dataSource.findService(response.data.staff_id, response.data.service_id),
|
252 |
date : start_date.clone().local().toDate(),
|
253 |
start_time : $scope.dataSource.findTime('start', start_date.format('HH:mm')),
|
254 |
end_time : start_date.format('YYYY-MM-DD') == end_date.format('YYYY-MM-DD')
|
255 |
? $scope.dataSource.findTime('end', end_date.format('HH:mm'))
|
256 |
: $scope.dataSource.findTime('end', (24 + end_date.hour()) + end_date.format(':mm')),
|
257 |
+
customers : [],
|
258 |
+
notification : 'no',
|
259 |
+
internal_note : response.data.internal_note
|
260 |
});
|
261 |
|
262 |
$scope.reInitChosen();
|
263 |
+
$scope.prepareExtras();
|
264 |
+
$scope.prepareCustomFields();
|
265 |
$scope.dataSource.resetCustomers();
|
266 |
|
267 |
+
var customers_ids = [];
|
268 |
+
response.data.customers.forEach(function (item, i, arr) {
|
269 |
+
var customer = $scope.dataSource.findCustomer(item.id),
|
270 |
+
clone = {};
|
271 |
+
if (customers_ids.indexOf(item.id) === -1) {
|
272 |
+
customers_ids.push(item.id);
|
273 |
+
clone = customer;
|
274 |
+
} else {
|
275 |
+
// For Error: ngRepeat:dupes & chosen directive
|
276 |
+
angular.copy(customer, clone);
|
277 |
+
}
|
278 |
+
clone.ca_id = item.ca_id;
|
279 |
+
clone.extras = item.extras;
|
280 |
+
clone.status = item.status;
|
281 |
+
clone.custom_fields = item.custom_fields;
|
282 |
+
clone.number_of_persons = item.number_of_persons;
|
283 |
+
clone.location_id = item.location_id;
|
284 |
+
clone.payment_id = item.payment_id;
|
285 |
+
clone.payment_type = item.payment_type;
|
286 |
+
clone.payment_title = item.payment_title;
|
287 |
+
clone.compound_token = item.compound_token;
|
288 |
+
clone.compound_service = item.compound_service;
|
289 |
+
$scope.form.customers.push(clone);
|
290 |
});
|
291 |
}
|
292 |
$scope.loading = false;
|
310 |
staff_id : $scope.form.staff ? $scope.form.staff.id : null,
|
311 |
service_id : $scope.form.service ? $scope.form.service.id : null
|
312 |
},
|
313 |
+
function (response) {
|
314 |
+
$scope.$apply(function ($scope) {
|
315 |
$scope.errors = response;
|
316 |
});
|
317 |
},
|
322 |
$scope.onServiceChange = function() {
|
323 |
$scope.dataSource.setEndTimeBasedOnService();
|
324 |
$scope.reInitChosen();
|
325 |
+
$scope.prepareExtras();
|
326 |
+
$scope.prepareCustomFields();
|
327 |
checkTimeInterval();
|
328 |
};
|
329 |
|
344 |
$scope.loading = true;
|
345 |
|
346 |
var dates = $scope.dataSource.getStartAndEndDates(),
|
347 |
+
customers = []
|
348 |
+
;
|
349 |
+
$scope.form.customers.forEach(function (item, i, arr) {
|
350 |
+
var customer_extras = {};
|
351 |
+
if ($scope.form.service) {
|
352 |
+
jQuery('#bookly-extras .service_' + $scope.form.service.id + ' input.extras-count').each(function () {
|
353 |
+
var extra_id = jQuery(this).data('id');
|
354 |
+
if (item.extras[extra_id] !== undefined) {
|
355 |
+
customer_extras[extra_id] = item.extras[extra_id];
|
356 |
+
}
|
357 |
+
});
|
358 |
+
}
|
359 |
customers.push({
|
360 |
id : item.id,
|
361 |
+
ca_id : item.ca_id,
|
362 |
custom_fields : item.custom_fields,
|
363 |
+
extras : customer_extras,
|
364 |
+
location_id : item.location_id,
|
365 |
+
number_of_persons : item.number_of_persons,
|
366 |
+
status : item.status
|
367 |
});
|
368 |
});
|
369 |
|
377 |
start_date : dates.start_date,
|
378 |
end_date : dates.end_date,
|
379 |
customers : JSON.stringify(customers),
|
380 |
+
notification : $scope.form.notification,
|
381 |
+
internal_note : $scope.form.internal_note
|
382 |
},
|
383 |
function (response) {
|
384 |
$scope.$apply(function($scope) {
|
400 |
};
|
401 |
|
402 |
// On 'Cancel' button click.
|
403 |
+
$scope.closeDialog = function () {
|
404 |
// Close the dialog.
|
405 |
$element.children().modal('hide');
|
406 |
};
|
407 |
|
408 |
+
$scope.reInitChosen = function () {
|
409 |
jQuery('#chosen')
|
410 |
.chosen('destroy')
|
411 |
.chosen({
|
415 |
});
|
416 |
};
|
417 |
|
418 |
+
$scope.statusToString = function (status) {
|
419 |
+
return dataSource.data.status.items[status];
|
420 |
+
};
|
421 |
+
|
422 |
/**************************************************************************************************************
|
423 |
* New customer *
|
424 |
**************************************************************************************************************/
|
433 |
id : customer.id.toString(),
|
434 |
name : customer.name,
|
435 |
custom_fields : customer.custom_fields,
|
436 |
+
extras : customer.extras,
|
437 |
+
status : customer.status,
|
438 |
+
number_of_persons : 1,
|
439 |
+
compound_token : null,
|
440 |
+
location_id : null,
|
441 |
+
payment_id : null,
|
442 |
+
payment_type : null,
|
443 |
+
payment_title : null
|
444 |
};
|
445 |
|
446 |
if (customer.email || customer.phone){
|
450 |
dataSource.data.customers.push(new_customer);
|
451 |
|
452 |
// Make it selected.
|
453 |
+
if (!dataSource.form.service || dataSource.form.customers.length < dataSource.form.service.capacity) {
|
454 |
dataSource.form.customers.push(new_customer);
|
455 |
}
|
456 |
|
457 |
+
setTimeout(function() { jQuery('#chosen').trigger('chosen:updated'); }, 0);
|
458 |
};
|
459 |
|
460 |
$scope.removeCustomer = function(customer) {
|
|
|
|
|
461 |
$scope.form.customers.splice($scope.form.customers.indexOf(customer), 1);
|
462 |
};
|
463 |
|
464 |
/**************************************************************************************************************
|
465 |
+
* Customer Details *
|
466 |
**************************************************************************************************************/
|
467 |
|
468 |
+
$scope.editCustomerDetails = function(customer) {
|
469 |
+
var $dialog = jQuery('#bookly-customer-details-dialog');
|
470 |
+
$dialog.find('input.ab-custom-field:text, textarea.ab-custom-field, select.ab-custom-field').val('');
|
471 |
+
$dialog.find('input.ab-custom-field:checkbox, input.ab-custom-field:radio').prop('checked', false);
|
472 |
+
$dialog.find('#bookly-extras :checkbox').prop('checked', false);
|
473 |
|
474 |
+
customer.custom_fields.forEach(function (field) {
|
475 |
+
var $custom_field = $dialog.find('#ab--custom-fields > *[data-id="' + field.id + '"]');
|
476 |
+
switch ($custom_field.data('type')) {
|
477 |
case 'checkboxes':
|
478 |
+
field.value.forEach(function (value) {
|
479 |
+
$custom_field.find('.ab-custom-field').filter(function () {
|
480 |
return this.value == value;
|
481 |
}).prop('checked', true);
|
482 |
});
|
483 |
break;
|
484 |
case 'radio-buttons':
|
485 |
+
$custom_field.find('.ab-custom-field').filter(function () {
|
486 |
return this.value == field.value;
|
487 |
}).prop('checked', true);
|
488 |
break;
|
489 |
default:
|
490 |
+
$custom_field.find('.ab-custom-field').val(field.value);
|
491 |
break;
|
492 |
}
|
493 |
});
|
494 |
|
495 |
+
angular.forEach(customer.extras, function (extra_count, extra_id) {
|
496 |
+
$dialog.find('#bookly-extras .extras-count[data-id="' + extra_id + '"]').val(extra_count);
|
497 |
+
});
|
498 |
+
|
499 |
// Prepare select for number of persons.
|
500 |
+
var $number_of_persons = $dialog.find('#ab-edit-number-of-persons');
|
501 |
+
|
502 |
var max = $scope.form.service
|
503 |
+
? parseInt($scope.form.service.capacity) - $scope.dataSource.getTotalNotCancelledNumberOfPersons() + ( customer.status != 'cancelled' ? parseInt(customer.number_of_persons) : 0 )
|
504 |
: 1;
|
505 |
$number_of_persons.empty();
|
506 |
+
for (var i = 1; i <= max; ++i) {
|
507 |
+
$number_of_persons.append('<option value="' + i + '">' + i + '</option>');
|
508 |
}
|
509 |
if (customer.number_of_persons > max) {
|
510 |
+
$number_of_persons.append('<option value="' + customer.number_of_persons + '">' + customer.number_of_persons + '</option>');
|
511 |
}
|
512 |
$number_of_persons.val(customer.number_of_persons);
|
513 |
+
$dialog.find('#ab-appointment-status').val(customer.status);
|
514 |
+
$dialog.find('#ab-appointment-location').val(customer.location_id);
|
515 |
+
$dialog.find('#ab-deposit-due').val(customer.due);
|
516 |
$scope.edit_customer = customer;
|
517 |
|
518 |
+
$dialog.modal({show: true, backdrop: false})
|
519 |
+
.on('hidden.bs.modal', function () {
|
520 |
+
jQuery('body').addClass('modal-open');
|
521 |
+
});
|
522 |
+
};
|
523 |
+
|
524 |
+
$scope.prepareExtras = function () {
|
525 |
+
if ($scope.form.service) {
|
526 |
+
jQuery('#bookly-extras > *').hide();
|
527 |
+
var $service_extras = jQuery('#bookly-extras .service_' + $scope.form.service.id);
|
528 |
+
if ($service_extras.length) {
|
529 |
+
$service_extras.show();
|
530 |
+
jQuery('#bookly-extras').show();
|
531 |
+
} else {
|
532 |
+
jQuery('#bookly-extras').hide();
|
533 |
+
}
|
534 |
+
} else {
|
535 |
+
jQuery('#bookly-extras').hide();
|
536 |
+
}
|
537 |
+
};
|
538 |
+
|
539 |
+
// Hide or unhide custom fields for current service
|
540 |
+
$scope.prepareCustomFields = function () {
|
541 |
+
if (BooklyL10nAppDialog.cf_per_service == 1) {
|
542 |
+
var show = false;
|
543 |
+
jQuery('#ab--custom-fields div[data-services]').each(function() {
|
544 |
+
var $this = jQuery(this);
|
545 |
+
if (dataSource.form.service !== null) {
|
546 |
+
var services = $this.data('services');
|
547 |
+
if (services && jQuery.inArray(dataSource.form.service.id, services) > -1) {
|
548 |
+
$this.show();
|
549 |
+
show = true;
|
550 |
+
} else {
|
551 |
+
$this.hide();
|
552 |
+
}
|
553 |
+
} else {
|
554 |
+
$this.hide();
|
555 |
+
}
|
556 |
+
});
|
557 |
+
if (show) {
|
558 |
+
jQuery('#ab--custom-fields').show();
|
559 |
+
} else {
|
560 |
+
jQuery('#ab--custom-fields').hide();
|
561 |
+
}
|
562 |
+
}
|
563 |
};
|
564 |
|
565 |
$scope.saveCustomFields = function() {
|
566 |
var result = [],
|
567 |
+
extras = {},
|
568 |
+
$fields = jQuery('#ab--custom-fields > *'),
|
569 |
+
$number_of_persons = jQuery('#bookly-customer-details-dialog #ab-edit-number-of-persons')
|
570 |
+
;
|
571 |
+
|
572 |
+
$fields.each(function () {
|
573 |
+
var $this = jQuery(this),
|
574 |
+
value;
|
575 |
+
if ($this.is(':visible')) {
|
576 |
+
switch ($this.data('type')) {
|
577 |
+
case 'checkboxes':
|
578 |
+
value = [];
|
579 |
+
$this.find('.ab-custom-field:checked').each(function () {
|
580 |
+
value.push(this.value);
|
581 |
+
});
|
582 |
+
break;
|
583 |
+
case 'radio-buttons':
|
584 |
+
value = $this.find('.ab-custom-field:checked').val();
|
585 |
+
break;
|
586 |
+
default:
|
587 |
+
value = $this.find('.ab-custom-field').val();
|
588 |
+
break;
|
589 |
+
}
|
590 |
+
result.push({id: $this.data('id'), value: value});
|
591 |
}
|
|
|
592 |
});
|
593 |
|
594 |
+
if ($scope.form.service) {
|
595 |
+
jQuery('#bookly-extras .service_' + $scope.form.service.id + ' input.extras-count').each(function () {
|
596 |
+
if (this.value > 0) {
|
597 |
+
extras[jQuery(this).data('id')] = this.value;
|
598 |
+
}
|
599 |
+
});
|
600 |
+
}
|
601 |
+
|
602 |
$scope.edit_customer.custom_fields = result;
|
603 |
$scope.edit_customer.number_of_persons = $number_of_persons.val();
|
604 |
+
$scope.edit_customer.location_id = jQuery('#bookly-customer-details-dialog #ab-appointment-location').val();
|
605 |
+
$scope.edit_customer.extras = extras;
|
606 |
+
$scope.edit_customer.status = jQuery('#bookly-customer-details-dialog #ab-appointment-status').val();
|
607 |
+
|
608 |
+
jQuery('#bookly-customer-details-dialog').modal('hide');
|
609 |
+
};
|
610 |
+
|
611 |
+
/**************************************************************************************************************
|
612 |
+
* Payment Details *
|
613 |
+
**************************************************************************************************************/
|
614 |
|
615 |
+
$scope.completePayment = function(payment_id, payment_title) {
|
616 |
+
jQuery.each($scope.dataSource.data.customers, function(key, item) {
|
617 |
+
if (item.payment_id == payment_id) {
|
618 |
+
item.payment_type = 'full';
|
619 |
+
item.payment_title = payment_title;
|
620 |
+
}
|
621 |
+
});
|
622 |
};
|
623 |
|
624 |
/**
|
625 |
* Datepicker options.
|
626 |
*/
|
627 |
$scope.dateOptions = {
|
628 |
+
dateFormat : BooklyL10nAppDialog.dpDateFormat,
|
629 |
+
dayNamesMin : BooklyL10nAppDialog.calendar.shortDays,
|
630 |
+
monthNames : BooklyL10nAppDialog.calendar.longMonths,
|
631 |
+
monthNamesShort : BooklyL10nAppDialog.calendar.shortMonths,
|
632 |
+
firstDay : BooklyL10nAppDialog.startOfWeek
|
633 |
};
|
634 |
});
|
635 |
|
656 |
module.directive('chosen',function($timeout) {
|
657 |
var linker = function(scope,element,attrs) {
|
658 |
scope.$watch(attrs['chosen'], function() {
|
659 |
+
element.trigger('chosen:updated');
|
660 |
});
|
661 |
|
662 |
scope.$watchCollection(attrs['ngModel'], function() {
|
663 |
$timeout(function() {
|
664 |
+
element.trigger('chosen:updated');
|
665 |
});
|
666 |
});
|
667 |
|
675 |
});
|
676 |
|
677 |
/**
|
678 |
+
* Directive for Popover jQuery plugin.
|
679 |
*/
|
680 |
+
module.directive('popover', function() {
|
681 |
return function(scope, element, attrs) {
|
682 |
element.popover({
|
683 |
trigger : 'hover',
|
684 |
+
content : function() { return this.getAttribute('popover'); },
|
685 |
+
html : true,
|
686 |
+
placement: 'top',
|
687 |
+
template: '<div class="popover bookly-font-xs" style="width: 220px" role="tooltip"><div class="popover-arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'
|
688 |
});
|
689 |
};
|
690 |
});
|
|
|
691 |
})();
|
692 |
|
693 |
/**
|
694 |
* @param int appointment_id
|
695 |
* @param int staff_id
|
696 |
* @param moment start_date
|
|
|
697 |
* @param function callback
|
698 |
*/
|
699 |
+
var showAppointmentDialog = function (appointment_id, staff_id, start_date, callback) {
|
700 |
+
var $dialog = jQuery('#bookly-appointment-dialog');
|
701 |
+
var $scope = angular.element($dialog[0]).scope();
|
702 |
$scope.$apply(function ($scope) {
|
703 |
+
$scope.loading = true;
|
704 |
+
$dialog
|
705 |
+
.find('.modal-title')
|
706 |
+
.text(appointment_id ? BooklyL10nAppDialog.title.edit_appointment : BooklyL10nAppDialog.title.new_appointment);
|
707 |
+
// Populate data source.
|
708 |
+
$scope.dataSource.loadData().then(function() {
|
709 |
+
$scope.loading = false;
|
710 |
+
if (appointment_id) {
|
711 |
+
$scope.configureEditForm(appointment_id, callback);
|
712 |
+
} else {
|
713 |
+
$scope.configureNewForm(staff_id, start_date, callback);
|
714 |
+
}
|
715 |
+
});
|
716 |
});
|
717 |
|
718 |
+
// hide customer details dialog, if it remained opened.
|
719 |
+
if (jQuery('#bookly-customer-details-dialog').hasClass('in')) {
|
720 |
+
jQuery('#bookly-customer-details-dialog').modal('hide');
|
721 |
}
|
722 |
|
723 |
// hide new customer dialog, if it remained opened.
|
724 |
+
if (jQuery('#bookly-customer-dialog').hasClass('in')) {
|
725 |
+
jQuery('#bookly-customer-dialog').modal('hide');
|
726 |
}
|
727 |
|
728 |
+
$dialog.modal('show');
|
729 |
+
};
|
|
backend/modules/calendar/templates/_appointment_dialog.php
ADDED
@@ -0,0 +1,149 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
|
2 |
+
<div ng-app="appointmentDialog" ng-controller="appointmentDialogCtrl">
|
3 |
+
<div id=bookly-appointment-dialog class="modal fade" tabindex=-1 role="dialog">
|
4 |
+
<div class="modal-dialog">
|
5 |
+
<div class="modal-content">
|
6 |
+
<form ng-submit=processForm()>
|
7 |
+
<div class="modal-header">
|
8 |
+
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
9 |
+
<div class="modal-title h2"><?php _e( 'New appointment', 'bookly' ) ?></div>
|
10 |
+
</div>
|
11 |
+
<div ng-show=loading class="modal-body">
|
12 |
+
<div class="bookly-loading"></div>
|
13 |
+
</div>
|
14 |
+
<div ng-hide=loading class="modal-body">
|
15 |
+
<div class=form-group>
|
16 |
+
<label for="ab_provider"><?php _e( 'Provider', 'bookly' ) ?></label>
|
17 |
+
<select id="ab_provider" class="field form-control" ng-model="form.staff" ng-options="s.full_name for s in dataSource.data.staff" ng-change="onStaffChange()"></select>
|
18 |
+
</div>
|
19 |
+
|
20 |
+
<div class=form-group>
|
21 |
+
<label for="ab_service"><?php _e( 'Service', 'bookly' ) ?></label>
|
22 |
+
<select id="ab_service" class="field form-control" ng-model="form.service"
|
23 |
+
ng-options="s.title for s in form.staff.services" ng-change="onServiceChange()">
|
24 |
+
<option value=""><?php _e( '-- Select a service --', 'bookly' ) ?></option>
|
25 |
+
</select>
|
26 |
+
<p class="text-danger" my-slide-up="errors.service_required">
|
27 |
+
<?php _e( 'Please select a service', 'bookly' ) ?>
|
28 |
+
</p>
|
29 |
+
</div>
|
30 |
+
|
31 |
+
<div class="row">
|
32 |
+
<div class="col-sm-4">
|
33 |
+
<div class=form-group>
|
34 |
+
<label for="ab_date"><?php _e( 'Date', 'bookly' ) ?></label>
|
35 |
+
<input id="ab_date" class="form-control" type=text
|
36 |
+
ng-model=form.date ui-date="dateOptions" autocomplete="off">
|
37 |
+
</div>
|
38 |
+
</div>
|
39 |
+
<div class="col-sm-8">
|
40 |
+
<div class="form-group">
|
41 |
+
<div ng-hide="form.service.duration == 86400">
|
42 |
+
<label for="ab_period"><?php _e( 'Period', 'bookly' ) ?></label>
|
43 |
+
<div class="bookly-flexbox">
|
44 |
+
<div class="bookly-flex-cell">
|
45 |
+
<select id="ab_period" class="form-control" ng-model=form.start_time
|
46 |
+
ng-options="t.title for t in dataSource.data.start_time"
|
47 |
+
ng-change=onStartTimeChange()></select>
|
48 |
+
</div>
|
49 |
+
<div class="bookly-flex-cell" style="width: 4%">
|
50 |
+
<div class="bookly-margin-horizontal-md"><?php _e( 'to', 'bookly' ) ?></div>
|
51 |
+
</div>
|
52 |
+
<div class="bookly-flex-cell" style="width: 48%">
|
53 |
+
<select class="form-control" ng-model=form.end_time
|
54 |
+
ng-options="t.title for t in dataSource.getDataForEndTime()"
|
55 |
+
ng-change=onEndTimeChange()></select>
|
56 |
+
</div>
|
57 |
+
</div>
|
58 |
+
<p class="text-success" my-slide-up=errors.date_interval_warning id=date_interval_warning_msg>
|
59 |
+
<?php _e( 'Selected period doesn\'t match service duration', 'bookly' ) ?>
|
60 |
+
</p>
|
61 |
+
<p class="text-success" my-slide-up="errors.time_interval" ng-bind="errors.time_interval"></p>
|
62 |
+
<p class="text-success" my-slide-up=errors.date_interval_not_available id=date_interval_not_available_msg>
|
63 |
+
<?php _e( 'The selected period is occupied by another appointment', 'bookly' ) ?>
|
64 |
+
</p>
|
65 |
+
</div>
|
66 |
+
</div>
|
67 |
+
</div>
|
68 |
+
</div>
|
69 |
+
|
70 |
+
<div class=form-group>
|
71 |
+
<label for="chosen"><?php _e( 'Customers', 'bookly' ) ?></label>
|
72 |
+
<span ng-show="form.service" title="<?php esc_attr_e( 'Selected / maximum', 'bookly' ) ?>">
|
73 |
+
({{dataSource.getTotalNumberOfPersons()}}/{{form.service.capacity}})
|
74 |
+
</span>
|
75 |
+
<ul class="ab-customer-list">
|
76 |
+
<li ng-repeat="customer in form.customers">
|
77 |
+
{{customer.number_of_persons}}×<i class="glyphicon glyphicon-user"></i>
|
78 |
+
<a ng-click="editCustomerDetails(customer)" title="<?php esc_attr_e( 'Edit booking details', 'bookly' ) ?>" href="#">{{customer.name}}</a>
|
79 |
+
<span ng-class="{'bookly-margin-left-sm glyphicon': true, 'glyphicon-time': customer.status == 'pending', 'glyphicon-ok': customer.status == 'approved', 'glyphicon-remove': customer.status == 'cancelled'}"
|
80 |
+
popover="<?php esc_attr_e( 'Status', 'bookly' ) ?>: {{statusToString(customer.status)}}"></span>
|
81 |
+
<span ng-show="customer.payment_id"
|
82 |
+
popover="<?php esc_attr_e( 'Payment', 'bookly' ) ?>: {{customer.payment_title}}">
|
83 |
+
<a data-toggle="modal"
|
84 |
+
href="#bookly-payment-details-modal"
|
85 |
+
data-payment_id="{{customer.payment_id}}"
|
86 |
+
ng-class="{'bookly-js-toggle-popover bookly-margin-left-sm glyphicon': true, 'glyphicon-usd': customer.payment_type == 'full', 'glyphicon-alert': customer.payment_type == 'partial'}"></a>
|
87 |
+
</span>
|
88 |
+
<a ng-click="removeCustomer(customer)" class="glyphicon glyphicon-trash text-danger bookly-margin-left-sm" href="#"
|
89 |
+
popover="<?php esc_attr_e( 'Remove customer', 'bookly' ) ?>"></a>
|
90 |
+
</li>
|
91 |
+
</ul>
|
92 |
+
|
93 |
+
<div ng-show="!form.service || dataSource.getTotalNotCancelledNumberOfPersons() < form.service.capacity">
|
94 |
+
<div class="form-group">
|
95 |
+
<div class="input-group">
|
96 |
+
<select id="chosen" multiple data-placeholder="<?php esc_attr_e( '-- Search customers --', 'bookly' ) ?>"
|
97 |
+
class="field chzn-select form-control" chosen="dataSource.data.customers"
|
98 |
+
ng-model="form.customers" ng-options="c.name for c in dataSource.data.customers">
|
99 |
+
</select>
|
100 |
+
<span class="input-group-btn">
|
101 |
+
<a href="#bookly-customer-dialog" class="btn btn-success" data-toggle="modal">
|
102 |
+
<i class="glyphicon glyphicon-plus"></i>
|
103 |
+
<?php _e( 'New customer', 'bookly' ) ?>
|
104 |
+
</a>
|
105 |
+
</span>
|
106 |
+
</div>
|
107 |
+
<p class="text-danger" my-slide-up="errors.customers_required">
|
108 |
+
<?php _e( 'Please select a customer', 'bookly' ) ?>
|
109 |
+
</p>
|
110 |
+
<p class="text-danger" my-slide-up="errors.overflow_capacity" ng-bind="errors.overflow_capacity"></p>
|
111 |
+
</div>
|
112 |
+
</div>
|
113 |
+
</div>
|
114 |
+
|
115 |
+
<div class=form-group>
|
116 |
+
<label for="ab_notification"><?php _e( 'Send notifications', 'bookly' ) ?></label>
|
117 |
+
<p class="help-block"><?php _e( 'If email or SMS notifications are enabled and you want customers or staff member to be notified about this appointment after saving, select appropriate option before clicking Save. With "If status changed" the notifications are sent to those customers whose status has just been changed. With "To all customers" the notifications are sent to everyone in the list.', 'bookly' ) ?></p>
|
118 |
+
<select class="form-control ab-inline-block ab-auto-w" style="margin-top: 0" ng-model=form.notification id="ab_notification">
|
119 |
+
<option value="no"><?php _e( 'Don\'t send', 'bookly' ) ?></option>
|
120 |
+
<option value="changed_status"><?php _e( 'If status changed', 'bookly' ) ?></option>
|
121 |
+
<option value="all"><?php _e( 'To all customers', 'bookly' ) ?></option>
|
122 |
+
</select>
|
123 |
+
</div>
|
124 |
+
|
125 |
+
<div class=form-group>
|
126 |
+
<label for="ab_internal_note"><?php _e( 'Internal note', 'bookly' ) ?></label>
|
127 |
+
<textarea class="form-control" ng-model=form.internal_note id="ab_internal_note"></textarea>
|
128 |
+
</div>
|
129 |
+
</div>
|
130 |
+
<div class="modal-footer">
|
131 |
+
<div ng-hide=loading>
|
132 |
+
<?php \BooklyLite\Lib\Utils\Common::submitButton() ?>
|
133 |
+
<button type="button" ng-click="closeDialog()" class="ab-reset-form btn btn-lg btn-default" data-dismiss="modal">
|
134 |
+
<?php _e( 'Cancel', 'bookly' ) ?>
|
135 |
+
</button>
|
136 |
+
</div>
|
137 |
+
</div>
|
138 |
+
</form>
|
139 |
+
</div><!-- /.modal-content -->
|
140 |
+
</div><!-- /.modal-dialog -->
|
141 |
+
</div><!-- /.modal -->
|
142 |
+
<?php ?>
|
143 |
+
<div customer-dialog=createCustomer(customer)></div>
|
144 |
+
<div payment-details-dialog="completePayment(payment_id, payment_title)"></div>
|
145 |
+
|
146 |
+
<?php include '_customer_details_dialog.php' ?>
|
147 |
+
<?php \BooklyLite\Backend\Modules\Customers\Components::getInstance()->renderCustomerDialog() ?>
|
148 |
+
<?php \BooklyLite\Backend\Modules\Payments\Components::getInstance()->renderPaymentDetailsDialog() ?>
|
149 |
+
</div>
|
backend/modules/calendar/templates/_appointment_form.php
DELETED
@@ -1,108 +0,0 @@
|
|
1 |
-
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
|
2 |
-
<style>
|
3 |
-
.search-choice { display: none; }
|
4 |
-
</style>
|
5 |
-
<div ng-controller=appointmentDialogCtrl>
|
6 |
-
<div id=ab_appointment_dialog class="modal fade">
|
7 |
-
<div class="modal-dialog">
|
8 |
-
<div ng-show=loading class="modal-content loading-indicator">
|
9 |
-
<div class="modal-body">
|
10 |
-
<span class="ab-loader"></span>
|
11 |
-
</div>
|
12 |
-
</div>
|
13 |
-
<div ng-hide=loading class="modal-content">
|
14 |
-
<form ng-submit=processForm() class=form-horizontal>
|
15 |
-
<div class="modal-header">
|
16 |
-
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
17 |
-
<h4 class="modal-title"><?php _e( 'New appointment', 'bookly' ) ?></h4>
|
18 |
-
</div>
|
19 |
-
<div class="modal-body">
|
20 |
-
|
21 |
-
<div style="padding: 0 15px;">
|
22 |
-
<div class=form-group>
|
23 |
-
<label for="ab_provider"><?php _e( 'Provider', 'bookly' ) ?></label>
|
24 |
-
<select id="ab_provider" class="field form-control" ng-model="form.staff" ng-options="s.full_name for s in dataSource.data.staff" ng-change="onStaffChange()"></select>
|
25 |
-
</div>
|
26 |
-
|
27 |
-
<div class=form-group>
|
28 |
-
<label for="ab_service"><?php _e( 'Service', 'bookly' ) ?></label>
|
29 |
-
<div my-slide-up="errors.service_required" style="color: red; margin-top: 5px;">
|
30 |
-
<?php _e( 'Please select a service', 'bookly' ) ?>
|
31 |
-
</div>
|
32 |
-
<select id="ab_service" class="field form-control" ng-model="form.service" ng-options="s.title for s in form.staff.services" ng-change="onServiceChange()">
|
33 |
-
<option value=""><?php _e( '-- Select a service --', 'bookly' ) ?></option>
|
34 |
-
</select>
|
35 |
-
</div>
|
36 |
-
|
37 |
-
<div class=form-group>
|
38 |
-
<label for="ab_date"><?php _e( 'Date', 'bookly' ) ?></label>
|
39 |
-
<input id="ab_date" class="form-control ab-auto-w" type=text ng-model=form.date ui-date="dateOptions" autocomplete="off" />
|
40 |
-
</div>
|
41 |
-
<div class="form-group">
|
42 |
-
<div ng-hide="form.service.duration == 86400">
|
43 |
-
<label for="ab_period"><?php _e( 'Period', 'bookly' ) ?></label>
|
44 |
-
<div>
|
45 |
-
<select id="ab_period" style="display: inline" class="form-control ab-auto-w" ng-model=form.start_time
|
46 |
-
ng-options="t.title for t in dataSource.data.start_time"
|
47 |
-
ng-change=onStartTimeChange()></select>
|
48 |
-
<span> <?php _e( 'to', 'bookly' ) ?> </span>
|
49 |
-
<select style="display: inline" class="form-control ab-auto-w" ng-model=form.end_time
|
50 |
-
ng-options="t.title for t in dataSource.getDataForEndTime()"
|
51 |
-
ng-change=onEndTimeChange()></select>
|
52 |
-
|
53 |
-
<div my-slide-up=errors.date_interval_warning id=date_interval_warning_msg style="color: green; margin-top: 5px;">
|
54 |
-
<?php _e( 'Selected period doesn\'t match service duration', 'bookly' ) ?>
|
55 |
-
</div>
|
56 |
-
<div my-slide-up="errors.time_interval" ng-bind="errors.time_interval" style="color: red; margin-top: 5px;"></div>
|
57 |
-
</div>
|
58 |
-
</div>
|
59 |
-
<div my-slide-up=errors.date_interval_not_available id=date_interval_not_available_msg style="color: red; margin-top: 5px;">
|
60 |
-
<?php _e( 'The selected period is occupied by another appointment', 'bookly' ) ?>
|
61 |
-
</div>
|
62 |
-
</div>
|
63 |
-
|
64 |
-
<div class=form-group>
|
65 |
-
<label>
|
66 |
-
<?php _e( 'Customers', 'bookly' ) ?>
|
67 |
-
<span ng-show="form.service" title="<?php echo esc_attr( __( 'Selected / maximum', 'bookly' ) ) ?>">({{dataSource.getTotalNumberOfPersons()}}/{{form.service.capacity}})</span>
|
68 |
-
</label>
|
69 |
-
<div my-slide-up="errors.customers_required" style="color: red; margin-top: 5px;"><?php _e( 'Please select a customer', 'bookly' ) ?></div>
|
70 |
-
<div my-slide-up="errors.overflow_capacity" ng-bind="errors.overflow_capacity" style="color: red; margin-top: 5px;"></div>
|
71 |
-
<ul class="ab-customer-list">
|
72 |
-
<li ng-repeat="customer in form.customers">
|
73 |
-
{{customer.number_of_persons}}×<i class="glyphicon glyphicon-user"></i>
|
74 |
-
<a ng-click="editCustomFields(customer)" title="<?php echo esc_attr( __( 'Edit booking details', 'bookly' ) ) ?>">{{customer.name}}</a>
|
75 |
-
<span ng-click="removeCustomer(customer)" class="glyphicon glyphicon-remove ab-pointer" title="<?php echo esc_attr( __( 'Remove customer', 'bookly' ) ) ?>"></span>
|
76 |
-
</li>
|
77 |
-
</ul>
|
78 |
-
|
79 |
-
<div ng-show="!form.service || dataSource.getTotalNumberOfPersons() < form.service.capacity">
|
80 |
-
<select id="chosen" multiple data-placeholder="<?php echo esc_attr( __( '-- Search customers --', 'bookly' ) ) ?>"
|
81 |
-
class="field chzn-select form-control" chosen="dataSource.data.customers"
|
82 |
-
ng-model="form.customers" ng-options="c.name for c in dataSource.data.customers">
|
83 |
-
</select><br/>
|
84 |
-
<a href=#ab_new_customer_dialog class="{{btn_class}}" data-backdrop={{backdrop}} data-toggle="modal"><?php _e( 'New customer', 'bookly' ) ?></a>
|
85 |
-
</div>
|
86 |
-
</div>
|
87 |
-
|
88 |
-
<div class=form-group>
|
89 |
-
<label></label>
|
90 |
-
<input class="form-control" style="margin-top: 0" type="checkbox" ng-model=form.email_notification /> <?php _e( 'Send email notifications', 'bookly' ) ?>
|
91 |
-
<?php AB_Utils::popover( __( 'If email or SMS notifications are enabled and you want the customer or the staff member to be notified about this appointment after saving, tick this checkbox before clicking Save.', 'bookly' ), 'width:16px;margin-left:0;' ) ?>
|
92 |
-
</div>
|
93 |
-
</div>
|
94 |
-
|
95 |
-
</div>
|
96 |
-
<div class="modal-footer">
|
97 |
-
<div class=dialog-button-wrapper>
|
98 |
-
<?php AB_Utils::submitButton() ?>
|
99 |
-
<a ng-click=closeDialog() class=ab-reset-form href="" data-dismiss="modal"><?php _e( 'Cancel', 'bookly' ) ?></a>
|
100 |
-
</div>
|
101 |
-
</div>
|
102 |
-
</form>
|
103 |
-
</div><!-- /.modal-content -->
|
104 |
-
</div><!-- /.modal-dialog -->
|
105 |
-
</div><!-- /.modal -->
|
106 |
-
<div style="margin-bottom: 2px;" class="ab-inline-block ab-create-customer" new-customer-dialog=createCustomer(customer) backdrop=false btn-class=""></div>
|
107 |
-
<?php include '_custom_fields_form.php' ?>
|
108 |
-
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/modules/calendar/templates/_custom_fields_form.php
DELETED
@@ -1,77 +0,0 @@
|
|
1 |
-
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
|
2 |
-
<div id="ab_custom_fields_dialog" class="modal fade">
|
3 |
-
<div class="modal-dialog">
|
4 |
-
<div class="modal-content">
|
5 |
-
<div class="modal-header">
|
6 |
-
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
7 |
-
<h4 class="modal-title"><?php _e( 'Edit booking details', 'bookly' ) ?></h4>
|
8 |
-
</div>
|
9 |
-
<form class="form-horizontal" ng-hide=loading style="z-index: 1050">
|
10 |
-
<div class="modal-body">
|
11 |
-
|
12 |
-
<fieldset>
|
13 |
-
<legend><?php _e( 'Participants', 'bookly' ) ?></legend>
|
14 |
-
<div class="col-md-12">
|
15 |
-
<div class="form-group">
|
16 |
-
<label for="ab-edit-number-of-persons"><?php _e( 'Number of persons', 'bookly' ) ?></label>
|
17 |
-
<select class="ab-custom-field form-control" id="ab-edit-number-of-persons"></select>
|
18 |
-
</div>
|
19 |
-
</div>
|
20 |
-
</fieldset>
|
21 |
-
<fieldset>
|
22 |
-
<legend><?php _e( 'Custom Fields', 'bookly' ) ?></legend>
|
23 |
-
<?php foreach ( json_decode( get_option( 'ab_custom_fields' ) ) as $custom_field ): ?>
|
24 |
-
<div class="col-md-12">
|
25 |
-
<div class="form-group">
|
26 |
-
<label class="ab-formLabel"><?php echo $custom_field->label ?></label>
|
27 |
-
<div class="ab-formField" data-type="<?php echo esc_attr( $custom_field->type )?>" data-id="<?php echo esc_attr( $custom_field->id ) ?>">
|
28 |
-
|
29 |
-
<?php if ( $custom_field->type == 'text-field' ): ?>
|
30 |
-
<input type="text" class="ab-custom-field form-control" />
|
31 |
-
|
32 |
-
<?php elseif ( $custom_field->type == 'textarea' ): ?>
|
33 |
-
<textarea rows="3" class="ab-custom-field form-control"></textarea>
|
34 |
-
|
35 |
-
<?php elseif ( $custom_field->type == 'checkboxes' ): ?>
|
36 |
-
<?php foreach ( $custom_field->items as $item ): ?>
|
37 |
-
<div class="checkbox">
|
38 |
-
<label>
|
39 |
-
<input class="ab-custom-field" type="checkbox" value="<?php echo esc_attr( $item ) ?>" />
|
40 |
-
<?php echo $item ?>
|
41 |
-
</label>
|
42 |
-
</div>
|
43 |
-
<?php endforeach ?>
|
44 |
-
|
45 |
-
<?php elseif ( $custom_field->type == 'radio-buttons' ): ?>
|
46 |
-
<?php foreach ( $custom_field->items as $item ): ?>
|
47 |
-
<div class="radio">
|
48 |
-
<label>
|
49 |
-
<input type="radio" name="<?php echo $custom_field->id ?>" class="ab-custom-field" value="<?php echo esc_attr( $item ) ?>" />
|
50 |
-
<?php echo $item ?>
|
51 |
-
</label>
|
52 |
-
</div>
|
53 |
-
<?php endforeach ?>
|
54 |
-
|
55 |
-
<?php elseif ( $custom_field->type == 'drop-down' ): ?>
|
56 |
-
<select class="ab-custom-field form-control">
|
57 |
-
<option value=""></option>
|
58 |
-
<?php foreach ( $custom_field->items as $item ): ?>
|
59 |
-
<option value="<?php echo esc_attr( $item ) ?>"><?php echo $item ?></option>
|
60 |
-
<?php endforeach ?>
|
61 |
-
</select>
|
62 |
-
<?php endif ?>
|
63 |
-
</div>
|
64 |
-
</div>
|
65 |
-
</div>
|
66 |
-
<?php endforeach ?>
|
67 |
-
</fieldset>
|
68 |
-
|
69 |
-
</div>
|
70 |
-
<div class="modal-footer">
|
71 |
-
<input type="button" data-customer="" ng-click=saveCustomFields() class="btn btn-info ab-popup-save" value="<?php _e( 'Apply', 'bookly' ) ?>">
|
72 |
-
<input type="button" class="ab-reset-form" data-dismiss=modal value="<?php _e( 'Cancel', 'bookly' ) ?>" aria-hidden=true>
|
73 |
-
</div>
|
74 |
-
</form>
|
75 |
-
</div><!-- /.modal-content -->
|
76 |
-
</div><!-- /.modal-dialog -->
|
77 |
-
</div><!-- /.modal -->
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/modules/calendar/templates/_customer_details_dialog.php
ADDED
@@ -0,0 +1,98 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
+
use BooklyLite\Lib\Entities\CustomerAppointment;
|
3 |
+
?>
|
4 |
+
<div id="bookly-customer-details-dialog" class="modal fade" tabindex=-1 role="dialog">
|
5 |
+
<div class="modal-dialog">
|
6 |
+
<div class="modal-content">
|
7 |
+
<div class="modal-header">
|
8 |
+
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
9 |
+
<div class="modal-title h2"><?php _e( 'Edit booking details', 'bookly' ) ?></div>
|
10 |
+
</div>
|
11 |
+
<form ng-hide=loading style="z-index: 1050">
|
12 |
+
<div class="modal-body">
|
13 |
+
<div class="form-group">
|
14 |
+
<label for="ab-appointment-status"><?php _e( 'Status', 'bookly' ) ?></label>
|
15 |
+
<select class="ab-custom-field form-control" id="ab-appointment-status">
|
16 |
+
<option value="<?php echo CustomerAppointment::STATUS_PENDING ?>"><?php echo esc_html( CustomerAppointment::statusToString( CustomerAppointment::STATUS_PENDING ) ) ?></option>
|
17 |
+
<option value="<?php echo CustomerAppointment::STATUS_APPROVED ?>"><?php echo esc_html( CustomerAppointment::statusToString( CustomerAppointment::STATUS_APPROVED ) ) ?></option>
|
18 |
+
<option value="<?php echo CustomerAppointment::STATUS_CANCELLED ?>"><?php echo esc_html( CustomerAppointment::statusToString( CustomerAppointment::STATUS_CANCELLED ) ) ?></option>
|
19 |
+
</select>
|
20 |
+
</div>
|
21 |
+
<div class="form-group">
|
22 |
+
<label for="ab-edit-number-of-persons"><?php _e( 'Number of persons', 'bookly' ) ?></label>
|
23 |
+
<select class="ab-custom-field form-control" id="ab-edit-number-of-persons"></select>
|
24 |
+
</div>
|
25 |
+
<?php do_action( 'bookly_render_customer_details_dialog' ) ?>
|
26 |
+
<h3 class="bookly-block-head bookly-color-gray">
|
27 |
+
<?php _e( 'Custom Fields', 'bookly' ) ?>
|
28 |
+
</h3>
|
29 |
+
<div id="ab--custom-fields">
|
30 |
+
<?php foreach ( $custom_fields as $custom_field ) : ?>
|
31 |
+
<div class="form-group" data-type="<?php echo esc_attr( $custom_field->type )?>" data-id="<?php echo esc_attr( $custom_field->id ) ?>" data-services="<?php echo esc_attr( json_encode( $custom_field->services ) ) ?>">
|
32 |
+
<label for="custom_field_<?php echo esc_attr( $custom_field->id ) ?>"><?php echo $custom_field->label ?></label>
|
33 |
+
<div>
|
34 |
+
<?php if ( $custom_field->type == 'text-field' ) : ?>
|
35 |
+
<input id="custom_field_<?php echo esc_attr( $custom_field->id ) ?>" type="text" class="ab-custom-field form-control" />
|
36 |
+
|
37 |
+
<?php elseif ( $custom_field->type == 'textarea' ) : ?>
|
38 |
+
<textarea id="custom_field_<?php echo esc_attr( $custom_field->id ) ?>" rows="3" class="ab-custom-field form-control"></textarea>
|
39 |
+
|
40 |
+
<?php elseif ( $custom_field->type == 'checkboxes' ) : ?>
|
41 |
+
<?php foreach ( $custom_field->items as $item ) : ?>
|
42 |
+
<div class="checkbox">
|
43 |
+
<label>
|
44 |
+
<input class="ab-custom-field" type="checkbox" value="<?php echo esc_attr( $item ) ?>" />
|
45 |
+
<?php echo $item ?>
|
46 |
+
</label>
|
47 |
+
</div>
|
48 |
+
<?php endforeach ?>
|
49 |
+
|
50 |
+
<?php elseif ( $custom_field->type == 'radio-buttons' ) : ?>
|
51 |
+
<?php foreach ( $custom_field->items as $item ) : ?>
|
52 |
+
<div class="radio">
|
53 |
+
<label>
|
54 |
+
<input type="radio" name="<?php echo $custom_field->id ?>" class="ab-custom-field" value="<?php echo esc_attr( $item ) ?>" />
|
55 |
+
<?php echo $item ?>
|
56 |
+
</label>
|
57 |
+
</div>
|
58 |
+
<?php endforeach ?>
|
59 |
+
|
60 |
+
<?php elseif ( $custom_field->type == 'drop-down' ) : ?>
|
61 |
+
<select id="custom_field_<?php echo esc_attr( $custom_field->id ) ?>" class="ab-custom-field form-control">
|
62 |
+
<option value=""></option>
|
63 |
+
<?php foreach ( $custom_field->items as $item ) : ?>
|
64 |
+
<option value="<?php echo esc_attr( $item ) ?>"><?php echo $item ?></option>
|
65 |
+
<?php endforeach ?>
|
66 |
+
</select>
|
67 |
+
<?php endif ?>
|
68 |
+
</div>
|
69 |
+
</div>
|
70 |
+
<?php endforeach ?>
|
71 |
+
</div>
|
72 |
+
|
73 |
+
<?php if ( $extras = apply_filters( 'bookly_extras_find_all', array() ) ) : ?>
|
74 |
+
<h3 class="bookly-block-head bookly-color-gray">
|
75 |
+
<?php _e( 'Extras', 'bookly' ) ?>
|
76 |
+
</h3>
|
77 |
+
<div id="bookly-extras" class="bookly-flexbox">
|
78 |
+
<?php foreach ( $extras as $extra ) : ?>
|
79 |
+
<div class="bookly-flex-row service_<?php echo $extra->get( 'service_id' ) ?> bookly-margin-bottom-sm">
|
80 |
+
<div class="bookly-flex-cell bookly-padding-bottom-sm" style="width:5em">
|
81 |
+
<input class="extras-count form-control" data-id="<?php echo $extra->get( 'id' ) ?>" type="number" min="0" name="extra[<?php echo $extra->get( 'id' ) ?>]" value="0" />
|
82 |
+
</div>
|
83 |
+
<div class="bookly-flex-cell bookly-padding-bottom-sm bookly-vertical-middle">
|
84 |
+
× <b><?php echo $extra->getTitle() ?></b> (<?php echo \BooklyLite\Lib\Utils\Common::formatPrice( $extra->get( 'price' ) ) ?>)
|
85 |
+
</div>
|
86 |
+
</div>
|
87 |
+
<?php endforeach ?>
|
88 |
+
</div>
|
89 |
+
<?php endif ?>
|
90 |
+
</div>
|
91 |
+
<div class="modal-footer">
|
92 |
+
<input type="button" data-customer="" ng-click=saveCustomFields() class="ab-popup-save btn btn-lg btn-success" value="<?php _e( 'Apply', 'bookly' ) ?>">
|
93 |
+
<input type="button" class="ab-reset-form btn btn-lg btn-default" data-dismiss=modal value="<?php _e( 'Cancel', 'bookly' ) ?>" aria-hidden=true>
|
94 |
+
</div>
|
95 |
+
</form>
|
96 |
+
</div><!-- /.modal-content -->
|
97 |
+
</div><!-- /.modal-dialog -->
|
98 |
+
</div><!-- /.modal -->
|
backend/modules/calendar/templates/calendar.php
CHANGED
@@ -4,59 +4,89 @@
|
|
4 |
?>
|
5 |
<style>
|
6 |
.fc-slats tr { height: <?php echo max( 21, intval( 620 / (1440 / get_option( 'ab_settings_time_slot_length' ) ) ) ) ?>px; }
|
|
|
7 |
</style>
|
8 |
-
<div
|
9 |
-
<div class="
|
10 |
-
<
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
20 |
</li>
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
55 |
</div>
|
56 |
-
|
57 |
-
</div>
|
58 |
</div>
|
59 |
-
|
60 |
</div>
|
61 |
</div>
|
62 |
</div>
|
4 |
?>
|
5 |
<style>
|
6 |
.fc-slats tr { height: <?php echo max( 21, intval( 620 / (1440 / get_option( 'ab_settings_time_slot_length' ) ) ) ) ?>px; }
|
7 |
+
.fc-time-grid-event.fc-short .fc-time::after { content: '' !important; }
|
8 |
</style>
|
9 |
+
<div id="bookly-tbs" class="wrap">
|
10 |
+
<div class="bookly-tbs-body">
|
11 |
+
<div class="page-header text-right clearfix">
|
12 |
+
<div class="bookly-page-title">
|
13 |
+
<?php _e( 'Calendar', 'bookly' ) ?>
|
14 |
+
</div>
|
15 |
+
</div>
|
16 |
+
<div class="panel panel-default bookly-main bookly-fc-inner">
|
17 |
+
<div class="panel-body">
|
18 |
+
<?php if ( $staff_members ) : ?>
|
19 |
+
<ul class="bookly-nav bookly-nav-tabs">
|
20 |
+
<?php if ( \BooklyLite\Lib\Utils\Common::isCurrentUserAdmin() ) : ?>
|
21 |
+
<li class="bookly-nav-item bookly-js-calendar-tab" data-staff_id="0">
|
22 |
+
<?php _e( 'All', 'bookly' ) ?>
|
23 |
+
</li>
|
24 |
+
<?php endif ?>
|
25 |
+
<?php foreach ( $staff_members as $i => $staff ) : ?>
|
26 |
+
<li class="bookly-nav-item bookly-js-calendar-tab" data-staff_id="<?php echo $staff->id ?>" style="display: none">
|
27 |
+
<?php echo $staff->full_name ?>
|
28 |
+
</li>
|
29 |
+
<?php endforeach ?>
|
30 |
+
<?php if ( \BooklyLite\Lib\Utils\Common::isCurrentUserAdmin() ) : ?>
|
31 |
+
<div class="btn-group pull-right" style="margin-top: 5px;">
|
32 |
+
<button class="btn btn-default dropdown-toggle bookly-flexbox" data-toggle="dropdown">
|
33 |
+
<div class="bookly-flex-cell"><i class="dashicons dashicons-admin-users bookly-margin-right-md"></i></div>
|
34 |
+
<div class="bookly-flex-cell text-left"><span id="ab-staff-button"></span></div>
|
35 |
+
<div class="bookly-flex-cell"><div class="bookly-margin-left-md"><span class="caret"></span></div></div>
|
36 |
+
</button>
|
37 |
+
<ul class="dropdown-menu bookly-entity-selector">
|
38 |
+
<li>
|
39 |
+
<a class="checkbox" href="javascript:void(0)">
|
40 |
+
<label><input type="checkbox" id="bookly-check-all-entities"><?php _e( 'All staff', 'bookly' ) ?></label>
|
41 |
+
</a>
|
42 |
</li>
|
43 |
+
<?php foreach ( $staff_members as $i => $staff ) : ?>
|
44 |
+
<li>
|
45 |
+
<a class="checkbox" href="javascript:void(0)">
|
46 |
+
<label>
|
47 |
+
<input type="checkbox" id="ab-filter-staff-<?php echo $staff->id ?>" value="<?php echo $staff->id ?>" data-staff_name="<?php echo esc_attr( $staff->full_name ) ?>" class="bookly-js-check-entity">
|
48 |
+
<?php echo $staff->full_name ?>
|
49 |
+
</label>
|
50 |
+
</a>
|
51 |
+
</li>
|
52 |
+
<?php endforeach ?>
|
53 |
+
</ul>
|
54 |
+
</div>
|
55 |
+
<?php endif ?>
|
56 |
+
</ul>
|
57 |
+
<?php endif ?>
|
58 |
+
<div class="bookly-margin-top-xlg">
|
59 |
+
<div style="display: none;" class="bookly-loading bookly-js-loading"></div>
|
60 |
+
<?php if ( $staff_members ) : ?>
|
61 |
+
<div class="fc-loading-inner" style="display: none">
|
62 |
+
<div class="fc-loading"></div>
|
63 |
+
</div>
|
64 |
+
<div id="bookly-fc-wrapper" class="bookly-calendar">
|
65 |
+
<div class="bookly-js-calendar-element"></div>
|
66 |
+
</div>
|
67 |
+
<?php \BooklyLite\Backend\Modules\Calendar\Components::getInstance()->renderAppointmentDialog() ?>
|
68 |
+
<?php do_action( 'bookly_render_component_calendar' ) ?>
|
69 |
+
<?php else : ?>
|
70 |
+
<div class="well">
|
71 |
+
<div class="h1"><?php _e( 'Welcome to Bookly!', 'bookly' ) ?></div>
|
72 |
+
<h3><?php _e( 'Thank you for purchasing our product.', 'bookly' ) ?></h3>
|
73 |
+
<h3><?php _e( 'Bookly offers a simple solution for making appointments. With our plugin you will be able to easily manage your availability time and handle the flow of your clients.', 'bookly' ) ?></h3>
|
74 |
+
<p><?php _e( 'To start using Bookly, you need to follow these steps which are the minimum requirements to get it running!', 'bookly' ) ?></p>
|
75 |
+
<ol>
|
76 |
+
<li><?php _e( 'Add staff members.', 'bookly' ) ?></li>
|
77 |
+
<li><?php _e( 'Add services and assign them to staff members.', 'bookly' ) ?></li>
|
78 |
+
</ol>
|
79 |
+
<hr>
|
80 |
+
<a class="btn btn-success" href="<?php echo \BooklyLite\Lib\Utils\Common::escAdminUrl( BooklyLite\Backend\Modules\Staff\Controller::page_slug ) ?>">
|
81 |
+
<?php _e( 'Add Staff Members', 'bookly' ) ?>
|
82 |
+
</a>
|
83 |
+
<a class="btn btn-success" href="<?php echo \BooklyLite\Lib\Utils\Common::escAdminUrl( BooklyLite\Backend\Modules\Services\Controller::page_slug ) ?>">
|
84 |
+
<?php _e( 'Add Services', 'bookly' ) ?>
|
85 |
+
</a>
|
86 |
</div>
|
87 |
+
<?php endif ?>
|
|
|
88 |
</div>
|
89 |
+
</div>
|
90 |
</div>
|
91 |
</div>
|
92 |
</div>
|
backend/modules/coupons/AB_CouponsController.php
DELETED
@@ -1,43 +0,0 @@
|
|
1 |
-
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Class AB_CouponsController
|
5 |
-
*/
|
6 |
-
class AB_CouponsController extends AB_Controller {
|
7 |
-
|
8 |
-
/**
|
9 |
-
* Default action
|
10 |
-
*/
|
11 |
-
public function index()
|
12 |
-
{
|
13 |
-
$this->enqueueStyles( array(
|
14 |
-
'backend' => array(
|
15 |
-
'css/bookly.main-backend.css',
|
16 |
-
'bootstrap/css/bootstrap.min.css',
|
17 |
-
),
|
18 |
-
'module' => array(
|
19 |
-
'css/coupons.css',
|
20 |
-
)
|
21 |
-
) );
|
22 |
-
|
23 |
-
$this->enqueueScripts( array(
|
24 |
-
'backend' => array(
|
25 |
-
'js/ab_popup.js' => array( 'jquery' ),
|
26 |
-
'bootstrap/js/bootstrap.min.js' => array( 'jquery' ),
|
27 |
-
),
|
28 |
-
'module' => array(
|
29 |
-
'js/coupons.js' => array( 'jquery' ),
|
30 |
-
)
|
31 |
-
) );
|
32 |
-
|
33 |
-
wp_localize_script( 'ab-coupons.js', 'BooklyL10n', array(
|
34 |
-
'are_you_sure' => __( 'Are you sure?', 'bookly' ),
|
35 |
-
'please_select_at_least_one_coupon' => __( 'Please select at least one coupon.', 'bookly' ),
|
36 |
-
) );
|
37 |
-
|
38 |
-
$this->coupons_collection = false;
|
39 |
-
|
40 |
-
$this->render( 'index' );
|
41 |
-
}
|
42 |
-
|
43 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/modules/coupons/Controller.php
ADDED
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace BooklyLite\Backend\Modules\Coupons;
|
3 |
+
|
4 |
+
use BooklyLite\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Controller
|
8 |
+
* @package BooklyLite\Backend\Modules\Coupons
|
9 |
+
*/
|
10 |
+
class Controller extends Lib\Base\Controller
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Default action
|
14 |
+
*/
|
15 |
+
public function index()
|
16 |
+
{
|
17 |
+
$this->enqueueStyles( array(
|
18 |
+
'backend' => array( 'bootstrap/css/bootstrap-theme.min.css', ),
|
19 |
+
'frontend' => array( 'css/ladda.min.css', ),
|
20 |
+
) );
|
21 |
+
|
22 |
+
$this->enqueueScripts( array(
|
23 |
+
'backend' => array(
|
24 |
+
'bootstrap/js/bootstrap.min.js' => array( 'jquery' ),
|
25 |
+
'js/datatables.min.js' => array( 'jquery' ),
|
26 |
+
'js/alert.js' => array( 'jquery' ),
|
27 |
+
),
|
28 |
+
'frontend' => array(
|
29 |
+
'js/spin.min.js' => array( 'jquery' ),
|
30 |
+
'js/ladda.min.js' => array( 'jquery' ),
|
31 |
+
),
|
32 |
+
'module' => array( 'js/coupons.js' => array( 'jquery' ) )
|
33 |
+
) );
|
34 |
+
|
35 |
+
wp_localize_script( 'ab-coupons.js', 'BooklyL10n', array(
|
36 |
+
'edit' => __( 'Edit', 'bookly' ),
|
37 |
+
'zeroRecords' => __( 'No coupons found.', 'bookly' ),
|
38 |
+
'processing' => __( 'Processing...', 'bookly' ),
|
39 |
+
'are_you_sure' => __( 'Are you sure?', 'bookly' ),
|
40 |
+
'selector' => array(
|
41 |
+
'all_selected' => __( 'All services', 'bookly' ),
|
42 |
+
'nothing_selected' => __( 'No service selected', 'bookly' ),
|
43 |
+
'collection' => array(),
|
44 |
+
),
|
45 |
+
'limitations' => __( '<b class="h4">This function is disabled in the Lite version of Bookly.</b><br><br>If you find the plugin useful for your business please consider buying a licence for the full version.<br>It costs just $59 and for this money you will get many useful functions, lifetime free updates and excellent support!<br>More information can be found here', 'bookly' ) . ': <a href="http://booking-wp-plugin.com" target="_blank" class="alert-link">http://booking-wp-plugin.com</a>',
|
46 |
+
) );
|
47 |
+
|
48 |
+
$this->render( 'index' );
|
49 |
+
}
|
50 |
+
|
51 |
+
// Protected methods.
|
52 |
+
|
53 |
+
/**
|
54 |
+
* Override parent method to add 'wp_ajax_ab_' prefix
|
55 |
+
* so current 'execute*' methods look nicer.
|
56 |
+
*
|
57 |
+
* @param string $prefix
|
58 |
+
*/
|
59 |
+
protected function registerWpActions( $prefix = '' )
|
60 |
+
{
|
61 |
+
parent::registerWpActions( 'wp_ajax_ab_' );
|
62 |
+
}
|
63 |
+
|
64 |
+
}
|
backend/modules/coupons/forms/Coupon.php
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace BooklyLite\Backend\Modules\Coupons\Forms;
|
3 |
+
|
4 |
+
use BooklyLite\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Coupon
|
8 |
+
* @package BooklyLite\Backend\Modules\Coupons\Forms
|
9 |
+
*/
|
10 |
+
class Coupon extends Lib\Base\Form
|
11 |
+
{
|
12 |
+
protected static $entity_class = 'Coupon';
|
13 |
+
|
14 |
+
public function configure()
|
15 |
+
{
|
16 |
+
$this->setFields( array( 'id', 'code', 'discount', 'deduction', 'usage_limit' ) );
|
17 |
+
}
|
18 |
+
|
19 |
+
}
|
backend/modules/coupons/resources/css/coupons.css
DELETED
@@ -1,20 +0,0 @@
|
|
1 |
-
#ab_coupons_wrapper .list-wrapper .list-actions { overflow: hidden; margin-top: 10px; }
|
2 |
-
#ab_coupons_wrapper .list-wrapper .list-actions .add-service { float: left }
|
3 |
-
#ab_coupons_wrapper .list-wrapper .list-actions .delete { float: right }
|
4 |
-
#coupons_list td.last,
|
5 |
-
#coupons_list th.last { width: 16px; vertical-align: middle; }
|
6 |
-
#coupons_list th, #coupons_list td { padding:5px }
|
7 |
-
#coupons_list .service-color-cell { width: 28px; }
|
8 |
-
|
9 |
-
#ab-coupons-list .displayed-value { border: 1px solid transparent; padding: 4px 12px; min-height: 20px; }
|
10 |
-
#ab-coupons-list .ab-text-focus,
|
11 |
-
#ab-coupons-list .ab-text-focus:focus { text-align: right; padding-right: 2px; width: 58px; margin: 0!important; height: 30px; }
|
12 |
-
#ab-coupons-list .displayed-value:hover {
|
13 |
-
border: 1px solid #aaa;
|
14 |
-
background: #fff;
|
15 |
-
border-radius: 3px;
|
16 |
-
-webkit-box-shadow: 1px 1px 2px rgba(0,0,0,0.1);
|
17 |
-
-moz-box-shadow: 1px 1px 2px rgba(0,0,0,0.1);
|
18 |
-
-o-box-shadow: 1px 1px 2px rgba(0,0,0,0.1);
|
19 |
-
box-shadow: 1px 1px 2px rgba(0,0,0,0.1);
|
20 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/modules/coupons/resources/js/coupons.js
CHANGED
@@ -1,5 +1,64 @@
|
|
1 |
jQuery(function($) {
|
2 |
-
|
3 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4 |
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5 |
});
|
1 |
jQuery(function($) {
|
2 |
+
|
3 |
+
var
|
4 |
+
$coupons_list = $('#bookly-coupons-list'),
|
5 |
+
$coupon_modal = $('#bookly-coupon-modal'),
|
6 |
+
$save_button = $('#bookly-coupon-save')
|
7 |
+
;
|
8 |
+
|
9 |
+
/**
|
10 |
+
* Init DataTables.
|
11 |
+
*/
|
12 |
+
var dt = $coupons_list.DataTable({
|
13 |
+
order: [[ 0, "asc" ]],
|
14 |
+
paging: false,
|
15 |
+
info: false,
|
16 |
+
searching: false,
|
17 |
+
processing: true,
|
18 |
+
responsive: true,
|
19 |
+
data: [],
|
20 |
+
|
21 |
+
columns: [
|
22 |
+
{ data: "code" },
|
23 |
+
{ data: "discount" },
|
24 |
+
{ data: "deduction" },
|
25 |
+
{ data: 'service_ids'},
|
26 |
+
{ data: "usage_limit" },
|
27 |
+
{ data: "used" },
|
28 |
+
{
|
29 |
+
responsivePriority: 1,
|
30 |
+
orderable: false,
|
31 |
+
searchable: false,
|
32 |
+
render: function ( data, type, row, meta ) {
|
33 |
+
return '';
|
34 |
+
}
|
35 |
+
},
|
36 |
+
{
|
37 |
+
responsivePriority: 1,
|
38 |
+
orderable: false,
|
39 |
+
searchable: false,
|
40 |
+
render: function ( data, type, row, meta ) {
|
41 |
+
return '';
|
42 |
+
}
|
43 |
+
}
|
44 |
+
],
|
45 |
+
language: {
|
46 |
+
zeroRecords: BooklyL10n.zeroRecords,
|
47 |
+
processing: BooklyL10n.processing
|
48 |
+
}
|
49 |
});
|
50 |
+
|
51 |
+
/**
|
52 |
+
* Save coupon.
|
53 |
+
*/
|
54 |
+
$save_button.on('click', function (e) {
|
55 |
+
e.preventDefault();
|
56 |
+
$coupon_modal.modal('hide');
|
57 |
+
booklyAlert({error: [BooklyL10n.limitations]});
|
58 |
+
});
|
59 |
+
$('#bookly-add,#bookly-delete').on('click', function (e) {
|
60 |
+
e.preventDefault();
|
61 |
+
booklyAlert({error: [BooklyL10n.limitations]});
|
62 |
+
});
|
63 |
+
|
64 |
});
|
backend/modules/coupons/templates/_list.php
DELETED
@@ -1,4 +0,0 @@
|
|
1 |
-
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
|
2 |
-
<div class="alert alert-warning" >
|
3 |
-
<?php _e('This function is disabled in the lite version of Bookly. If you find the plugin useful for your business please consider buying a licence for the full version. It costs just $46 and for this money you will get many useful functions, lifetime free update and excellent support! More information can be found here', 'bookly'); ?>: <a href="http://booking-wp-plugin.com" target="_blank">http://booking-wp-plugin.com</a>
|
4 |
-
</div>
|
|
|
|
|
|
|
|
backend/modules/coupons/templates/_modal.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
|
backend/modules/coupons/templates/index.php
CHANGED
@@ -1,35 +1,45 @@
|
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
|
2 |
-
|
3 |
-
<div
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
<div class="panel-body">
|
8 |
-
<div class="list-wrapper">
|
9 |
-
<div id="ab-coupons-list">
|
10 |
-
<?php include '_list.php' ?>
|
11 |
</div>
|
12 |
</div>
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
18 |
</div>
|
19 |
</div>
|
20 |
-
|
21 |
-
|
22 |
-
<div class="modal-dialog">
|
23 |
-
<div class="modal-content">
|
24 |
-
<div class="modal-header">
|
25 |
-
<h4 class="modal-title"><?php _e('Notice', 'bookly') ?></h4>
|
26 |
-
</div>
|
27 |
-
<div class="modal-body">
|
28 |
-
<?php _e('This function is disabled in the lite version of Bookly. If you find the plugin useful for your business please consider buying a licence for the full version. It costs just $46 and for this money you will get many useful functions, lifetime free update and excellent support! More information can be found here', 'bookly'); ?>: <a href="http://booking-wp-plugin.com" target="_blank">http://booking-wp-plugin.com</a>
|
29 |
-
</div>
|
30 |
-
<div class="modal-footer">
|
31 |
-
<button type="button" class="btn btn-default" data-dismiss="modal"><?php _e('Close', 'bookly') ?></button>
|
32 |
-
</div>
|
33 |
-
</div><!-- /.modal-content -->
|
34 |
-
</div><!-- /.modal-dialog -->
|
35 |
-
</div><!-- /.modal -->
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
|
2 |
+
<div id="bookly-tbs" class="wrap">
|
3 |
+
<div class="bookly-tbs-body">
|
4 |
+
<div class="page-header text-right clearfix">
|
5 |
+
<div class="bookly-page-title">
|
6 |
+
<?php _e( 'Coupons', 'bookly' ) ?>
|
|
|
|
|
|
|
|
|
7 |
</div>
|
8 |
</div>
|
9 |
+
<div class="panel panel-default bookly-main">
|
10 |
+
<div class="panel-body">
|
11 |
+
<div class="form-inline bookly-margin-bottom-lg text-right">
|
12 |
+
<div class="form-group">
|
13 |
+
<button type="button"
|
14 |
+
id="bookly-add"
|
15 |
+
class="btn btn-success">
|
16 |
+
<i class="glyphicon glyphicon-plus"></i> <?php _e( 'Add Coupon', 'bookly' ) ?>
|
17 |
+
</button>
|
18 |
+
</div>
|
19 |
+
</div>
|
20 |
+
|
21 |
+
<table class="table table-striped"
|
22 |
+
id="bookly-coupons-list"
|
23 |
+
width="100%">
|
24 |
+
<thead>
|
25 |
+
<tr>
|
26 |
+
<th><?php _e( 'Code', 'bookly' ) ?></th>
|
27 |
+
<th><?php _e( 'Discount (%)', 'bookly' ) ?></th>
|
28 |
+
<th><?php _e( 'Deduction', 'bookly' ) ?></th>
|
29 |
+
<th><?php _e( 'Services', 'bookly' ) ?></th>
|
30 |
+
<th><?php _e( 'Usage limit', 'bookly' ) ?></th>
|
31 |
+
<th><?php _e( 'Number of times used', 'bookly' ) ?></th>
|
32 |
+
<th></th>
|
33 |
+
<th width="16"><input type="checkbox" id="bookly-check-all"></th>
|
34 |
+
</tr>
|
35 |
+
</thead>
|
36 |
+
</table>
|
37 |
+
|
38 |
+
<div class="text-right bookly-margin-top-lg">
|
39 |
+
<?php \BooklyLite\Lib\Utils\Common::deleteButton() ?>
|
40 |
+
</div>
|
41 |
+
</div>
|
42 |
</div>
|
43 |
</div>
|
44 |
+
<?php include '_modal.php' ?>
|
45 |
+
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/modules/custom_fields/AB_CustomFieldsController.php
DELETED
@@ -1,63 +0,0 @@
|
|
1 |
-
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Class AB_CustomFieldsController
|
5 |
-
*/
|
6 |
-
class AB_CustomFieldsController extends AB_Controller {
|
7 |
-
|
8 |
-
/**
|
9 |
-
* Default Action
|
10 |
-
*/
|
11 |
-
public function index()
|
12 |
-
{
|
13 |
-
$this->enqueueStyles( array(
|
14 |
-
'module' => array(
|
15 |
-
'css/custom_fields.css'
|
16 |
-
),
|
17 |
-
'frontend' => array(
|
18 |
-
'css/ladda.min.css'
|
19 |
-
),
|
20 |
-
'backend' => array(
|
21 |
-
'css/bookly.main-backend.css',
|
22 |
-
'bootstrap/css/bootstrap.min.css',
|
23 |
-
)
|
24 |
-
) );
|
25 |
-
|
26 |
-
$this->enqueueScripts( array(
|
27 |
-
'module' => array(
|
28 |
-
'js/custom_fields.js' => array( 'jquery-ui-sortable' )
|
29 |
-
),
|
30 |
-
'frontend' => array(
|
31 |
-
'js/spin.min.js' => array( 'jquery' ),
|
32 |
-
'js/ladda.min.js' => array( 'jquery' ),
|
33 |
-
)
|
34 |
-
) );
|
35 |
-
|
36 |
-
wp_localize_script( 'ab-custom_fields.js', 'BooklyL10n', array(
|
37 |
-
'custom_fields' => get_option( 'ab_custom_fields' )
|
38 |
-
) );
|
39 |
-
|
40 |
-
$this->render( 'index' );
|
41 |
-
} // index
|
42 |
-
|
43 |
-
/**
|
44 |
-
* Save custom fields.
|
45 |
-
*/
|
46 |
-
public function executeSaveCustomFields()
|
47 |
-
{
|
48 |
-
update_option( 'ab_custom_fields', $this->getParameter( 'fields' ) );
|
49 |
-
|
50 |
-
wp_send_json_success();
|
51 |
-
}
|
52 |
-
|
53 |
-
/**
|
54 |
-
* Override parent method to add 'wp_ajax_ab_' prefix
|
55 |
-
* so current 'execute*' methods look nicer.
|
56 |
-
*
|
57 |
-
* @param string $prefix
|
58 |
-
*/
|
59 |
-
protected function registerWpActions( $prefix = '' )
|
60 |
-
{
|
61 |
-
parent::registerWpActions( 'wp_ajax_ab_' );
|
62 |
-
}
|
63 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/modules/custom_fields/Controller.php
ADDED
@@ -0,0 +1,88 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace BooklyLite\Backend\Modules\CustomFields;
|
3 |
+
|
4 |
+
use BooklyLite\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Controller
|
8 |
+
* @package BooklyLite\Backend\Modules\CustomFields
|
9 |
+
*/
|
10 |
+
class Controller extends Lib\Base\Controller
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Default Action
|
14 |
+
*/
|
15 |
+
public function index()
|
16 |
+
{
|
17 |
+
$this->enqueueStyles( array(
|
18 |
+
'frontend' => array( 'css/ladda.min.css' ),
|
19 |
+
'backend' => array( 'bootstrap/css/bootstrap-theme.min.css', ),
|
20 |
+
) );
|
21 |
+
|
22 |
+
$this->enqueueScripts( array(
|
23 |
+
'module' => array( 'js/custom_fields.js' => array( 'jquery-ui-sortable' ) ),
|
24 |
+
'frontend' => array(
|
25 |
+
'js/spin.min.js' => array( 'jquery' ),
|
26 |
+
'js/ladda.min.js' => array( 'jquery' ),
|
27 |
+
),
|
28 |
+
'backend' => array(
|
29 |
+
'bootstrap/js/bootstrap.min.js' => array( 'jquery' ),
|
30 |
+
'js/alert.js' => array( 'jquery' ),
|
31 |
+
),
|
32 |
+
) );
|
33 |
+
|
34 |
+
wp_localize_script( 'ab-custom_fields.js', 'BooklyL10n', array(
|
35 |
+
'custom_fields' => get_option( 'ab_custom_fields' ),
|
36 |
+
'saved' => __( 'Settings saved.', 'bookly' ),
|
37 |
+
'selector' => array(
|
38 |
+
'all_selected' => __( 'All services', 'bookly' ),
|
39 |
+
'nothing_selected' => __( 'No service selected', 'bookly' ),
|
40 |
+
),
|
41 |
+
'limitations' => __( '<b class="h4">This function is disabled in the Lite version of Bookly.</b><br><br>If you find the plugin useful for your business please consider buying a licence for the full version.<br>It costs just $59 and for this money you will get many useful functions, lifetime free updates and excellent support!<br>More information can be found here', 'bookly' ) . ': <a href="http://booking-wp-plugin.com" target="_blank" class="alert-link">http://booking-wp-plugin.com</a>',
|
42 |
+
) );
|
43 |
+
|
44 |
+
$services = $this->render( '_services', array( 'services' => Lib\Entities\Service::query()->select( 'id, title' )->where( 'type', Lib\Entities\Service::TYPE_SIMPLE )->fetchArray() ), false );
|
45 |
+
$this->render( 'index', array( 'services_html' => $services ) );
|
46 |
+
}
|
47 |
+
|
48 |
+
/**
|
49 |
+
* Save custom fields.
|
50 |
+
*/
|
51 |
+
public function executeSaveCustomFields()
|
52 |
+
{
|
53 |
+
$custom_fields = $this->getParameter( 'fields' );
|
54 |
+
foreach ( json_decode( $custom_fields ) as $custom_field ) {
|
55 |
+
switch ( $custom_field->type ) {
|
56 |
+
case 'textarea':
|
57 |
+
case 'text-content':
|
58 |
+
case 'text-field':
|
59 |
+
case 'captcha':
|
60 |
+
do_action( 'wpml_register_single_string', 'bookly', 'custom_field_' . $custom_field->id . '_' .sanitize_title( $custom_field->label ), $custom_field->label );
|
61 |
+
break;
|
62 |
+
case 'checkboxes':
|
63 |
+
case 'radio-buttons':
|
64 |
+
case 'drop-down':
|
65 |
+
do_action( 'wpml_register_single_string', 'bookly', 'custom_field_' . $custom_field->id . '_' . sanitize_title( $custom_field->label ), $custom_field->label );
|
66 |
+
foreach ( $custom_field->items as $label ) {
|
67 |
+
do_action( 'wpml_register_single_string', 'bookly', 'custom_field_' . $custom_field->id . '_' . sanitize_title( $custom_field->label ) . '=' . sanitize_title( $label ), $label );
|
68 |
+
}
|
69 |
+
break;
|
70 |
+
}
|
71 |
+
}
|
72 |
+
update_option( 'ab_custom_fields', $custom_fields );
|
73 |
+
update_option( 'ab_custom_fields_per_service', '0' );
|
74 |
+
wp_send_json_success();
|
75 |
+
}
|
76 |
+
|
77 |
+
/**
|
78 |
+
* Override parent method to add 'wp_ajax_ab_' prefix
|
79 |
+
* so current 'execute*' methods look nicer.
|
80 |
+
*
|
81 |
+
* @param string $prefix
|
82 |
+
*/
|
83 |
+
protected function registerWpActions( $prefix = '' )
|
84 |
+
{
|
85 |
+
parent::registerWpActions( 'wp_ajax_ab_' );
|
86 |
+
}
|
87 |
+
|
88 |
+
}
|
backend/modules/custom_fields/resources/css/custom_fields.css
DELETED
@@ -1,86 +0,0 @@
|
|
1 |
-
ul#ab-custom-fields {
|
2 |
-
list-style-type: none;
|
3 |
-
margin: 0 0 15px 0;
|
4 |
-
padding: 0;
|
5 |
-
}
|
6 |
-
ul#ab-custom-fields .input-group { margin-left: 15px; }
|
7 |
-
ul#ab-custom-fields > li {
|
8 |
-
margin: 0 0 10px;
|
9 |
-
padding: 15px 20px;
|
10 |
-
background: #fff;
|
11 |
-
border: 1px solid #ddd;
|
12 |
-
}
|
13 |
-
ul#ab-custom-fields > li .ab-field-title {
|
14 |
-
font-size: 16px;
|
15 |
-
font-weight: normal;
|
16 |
-
margin: 2px 0px 13px 0px;
|
17 |
-
line-height: normal;
|
18 |
-
}
|
19 |
-
ul#ab-custom-fields > li input[type=text] {
|
20 |
-
margin-bottom: 0;
|
21 |
-
width: 300px;
|
22 |
-
}
|
23 |
-
ul#ab-custom-fields > li input.ab-label {
|
24 |
-
width: 322px;
|
25 |
-
}
|
26 |
-
ul#ab-custom-fields > li .input-group {
|
27 |
-
margin-bottom: 0;
|
28 |
-
}
|
29 |
-
ul#ab-custom-fields > li .ab-required {
|
30 |
-
margin: -2px 0 0 0;
|
31 |
-
border-radius: 0;
|
32 |
-
-webkit-border-radius: 0;
|
33 |
-
}
|
34 |
-
ul#ab-custom-fields > li span.input-group-addon {
|
35 |
-
width: auto;
|
36 |
-
}
|
37 |
-
ul#ab-custom-fields > li span.input-group-addon label {
|
38 |
-
margin: 0;
|
39 |
-
}
|
40 |
-
ul#ab-custom-fields > li span.input-group-addon span {
|
41 |
-
display: inline-block;
|
42 |
-
position: relative;
|
43 |
-
top: 1px;
|
44 |
-
font-size: 13px;
|
45 |
-
}
|
46 |
-
ul#ab-custom-fields > li button {
|
47 |
-
margin-left: 37px;
|
48 |
-
}
|
49 |
-
ul#ab-custom-fields > li i.ab-handle{
|
50 |
-
display: block;
|
51 |
-
float: left;
|
52 |
-
margin: 0 8px 0 -8px;
|
53 |
-
line-height: 20px;
|
54 |
-
text-align: center;
|
55 |
-
cursor: move;
|
56 |
-
}
|
57 |
-
ul#ab-custom-fields > li i.ab-inner-handle {
|
58 |
-
float: left;
|
59 |
-
margin: 10px 8px 0 0;
|
60 |
-
cursor: move;
|
61 |
-
}
|
62 |
-
ul#ab-custom-fields ul.ab-items {
|
63 |
-
margin: 0 0 15px 0;
|
64 |
-
}
|
65 |
-
ul#ab-custom-fields ul.ab-items > li {
|
66 |
-
margin: 0;
|
67 |
-
padding: 15px 15px 0 15px;
|
68 |
-
background: transparent;
|
69 |
-
}
|
70 |
-
ul#ab-custom-fields li .ab-delete {
|
71 |
-
cursor: pointer;
|
72 |
-
}
|
73 |
-
#ab-add-fields { margin-bottom: 15px; }
|
74 |
-
|
75 |
-
@media screen and (max-width: 782px) {
|
76 |
-
ul#ab-custom-fields > li .ab-required {
|
77 |
-
height: 16px;
|
78 |
-
width: 16px;
|
79 |
-
}
|
80 |
-
ul#ab-custom-fields > li input[type=text] {
|
81 |
-
width: 127px;
|
82 |
-
}
|
83 |
-
ul#ab-custom-fields > li input.ab-label {
|
84 |
-
width: 150px;
|
85 |
-
}
|
86 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/modules/custom_fields/resources/js/custom_fields.js
CHANGED
@@ -1,10 +1,19 @@
|
|
1 |
jQuery(function($) {
|
2 |
|
3 |
-
var $fields = $("
|
|
|
4 |
|
5 |
$fields.sortable({
|
6 |
axis : 'y',
|
7 |
-
handle : '.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
8 |
});
|
9 |
|
10 |
/**
|
@@ -29,7 +38,8 @@ jQuery(function($) {
|
|
29 |
/**
|
30 |
* Delete field or checkbox/radio button/drop-down option.
|
31 |
*/
|
32 |
-
$fields.on('click', '.ab-delete', function() {
|
|
|
33 |
$(this).closest('li').fadeOut('fast', function() { $(this).remove(); });
|
34 |
});
|
35 |
|
@@ -40,22 +50,27 @@ jQuery(function($) {
|
|
40 |
e.preventDefault();
|
41 |
var data = [];
|
42 |
$fields.children('li').each(function() {
|
43 |
-
var $this = $(this)
|
44 |
-
|
45 |
switch ($this.data('type')) {
|
46 |
case 'checkboxes':
|
47 |
case 'radio-buttons':
|
48 |
case 'drop-down':
|
49 |
field.items = [];
|
50 |
-
$this.find('li').each(function() {
|
51 |
field.items.push($(this).find('input').val());
|
52 |
});
|
53 |
-
case 'text-field':
|
54 |
case 'textarea':
|
|
|
|
|
|
|
55 |
field.type = $this.data('type');
|
56 |
field.label = $this.find('.ab-label').val();
|
57 |
field.required = $this.find('.ab-required').prop('checked');
|
58 |
field.id = $this.data('ab-field-id');
|
|
|
|
|
|
|
59 |
}
|
60 |
data.push(field);
|
61 |
});
|
@@ -66,9 +81,10 @@ jQuery(function($) {
|
|
66 |
type : 'POST',
|
67 |
url : ajaxurl,
|
68 |
xhrFields : { withCredentials: true },
|
69 |
-
data : { action: 'ab_save_custom_fields', fields: JSON.stringify(data) },
|
70 |
complete : function() {
|
71 |
ladda.stop();
|
|
|
72 |
}
|
73 |
});
|
74 |
});
|
@@ -88,9 +104,10 @@ jQuery(function($) {
|
|
88 |
* @param id
|
89 |
* @param label
|
90 |
* @param required
|
91 |
-
* @
|
|
|
92 |
*/
|
93 |
-
function addField(type, id, label, required) {
|
94 |
var $new_field = $('ul#ab-templates > li[data-type=' + type + ']').clone();
|
95 |
// Set id, label and required.
|
96 |
if (typeof id == 'undefined') {
|
@@ -111,14 +128,22 @@ jQuery(function($) {
|
|
111 |
})
|
112 |
.next('label').attr('for', 'required-' + id)
|
113 |
.end().end()
|
114 |
-
.find('.ab-label').val(label)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
115 |
// Add new field to the list.
|
116 |
$fields.append($new_field);
|
117 |
$new_field.fadeIn('fast');
|
118 |
// Make it sortable.
|
119 |
$new_field.find('ul.ab-items').sortable({
|
120 |
axis : 'y',
|
121 |
-
handle : '.
|
122 |
});
|
123 |
// Set focus to label field.
|
124 |
$new_field.find('.ab-label').focus();
|
@@ -150,17 +175,59 @@ jQuery(function($) {
|
|
150 |
function restoreFields() {
|
151 |
if (BooklyL10n.custom_fields) {
|
152 |
var custom_fields = jQuery.parseJSON(BooklyL10n.custom_fields);
|
153 |
-
$.each(custom_fields, function(i, field) {
|
154 |
-
var $new_field = addField(field.type, field.id, field.label, field.required);
|
155 |
-
|
156 |
-
//add children
|
157 |
if (field.items) {
|
158 |
-
$.each(field.items, function(i, value) {
|
159 |
-
addItem($new_field.find('ul'), field.type + '-item', value);
|
160 |
});
|
161 |
}
|
162 |
});
|
163 |
}
|
|
|
|
|
|
|
|
|
164 |
$(':focus').blur();
|
165 |
}
|
166 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
jQuery(function($) {
|
2 |
|
3 |
+
var $fields = $("#ab-custom-fields"),
|
4 |
+
$cf_per_service = $('#ab_custom_fields_per_service');
|
5 |
|
6 |
$fields.sortable({
|
7 |
axis : 'y',
|
8 |
+
handle : '.bookly-js-handle'
|
9 |
+
});
|
10 |
+
|
11 |
+
$cf_per_service.change(function() {
|
12 |
+
if($(this).val() == 1){
|
13 |
+
$(this).val('0');
|
14 |
+
booklyAlert({error: [BooklyL10n.limitations]});
|
15 |
+
$(this).find('option:gt(0)').prop('disabled', true);
|
16 |
+
}
|
17 |
});
|
18 |
|
19 |
/**
|
38 |
/**
|
39 |
* Delete field or checkbox/radio button/drop-down option.
|
40 |
*/
|
41 |
+
$fields.on('click', '.ab-delete', function(e) {
|
42 |
+
e.preventDefault();
|
43 |
$(this).closest('li').fadeOut('fast', function() { $(this).remove(); });
|
44 |
});
|
45 |
|
50 |
e.preventDefault();
|
51 |
var data = [];
|
52 |
$fields.children('li').each(function() {
|
53 |
+
var $this = $(this),
|
54 |
+
field = {};
|
55 |
switch ($this.data('type')) {
|
56 |
case 'checkboxes':
|
57 |
case 'radio-buttons':
|
58 |
case 'drop-down':
|
59 |
field.items = [];
|
60 |
+
$this.find('ul.ab-items li').each(function() {
|
61 |
field.items.push($(this).find('input').val());
|
62 |
});
|
|
|
63 |
case 'textarea':
|
64 |
+
case 'text-field':
|
65 |
+
case 'text-content':
|
66 |
+
case 'captcha':
|
67 |
field.type = $this.data('type');
|
68 |
field.label = $this.find('.ab-label').val();
|
69 |
field.required = $this.find('.ab-required').prop('checked');
|
70 |
field.id = $this.data('ab-field-id');
|
71 |
+
field.services = $this.find('.ab--services-holder input:checked')
|
72 |
+
.map(function() { return this.value; })
|
73 |
+
.get();
|
74 |
}
|
75 |
data.push(field);
|
76 |
});
|
81 |
type : 'POST',
|
82 |
url : ajaxurl,
|
83 |
xhrFields : { withCredentials: true },
|
84 |
+
data : { action: 'ab_save_custom_fields', fields: JSON.stringify(data), cf_per_service: $cf_per_service.val() },
|
85 |
complete : function() {
|
86 |
ladda.stop();
|
87 |
+
booklyAlert({success : [BooklyL10n.saved]});
|
88 |
}
|
89 |
});
|
90 |
});
|
104 |
* @param id
|
105 |
* @param label
|
106 |
* @param required
|
107 |
+
* @param services
|
108 |
+
* @returns {*|jQuery}
|
109 |
*/
|
110 |
+
function addField(type, id, label, required, services) {
|
111 |
var $new_field = $('ul#ab-templates > li[data-type=' + type + ']').clone();
|
112 |
// Set id, label and required.
|
113 |
if (typeof id == 'undefined') {
|
128 |
})
|
129 |
.next('label').attr('for', 'required-' + id)
|
130 |
.end().end()
|
131 |
+
.find('.ab-label').val(label)
|
132 |
+
.end()
|
133 |
+
.find('.ab--services-holder input:checkbox').each(function (index) {
|
134 |
+
if (services && $.inArray(this.value, services) > -1) {
|
135 |
+
this.checked = true;
|
136 |
+
}
|
137 |
+
this.id = 'check-' + id + '-' + index;
|
138 |
+
$(this).next().attr('for', 'check-' + id + '-' + index);
|
139 |
+
});
|
140 |
// Add new field to the list.
|
141 |
$fields.append($new_field);
|
142 |
$new_field.fadeIn('fast');
|
143 |
// Make it sortable.
|
144 |
$new_field.find('ul.ab-items').sortable({
|
145 |
axis : 'y',
|
146 |
+
handle : '.bookly-js-handle'
|
147 |
});
|
148 |
// Set focus to label field.
|
149 |
$new_field.find('.ab-label').focus();
|
175 |
function restoreFields() {
|
176 |
if (BooklyL10n.custom_fields) {
|
177 |
var custom_fields = jQuery.parseJSON(BooklyL10n.custom_fields);
|
178 |
+
$.each(custom_fields, function (i, field) {
|
179 |
+
var $new_field = addField(field.type, field.id, field.label, field.required, field.services);
|
180 |
+
// add children
|
|
|
181 |
if (field.items) {
|
182 |
+
$.each(field.items, function (i, value) {
|
183 |
+
addItem($new_field.find('ul.ab-items'), field.type + '-item', value);
|
184 |
});
|
185 |
}
|
186 |
});
|
187 |
}
|
188 |
+
$cf_per_service.change();
|
189 |
+
$('.ab--services-holder').each(function (id, elem) {
|
190 |
+
updateServiceButton($(elem));
|
191 |
+
});
|
192 |
$(':focus').blur();
|
193 |
}
|
194 |
+
|
195 |
+
$('.ab-popover').popover({trigger: 'hover'});
|
196 |
+
|
197 |
+
function updateServiceButton($holder) {
|
198 |
+
var service_checked = $holder.find('.bookly-js-check-entity:checked').length;
|
199 |
+
if (service_checked == 0) {
|
200 |
+
$holder.find('.ab-count').text(BooklyL10n.selector.nothing_selected);
|
201 |
+
$holder.find('.bookly-check-all-entities').prop('checked', false);
|
202 |
+
} else if (service_checked == 1) {
|
203 |
+
$holder.find('.ab-count').text($holder.find('.bookly-js-check-entity:checked').data('title'));
|
204 |
+
$holder.find('.bookly-check-all-entities').prop('checked', (service_checked == $holder.find('.bookly-js-check-entity').length));
|
205 |
+
} else {
|
206 |
+
if( service_checked == $holder.find('.bookly-js-check-entity').length) {
|
207 |
+
$holder.find('.bookly-check-all-entities').prop('checked', true);
|
208 |
+
$holder.find('.ab-count').text(BooklyL10n.selector.all_selected);
|
209 |
+
} else {
|
210 |
+
$holder.find('.bookly-check-all-entities').prop('checked', false);
|
211 |
+
$holder.find('.ab-count').text(service_checked + '/' + $holder.find('.bookly-js-check-entity').length);
|
212 |
+
}
|
213 |
+
}
|
214 |
+
}
|
215 |
+
|
216 |
+
$(document).on('click', '.bookly-check-all-entities', function () {
|
217 |
+
var $holder = $(this).parents('.ab--services-holder');
|
218 |
+
$holder.find('.bookly-js-check-entity').prop('checked', $(this).prop('checked'));
|
219 |
+
updateServiceButton($holder);
|
220 |
+
});
|
221 |
+
|
222 |
+
$(document).on('click', '.ab--services-holder ul.dropdown-menu li a[href]', function (e) {
|
223 |
+
updateServiceButton($(this).parents('.ab--services-holder'));
|
224 |
+
e.stopPropagation();
|
225 |
+
});
|
226 |
+
|
227 |
+
$('[data-toggle="popover"]').popover({
|
228 |
+
html: true,
|
229 |
+
placement: 'top',
|
230 |
+
trigger: 'hover',
|
231 |
+
template: '<div class="popover bookly-font-xs" style="width: 220px" role="tooltip"><div class="popover-arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'
|
232 |
+
});
|
233 |
+
});
|
backend/modules/custom_fields/templates/_services.php
ADDED
@@ -0,0 +1,32 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
|
2 |
+
<div class="btn-group ab--services-holder bookly-margin-top-screenxs-sm" style="display: none;">
|
3 |
+
<button class="btn btn-default btn-block dropdown-toggle bookly-flexbox" data-toggle="dropdown">
|
4 |
+
<div class="bookly-flex-cell">
|
5 |
+
<i class="glyphicon glyphicon-tag bookly-margin-right-md"></i>
|
6 |
+
</div>
|
7 |
+
<div class="bookly-flex-cell text-left" style="width: 100%">
|
8 |
+
<span class="ab-count"></span>
|
9 |
+
</div>
|
10 |
+
<div class="bookly-flex-cell"><div class="bookly-margin-left-md"><span class="caret"></span></div></div>
|
11 |
+
</button>
|
12 |
+
<ul class="dropdown-menu bookly-entity-selector">
|
13 |
+
<li>
|
14 |
+
<a class="checkbox" href="javascript:void(0)">
|
15 |
+
<label>
|
16 |
+
<input type="checkbox" class="bookly-check-all-entities">
|
17 |
+
<?php _e( 'All services', 'bookly' ) ?>
|
18 |
+
</label>
|
19 |
+
</a>
|
20 |
+
</li>
|
21 |
+
<?php foreach ( $services as $service ) : ?>
|
22 |
+
<li>
|
23 |
+
<a class="checkbox" href="javascript:void(0)">
|
24 |
+
<label>
|
25 |
+
<input type="checkbox" class="bookly-js-check-entity" value="<?php echo $service['id'] ?>" data-title="<?php echo esc_attr( $service['title'] ) ?>">
|
26 |
+
<?php echo $service['title'] ?>
|
27 |
+
</label>
|
28 |
+
</a>
|
29 |
+
</li>
|
30 |
+
<?php endforeach ?>
|
31 |
+
</ul>
|
32 |
+
</div>
|
backend/modules/custom_fields/templates/index.php
CHANGED
@@ -1,134 +1,308 @@
|
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
|
2 |
-
<div
|
3 |
-
<div class="
|
4 |
-
<
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
<div id="ab-add-fields">
|
10 |
-
<button class="button" data-type="text-field"><i class="glyphicon glyphicon-plus"></i> <?php _e( 'Text Field', 'bookly' ) ?></button>
|
11 |
-
<button class="button" data-type="textarea"><i class="glyphicon glyphicon-plus"></i> <?php _e( 'Text Area', 'bookly' ) ?></button>
|
12 |
-
<button class="button" data-type="checkboxes"><i class="glyphicon glyphicon-plus"></i> <?php _e( 'Checkbox Group', 'bookly' ) ?></button>
|
13 |
-
<button class="button" data-type="radio-buttons"><i class="glyphicon glyphicon-plus"></i> <?php _e( 'Radio Button Group', 'bookly' ) ?></button>
|
14 |
-
<button class="button" data-type="drop-down"><i class="glyphicon glyphicon-plus"></i> <?php _e( 'Drop Down', 'bookly' ) ?></button>
|
15 |
</div>
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
<label>
|
29 |
-
<input class="ab-required" type="checkbox" />
|
30 |
-
<span><?php _e( 'Required field', 'bookly' ) ?></span>
|
31 |
-
</label>
|
32 |
-
</span>
|
33 |
-
</div>
|
34 |
-
</li>
|
35 |
-
|
36 |
-
<li data-type="textarea">
|
37 |
-
<i class="ab-handle glyphicon glyphicon-align-justify" title="<?php esc_attr_e( 'Reorder', 'bookly' ) ?>"></i>
|
38 |
-
<h2 class="ab-field-title">
|
39 |
-
<?php _e( 'Text Area', 'bookly' ) ?>
|
40 |
-
<i class="ab-delete glyphicon glyphicon-trash" title="<?php echo esc_attr( __( 'Remove field', 'bookly' ) ) ?>"></i>
|
41 |
-
</h2>
|
42 |
-
<div class="input-group">
|
43 |
-
<input class="ab-label form-control" type="text" value="" placeholder="<?php echo esc_attr( __( 'Enter a label', 'bookly' ) ) ?>" />
|
44 |
-
<span class="input-group-addon">
|
45 |
-
<label>
|
46 |
-
<input class="ab-required" type="checkbox" />
|
47 |
-
<span><?php _e( 'Required field', 'bookly' ) ?></span>
|
48 |
-
</label>
|
49 |
-
</span>
|
50 |
-
</div>
|
51 |
-
</li>
|
52 |
-
|
53 |
-
<li data-type="checkboxes">
|
54 |
-
<i class="ab-handle glyphicon glyphicon-align-justify" title="<?php esc_attr_e( 'Reorder', 'bookly' ) ?>"></i>
|
55 |
-
<h2 class="ab-field-title">
|
56 |
-
<?php _e( 'Checkbox Group', 'bookly' ) ?>
|
57 |
-
<i class="ab-delete glyphicon glyphicon-trash" title="<?php echo esc_attr( __( 'Remove field', 'bookly' ) ) ?>"></i>
|
58 |
-
</h2>
|
59 |
-
<div class="input-group">
|
60 |
-
<input class="ab-label form-control" type="text" value="" placeholder="<?php echo esc_attr( __( 'Enter a label', 'bookly' ) ) ?>" />
|
61 |
-
<span class="input-group-addon">
|
62 |
-
<label>
|
63 |
-
<input class="ab-required" type="checkbox" />
|
64 |
-
<span><?php _e( 'Required field', 'bookly' ) ?></span>
|
65 |
-
</label>
|
66 |
-
</span>
|
67 |
</div>
|
68 |
-
|
69 |
-
<
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
<
|
74 |
-
|
75 |
-
<?php _e( '
|
76 |
-
<
|
77 |
-
|
78 |
-
|
79 |
-
<
|
80 |
-
<
|
81 |
-
<label>
|
82 |
-
<input class="ab-required" type="checkbox" />
|
83 |
-
<span><?php _e( 'Required field', 'bookly' ) ?></span>
|
84 |
-
</label>
|
85 |
-
</span>
|
86 |
-
</div>
|
87 |
-
<ul class="ab-items"></ul>
|
88 |
-
<button class="button" data-type="radio-buttons-item"><i class="glyphicon glyphicon-plus"></i> <?php _e( 'Radio Button', 'bookly' ) ?></button>
|
89 |
-
</li>
|
90 |
-
|
91 |
-
<li data-type="drop-down">
|
92 |
-
<i class="ab-handle glyphicon glyphicon-align-justify" title="<?php esc_attr_e( 'Reorder', 'bookly' ) ?>"></i>
|
93 |
-
<h2 class="ab-field-title">
|
94 |
-
<?php _e( 'Drop Down', 'bookly' ) ?>
|
95 |
-
<i class="ab-delete glyphicon glyphicon-trash" title="<?php echo esc_attr( __( 'Remove field', 'bookly' ) ) ?>"></i>
|
96 |
-
</h2>
|
97 |
-
<div class="input-group">
|
98 |
-
<input class="ab-label form-control" type="text" value="" placeholder="<?php echo esc_attr( __( 'Enter a label', 'bookly' ) ) ?>" />
|
99 |
-
<span class="input-group-addon">
|
100 |
-
<label>
|
101 |
-
<input class="ab-required" type="checkbox" />
|
102 |
-
<span><?php _e( 'Required field', 'bookly' ) ?></span>
|
103 |
-
</label>
|
104 |
-
</span>
|
105 |
</div>
|
106 |
-
<
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
133 |
</div>
|
134 |
-
</div>
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
|
2 |
+
<div id="bookly-tbs" class="wrap">
|
3 |
+
<div class="bookly-tbs-body">
|
4 |
+
<div class="page-header text-right clearfix">
|
5 |
+
<div class="bookly-page-title">
|
6 |
+
<?php _e( 'Custom Fields', 'bookly' ) ?>
|
7 |
+
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
8 |
</div>
|
9 |
+
<div class="panel panel-default bookly-main">
|
10 |
+
<div class="panel-body">
|
11 |
+
<div class="row">
|
12 |
+
<div class="col-md-4">
|
13 |
+
<div class="form-group">
|
14 |
+
<label for="ab_custom_fields_per_service">
|
15 |
+
<?php _e( 'Bind fields to services', 'bookly' ) ?>
|
16 |
+
</label>
|
17 |
+
<p class="help-block"><?php _e( 'When this setting is enabled you will be able to create service specific custom fields.', 'bookly' ) ?></p>
|
18 |
+
<?php \BooklyLite\Lib\Utils\Common::optionToggle( 'ab_custom_fields_per_service' ) ?>
|
19 |
+
</div>
|
20 |
+
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
21 |
</div>
|
22 |
+
|
23 |
+
<hr />
|
24 |
+
|
25 |
+
<ul id="ab-custom-fields"></ul>
|
26 |
+
|
27 |
+
<div id="ab-add-fields">
|
28 |
+
<button class="btn btn-default bookly-margin-bottom-sm bookly-margin-right-sm" data-type="text-field"><i class="glyphicon glyphicon-plus"></i> <?php _e( 'Text Field', 'bookly' ) ?></button>
|
29 |
+
<button class="btn btn-default bookly-margin-bottom-sm bookly-margin-right-sm" data-type="textarea"><i class="glyphicon glyphicon-plus"></i> <?php _e( 'Text Area', 'bookly' ) ?></button>
|
30 |
+
<button class="btn btn-default bookly-margin-bottom-sm bookly-margin-right-sm" data-type="text-content"><i class="glyphicon glyphicon-plus"></i> <?php _e( 'Text Content', 'bookly' ) ?></button>
|
31 |
+
<button class="btn btn-default bookly-margin-bottom-sm bookly-margin-right-sm" data-type="checkboxes"><i class="glyphicon glyphicon-plus"></i> <?php _e( 'Checkbox Group', 'bookly' ) ?></button>
|
32 |
+
<button class="btn btn-default bookly-margin-bottom-sm bookly-margin-right-sm" data-type="radio-buttons"><i class="glyphicon glyphicon-plus"></i> <?php _e( 'Radio Button Group', 'bookly' ) ?></button>
|
33 |
+
<button class="btn btn-default bookly-margin-bottom-sm bookly-margin-right-sm" data-type="drop-down"><i class="glyphicon glyphicon-plus"></i> <?php _e( 'Drop Down', 'bookly' ) ?></button>
|
34 |
+
<button class="btn btn-default bookly-margin-bottom-sm bookly-margin-right-sm" data-type="captcha"><i class="glyphicon glyphicon-plus"></i> <?php _e( 'Captcha', 'bookly' ) ?></button>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
35 |
</div>
|
36 |
+
<p class="help-block"><?php _e( 'HTML allowed in all texts and labels.', 'bookly' ) ?></p>
|
37 |
+
|
38 |
+
<ul id="ab-templates" style="display:none">
|
39 |
+
|
40 |
+
<li data-type="textarea">
|
41 |
+
<div class="bookly-flexbox">
|
42 |
+
<div class="bookly-flex-cell">
|
43 |
+
<i title="<?php esc_attr_e( 'Reorder', 'bookly' ) ?>" class="bookly-js-handle bookly-icon bookly-icon-draghandle bookly-margin-right-sm bookly-cursor-move"></i>
|
44 |
+
</div>
|
45 |
+
<div class="bookly-flex-cell" style="width: 100%">
|
46 |
+
<p><b><?php _e( 'Text Area', 'bookly' ) ?></b><a class="ab-delete glyphicon glyphicon-trash text-danger bookly-margin-left-sm" href="#"
|
47 |
+
title="<?php esc_attr_e( 'Remove field', 'bookly' ) ?>"></a></p>
|
48 |
+
<div class="row">
|
49 |
+
<div class="col-md-8">
|
50 |
+
<div class="input-group">
|
51 |
+
<input class="ab-label form-control" type="text" value="" placeholder="<?php esc_attr_e( 'Enter a label', 'bookly' ) ?>">
|
52 |
+
<label class="input-group-addon">
|
53 |
+
<input class="ab-required" type="checkbox">
|
54 |
+
<span class="hidden-xs"><?php _e( 'Required field', 'bookly' ) ?></span>
|
55 |
+
<i class="visible-xs-inline-block glyphicon glyphicon-warning"></i>
|
56 |
+
</label>
|
57 |
+
</div>
|
58 |
+
</div>
|
59 |
+
<div class="col-md-4">
|
60 |
+
<?php echo $services_html ?>
|
61 |
+
</div>
|
62 |
+
</div>
|
63 |
+
</div>
|
64 |
+
</div>
|
65 |
+
<hr>
|
66 |
+
</li>
|
67 |
+
|
68 |
+
<li data-type="text-content">
|
69 |
+
<div class="bookly-flexbox">
|
70 |
+
<div class="bookly-flex-cell">
|
71 |
+
<i title="<?php esc_attr_e( 'Reorder', 'bookly' ) ?>" class="bookly-js-handle bookly-icon bookly-icon-draghandle bookly-margin-right-sm bookly-cursor-move"></i>
|
72 |
+
</div>
|
73 |
+
<div class="bookly-flex-cell" style="width: 100%">
|
74 |
+
<p><b><?php _e( 'Text Content', 'bookly' ) ?></b><a class="ab-delete glyphicon glyphicon-trash text-danger bookly-margin-left-sm" href="#"
|
75 |
+
title="<?php esc_attr_e( 'Remove field', 'bookly' ) ?>"></a></p>
|
76 |
+
<div class="row">
|
77 |
+
<div class="col-md-8">
|
78 |
+
<textarea class="ab-label form-control" type="text" rows="3"
|
79 |
+
placeholder="<?php esc_attr_e( 'Enter a content', 'bookly' ) ?>"></textarea>
|
80 |
+
<input class="ab-required hidden" type="checkbox" disabled="disabled">
|
81 |
+
</div>
|
82 |
+
<div class="col-md-4">
|
83 |
+
<?php echo $services_html ?>
|
84 |
+
</div>
|
85 |
+
</div>
|
86 |
+
</div>
|
87 |
+
</div>
|
88 |
+
<hr>
|
89 |
+
</li>
|
90 |
+
|
91 |
+
<li data-type="text-field">
|
92 |
+
<div class="bookly-flexbox">
|
93 |
+
<div class="bookly-flex-cell">
|
94 |
+
<i title="<?php esc_attr_e( 'Reorder', 'bookly' ) ?>" class="bookly-js-handle bookly-icon bookly-icon-draghandle bookly-margin-right-sm bookly-cursor-move"></i>
|
95 |
+
</div>
|
96 |
+
<div class="bookly-flex-cell" style="width: 100%">
|
97 |
+
<p><b><?php _e( 'Text Field', 'bookly' ) ?></b><a class="ab-delete glyphicon glyphicon-trash text-danger bookly-margin-left-sm" href="#"
|
98 |
+
title="<?php esc_attr_e( 'Remove field', 'bookly' ) ?>"></a></p>
|
99 |
+
<div class="row">
|
100 |
+
<div class="col-md-8">
|
101 |
+
<div class="input-group">
|
102 |
+
<input class="ab-label form-control" type="text" value=""
|
103 |
+
placeholder="<?php esc_attr_e( 'Enter a label', 'bookly' ) ?>">
|
104 |
+
<label class="input-group-addon">
|
105 |
+
<input class="ab-required" type="checkbox">
|
106 |
+
<span class="hidden-xs"><?php _e( 'Required field', 'bookly' ) ?></span>
|
107 |
+
<i class="visible-xs-inline-block glyphicon glyphicon-warning"></i>
|
108 |
+
</label>
|
109 |
+
</div>
|
110 |
+
</div>
|
111 |
+
<div class="col-md-4">
|
112 |
+
<?php echo $services_html ?>
|
113 |
+
</div>
|
114 |
+
</div>
|
115 |
+
</div>
|
116 |
+
</div>
|
117 |
+
<hr>
|
118 |
+
</li>
|
119 |
+
|
120 |
+
<li data-type="checkboxes">
|
121 |
+
<div class="bookly-flexbox">
|
122 |
+
<div class="bookly-flex-cell">
|
123 |
+
<i title="<?php esc_attr_e( 'Reorder', 'bookly' ) ?>" class="bookly-js-handle bookly-icon bookly-icon-draghandle bookly-margin-right-sm bookly-cursor-move"></i>
|
124 |
+
</div>
|
125 |
+
<div class="bookly-flex-cell" style="width: 100%">
|
126 |
+
<p><b><?php _e( 'Checkbox Group', 'bookly' ) ?></b><a class="ab-delete glyphicon glyphicon-trash text-danger bookly-margin-left-sm" href="#"
|
127 |
+
title="<?php esc_attr_e( 'Remove field', 'bookly' ) ?>"></a></p>
|
128 |
+
<div class="row">
|
129 |
+
<div class="col-md-8">
|
130 |
+
<div class="input-group">
|
131 |
+
<input class="ab-label form-control" type="text" value=""
|
132 |
+
placeholder="<?php esc_attr_e( 'Enter a label', 'bookly' ) ?>">
|
133 |
+
<label class="input-group-addon">
|
134 |
+
<input class="ab-required" type="checkbox">
|
135 |
+
<span class="hidden-xs"><?php _e( 'Required field', 'bookly' ) ?></span>
|
136 |
+
<i class="visible-xs-inline-block glyphicon glyphicon-warning"></i>
|
137 |
+
</label>
|
138 |
+
</div>
|
139 |
+
|
140 |
+
<ul class="ab-items bookly-margin-top-sm"></ul>
|
141 |
+
<button class="btn btn-sm btn-default" data-type="checkboxes-item">
|
142 |
+
<i class="glyphicon glyphicon-plus"></i> <?php _e( 'Checkbox', 'bookly' ) ?>
|
143 |
+
</button>
|
144 |
+
</div>
|
145 |
+
<div class="col-md-4">
|
146 |
+
<?php echo $services_html ?>
|
147 |
+
</div>
|
148 |
+
</div>
|
149 |
+
</div>
|
150 |
+
</div>
|
151 |
+
<hr>
|
152 |
+
</li>
|
153 |
+
|
154 |
+
<li data-type="radio-buttons">
|
155 |
+
<div class="bookly-flexbox">
|
156 |
+
<div class="bookly-flex-cell">
|
157 |
+
<i title="<?php esc_attr_e( 'Reorder', 'bookly' ) ?>" class="bookly-js-handle bookly-icon bookly-icon-draghandle bookly-margin-right-sm bookly-cursor-move"></i>
|
158 |
+
</div>
|
159 |
+
<div class="bookly-flex-cell" style="width: 100%">
|
160 |
+
<p><b><?php _e( 'Radio Button Group', 'bookly' ) ?></b><a class="ab-delete glyphicon glyphicon-trash text-danger bookly-margin-left-sm" href="#"
|
161 |
+
title="<?php esc_attr_e( 'Remove field', 'bookly' ) ?>"></a></p>
|
162 |
+
<div class="row">
|
163 |
+
<div class="col-md-8">
|
164 |
+
<div class="input-group">
|
165 |
+
<input class="ab-label form-control" type="text" value=""
|
166 |
+
placeholder="<?php esc_attr_e( 'Enter a label', 'bookly' ) ?>">
|
167 |
+
<label class="input-group-addon">
|
168 |
+
<input class="ab-required" type="checkbox">
|
169 |
+
<span class="hidden-xs"><?php _e( 'Required field', 'bookly' ) ?></span>
|
170 |
+
<i class="visible-xs-inline-block glyphicon glyphicon-warning"></i>
|
171 |
+
</label>
|
172 |
+
</div>
|
173 |
+
|
174 |
+
<ul class="ab-items bookly-margin-top-sm"></ul>
|
175 |
+
<button class="btn btn-sm btn-default" data-type="radio-buttons-item">
|
176 |
+
<i class="glyphicon glyphicon-plus"></i> <?php _e( 'Radio Button', 'bookly' ) ?>
|
177 |
+
</button>
|
178 |
+
</div>
|
179 |
+
<div class="col-md-4">
|
180 |
+
<?php echo $services_html ?>
|
181 |
+
</div>
|
182 |
+
</div>
|
183 |
+
</div>
|
184 |
+
</div>
|
185 |
+
<hr>
|
186 |
+
</li>
|
187 |
+
|
188 |
+
<li data-type="drop-down">
|
189 |
+
<div class="bookly-flexbox">
|
190 |
+
<div class="bookly-flex-cell">
|
191 |
+
<i title="<?php esc_attr_e( 'Reorder', 'bookly' ) ?>" class="bookly-js-handle bookly-icon bookly-icon-draghandle bookly-margin-right-sm bookly-cursor-move"></i>
|
192 |
+
</div>
|
193 |
+
<div class="bookly-flex-cell" style="width: 100%">
|
194 |
+
<p><b><?php _e( 'Drop Down', 'bookly' ) ?></b><a class="ab-delete glyphicon glyphicon-trash text-danger bookly-margin-left-sm" href="#"
|
195 |
+
title="<?php esc_attr_e( 'Remove field', 'bookly' ) ?>"></a></p>
|
196 |
+
<div class="row">
|
197 |
+
<div class="col-md-8">
|
198 |
+
<div class="input-group">
|
199 |
+
<input class="ab-label form-control" type="text" value=""
|
200 |
+
placeholder="<?php esc_attr_e( 'Enter a label', 'bookly' ) ?>">
|
201 |
+
<label class="input-group-addon">
|
202 |
+
<input class="ab-required" type="checkbox">
|
203 |
+
<span class="hidden-xs"><?php _e( 'Required field', 'bookly' ) ?></span>
|
204 |
+
<i class="visible-xs-inline-block glyphicon glyphicon-warning"></i>
|
205 |
+
</label>
|
206 |
+
</div>
|
207 |
+
|
208 |
+
<ul class="ab-items bookly-margin-top-sm"></ul>
|
209 |
+
<button class="btn btn-sm btn-default" data-type="drop-down-item">
|
210 |
+
<i class="glyphicon glyphicon-plus"></i> <?php _e( 'Option', 'bookly' ) ?>
|
211 |
+
</button>
|
212 |
+
</div>
|
213 |
+
<div class="col-md-4">
|
214 |
+
<?php echo $services_html ?>
|
215 |
+
</div>
|
216 |
+
</div>
|
217 |
+
</div>
|
218 |
+
</div>
|
219 |
+
<hr>
|
220 |
+
</li>
|
221 |
+
|
222 |
+
<li data-type="captcha">
|
223 |
+
<div class="bookly-flexbox">
|
224 |
+
<div class="bookly-flex-cell">
|
225 |
+
<i title="<?php esc_attr_e( 'Reorder', 'bookly' ) ?>" class="bookly-js-handle bookly-icon bookly-icon-draghandle bookly-margin-right-sm bookly-cursor-move"></i>
|
226 |
+
</div>
|
227 |
+
<div class="bookly-flex-cell" style="width: 100%">
|
228 |
+
<p><b><?php _e( 'Captcha', 'bookly' ) ?></b><a class="ab-delete glyphicon glyphicon-trash text-danger bookly-margin-left-sm" href="#"
|
229 |
+
title="<?php esc_attr_e( 'Remove field', 'bookly' ) ?>"></a></p>
|
230 |
+
<div class="row">
|
231 |
+
<div class="col-md-8">
|
232 |
+
<div class="input-group">
|
233 |
+
<input class="ab-label form-control" type="text" value=""
|
234 |
+
placeholder="<?php esc_attr_e( 'Enter a label', 'bookly' ) ?>">
|
235 |
+
<label class="input-group-addon">
|
236 |
+
<input class="ab-required hidden" type="checkbox">
|
237 |
+
<input type="checkbox" disabled="disabled" checked="checked">
|
238 |
+
<span class="hidden-xs"><?php _e( 'Required field', 'bookly' ) ?></span>
|
239 |
+
<i class="visible-xs-inline-block glyphicon glyphicon-warning"></i>
|
240 |
+
</label>
|
241 |
+
</div>
|
242 |
+
</div>
|
243 |
+
<div class="col-md-4">
|
244 |
+
<?php echo $services_html ?>
|
245 |
+
</div>
|
246 |
+
</div>
|
247 |
+
</div>
|
248 |
+
</div>
|
249 |
+
<hr>
|
250 |
+
</li>
|
251 |
+
|
252 |
+
<li data-type="checkboxes-item">
|
253 |
+
<div class="bookly-flexbox">
|
254 |
+
<div class="bookly-flex-cell bookly-vertical-middle">
|
255 |
+
<i title="<?php esc_attr_e( 'Reorder', 'bookly' ) ?>" class="bookly-js-handle bookly-icon bookly-icon-draghandle bookly-margin-right-sm bookly-cursor-move"></i>
|
256 |
+
</div>
|
257 |
+
<div class="bookly-flex-cell bookly-vertical-middle" style="width: 100%">
|
258 |
+
<input class="form-control" type="text" value=""
|
259 |
+
placeholder="<?php esc_attr_e( 'Enter a label', 'bookly' ) ?>">
|
260 |
+
</div>
|
261 |
+
<div class="bookly-flex-cell bookly-vertical-middle">
|
262 |
+
<a class="ab-delete glyphicon glyphicon-trash text-danger bookly-margin-left-sm" href="#"
|
263 |
+
title="<?php esc_attr_e( 'Remove item', 'bookly' ) ?>"></a>
|
264 |
+
</div>
|
265 |
+
</div>
|
266 |
+
</li>
|
267 |
+
|
268 |
+
<li data-type="radio-buttons-item">
|
269 |
+
<div class="bookly-flexbox">
|
270 |
+
<div class="bookly-flex-cell bookly-vertical-middle">
|
271 |
+
<i title="<?php esc_attr_e( 'Reorder', 'bookly' ) ?>" class="bookly-js-handle bookly-icon bookly-icon-draghandle bookly-margin-right-sm bookly-cursor-move"></i>
|
272 |
+
</div>
|
273 |
+
<div class="bookly-flex-cell bookly-vertical-middle" style="width: 100%">
|
274 |
+
<input class="form-control" type="text" value=""
|
275 |
+
placeholder="<?php esc_attr_e( 'Enter a label', 'bookly' ) ?>">
|
276 |
+
</div>
|
277 |
+
<div class="bookly-flex-cell bookly-vertical-middle">
|
278 |
+
<a class="ab-delete glyphicon glyphicon-trash text-danger bookly-margin-left-sm" href="#"
|
279 |
+
title="<?php esc_attr_e( 'Remove item', 'bookly' ) ?>"></a>
|
280 |
+
</div>
|
281 |
+
</div>
|
282 |
+
</li>
|
283 |
+
|
284 |
+
<li data-type="drop-down-item">
|
285 |
+
<div class="bookly-flexbox">
|
286 |
+
<div class="bookly-flex-cell bookly-vertical-middle">
|
287 |
+
<i title="<?php esc_attr_e( 'Reorder', 'bookly' ) ?>" class="bookly-js-handle bookly-icon bookly-icon-draghandle bookly-margin-right-sm bookly-cursor-move"></i>
|
288 |
+
</div>
|
289 |
+
<div class="bookly-flex-cell bookly-vertical-middle" style="width: 100%">
|
290 |
+
<input class="form-control" type="text" value=""
|
291 |
+
placeholder="<?php esc_attr_e( 'Enter a label', 'bookly' ) ?>">
|
292 |
+
</div>
|
293 |
+
<div class="bookly-flex-cell bookly-vertical-middle">
|
294 |
+
<a class="ab-delete glyphicon glyphicon-trash text-danger bookly-margin-left-sm" href="#"
|
295 |
+
title="<?php esc_attr_e( 'Remove item', 'bookly' ) ?>"></a>
|
296 |
+
</div>
|
297 |
+
</div>
|
298 |
+
</li>
|
299 |
+
</ul>
|
300 |
+
</div>
|
301 |
+
|
302 |
+
<div class="panel-footer">
|
303 |
+
<?php \BooklyLite\Lib\Utils\Common::submitButton( 'ajax-send-custom-fields' ) ?>
|
304 |
+
<?php \BooklyLite\Lib\Utils\Common::resetButton() ?>
|
305 |
+
</div>
|
306 |
+
</div>
|
307 |
</div>
|
308 |
+
</div>
|
backend/modules/customer/AB_CustomerController.php
DELETED
@@ -1,254 +0,0 @@
|
|
1 |
-
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
-
|
3 |
-
class AB_CustomerController extends AB_Controller {
|
4 |
-
|
5 |
-
const page_slug = 'ab-customers';
|
6 |
-
|
7 |
-
protected function getPermissions()
|
8 |
-
{
|
9 |
-
return array(
|
10 |
-
'executeSaveCustomer' => 'user',
|
11 |
-
'executeGetNgNewCustomerDialogTemplate' => 'user',
|
12 |
-
);
|
13 |
-
}
|
14 |
-
|
15 |
-
public function index()
|
16 |
-
{
|
17 |
-
if ( $this->hasParameter( 'import-customers' ) ) {
|
18 |
-
$this->importCustomers();
|
19 |
-
}
|
20 |
-
|
21 |
-
$this->enqueueStyles( array(
|
22 |
-
'module' => array(
|
23 |
-
'css/customers.css' => array( 'ab-intlTelInput.css' ),
|
24 |
-
),
|
25 |
-
'backend' => array(
|
26 |
-
'css/bookly.main-backend.css',
|
27 |
-
'bootstrap/css/bootstrap.min.css',
|
28 |
-
),
|
29 |
-
'frontend' => array(
|
30 |
-
'css/intlTelInput.css',
|
31 |
-
)
|
32 |
-
) );
|
33 |
-
|
34 |
-
$this->enqueueScripts( array(
|
35 |
-
'backend' => array(
|
36 |
-
'bootstrap/js/bootstrap.min.js' => array( 'jquery' ),
|
37 |
-
'js/angular.min.js',
|
38 |
-
'js/angular-sanitize.min.js',
|
39 |
-
'js/angular-ui-utils-0.2.1.min.js',
|
40 |
-
'js/angular-ui-date-0.0.8.js',
|
41 |
-
'js/ng-new_customer_dialog.js' => array( 'jquery', 'ab-angular.min.js' ),
|
42 |
-
),
|
43 |
-
'module' => array(
|
44 |
-
'js/ng-app.js' => array(
|
45 |
-
'jquery',
|
46 |
-
'ab-angular.min.js',
|
47 |
-
'ab-angular-ui-utils-0.2.1.min.js',
|
48 |
-
'ab-angular-ui-date-0.0.8.js',
|
49 |
-
'ab-intlTelInput.utils.js',
|
50 |
-
),
|
51 |
-
),
|
52 |
-
'frontend' => array(
|
53 |
-
'js/intlTelInput.min.js' => array( 'jquery' ),
|
54 |
-
'js/intlTelInput.utils.js' => array( 'jquery' )
|
55 |
-
)
|
56 |
-
) );
|
57 |
-
|
58 |
-
wp_localize_script( 'ab-ng-app.js', 'BooklyL10n', array(
|
59 |
-
'are_you_sure' => __( 'Are you sure?', 'bookly' ),
|
60 |
-
'wp_users' => $this->getWpUsers(),
|
61 |
-
'country' => get_option( 'ab_settings_phone_default_country' ),
|
62 |
-
'intlTelInput_utils' => plugins_url( 'intlTelInput.utils.js', AB_PATH . '/frontend/resources/js/intlTelInput.utils.js' ),
|
63 |
-
'please_select_at_least_one_row' => __( 'Please select at least one customer.', 'bookly' ),
|
64 |
-
) );
|
65 |
-
|
66 |
-
$this->render( 'index' );
|
67 |
-
}
|
68 |
-
|
69 |
-
/**
|
70 |
-
* Get list of customers.
|
71 |
-
*/
|
72 |
-
public function executeGetCustomers()
|
73 |
-
{
|
74 |
-
$wpdb = $this->getWpdb();
|
75 |
-
$items_per_page = 20;
|
76 |
-
$response = array(
|
77 |
-
'customers' => array(),
|
78 |
-
'total' => 0,
|
79 |
-
'pages' => 0,
|
80 |
-
'active_page' => 0,
|
81 |
-
);
|
82 |
-
|
83 |
-
$page = intval( $this->getParameter( 'page' ) );
|
84 |
-
$sort = in_array( $this->getParameter( 'sort' ), array( 'name', 'phone', 'email', 'notes', 'last_appointment', 'total_appointments', 'payments', 'wp_user' ) )
|
85 |
-
? $this->getParameter( 'sort' ) : 'name';
|
86 |
-
$order = in_array( $this->getParameter( 'order' ), array( 'asc', 'desc' ) ) ? $this->getParameter( 'order' ) : 'asc';
|
87 |
-
$filter = $wpdb->_real_escape( $this->getParameter( 'filter' ) );
|
88 |
-
|
89 |
-
$query = AB_Customer::query( 'c' );
|
90 |
-
// WHERE
|
91 |
-
if ( $filter !== '' ) {
|
92 |
-
$query->whereLike( 'c.name', "%{$filter}%")
|
93 |
-
->whereLike( 'c.phone', "%{$filter}%", 'OR' )
|
94 |
-
->whereLike( 'c.email', "%{$filter}%", 'OR' );
|
95 |
-
}
|
96 |
-
$total = $query->count();
|
97 |
-
|
98 |
-
$pages = ceil( $total / $items_per_page );
|
99 |
-
if ( $page < 1 || $page > $pages ) {
|
100 |
-
$page = 1;
|
101 |
-
}
|
102 |
-
|
103 |
-
$data = $query->select( 'c.*, MAX(a.start_date) AS last_appointment,
|
104 |
-
COUNT(a.id) AS total_appointments,
|
105 |
-
COALESCE(SUM(p.total),0) AS payments,
|
106 |
-
wpu.display_name AS wp_user' )
|
107 |
-
->leftJoin( 'AB_CustomerAppointment', 'ca', 'ca.customer_id = c.id' )
|
108 |
-
->leftJoin( 'AB_Appointment', 'a', 'a.id = ca.appointment_id' )
|
109 |
-
->leftJoin( 'AB_Payment', 'p', 'p.customer_appointment_id = ca.id' )
|
110 |
-
->tableJoin( $wpdb->users, 'wpu', 'wpu.ID = c.wp_user_id' )
|
111 |
-
->groupBy( 'c.id' )
|
112 |
-
->sortBy( $sort )
|
113 |
-
->order( $order )
|
114 |
-
->limit( $items_per_page )
|
115 |
-
->offset( ( $page - 1 ) * $items_per_page )
|
116 |
-
->fetchArray();
|
117 |
-
|
118 |
-
array_walk( $data, function ( &$row ) {
|
119 |
-
if ( $row['last_appointment'] ) {
|
120 |
-
$row['last_appointment'] = AB_DateTimeUtils::formatDateTime( $row['last_appointment'] );
|
121 |
-
}
|
122 |
-
$row['payments'] = AB_Utils::formatPrice( $row['payments'] );
|
123 |
-
} );
|
124 |
-
|
125 |
-
// Populate response.
|
126 |
-
$response[ 'customers' ] = $data;
|
127 |
-
$response[ 'total' ] = $total;
|
128 |
-
$response[ 'pages' ] = $pages;
|
129 |
-
$response[ 'active_page' ] = $page;
|
130 |
-
|
131 |
-
wp_send_json_success( $response );
|
132 |
-
}
|
133 |
-
|
134 |
-
/**
|
135 |
-
* Get WP users array.
|
136 |
-
*
|
137 |
-
* @return array
|
138 |
-
*/
|
139 |
-
public function getWpUsers()
|
140 |
-
{
|
141 |
-
return get_users( array( 'fields' => array( 'ID', 'display_name' ), 'orderby' => 'display_name' ) );
|
142 |
-
}
|
143 |
-
|
144 |
-
/**
|
145 |
-
* Create or edit a customer.
|
146 |
-
*/
|
147 |
-
public function executeSaveCustomer()
|
148 |
-
{
|
149 |
-
$response = array();
|
150 |
-
$form = new AB_CustomerForm();
|
151 |
-
|
152 |
-
do {
|
153 |
-
if ( $this->getParameter( 'name' ) !== '' ) {
|
154 |
-
$form->bind( $this->getPostParameters() );
|
155 |
-
/** @var AB_Customer $customer */
|
156 |
-
$customer = $form->save();
|
157 |
-
if ( $customer ) {
|
158 |
-
$response[ 'success' ] = true;
|
159 |
-
$response[ 'customer' ] = array(
|
160 |
-
'id' => $customer->id,
|
161 |
-
'name' => $customer->name,
|
162 |
-
'wp_user_id' => $customer->wp_user_id,
|
163 |
-
'phone' => $customer->phone,
|
164 |
-
'email' => $customer->email,
|
165 |
-
'notes' => $customer->notes,
|
166 |
-
'jsonString' => json_encode( array(
|
167 |
-
'name' => $customer->name,
|
168 |
-
'phone' => $customer->phone,
|
169 |
-
'email' => $customer->email,
|
170 |
-
'notes' => $customer->notes
|
171 |
-
) )
|
172 |
-
);
|
173 |
-
break;
|
174 |
-
}
|
175 |
-
}
|
176 |
-
$response[ 'success' ] = false;
|
177 |
-
$response[ 'errors' ] = array( 'name' => array( 'required' ) );
|
178 |
-
} while ( 0 );
|
179 |
-
|
180 |
-
wp_send_json( $response );
|
181 |
-
}
|
182 |
-
|
183 |
-
/**
|
184 |
-
* Import customers from CSV.
|
185 |
-
*/
|
186 |
-
private function importCustomers()
|
187 |
-
{
|
188 |
-
@ini_set( 'auto_detect_line_endings', true );
|
189 |
-
|
190 |
-
$csv_mime_types = array(
|
191 |
-
'text/csv',
|
192 |
-
'application/csv',
|
193 |
-
'text/comma-separated-values',
|
194 |
-
'application/excel',
|
195 |
-
'application/vnd.ms-excel',
|
196 |
-
'application/vnd.msexcel'
|
197 |
-
);
|
198 |
-
|
199 |
-
if ( in_array( $_FILES[ 'import_customers_file' ][ 'type' ], $csv_mime_types ) ) {
|
200 |
-
$file = fopen ( $_FILES[ 'import_customers_file' ][ 'tmp_name' ], 'r' );
|
201 |
-
while ( $line = fgetcsv( $file, null, $this->getParameter( 'import_customers_delimiter' ) ) ) {
|
202 |
-
if ( !empty ( $line[ 0 ] ) ) {
|
203 |
-
$customer = new AB_Customer();
|
204 |
-
$customer->set( 'name', $line[ 0 ] );
|
205 |
-
if ( isset ( $line[ 1 ] ) ) {
|
206 |
-
$customer->set( 'phone', $line[ 1 ] );
|
207 |
-
}
|
208 |
-
if ( isset ( $line[ 2 ] ) ) {
|
209 |
-
$customer->set( 'email', $line[ 2 ] );
|
210 |
-
}
|
211 |
-
$customer->save();
|
212 |
-
}
|
213 |
-
}
|
214 |
-
}
|
215 |
-
}
|
216 |
-
|
217 |
-
/**
|
218 |
-
* Get angulars template for new customer dialog.
|
219 |
-
*/
|
220 |
-
public function executeGetNgNewCustomerDialogTemplate()
|
221 |
-
{
|
222 |
-
$this->render( 'ng-new_customer_dialog', array(
|
223 |
-
'custom_fields' => json_decode( get_option( 'ab_custom_fields' ) ),
|
224 |
-
'module' => $this->getParameter( 'module' ),
|
225 |
-
'wp_users' => $this->getWpUsers()
|
226 |
-
) );
|
227 |
-
exit ( 0 );
|
228 |
-
}
|
229 |
-
|
230 |
-
/**
|
231 |
-
* Delete a customer.
|
232 |
-
*/
|
233 |
-
public function executeDeleteCustomer()
|
234 |
-
{
|
235 |
-
foreach ( $this->getParameter( 'ids' ) as $id ) {
|
236 |
-
$customer = new AB_Customer();
|
237 |
-
$customer->load( $id );
|
238 |
-
$customer->deleteWithWPUser( (bool) $this->getParameter( 'with_wp_user' ) );
|
239 |
-
}
|
240 |
-
wp_send_json_success();
|
241 |
-
}
|
242 |
-
|
243 |
-
/**
|
244 |
-
* Override parent method to add 'wp_ajax_ab_' prefix
|
245 |
-
* so current 'execute*' methods look nicer.
|
246 |
-
*
|
247 |
-
* @param string $prefix
|
248 |
-
*/
|
249 |
-
protected function registerWpActions( $prefix = '' )
|
250 |
-
{
|
251 |
-
parent::registerWpActions( 'wp_ajax_ab_' );
|
252 |
-
}
|
253 |
-
|
254 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/modules/customer/forms/AB_CustomerForm.php
DELETED
@@ -1,28 +0,0 @@
|
|
1 |
-
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Class AB_CustomerForm
|
5 |
-
*/
|
6 |
-
class AB_CustomerForm extends AB_Form {
|
7 |
-
|
8 |
-
/**
|
9 |
-
* Constructor.
|
10 |
-
*/
|
11 |
-
public function __construct()
|
12 |
-
{
|
13 |
-
parent::$entity_class = 'AB_Customer';
|
14 |
-
parent::__construct();
|
15 |
-
}
|
16 |
-
|
17 |
-
public function configure()
|
18 |
-
{
|
19 |
-
$this->setFields( array(
|
20 |
-
'name',
|
21 |
-
'wp_user_id',
|
22 |
-
'phone',
|
23 |
-
'email',
|
24 |
-
'notes'
|
25 |
-
) );
|
26 |
-
}
|
27 |
-
|
28 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/modules/customer/resources/css/customers.css
DELETED
@@ -1,10 +0,0 @@
|
|
1 |
-
/* intlTelInput.js */
|
2 |
-
.iti-flag{background-image:url("../../../../../frontend/resources/images/flags.png");}
|
3 |
-
@media only screen and (min-resolution: 2dppx){.iti-flag{background-image:url("../../../../../frontend/resources/images/flags@2x.png")}}
|
4 |
-
#ab_customers_list .intl-tel-input,
|
5 |
-
#ab_customers_list td.ab-phone .displayed-value { width: 170px; }
|
6 |
-
.ab-customers-list .table-responsive { overflow: visible; }
|
7 |
-
/* media query */
|
8 |
-
@media screen and (max-width: 782px) {
|
9 |
-
.ab-customers-list .table-responsive { overflow-y: hidden; }
|
10 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/modules/customer/resources/js/ng-app.js
DELETED
@@ -1,356 +0,0 @@
|
|
1 |
-
;(function() {
|
2 |
-
var module = angular.module('customers', ['ui.utils', 'ui.date', 'newCustomerDialog', 'ngSanitize']);
|
3 |
-
|
4 |
-
module.factory('dataSource', function($q, $rootScope) {
|
5 |
-
var ds = {
|
6 |
-
customers : [],
|
7 |
-
total : 0,
|
8 |
-
pages : [],
|
9 |
-
form : {
|
10 |
-
new_customer : {
|
11 |
-
name : null,
|
12 |
-
wp_user_id : null,
|
13 |
-
phone : null,
|
14 |
-
email : null,
|
15 |
-
notes : null
|
16 |
-
}
|
17 |
-
},
|
18 |
-
loadData : function(params) {
|
19 |
-
var deferred = $q.defer();
|
20 |
-
jQuery.ajax({
|
21 |
-
url : ajaxurl,
|
22 |
-
type : 'POST',
|
23 |
-
data : jQuery.extend({ action : 'ab_get_customers' }, params),
|
24 |
-
dataType : 'json',
|
25 |
-
success : function(response) {
|
26 |
-
if (response.success) {
|
27 |
-
|
28 |
-
ds.customers = response.data.customers;
|
29 |
-
ds.total = response.data.total;
|
30 |
-
ds.pages = [];
|
31 |
-
ds.paginator = {beg : false, end: false};
|
32 |
-
var neighbor = 5;
|
33 |
-
var beg = Math.max(1, response.data.active_page - neighbor);
|
34 |
-
var end = Math.min(response.data.pages, (response.data.active_page + neighbor));
|
35 |
-
if (beg > 1) {
|
36 |
-
ds.paginator.beg = true;
|
37 |
-
beg++;
|
38 |
-
}
|
39 |
-
for (var i = beg; i < end; i++) {
|
40 |
-
ds.pages.push({ number : i, active : response.data.active_page == i });
|
41 |
-
}
|
42 |
-
if (end >= response.data.pages) {
|
43 |
-
ds.pages.push({number: response.data.pages, active: response.data.active_page == response.data.pages});
|
44 |
-
} else {
|
45 |
-
ds.paginator.end = {number: response.data.pages, active: false};
|
46 |
-
}
|
47 |
-
for (var i = 0; i < ds.customers.length; ++ i) {
|
48 |
-
for (var j = 0; j < BooklyL10n.wp_users.length; ++ j) {
|
49 |
-
if (ds.customers[i].wp_user_id == BooklyL10n.wp_users[j].ID) {
|
50 |
-
ds.customers[i].wp_user = BooklyL10n.wp_users[j];
|
51 |
-
break;
|
52 |
-
}
|
53 |
-
}
|
54 |
-
}
|
55 |
-
}
|
56 |
-
$rootScope.$apply(deferred.resolve);
|
57 |
-
},
|
58 |
-
error : function() {
|
59 |
-
ds.customers = [];
|
60 |
-
ds.total = 0;
|
61 |
-
$rootScope.$apply(deferred.resolve);
|
62 |
-
}
|
63 |
-
});
|
64 |
-
return deferred.promise;
|
65 |
-
}
|
66 |
-
};
|
67 |
-
ds.wp_users = BooklyL10n.wp_users;
|
68 |
-
return ds;
|
69 |
-
});
|
70 |
-
|
71 |
-
module.factory('intlTelInputSrv', function() {
|
72 |
-
var srv = {
|
73 |
-
elements: {},
|
74 |
-
init: function(id, element) {
|
75 |
-
if (srv.elements[id]) {
|
76 |
-
srv.destroy(id);
|
77 |
-
}
|
78 |
-
srv.elements[id] = element;
|
79 |
-
srv.elements[id].intlTelInput({
|
80 |
-
preferredCountries: [BooklyL10n.country],
|
81 |
-
defaultCountry: BooklyL10n.country,
|
82 |
-
geoIpLookup: function(callback) {
|
83 |
-
jQuery.get(ajaxurl, {action: 'ab_ip_info'}, function() {}, 'json' ).always(function(resp) {
|
84 |
-
var countryCode = (resp && resp.country) ? resp.country : '';
|
85 |
-
callback(countryCode);
|
86 |
-
});
|
87 |
-
},
|
88 |
-
utilsScript: BooklyL10n.intlTelInput_utils
|
89 |
-
});
|
90 |
-
},
|
91 |
-
destroy: function(id) {
|
92 |
-
if (srv.elements[id]) {
|
93 |
-
srv.elements[id].val(srv.getNumber(id));
|
94 |
-
srv.elements[id].intlTelInput('destroy');
|
95 |
-
delete srv.elements[id];
|
96 |
-
}
|
97 |
-
},
|
98 |
-
getNumber: function(id) {
|
99 |
-
return srv.elements[id] ? srv.elements[id].intlTelInput('getNumber') : null;
|
100 |
-
}
|
101 |
-
};
|
102 |
-
|
103 |
-
return srv;
|
104 |
-
});
|
105 |
-
|
106 |
-
module.controller('customersCtrl', function($scope, dataSource, intlTelInputSrv) {
|
107 |
-
// Set up initial data.
|
108 |
-
var params = {
|
109 |
-
page : 1,
|
110 |
-
sort : 'name',
|
111 |
-
order : 'asc',
|
112 |
-
filter : ''
|
113 |
-
};
|
114 |
-
$scope.loading = true;
|
115 |
-
$scope.css_class = {
|
116 |
-
name : 'asc',
|
117 |
-
wp_user : '',
|
118 |
-
phone : '',
|
119 |
-
email : '',
|
120 |
-
notes : '',
|
121 |
-
last_appointment : '',
|
122 |
-
total_appointments : '',
|
123 |
-
payments : ''
|
124 |
-
};
|
125 |
-
// Set up data source (data will be loaded in reload function).
|
126 |
-
$scope.dataSource = dataSource;
|
127 |
-
|
128 |
-
$scope.reload = function( opt ) {
|
129 |
-
$scope.loading = true;
|
130 |
-
if (opt !== undefined) {
|
131 |
-
if (opt.sort !== undefined) {
|
132 |
-
if (params.sort === opt.sort) {
|
133 |
-
// Toggle order when sorting by the same field.
|
134 |
-
params.order = params.order === 'asc' ? 'desc' : 'asc';
|
135 |
-
} else {
|
136 |
-
params.order = 'asc';
|
137 |
-
}
|
138 |
-
$scope.css_class = {
|
139 |
-
name : '',
|
140 |
-
wp_user : '',
|
141 |
-
phone : '',
|
142 |
-
email : '',
|
143 |
-
notes : '',
|
144 |
-
last_appointment : '',
|
145 |
-
total_appointments : '',
|
146 |
-
payments : ''
|
147 |
-
};
|
148 |
-
$scope.css_class[opt.sort] = params.order;
|
149 |
-
}
|
150 |
-
jQuery.extend(params, opt);
|
151 |
-
}
|
152 |
-
dataSource.loadData(params).then(function() {
|
153 |
-
$scope.loading = false;
|
154 |
-
});
|
155 |
-
};
|
156 |
-
|
157 |
-
var filter_delay = null;
|
158 |
-
$scope.$watch('filter', function() {
|
159 |
-
if (filter_delay !== null) {
|
160 |
-
clearTimeout(filter_delay);
|
161 |
-
}
|
162 |
-
filter_delay = setTimeout(function() {
|
163 |
-
filter_delay = null;
|
164 |
-
$scope.$apply(function($scope) {
|
165 |
-
$scope.reload({filter: $scope.filter});
|
166 |
-
});
|
167 |
-
}, 400);
|
168 |
-
});
|
169 |
-
|
170 |
-
/**
|
171 |
-
* Edit customer.
|
172 |
-
*
|
173 |
-
* @param object customer
|
174 |
-
*/
|
175 |
-
$scope.saveCustomer = function(customer) {
|
176 |
-
customer.edit_name = false;
|
177 |
-
customer.edit_wp_user = false;
|
178 |
-
customer.edit_phone = false;
|
179 |
-
customer.edit_email = false;
|
180 |
-
customer.edit_notes = false;
|
181 |
-
customer.errors = {};
|
182 |
-
|
183 |
-
$scope.loading = true;
|
184 |
-
jQuery.ajax({
|
185 |
-
url : ajaxurl,
|
186 |
-
type : 'POST',
|
187 |
-
data : {
|
188 |
-
action : 'ab_save_customer',
|
189 |
-
id : customer.id,
|
190 |
-
wp_user_id : customer.wp_user ? customer.wp_user.ID : null,
|
191 |
-
name : customer.name,
|
192 |
-
phone : customer.phone,
|
193 |
-
email : customer.email,
|
194 |
-
notes : customer.notes
|
195 |
-
},
|
196 |
-
dataType : 'json',
|
197 |
-
success : function(response) {
|
198 |
-
$scope.$apply(function($scope) {
|
199 |
-
if ( response.success == false) {
|
200 |
-
jQuery.each(response.errors, function(field, errors) {
|
201 |
-
customer.errors[field] = {};
|
202 |
-
customer['edit_' + field] = true;
|
203 |
-
jQuery.each(errors, function(key, error) {
|
204 |
-
customer.errors[field][error] = true;
|
205 |
-
});
|
206 |
-
});
|
207 |
-
}
|
208 |
-
$scope.loading = false;
|
209 |
-
});
|
210 |
-
},
|
211 |
-
error : function(response) {
|
212 |
-
$scope.$apply(function($scope) {
|
213 |
-
$scope.loading = false;
|
214 |
-
});
|
215 |
-
}
|
216 |
-
});
|
217 |
-
};
|
218 |
-
|
219 |
-
$scope.saveCustomerPhone = function(customer) {
|
220 |
-
if (customer.edit_phone) {
|
221 |
-
customer.phone = intlTelInputSrv.getNumber(customer.id);
|
222 |
-
$scope.saveCustomer(customer);
|
223 |
-
}
|
224 |
-
};
|
225 |
-
|
226 |
-
/**
|
227 |
-
* Callback for creating new customer.
|
228 |
-
*
|
229 |
-
* @param object customer
|
230 |
-
*/
|
231 |
-
$scope.createCustomer = function(customer) {
|
232 |
-
dataSource.customers.push(customer);
|
233 |
-
$scope.reload(params.page);
|
234 |
-
};
|
235 |
-
|
236 |
-
/**
|
237 |
-
* Delete customer.
|
238 |
-
*/
|
239 |
-
$scope.deleteCustomers = function() {
|
240 |
-
var ids = [];
|
241 |
-
jQuery('table input[type=checkbox]:checked').each(function() {
|
242 |
-
ids.push(jQuery(this).data('customer_id'));
|
243 |
-
});
|
244 |
-
if (ids.length) {
|
245 |
-
if (delete_customers_choice === null) {
|
246 |
-
$modal.data('customer_ids', ids).modal('show');
|
247 |
-
} else {
|
248 |
-
deleteCustomers(ids, delete_customers_choice);
|
249 |
-
}
|
250 |
-
} else {
|
251 |
-
alert(BooklyL10n.please_select_at_least_one_row);
|
252 |
-
}
|
253 |
-
};
|
254 |
-
|
255 |
-
/**
|
256 |
-
* Popup for deleting customer.
|
257 |
-
*/
|
258 |
-
var delete_customers_choice = null;
|
259 |
-
var deleteCustomers = function(ids, with_wp_user) {
|
260 |
-
$scope.loading = true;
|
261 |
-
jQuery.ajax({
|
262 |
-
url: ajaxurl,
|
263 |
-
type: 'POST',
|
264 |
-
data: {
|
265 |
-
action: 'ab_delete_customer',
|
266 |
-
ids : ids,
|
267 |
-
with_wp_user: with_wp_user ? 1 : 0
|
268 |
-
},
|
269 |
-
dataType : 'json',
|
270 |
-
success : function (response) {
|
271 |
-
$scope.$apply(function ($scope) {
|
272 |
-
$scope.reload();
|
273 |
-
});
|
274 |
-
}
|
275 |
-
});
|
276 |
-
};
|
277 |
-
var $modal = jQuery('#ab-customer-delete');
|
278 |
-
$modal
|
279 |
-
.on('click', '.ab-yes', function () {
|
280 |
-
$modal.modal('hide');
|
281 |
-
if ( jQuery('#ab-remember-my-choice').prop('checked') ) {
|
282 |
-
delete_customers_choice = true;
|
283 |
-
}
|
284 |
-
deleteCustomers($modal.data('customer_ids'), true);
|
285 |
-
})
|
286 |
-
.on('click', '.ab-no', function () {
|
287 |
-
if ( jQuery('#ab-remember-my-choice').prop('checked') ) {
|
288 |
-
delete_customers_choice = false;
|
289 |
-
}
|
290 |
-
deleteCustomers($modal.data('customer_ids'), false);
|
291 |
-
});
|
292 |
-
|
293 |
-
});
|
294 |
-
|
295 |
-
/**
|
296 |
-
* Directive for setting focus to element.
|
297 |
-
*/
|
298 |
-
module.directive('focusMe', function($timeout) {
|
299 |
-
return {
|
300 |
-
link: function(scope, element, attrs) {
|
301 |
-
scope.$watch(attrs.focusMe, function(value) {
|
302 |
-
if (value) {
|
303 |
-
$timeout(function() {
|
304 |
-
element[0].focus();
|
305 |
-
});
|
306 |
-
}
|
307 |
-
});
|
308 |
-
}
|
309 |
-
};
|
310 |
-
});
|
311 |
-
|
312 |
-
module.directive('intlTelInput', ['intlTelInputSrv', function(intlTelInputSrv) {
|
313 |
-
return function(scope, element, attrs) {
|
314 |
-
scope.$watch(attrs.intlTelInput, function(value) {
|
315 |
-
if (value) {
|
316 |
-
intlTelInputSrv.init(scope.$eval(attrs.intlTelInputId), element);
|
317 |
-
} else {
|
318 |
-
intlTelInputSrv.destroy(scope.$eval(attrs.intlTelInputId));
|
319 |
-
}
|
320 |
-
});
|
321 |
-
};
|
322 |
-
}]);
|
323 |
-
|
324 |
-
module.directive('clickOutside', ['$document', function ($document) {
|
325 |
-
return {
|
326 |
-
restrict: 'A',
|
327 |
-
scope: {
|
328 |
-
clickOutside: '&'
|
329 |
-
},
|
330 |
-
link: function ($scope, elem, attr) {
|
331 |
-
$document.on('click', function (e) {
|
332 |
-
var element;
|
333 |
-
|
334 |
-
if (!e.target) return;
|
335 |
-
|
336 |
-
for (element = e.target; element; element = element.parentNode) {
|
337 |
-
if (element == elem.get(0)) {
|
338 |
-
return;
|
339 |
-
}
|
340 |
-
}
|
341 |
-
|
342 |
-
$scope.$apply(function($scope) {
|
343 |
-
$scope.$eval($scope.clickOutside);
|
344 |
-
});
|
345 |
-
});
|
346 |
-
}
|
347 |
-
};
|
348 |
-
}]);
|
349 |
-
|
350 |
-
module.filter('nl2br', function() {
|
351 |
-
return function(input) {
|
352 |
-
return ('' + input).split('\n').join('<br>');
|
353 |
-
};
|
354 |
-
});
|
355 |
-
|
356 |
-
})();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/modules/customer/templates/_import.php
DELETED
@@ -1,37 +0,0 @@
|
|
1 |
-
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
|
2 |
-
<div id="ab_import_customers_dialog" class="modal fade" tabindex=-1 role="dialog" aria-labelledby="importCustomersModalLabel" aria-hidden="true">
|
3 |
-
<div class="modal-dialog">
|
4 |
-
<form class="form-horizontal" enctype="multipart/form-data" action="<?php echo AB_Utils::escAdminUrl( AB_CustomerController::page_slug ) ?>" method="POST">
|
5 |
-
<div class="modal-content">
|
6 |
-
<div class="modal-header">
|
7 |
-
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
8 |
-
<h4 class="modal-title"><?php _e( 'Import', 'bookly' ) ?></h4>
|
9 |
-
</div>
|
10 |
-
<div class="modal-body">
|
11 |
-
<div class="container-fluid">
|
12 |
-
<div class="form-group">
|
13 |
-
<label><?php _e( 'Note', 'bookly' ) ?></label>
|
14 |
-
<?php _e( 'You may import list of clients in CSV format. The file needs to have three columns: Name, Phone and Email.', 'bookly' ) ?>
|
15 |
-
</div>
|
16 |
-
<div class="form-group">
|
17 |
-
<label for="import_customers_file"><?php _e( 'Select file', 'bookly' ) ?></label>
|
18 |
-
<input name="import_customers_file" id="import_customers_file" type="file">
|
19 |
-
</div>
|
20 |
-
<div class="form-group">
|
21 |
-
<label for="import_customers_delimiter"><?php _e( 'Delimiter', 'bookly' ) ?></label>
|
22 |
-
<select name="import_customers_delimiter" id="import_customers_delimiter" class="form-control">
|
23 |
-
<option value=","><?php _e( 'Comma (,)', 'bookly' ) ?></option>
|
24 |
-
<option value=";"><?php _e( 'Semicolon (;)', 'bookly' ) ?></option>
|
25 |
-
</select>
|
26 |
-
</div>
|
27 |
-
<input type="hidden" name="import">
|
28 |
-
</div>
|
29 |
-
</div>
|
30 |
-
<div class="modal-footer">
|
31 |
-
<button type="submit" class="btn btn-info ab-popup-save" name="import-customers"><?php _e( 'Import', 'bookly' ) ?></button>
|
32 |
-
<button class="ab-reset-form" data-dismiss="modal" aria-hidden="true"><?php _e( 'Cancel', 'bookly' ) ?></button>
|
33 |
-
</div>
|
34 |
-
</div>
|
35 |
-
</form>
|
36 |
-
</div>
|
37 |
-
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/modules/customer/templates/index.php
DELETED
@@ -1,136 +0,0 @@
|
|
1 |
-
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
|
2 |
-
<div class="panel panel-default">
|
3 |
-
<div class="panel-heading">
|
4 |
-
<h3 class="panel-title"><?php _e( 'Customers', 'bookly' ) ?></h3>
|
5 |
-
</div>
|
6 |
-
<div class="panel-body ab-customers-list">
|
7 |
-
<div ng-app="customers" ng-controller="customersCtrl" class="form-horizontal ng-cloak">
|
8 |
-
<div class="row">
|
9 |
-
<div class="col-md-6 col-xs-12">
|
10 |
-
<div class="control-group">
|
11 |
-
<label for="ab_filter"><?php _e( 'Quick search customer', 'bookly' ) ?></label>
|
12 |
-
<div class=controls>
|
13 |
-
<input id="ab_filter" style="display: inline-block;width: auto;margin-bottom: 20px" class="form-control" type=text ng-model=filter />
|
14 |
-
</div>
|
15 |
-
</div>
|
16 |
-
</div>
|
17 |
-
<div class="col-md-6 col-xs-12">
|
18 |
-
<div style="display: inline;" new-customer-dialog="createCustomer(customer)" backdrop="true" btn-class="btn btn-info"></div>
|
19 |
-
<a href="#ab_import_customers_dialog" class="btn btn-info pull-right" data-toggle="modal"><?php _e( 'Import', 'bookly' ) ?></a>
|
20 |
-
<a style="margin-right: 5px;" href=#ab_new_customer_dialog class="btn btn-info pull-right" data-backdrop=true data-toggle="modal"><?php _e( 'New customer', 'bookly' ) ?></a>
|
21 |
-
<?php include "_import.php" ?>
|
22 |
-
</div>
|
23 |
-
</div>
|
24 |
-
|
25 |
-
<div class="table-responsive">
|
26 |
-
<table id="ab_customers_list" class="table table-striped" cellspacing=0 cellpadding=0 border=0 style="clear: both;">
|
27 |
-
<thead>
|
28 |
-
<tr>
|
29 |
-
<th style="width: 150px" ng-class=css_class.name><a href="" ng-click=reload({sort:'name'})><?php _e( 'Name', 'bookly' ) ?></a></th>
|
30 |
-
<th style="width: 150px" ng-class=css_class.wp_user><a href="" ng-click=reload({sort:'wp_user'})><?php _e( 'User', 'bookly' ) ?></a></th>
|
31 |
-
<th style="width: 100px" ng-class=css_class.phone><a href="" ng-click=reload({sort:'phone'})><?php _e( 'Phone', 'bookly' ) ?></a></th>
|
32 |
-
<th style="width: 250px" ng-class=css_class.email><a href="" ng-click=reload({sort:'email'})><?php _e( 'Email', 'bookly' ) ?></a></th>
|
33 |
-
<th style="width: 250px" ng-class=css_class.notes><a href="" ng-click=reload({sort:'notes'})><?php _e( 'Notes', 'bookly' ) ?></a></th>
|
34 |
-
<th ng-class=css_class.last_appointment><a href="" ng-click=reload({sort:'last_appointment'})><?php _e( 'Last appointment', 'bookly' ) ?></a></th>
|
35 |
-
<th ng-class=css_class.total_appointments><a href="" ng-click=reload({sort:'total_appointments'})><?php _e( 'Total appointments', 'bookly') ?></a></th>
|
36 |
-
<th ng-class=css_class.payments><a href="" ng-click=reload({sort:'payments'})><?php _e( 'Payments', 'bookly') ?></a></th>
|
37 |
-
<th></th>
|
38 |
-
</tr>
|
39 |
-
</thead>
|
40 |
-
<tbody>
|
41 |
-
<tr ng-repeat="customer in dataSource.customers">
|
42 |
-
<td>
|
43 |
-
<div ng-click="customer.edit_name = true" ng-hide=customer.edit_name class=displayed-value>{{customer.name}}</div>
|
44 |
-
<span ng-show=customer.errors.name.required><?php _e( 'Required', 'bookly' ) ?></span>
|
45 |
-
<input class="form-control ab-value" ng-model=customer.name ui-event="{blur:'saveCustomer(customer)'}" ng-show=customer.edit_name focus-me=customer.edit_name required />
|
46 |
-
</td>
|
47 |
-
<td>
|
48 |
-
<div ng-click="customer.edit_wp_user = true" ng-hide=customer.edit_wp_user class=displayed-value>{{customer.wp_user.display_name}}</div>
|
49 |
-
<select ng-model="customer.wp_user" ng-options="wp_user as wp_user.display_name for wp_user in dataSource.wp_users" ui-event="{blur:'saveCustomer(customer)'}" ng-show=customer.edit_wp_user
|
50 |
-
focus-me=customer.edit_wp_user class="form-control">
|
51 |
-
<option value=""></option>
|
52 |
-
</select>
|
53 |
-
</td>
|
54 |
-
<td class="ab-phone" click-outside="saveCustomerPhone(customer)">
|
55 |
-
<div ng-click="customer.edit_phone = true" ng-hide=customer.edit_phone class=displayed-value>{{customer.phone}}</div>
|
56 |
-
<input class="form-control ab-value" intl-tel-input=customer.edit_phone intl-tel-input-id=customer.id ng-model=customer.phone ng-show=customer.edit_phone focus-me=customer.edit_phone />
|
57 |
-
</td>
|
58 |
-
<td>
|
59 |
-
<div ng-click="customer.edit_email = true" ng-hide=customer.edit_email class=displayed-value>{{customer.email}}</div>
|
60 |
-
<input class="form-control ab-value" ng-model=customer.email ui-event="{blur:'saveCustomer(customer)'}" ng-show=customer.edit_email focus-me=customer.edit_email />
|
61 |
-
</td>
|
62 |
-
<td>
|
63 |
-
<div ng-click="customer.edit_notes = true" ng-hide=customer.edit_notes class=displayed-value ng-bind-html="customer.notes | nl2br"></div>
|
64 |
-
<textarea class="form-control ab-value" ng-model="customer.notes" ui-event="{blur:'saveCustomer(customer)'}" ng-show=customer.edit_notes focus-me=customer.edit_notes></textarea>
|
65 |
-
</td>
|
66 |
-
<td>
|
67 |
-
<div ng-model=customer.last_appointment >{{customer.last_appointment}}</div>
|
68 |
-
</td>
|
69 |
-
<td class="text-right">
|
70 |
-
<div ng-model=customer.total_appointments >{{customer.total_appointments}}</div>
|
71 |
-
</td>
|
72 |
-
<td class="text-right">
|
73 |
-
<div ng-model=customer.payments >{{customer.payments}}</div>
|
74 |
-
</td>
|
75 |
-
<td><input type="checkbox" data-customer_id="{{customer.id}}"></td>
|
76 |
-
</tr>
|
77 |
-
</tbody>
|
78 |
-
</table>
|
79 |
-
<div ng-hide="dataSource.customers.length || loading">
|
80 |
-
<td colspan=9><div class="alert alert-info"><?php _e( 'No customers.', 'bookly' ) ?></div></td>
|
81 |
-
</div>
|
82 |
-
</div>
|
83 |
-
|
84 |
-
<div class="btn-toolbar">
|
85 |
-
<div class="col-xs-8">
|
86 |
-
<div ng-show="dataSource.pages.length > 1">
|
87 |
-
<div class="btn-group" ng-show="dataSource.paginator.beg">
|
88 |
-
<button ng-click=reload({page:1}) class="btn btn-default">
|
89 |
-
1
|
90 |
-
</button>
|
91 |
-
</div>
|
92 |
-
<div class="btn-group">
|
93 |
-
<button ng-click=reload({page:page.number}) class="btn btn-default" ng-class="{'active': page.active}" ng-repeat="page in dataSource.pages">
|
94 |
-
{{page.number}}
|
95 |
-
</button>
|
96 |
-
</div>
|
97 |
-
<div class="btn-group" ng-show="dataSource.paginator.end != false">
|
98 |
-
<button ng-click=reload({page:dataSource.paginator.end.number}) class="btn btn-default">
|
99 |
-
{{dataSource.paginator.end.number}}
|
100 |
-
</button>
|
101 |
-
</div>
|
102 |
-
</div>
|
103 |
-
</div>
|
104 |
-
<div class="col-xs-4">
|
105 |
-
<a class="btn btn-info pull-right" ng-click="deleteCustomers()"><?php _e( 'Delete', 'bookly' ) ?></a>
|
106 |
-
</div>
|
107 |
-
</div>
|
108 |
-
<div ng-show="loading" class="loading-indicator">
|
109 |
-
<span class="ab-loader"></span>
|
110 |
-
</div>
|
111 |
-
</div>
|
112 |
-
</div>
|
113 |
-
</div>
|
114 |
-
|
115 |
-
<div class="modal fade" id="ab-customer-delete">
|
116 |
-
<div class="modal-dialog">
|
117 |
-
<div class="modal-content">
|
118 |
-
<div class="modal-header">
|
119 |
-
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
120 |
-
<h4 class="modal-title"><?php _e( 'Delete customers', 'bookly' ) ?></h4>
|
121 |
-
</div>
|
122 |
-
<div class="modal-body">
|
123 |
-
<?php _e( 'You are about to delete customers which may have WordPress accounts associated to them. Do you want to delete those accounts too (if there are any)?', 'bookly' ) ?>
|
124 |
-
<div class="checkbox">
|
125 |
-
<label>
|
126 |
-
<input id="ab-remember-my-choice" type="checkbox"> <?php _e( 'Remember my choice', 'bookly' ) ?>
|
127 |
-
</label>
|
128 |
-
</div>
|
129 |
-
</div>
|
130 |
-
<div class="modal-footer">
|
131 |
-
<button type="button" class="btn btn-default ab-no" data-dismiss="modal"><?php _e( 'No, delete just customers', 'bookly' ) ?></button>
|
132 |
-
<button type="button" class="btn btn-info ab-yes"><?php _e( 'Yes', 'bookly' ) ?></button>
|
133 |
-
</div>
|
134 |
-
</div>
|
135 |
-
</div>
|
136 |
-
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/modules/customer/templates/ng-new_customer_dialog.php
DELETED
@@ -1,110 +0,0 @@
|
|
1 |
-
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
|
2 |
-
<div>
|
3 |
-
<div id="ab_new_customer_dialog" class="modal fade">
|
4 |
-
<div class="modal-dialog">
|
5 |
-
<form style="margin-bottom: 0;" ng-hide=loading>
|
6 |
-
<div class="modal-content">
|
7 |
-
<div class="modal-header">
|
8 |
-
<button type=button class=close data-dismiss=modal aria-hidden=true>×</button>
|
9 |
-
<h4 class="modal-title"><?php _e( 'New Customer', 'bookly' ) ?></h4>
|
10 |
-
</div>
|
11 |
-
<div class="modal-body">
|
12 |
-
<fieldset>
|
13 |
-
<legend><?php _e( 'Personal Information', 'bookly' ) ?></legend>
|
14 |
-
<div class="col-md-12">
|
15 |
-
<div class="form-group">
|
16 |
-
<label for="wp_user"><?php _e( 'User', 'bookly' ) ?></label>
|
17 |
-
<select ng-model="form.wp_user_id" class="form-control" id="wp_user">
|
18 |
-
<option value=""></option>
|
19 |
-
<?php foreach ( $wp_users as $wp_user ): ?>
|
20 |
-
<option value="<?php echo $wp_user->ID ?>">
|
21 |
-
<?php echo $wp_user->display_name ?>
|
22 |
-
</option>
|
23 |
-
<?php endforeach ?>
|
24 |
-
</select>
|
25 |
-
</div>
|
26 |
-
|
27 |
-
<div class="form-group">
|
28 |
-
<label for="username"><?php _e( 'Name', 'bookly' ) ?></label>
|
29 |
-
<input class="form-control" type="text" ng-model="form.name" id="username"/>
|
30 |
-
<span style="font-size: 11px;color: red" ng-show="errors.name.required"><?php _e( 'Required', 'bookly' ) ?></span>
|
31 |
-
</div>
|
32 |
-
<div class="form-group">
|
33 |
-
<label for="phone"><?php _e( 'Phone', 'bookly' ) ?></label><br/>
|
34 |
-
<input class="form-control" type="text" ng-model=form.phone id="phone"/>
|
35 |
-
</div>
|
36 |
-
<div class="form-group">
|
37 |
-
<label for="email"><?php _e( 'Email', 'bookly' ) ?></label>
|
38 |
-
<input class="form-control" type="text" ng-model=form.email id="email"/>
|
39 |
-
</div>
|
40 |
-
<div class="form-group">
|
41 |
-
<label for="notes"><?php _e( 'Notes', 'bookly' ) ?></label>
|
42 |
-
<textarea class="form-control" ng-model=form.notes id="notes"></textarea>
|
43 |
-
</div>
|
44 |
-
</div>
|
45 |
-
</fieldset>
|
46 |
-
|
47 |
-
<?php if ( $module !== 'customer' ): ?>
|
48 |
-
<fieldset>
|
49 |
-
<legend><?php _e( 'Custom Fields', 'bookly' ) ?></legend>
|
50 |
-
<div class="new-customer-custom-fields">
|
51 |
-
<div class="col-md-12">
|
52 |
-
<?php foreach ( $custom_fields as $custom_field ): ?>
|
53 |
-
<div class="form-group">
|
54 |
-
<label><?php echo $custom_field->label ?></label>
|
55 |
-
<div class="ab-formField" data-type="<?php echo $custom_field->type ?>" data-id="<?php echo $custom_field->id ?>">
|
56 |
-
<?php if ( $custom_field->type == 'text-field' ): ?>
|
57 |
-
<input type="text" class="form-control ab-custom-field" />
|
58 |
-
|
59 |
-
<?php elseif ( $custom_field->type == 'textarea' ): ?>
|
60 |
-
<textarea rows="3" class="form-control ab-custom-field"></textarea>
|
61 |
-
|
62 |
-
<?php elseif ( $custom_field->type == 'checkboxes' ): ?>
|
63 |
-
<?php foreach ( $custom_field->items as $item ): ?>
|
64 |
-
<div class="checkbox">
|
65 |
-
<label>
|
66 |
-
<input class="ab-custom-field" type="checkbox" value="<?php echo esc_attr( $item ) ?>" />
|
67 |
-
<?php echo $item ?>
|
68 |
-
</label>
|
69 |
-
</div>
|
70 |
-
<?php endforeach ?>
|
71 |
-
|
72 |
-
<?php elseif ( $custom_field->type == 'radio-buttons' ): ?>
|
73 |
-
<?php foreach ( $custom_field->items as $item ): ?>
|
74 |
-
<div class="radio">
|
75 |
-
<label>
|
76 |
-
<input type="radio" name="<?php echo $custom_field->id ?>" class="ab-custom-field" value="<?php echo esc_attr( $item ) ?>" />
|
77 |
-
<?php echo $item ?>
|
78 |
-
</label>
|
79 |
-
</div>
|
80 |
-
<?php endforeach ?>
|
81 |
-
|
82 |
-
<?php elseif ( $custom_field->type == 'drop-down' ): ?>
|
83 |
-
<select class="form-control ab-custom-field">
|
84 |
-
<option value=""></option>
|
85 |
-
<?php foreach ( $custom_field->items as $item ): ?>
|
86 |
-
<option value="<?php echo esc_attr( $item ) ?>"><?php echo $item ?></option>
|
87 |
-
<?php endforeach ?>
|
88 |
-
</select>
|
89 |
-
|
90 |
-
<?php endif ?>
|
91 |
-
</div>
|
92 |
-
</div>
|
93 |
-
<?php endforeach ?>
|
94 |
-
</div>
|
95 |
-
</div>
|
96 |
-
</fieldset>
|
97 |
-
<?php endif ?>
|
98 |
-
</div>
|
99 |
-
<div class="modal-footer">
|
100 |
-
<button ng-click=processForm() class="btn btn-info ab-popup-save"><?php _e( 'Create customer', 'bookly' ) ?></button>
|
101 |
-
<button class=ab-reset-form data-dismiss=modal aria-hidden=true><?php _e( 'Cancel', 'bookly' ) ?></button>
|
102 |
-
</div>
|
103 |
-
</div><!-- /.modal-content -->
|
104 |
-
</form>
|
105 |
-
<div ng-show=loading class=loading-indicator>
|
106 |
-
<span class="ab-loader"></span>
|
107 |
-
</div>
|
108 |
-
</div><!-- /.modal-dialog -->
|
109 |
-
</div><!-- /.modal -->
|
110 |
-
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/modules/customers/Components.php
ADDED
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace BooklyLite\Backend\Modules\Customers;
|
3 |
+
|
4 |
+
use BooklyLite\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Components
|
8 |
+
* @package BooklyLite\Backend\Modules\Customers
|
9 |
+
*/
|
10 |
+
class Components extends Lib\Base\Components
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Render customer dialog.
|
14 |
+
* @throws \Exception
|
15 |
+
*/
|
16 |
+
public function renderCustomerDialog()
|
17 |
+
{
|
18 |
+
$this->enqueueStyles( array(
|
19 |
+
'frontend' => get_option( 'ab_settings_phone_default_country' ) == 'disabled'
|
20 |
+
? array()
|
21 |
+
: array( 'css/intlTelInput.css' ),
|
22 |
+
) );
|
23 |
+
|
24 |
+
$this->enqueueScripts( array(
|
25 |
+
'backend' => array( 'js/angular.min.js' => array( 'jquery' ), ),
|
26 |
+
'frontend' => get_option( 'ab_settings_phone_default_country' ) == 'disabled'
|
27 |
+
? array()
|
28 |
+
: array( 'js/intlTelInput.min.js' => array( 'jquery' ) ),
|
29 |
+
'module' => array( 'js/ng-customer_dialog.js' => array( 'ab-angular.min.js' ), )
|
30 |
+
) );
|
31 |
+
|
32 |
+
wp_localize_script( 'ab-ng-customer_dialog.js', 'BooklyL10nCustDialog', array(
|
33 |
+
'default_status' => get_option( 'ab_settings_default_appointment_status' ),
|
34 |
+
'intlTelInput' => array(
|
35 |
+
'enabled' => ( get_option( 'ab_settings_phone_default_country' ) != 'disabled' ),
|
36 |
+
'utils' => plugins_url( 'intlTelInput.utils.js', Lib\Plugin::getDirectory() . '/frontend/resources/js/intlTelInput.utils.js' ),
|
37 |
+
'country' => get_option( 'ab_settings_phone_default_country' ),
|
38 |
+
),
|
39 |
+
) );
|
40 |
+
|
41 |
+
$this->render( '_customer_dialog' );
|
42 |
+
}
|
43 |
+
|
44 |
+
}
|
backend/modules/customers/Controller.php
ADDED
@@ -0,0 +1,235 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace BooklyLite\Backend\Modules\Customers;
|
3 |
+
|
4 |
+
use BooklyLite\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Controller
|
8 |
+
* @package BooklyLite\Backend\Modules\Customers
|
9 |
+
*/
|
10 |
+
class Controller extends Lib\Base\Controller
|
11 |
+
{
|
12 |
+
const page_slug = 'ab-customers';
|
13 |
+
|
14 |
+
protected function getPermissions()
|
15 |
+
{
|
16 |
+
return array(
|
17 |
+
'executeSaveCustomer' => 'user',
|
18 |
+
);
|
19 |
+
}
|
20 |
+
|
21 |
+
public function index()
|
22 |
+
{
|
23 |
+
if ( $this->hasParameter( 'import-customers' ) ) {
|
24 |
+
$this->importCustomers();
|
25 |
+
}
|
26 |
+
|
27 |
+
$this->enqueueStyles( array(
|
28 |
+
'backend' => array( 'bootstrap/css/bootstrap-theme.min.css', ),
|
29 |
+
'frontend' => array( 'css/ladda.min.css', ),
|
30 |
+
) );
|
31 |
+
|
32 |
+
$this->enqueueScripts( array(
|
33 |
+
'backend' => array(
|
34 |
+
'bootstrap/js/bootstrap.min.js' => array( 'jquery' ),
|
35 |
+
'js/datatables.min.js' => array( 'jquery' ),
|
36 |
+
'js/alert.js' => array( 'jquery' ),
|
37 |
+
),
|
38 |
+
'frontend' => array(
|
39 |
+
'js/spin.min.js' => array( 'jquery' ),
|
40 |
+
'js/ladda.min.js' => array( 'jquery' ),
|
41 |
+
),
|
42 |
+
'module' => array(
|
43 |
+
'js/customers.js' => array( 'ab-datatables.min.js', 'ab-ng-customer_dialog.js' ),
|
44 |
+
),
|
45 |
+
) );
|
46 |
+
|
47 |
+
wp_localize_script( 'ab-customers.js', 'BooklyL10n', array(
|
48 |
+
'edit' => __( 'Edit', 'bookly' ),
|
49 |
+
'are_you_sure' => __( 'Are you sure?', 'bookly' ),
|
50 |
+
'wp_users' => get_users( array( 'fields' => array( 'ID', 'display_name' ), 'orderby' => 'display_name' ) ),
|
51 |
+
'zeroRecords' => __( 'No customers found.', 'bookly' ),
|
52 |
+
'processing' => __( 'Processing...', 'bookly' ),
|
53 |
+
'edit_customer' => __( 'Edit customer', 'bookly' ),
|
54 |
+
'new_customer' => __( 'New customer', 'bookly' ),
|
55 |
+
'create_customer' => __( 'Create customer', 'bookly' ),
|
56 |
+
'save' => __( 'Save', 'bookly' ),
|
57 |
+
'search' => __( 'Quick search customer', 'bookly' ),
|
58 |
+
'limitations' => __( '<b class="h4">This function is disabled in the Lite version of Bookly.</b><br><br>If you find the plugin useful for your business please consider buying a licence for the full version.<br>It costs just $59 and for this money you will get many useful functions, lifetime free updates and excellent support!<br>More information can be found here', 'bookly' ) . ': <a href="http://booking-wp-plugin.com" target="_blank" class="alert-link">http://booking-wp-plugin.com</a>',
|
59 |
+
) );
|
60 |
+
|
61 |
+
$this->render( 'index' );
|
62 |
+
}
|
63 |
+
|
64 |
+
/**
|
65 |
+
* Get list of customers.
|
66 |
+
*/
|
67 |
+
public function executeGetCustomers()
|
68 |
+
{
|
69 |
+
global $wpdb;
|
70 |
+
|
71 |
+
$columns = $this->getParameter( 'columns' );
|
72 |
+
$order = $this->getParameter( 'order' );
|
73 |
+
$filter = $this->getParameter( 'filter' );
|
74 |
+
|
75 |
+
$query = Lib\Entities\Customer::query( 'c' );
|
76 |
+
|
77 |
+
$total = $query->count();
|
78 |
+
|
79 |
+
$query
|
80 |
+
->select( 'SQL_CALC_FOUND_ROWS c.*, MAX(a.start_date) AS last_appointment,
|
81 |
+
COUNT(DISTINCT a.id) AS total_appointments,
|
82 |
+
SUM(p.total)*COUNT(DISTINCT p.id)/COUNT(*) AS payments,
|
83 |
+
wpu.display_name AS wp_user' )
|
84 |
+
->leftJoin( 'CustomerAppointment', 'ca', 'ca.customer_id = c.id' )
|
85 |
+
->leftJoin( 'Appointment', 'a', 'a.id = ca.appointment_id' )
|
86 |
+
->leftJoin( 'Payment', 'p', 'p.id = ca.payment_id' )
|
87 |
+
->tableJoin( $wpdb->users, 'wpu', 'wpu.ID = c.wp_user_id' )
|
88 |
+
->groupBy( 'c.id' );
|
89 |
+
|
90 |
+
if ( $filter != '' ) {
|
91 |
+
$search_value = Lib\Query::escape( $filter );
|
92 |
+
$query
|
93 |
+
->whereLike( 'c.name', "%{$search_value}%" )
|
94 |
+
->whereLike( 'c.phone', "%{$search_value}%", 'OR' )
|
95 |
+
->whereLike( 'c.email', "%{$search_value}%", 'OR' );
|
96 |
+
}
|
97 |
+
|
98 |
+
foreach ( $order as $sort_by ) {
|
99 |
+
$query->sortBy( str_replace( '.', '_', $columns[ $sort_by['column'] ]['data'] ) )
|
100 |
+
->order( $sort_by['dir'] == 'desc' ? Lib\Query::ORDER_DESCENDING : Lib\Query::ORDER_ASCENDING );
|
101 |
+
}
|
102 |
+
|
103 |
+
$query->limit( $this->getParameter( 'length' ) )->offset( $this->getParameter( 'start' ) );
|
104 |
+
|
105 |
+
$data = array();
|
106 |
+
foreach ( $query->fetchArray() as $row ) {
|
107 |
+
$data[] = array(
|
108 |
+
'id' => $row['id'],
|
109 |
+
'name' => $row['name'],
|
110 |
+
'wp_user' => $row['wp_user'],
|
111 |
+
'wp_user_id' => $row['wp_user_id'],
|
112 |
+
'phone' => $row['phone'],
|
113 |
+
'email' => $row['email'],
|
114 |
+
'notes' => $row['notes'],
|
115 |
+
'last_appointment' => $row['last_appointment'] ? Lib\Utils\DateTime::formatDateTime( $row['last_appointment'] ) : '',
|
116 |
+
'total_appointments' => $row['total_appointments'],
|
117 |
+
'payments' => Lib\Utils\Common::formatPrice( $row['payments'] ),
|
118 |
+
);
|
119 |
+
}
|
120 |
+
|
121 |
+
wp_send_json( array(
|
122 |
+
'draw' => ( int ) $this->getParameter( 'draw' ),
|
123 |
+
'recordsTotal' => $total,
|
124 |
+
'recordsFiltered' => ( int ) $wpdb->get_var( 'SELECT FOUND_ROWS()' ),
|
125 |
+
'data' => $data,
|
126 |
+
) );
|
127 |
+
}
|
128 |
+
|
129 |
+
/**
|
130 |
+
* Create or edit a customer.
|
131 |
+
*/
|
132 |
+
public function executeSaveCustomer()
|
133 |
+
{
|
134 |
+
$response = array();
|
135 |
+
$form = new Forms\Customer();
|
136 |
+
|
137 |
+
do {
|
138 |
+
if ( $this->getParameter( 'name' ) !== '' ) {
|
139 |
+
$params = $this->getPostParameters();
|
140 |
+
if ( ! $params['wp_user_id'] ) {
|
141 |
+
$params['wp_user_id'] = null;
|
142 |
+
}
|
143 |
+
$form->bind( $params );
|
144 |
+
/** @var Lib\Entities\Customer $customer */
|
145 |
+
$customer = $form->save();
|
146 |
+
if ( $customer ) {
|
147 |
+
$response['success'] = true;
|
148 |
+
$response['customer'] = array(
|
149 |
+
'id' => $customer->get( 'id' ),
|
150 |
+
'name' => $customer->get( 'name' ),
|
151 |
+
'phone' => $customer->get( 'phone' ),
|
152 |
+
'email' => $customer->get( 'email' ),
|
153 |
+
'notes' => $customer->get( 'notes' ),
|
154 |
+
'wp_user_id' => $customer->get( 'wp_user_id' ),
|
155 |
+
'jsonString' => json_encode( array(
|
156 |
+
'name' => $customer->get( 'name' ),
|
157 |
+
'phone' => $customer->get( 'phone' ),
|
158 |
+
'email' => $customer->get( 'email' ),
|
159 |
+
'notes' => $customer->get( 'notes' )
|
160 |
+
) )
|
161 |
+
);
|
162 |
+
break;
|
163 |
+
}
|
164 |
+
}
|
165 |
+
$response['success'] = false;
|
166 |
+
$response['errors'] = array( 'name' => array( 'required' ) );
|
167 |
+
} while ( 0 );
|
168 |
+
|
169 |
+
wp_send_json( $response );
|
170 |
+
}
|
171 |
+
|
172 |
+
/**
|
173 |
+
* Import customers from CSV.
|
174 |
+
*/
|
175 |
+
private function importCustomers()
|
176 |
+
{
|
177 |
+
@ini_set( 'auto_detect_line_endings', true );
|
178 |
+
|
179 |
+
$csv_mime_types = array(
|
180 |
+
'text/csv',
|
181 |
+
'application/csv',
|
182 |
+
'text/comma-separated-values',
|
183 |
+
'application/excel',
|
184 |
+
'application/vnd.ms-excel',
|
185 |
+
'application/vnd.msexcel'
|
186 |
+
);
|
187 |
+
|
188 |
+
if ( in_array( $_FILES['import_customers_file']['type'], $csv_mime_types ) ) {
|
189 |
+
$file = fopen( $_FILES['import_customers_file']['tmp_name'], 'r' );
|
190 |
+
while ( $line = fgetcsv( $file, null, $this->getParameter( 'import_customers_delimiter' ) ) ) {
|
191 |
+
if ( $line[0] != '' ) {
|
192 |
+
$customer = new Lib\Entities\Customer();
|
193 |
+
$customer->set( 'name', $line[0] );
|
194 |
+
if ( isset( $line[1] ) ) {
|
195 |
+
$customer->set( 'phone', $line[1] );
|
196 |
+
}
|
197 |
+
if ( isset( $line[2] ) ) {
|
198 |
+
$customer->set( 'email', $line[2] );
|
199 |
+
}
|
200 |
+
$customer->save();
|
201 |
+
}
|
202 |
+
}
|
203 |
+
}
|
204 |
+
}
|
205 |
+
|
206 |
+
/**
|
207 |
+
* Delete customers.
|
208 |
+
*/
|
209 |
+
public function executeDeleteCustomers()
|
210 |
+
{
|
211 |
+
foreach ( $this->getParameter( 'data', array() ) as $id ) {
|
212 |
+
$customer = new Lib\Entities\Customer();
|
213 |
+
$customer->load( $id );
|
214 |
+
$customer->deleteWithWPUser( (bool) $this->getParameter( 'with_wp_user' ) );
|
215 |
+
}
|
216 |
+
wp_send_json_success();
|
217 |
+
}
|
218 |
+
|
219 |
+
/**
|
220 |
+
* Export Customers to CSV
|
221 |
+
*/
|
222 |
+
public function executeExportCustomers() { exit; }
|
223 |
+
|
224 |
+
/**
|
225 |
+
* Override parent method to add 'wp_ajax_ab_' prefix
|
226 |
+
* so current 'execute*' methods look nicer.
|
227 |
+
*
|
228 |
+
* @param string $prefix
|
229 |
+
*/
|
230 |
+
protected function registerWpActions( $prefix = '' )
|
231 |
+
{
|
232 |
+
parent::registerWpActions( 'wp_ajax_ab_' );
|
233 |
+
}
|
234 |
+
|
235 |
+
}
|
backend/modules/customers/forms/Customer.php
ADDED
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace BooklyLite\Backend\Modules\Customers\Forms;
|
3 |
+
|
4 |
+
use BooklyLite\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Customer
|
8 |
+
* @package BooklyLite\Backend\Modules\Customers\Forms
|
9 |
+
*/
|
10 |
+
class Customer extends Lib\Base\Form
|
11 |
+
{
|
12 |
+
protected static $entity_class = 'Customer';
|
13 |
+
|
14 |
+
public function configure()
|
15 |
+
{
|
16 |
+
$this->setFields( array(
|
17 |
+
'name',
|
18 |
+
'wp_user_id',
|
19 |
+
'phone',
|
20 |
+
'email',
|
21 |
+
'notes'
|
22 |
+
) );
|
23 |
+
}
|
24 |
+
|
25 |
+
}
|
backend/modules/customers/resources/js/customers.js
ADDED
@@ -0,0 +1,222 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
jQuery(function($) {
|
2 |
+
|
3 |
+
var
|
4 |
+
$customers_list = $('#bookly-customers-list'),
|
5 |
+
$filter = $('#bookly-filter'),
|
6 |
+
$check_all_button = $('#bookly-check-all'),
|
7 |
+
$customer_dialog = $('#bookly-customer-dialog'),
|
8 |
+
$add_button = $('#bookly-add'),
|
9 |
+
$delete_button = $('#bookly-delete'),
|
10 |
+
$delete_dialog = $('#bookly-delete-dialog'),
|
11 |
+
$delete_button_no = $('#bookly-delete-no'),
|
12 |
+
$delete_button_yes = $('#bookly-delete-yes'),
|
13 |
+
$remember_choice = $('#bookly-delete-remember-choice'),
|
14 |
+
remembered_choice,
|
15 |
+
row
|
16 |
+
;
|
17 |
+
|
18 |
+
/**
|
19 |
+
* Init DataTables.
|
20 |
+
*/
|
21 |
+
var dt = $customers_list.DataTable({
|
22 |
+
order: [[ 0, 'asc' ]],
|
23 |
+
info: false,
|
24 |
+
searching: false,
|
25 |
+
lengthChange: false,
|
26 |
+
pageLength: 25,
|
27 |
+
pagingType: 'numbers',
|
28 |
+
processing: true,
|
29 |
+
responsive: true,
|
30 |
+
serverSide: true,
|
31 |
+
ajax: {
|
32 |
+
url: ajaxurl,
|
33 |
+
data: function (d) {
|
34 |
+
return $.extend({}, d, {
|
35 |
+
action: 'ab_get_customers',
|
36 |
+
filter: $filter.val()
|
37 |
+
});
|
38 |
+
}
|
39 |
+
},
|
40 |
+
columns: [
|
41 |
+
{ data: 'name' },
|
42 |
+
{ data: 'wp_user' },
|
43 |
+
{ data: 'phone' },
|
44 |
+
{ data: 'email' },
|
45 |
+
{ data: 'notes' },
|
46 |
+
{ data: 'last_appointment' },
|
47 |
+
{ data: 'total_appointments' },
|
48 |
+
{ data: 'payments' },
|
49 |
+
{
|
50 |
+
responsivePriority: 1,
|
51 |
+
orderable: false,
|
52 |
+
searchable: false,
|
53 |
+
render: function ( data, type, row, meta ) {
|
54 |
+
return '<button type="button" class="btn btn-default" data-toggle="modal" data-target="#bookly-customer-dialog"><i class="glyphicon glyphicon-edit"></i> ' + BooklyL10n.edit + '</button>';
|
55 |
+
}
|
56 |
+
},
|
57 |
+
{
|
58 |
+
responsivePriority: 1,
|
59 |
+
orderable: false,
|
60 |
+
searchable: false,
|
61 |
+
render: function ( data, type, row, meta ) {
|
62 |
+
return '<input type="checkbox" value="' + row.id + '">';
|
63 |
+
}
|
64 |
+
}
|
65 |
+
],
|
66 |
+
dom: "<'row'<'col-sm-6'l><'col-sm-6'f>>" +
|
67 |
+
"<'row'<'col-sm-12'tr>>" +
|
68 |
+
"<'row pull-left'<'col-sm-12 bookly-margin-top-lg'p>>",
|
69 |
+
language: {
|
70 |
+
zeroRecords: BooklyL10n.zeroRecords,
|
71 |
+
processing: BooklyL10n.processing
|
72 |
+
}
|
73 |
+
});
|
74 |
+
|
75 |
+
/**
|
76 |
+
* Select all coupons.
|
77 |
+
*/
|
78 |
+
$check_all_button.on('change', function () {
|
79 |
+
$customers_list.find('tbody input:checkbox').prop('checked', this.checked);
|
80 |
+
});
|
81 |
+
|
82 |
+
/**
|
83 |
+
* On coupon select.
|
84 |
+
*/
|
85 |
+
$customers_list.on('change', 'tbody input:checkbox', function () {
|
86 |
+
$check_all_button.prop('checked', $customers_list.find('tbody input:not(:checked)').length == 0);
|
87 |
+
});
|
88 |
+
|
89 |
+
/**
|
90 |
+
* Edit customer.
|
91 |
+
*/
|
92 |
+
$customers_list.on('click', 'button', function () {
|
93 |
+
row = dt.row($(this).closest('td'));
|
94 |
+
});
|
95 |
+
|
96 |
+
/**
|
97 |
+
* New coupon.
|
98 |
+
*/
|
99 |
+
$add_button.on('click', function () {
|
100 |
+
row = null;
|
101 |
+
});
|
102 |
+
|
103 |
+
/**
|
104 |
+
* On show modal.
|
105 |
+
*/
|
106 |
+
$customer_dialog.on('show.bs.modal', function () {
|
107 |
+
var $title = $customer_dialog.find('.modal-title');
|
108 |
+
var $button = $customer_dialog.find('.modal-footer button:first');
|
109 |
+
var customer;
|
110 |
+
if (row) {
|
111 |
+
customer = row.data();
|
112 |
+
$title.text(BooklyL10n.edit_customer);
|
113 |
+
$button.text(BooklyL10n.save);
|
114 |
+
} else {
|
115 |
+
customer = {
|
116 |
+
id : '',
|
117 |
+
wp_user_id : '',
|
118 |
+
name : '',
|
119 |
+
phone : '',
|
120 |
+
email : '',
|
121 |
+
notes : ''
|
122 |
+
};
|
123 |
+
$title.text(BooklyL10n.new_customer);
|
124 |
+
$button.text(BooklyL10n.create);
|
125 |
+
}
|
126 |
+
|
127 |
+
var $scope = angular.element(this).scope();
|
128 |
+
$scope.$apply(function ($scope) {
|
129 |
+
$scope.customer.id = customer.id;
|
130 |
+
$scope.customer.wp_user_id = customer.wp_user_id;
|
131 |
+
$scope.customer.name = customer.name;
|
132 |
+
$scope.customer.phone = customer.phone;
|
133 |
+
$scope.customer.email = customer.email;
|
134 |
+
$scope.customer.notes = customer.notes;
|
135 |
+
setTimeout(function() {
|
136 |
+
$customer_dialog.find('#phone').intlTelInput('setNumber', customer.phone);
|
137 |
+
}, 0);
|
138 |
+
});
|
139 |
+
});
|
140 |
+
|
141 |
+
/**
|
142 |
+
* Delete customers.
|
143 |
+
*/
|
144 |
+
$delete_button.on('click', function () {
|
145 |
+
if (!remembered_choice) {
|
146 |
+
$delete_dialog.modal('show');
|
147 |
+
} else {
|
148 |
+
deleteCustomers(this, remembered_choice);
|
149 |
+
}}
|
150 |
+
);
|
151 |
+
|
152 |
+
$delete_button_no.on('click', function () {
|
153 |
+
if ($remember_choice.prop('checked')) {
|
154 |
+
remembered_choice = false;
|
155 |
+
}
|
156 |
+
deleteCustomers(this, false);
|
157 |
+
});
|
158 |
+
|
159 |
+
$delete_button_yes.on('click', function () {
|
160 |
+
if ($remember_choice.prop('checked')) {
|
161 |
+
remembered_choice = true;
|
162 |
+
}
|
163 |
+
deleteCustomers(this, true);
|
164 |
+
});
|
165 |
+
|
166 |
+
function deleteCustomers(button, with_wp_user) {
|
167 |
+
var ladda = Ladda.create(button);
|
168 |
+
ladda.start();
|
169 |
+
|
170 |
+
var data = [];
|
171 |
+
var $checkboxes = $customers_list.find('tbody input:checked');
|
172 |
+
$checkboxes.each(function () {
|
173 |
+
data.push(this.value);
|
174 |
+
});
|
175 |
+
|
176 |
+
$.ajax({
|
177 |
+
url : ajaxurl,
|
178 |
+
type : 'POST',
|
179 |
+
data : {
|
180 |
+
action : 'ab_delete_customers',
|
181 |
+
data : data,
|
182 |
+
with_wp_user : with_wp_user
|
183 |
+
},
|
184 |
+
dataType : 'json',
|
185 |
+
success : function(response) {
|
186 |
+
ladda.stop();
|
187 |
+
$delete_dialog.modal('hide');
|
188 |
+
if (response.success) {
|
189 |
+
dt.ajax.reload(null, false);
|
190 |
+
} else {
|
191 |
+
alert(response.data.message);
|
192 |
+
}
|
193 |
+
}
|
194 |
+
});
|
195 |
+
}
|
196 |
+
|
197 |
+
/**
|
198 |
+
* On filters change.
|
199 |
+
*/
|
200 |
+
$filter.on('keyup', function () { dt.ajax.reload(); });
|
201 |
+
|
202 |
+
$('.bookly-limitation').on('click', function () {
|
203 |
+
booklyAlert({error: [BooklyL10n.limitations]});
|
204 |
+
});
|
205 |
+
});
|
206 |
+
|
207 |
+
(function() {
|
208 |
+
var module = angular.module('customer', ['customerDialog']);
|
209 |
+
module.controller('customerCtrl', function($scope) {
|
210 |
+
$scope.customer = {
|
211 |
+
id : '',
|
212 |
+
wp_user_id : '',
|
213 |
+
name : '',
|
214 |
+
phone : '',
|
215 |
+
email : '',
|
216 |
+
notes : ''
|
217 |
+
};
|
218 |
+
$scope.saveCustomer = function(customer) {
|
219 |
+
jQuery('#bookly-customers-list').DataTable().ajax.reload(null, false);
|
220 |
+
};
|
221 |
+
});
|
222 |
+
})();
|
backend/modules/customers/resources/js/ng-customer_dialog.js
ADDED
@@ -0,0 +1,122 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
;(function() {
|
2 |
+
|
3 |
+
angular.module('customerDialog', []).directive('customerDialog', function() {
|
4 |
+
return {
|
5 |
+
restrict : 'A',
|
6 |
+
replace : true,
|
7 |
+
scope : {
|
8 |
+
callback : '&customerDialog',
|
9 |
+
form : '=customer'
|
10 |
+
},
|
11 |
+
templateUrl : 'bookly-customer-dialog.tpl',
|
12 |
+
// The linking function will add behavior to the template.
|
13 |
+
link: function(scope, element, attrs) {
|
14 |
+
// Init properties.
|
15 |
+
var init = function() {
|
16 |
+
// Form fields.
|
17 |
+
if (!scope.form) {
|
18 |
+
scope.form = {
|
19 |
+
id : '',
|
20 |
+
wp_user_id : '',
|
21 |
+
name : '',
|
22 |
+
phone : '',
|
23 |
+
email : '',
|
24 |
+
notes : ''
|
25 |
+
};
|
26 |
+
}
|
27 |
+
if (BooklyL10nCustDialog.intlTelInput.enabled) {
|
28 |
+
element.find('#phone').intlTelInput({
|
29 |
+
preferredCountries: [BooklyL10nCustDialog.intlTelInput.country],
|
30 |
+
defaultCountry: BooklyL10nCustDialog.intlTelInput.country,
|
31 |
+
geoIpLookup: function (callback) {
|
32 |
+
jQuery.get(ajaxurl, {action: 'ab_ip_info'}, function () {
|
33 |
+
}, 'json').always(function (resp) {
|
34 |
+
var countryCode = (resp && resp.country) ? resp.country : '';
|
35 |
+
callback(countryCode);
|
36 |
+
});
|
37 |
+
},
|
38 |
+
utilsScript: BooklyL10nCustDialog.intlTelInput.utils
|
39 |
+
});
|
40 |
+
}
|
41 |
+
// Form errors.
|
42 |
+
scope.errors = {
|
43 |
+
name: {required: false}
|
44 |
+
};
|
45 |
+
// Loading indicator.
|
46 |
+
scope.loading = false;
|
47 |
+
|
48 |
+
jQuery('#bookly-customer-dialog').modal('hide')
|
49 |
+
.on('hidden.bs.modal', function () {
|
50 |
+
if (jQuery('#bookly-appointment-dialog').length) {
|
51 |
+
jQuery('body').addClass('modal-open');
|
52 |
+
}
|
53 |
+
});
|
54 |
+
};
|
55 |
+
|
56 |
+
// Run init.
|
57 |
+
init();
|
58 |
+
|
59 |
+
// On 'Cancel' button click.
|
60 |
+
scope.closeDialog = function () {
|
61 |
+
// Close the dialog.
|
62 |
+
jQuery('#bookly-customer-dialog').modal('hide');
|
63 |
+
// Re-init all properties.
|
64 |
+
init();
|
65 |
+
};
|
66 |
+
|
67 |
+
/**
|
68 |
+
* Send form to server.
|
69 |
+
*/
|
70 |
+
scope.processForm = function() {
|
71 |
+
scope.errors = {};
|
72 |
+
scope.loading = true;
|
73 |
+
scope.form.phone = BooklyL10nCustDialog.intlTelInput.enabled
|
74 |
+
? element.find('#phone').intlTelInput('getNumber')
|
75 |
+
: element.find('#phone').val();
|
76 |
+
jQuery.ajax({
|
77 |
+
url : ajaxurl,
|
78 |
+
type : 'POST',
|
79 |
+
data : jQuery.extend({ action : 'ab_save_customer' }, scope.form),
|
80 |
+
dataType : 'json',
|
81 |
+
success : function ( response ) {
|
82 |
+
scope.$apply(function(scope) {
|
83 |
+
if (response.success) {
|
84 |
+
response.customer.custom_fields = [];
|
85 |
+
response.customer.extras = [];
|
86 |
+
response.customer.status = BooklyL10nCustDialog.default_status;
|
87 |
+
// Send new customer to the parent scope.
|
88 |
+
scope.callback({customer : response.customer});
|
89 |
+
scope.form = {
|
90 |
+
id : '',
|
91 |
+
wp_user_id : '',
|
92 |
+
name : '',
|
93 |
+
phone : '',
|
94 |
+
email : '',
|
95 |
+
notes : ''
|
96 |
+
};
|
97 |
+
// Close the dialog.
|
98 |
+
scope.closeDialog();
|
99 |
+
} else {
|
100 |
+
// Set errors.
|
101 |
+
jQuery.each(response.errors, function(field, errors) {
|
102 |
+
scope.errors[field] = {};
|
103 |
+
jQuery.each(errors, function(key, error) {
|
104 |
+
scope.errors[field][error] = true;
|
105 |
+
});
|
106 |
+
});
|
107 |
+
}
|
108 |
+
scope.loading = false;
|
109 |
+
});
|
110 |
+
},
|
111 |
+
error : function() {
|
112 |
+
scope.$apply(function(scope) {
|
113 |
+
scope.loading = false;
|
114 |
+
});
|
115 |
+
}
|
116 |
+
});
|
117 |
+
};
|
118 |
+
}
|
119 |
+
};
|
120 |
+
});
|
121 |
+
|
122 |
+
})();
|
backend/modules/customers/templates/_customer_dialog.php
ADDED
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
|
2 |
+
<script type="text/ng-template" id="bookly-customer-dialog.tpl">
|
3 |
+
<div id="bookly-customer-dialog" class="modal fade" tabindex=-1 role="dialog">
|
4 |
+
<div class="modal-dialog">
|
5 |
+
<div class="modal-content">
|
6 |
+
<div class="modal-header">
|
7 |
+
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
8 |
+
<div class="modal-title h2"><?php _e( 'New Customer', 'bookly' ) ?></div>
|
9 |
+
</div>
|
10 |
+
<div ng-show=loading class="modal-body">
|
11 |
+
<div class="bookly-loading"></div>
|
12 |
+
</div>
|
13 |
+
<div class="modal-body" ng-hide="loading">
|
14 |
+
<div class="form-group">
|
15 |
+
<label for="wp_user"><?php _e( 'User', 'bookly' ) ?></label>
|
16 |
+
<select ng-model="form.wp_user_id" class="form-control" id="wp_user">
|
17 |
+
<option value=""></option>
|
18 |
+
<?php foreach ( get_users( array( 'fields' => array( 'ID', 'display_name' ), 'orderby' => 'display_name' ) ) as $wp_user ) : ?>
|
19 |
+
<option value="<?php echo $wp_user->ID ?>">
|
20 |
+
<?php echo $wp_user->display_name ?>
|
21 |
+
</option>
|
22 |
+
<?php endforeach ?>
|
23 |
+
</select>
|
24 |
+
</div>
|
25 |
+
|
26 |
+
<div class="form-group">
|
27 |
+
<label for="username"><?php _e( 'Name', 'bookly' ) ?></label>
|
28 |
+
<input class="form-control" type="text" ng-model="form.name" id="username">
|
29 |
+
<span style="font-size: 11px;color: red" ng-show="errors.name.required"><?php _e( 'Required', 'bookly' ) ?></span>
|
30 |
+
</div>
|
31 |
+
|
32 |
+
<div class="form-group">
|
33 |
+
<label for="phone"><?php _e( 'Phone', 'bookly' ) ?></label>
|
34 |
+
<input class="form-control" type="text" ng-model=form.phone id="phone">
|
35 |
+
</div>
|
36 |
+
|
37 |
+
<div class="form-group">
|
38 |
+
<label for="email"><?php _e( 'Email', 'bookly' ) ?></label>
|
39 |
+
<input class="form-control" type="text" ng-model=form.email id="email">
|
40 |
+
</div>
|
41 |
+
|
42 |
+
<div class="form-group">
|
43 |
+
<label for="notes"><?php _e( 'Notes', 'bookly' ) ?></label>
|
44 |
+
<textarea class="form-control" ng-model=form.notes id="notes"></textarea>
|
45 |
+
</div>
|
46 |
+
</div>
|
47 |
+
<div class="modal-footer">
|
48 |
+
<div ng-hide=loading>
|
49 |
+
<button ng-click="processForm()" class="btn btn-lg btn-success">
|
50 |
+
<?php _e( 'Create customer', 'bookly' ) ?>
|
51 |
+
</button>
|
52 |
+
<button type="button" class="ab-reset-form btn btn-lg btn-default" data-dismiss="modal">
|
53 |
+
<?php _e( 'Cancel', 'bookly' ) ?>
|
54 |
+
</button>
|
55 |
+
</div>
|
56 |
+
</div>
|
57 |
+
</div>
|
58 |
+
</div>
|
59 |
+
</div>
|
60 |
+
</script>
|
backend/modules/customers/templates/_export.php
ADDED
@@ -0,0 +1 @@
|
|
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
|
backend/modules/customers/templates/_import.php
ADDED
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
|
2 |
+
<div id="bookly-import-customers-dialog" class="modal fade" tabindex=-1 role="dialog">
|
3 |
+
<div class="modal-dialog">
|
4 |
+
<form enctype="multipart/form-data" action="<?php echo \BooklyLite\Lib\Utils\Common::escAdminUrl( \BooklyLite\Backend\Modules\Customers\Controller::page_slug ) ?>" method="POST">
|
5 |
+
<div class="modal-content">
|
6 |
+
<div class="modal-header">
|
7 |
+
<button type="button" class="close" data-dismiss="modal"><span>×</span></button>
|
8 |
+
<div class="modal-title h2"><?php _e( 'Import', 'bookly' ) ?></div>
|
9 |
+
</div>
|
10 |
+
<div class="modal-body">
|
11 |
+
<h4><?php _e( 'Note', 'bookly' ) ?></h4>
|
12 |
+
<p>
|
13 |
+
<?php _e( 'You may import list of clients in CSV format. The file needs to have three columns: Name, Phone and Email.', 'bookly' ) ?>
|
14 |
+
</p>
|
15 |
+
<div class="form-group">
|
16 |
+
<label for="import_customers_file"><?php _e( 'Select file', 'bookly' ) ?></label>
|
17 |
+
<input name="import_customers_file" id="import_customers_file" type="file">
|
18 |
+
</div>
|
19 |
+
<div class="form-group">
|
20 |
+
<label for="import_customers_delimiter"><?php _e( 'Delimiter', 'bookly' ) ?></label>
|
21 |
+
<select name="import_customers_delimiter" id="import_customers_delimiter" class="form-control">
|
22 |
+
<option value=","><?php _e( 'Comma (,)', 'bookly' ) ?></option>
|
23 |
+
<option value=";"><?php _e( 'Semicolon (;)', 'bookly' ) ?></option>
|
24 |
+
</select>
|
25 |
+
</div>
|
26 |
+
<input type="hidden" name="import">
|
27 |
+
</div>
|
28 |
+
<div class="modal-footer">
|
29 |
+
<button type="submit" class="btn btn-lg btn-success ab-popup-save" name="import-customers">
|
30 |
+
<?php _e( 'Import', 'bookly' ) ?>
|
31 |
+
</button>
|
32 |
+
</div>
|
33 |
+
</div>
|
34 |
+
</form>
|
35 |
+
</div>
|
36 |
+
</div>
|
backend/modules/customers/templates/index.php
ADDED
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
|
2 |
+
<div id="bookly-tbs" class="wrap">
|
3 |
+
<div class="bookly-tbs-body">
|
4 |
+
<div class="page-header text-right clearfix">
|
5 |
+
<div class="bookly-page-title">
|
6 |
+
<?php _e( 'Customers', 'bookly' ) ?>
|
7 |
+
</div>
|
8 |
+
</div>
|
9 |
+
<div class="panel panel-default bookly-main">
|
10 |
+
<div class="panel-body">
|
11 |
+
<div class="row">
|
12 |
+
<div class="form-inline bookly-margin-bottom-lg text-right">
|
13 |
+
<div class="form-group">
|
14 |
+
<button type="button" class="btn btn-default bookly-btn-block-xs bookly-limitation"><i class="glyphicon glyphicon-export"></i> <?php _e( 'Export to CSV', 'bookly' ) ?></button>
|
15 |
+
</div>
|
16 |
+
<div class="form-group">
|
17 |
+
<button type="button" class="btn btn-default bookly-btn-block-xs" data-toggle="modal" data-target="#bookly-import-customers-dialog"><i class="glyphicon glyphicon-import"></i> <?php _e( 'Import', 'bookly' ) ?></button>
|
18 |
+
</div>
|
19 |
+
<div class="form-group">
|
20 |
+
<button type="button" class="btn btn-success bookly-btn-block-xs" id="bookly-add" data-toggle="modal" data-target="#bookly-customer-dialog"><i class="glyphicon glyphicon-plus"></i> <?php _e( 'New customer', 'bookly' ) ?></button>
|
21 |
+
</div>
|
22 |
+
</div>
|
23 |
+
|
24 |
+
<div class="col-md-4">
|
25 |
+
<div class="form-group">
|
26 |
+
<input class="form-control" type="text" id="bookly-filter" placeholder="<?php esc_attr_e( 'Quick search customer', 'bookly' ) ?>" />
|
27 |
+
</div>
|
28 |
+
</div>
|
29 |
+
</div>
|
30 |
+
|
31 |
+
<table id="bookly-customers-list" class="table table-striped" width="100%">
|
32 |
+
<thead>
|
33 |
+
<tr>
|
34 |
+
<th><?php echo \BooklyLite\Lib\Utils\Common::getTranslatedOption( 'ab_appearance_text_label_name' ) ?></th>
|
35 |
+
<th><?php _e( 'User', 'bookly' ) ?></th>
|
36 |
+
<th><?php echo \BooklyLite\Lib\Utils\Common::getTranslatedOption( 'ab_appearance_text_label_phone' ) ?></th>
|
37 |
+
<th><?php echo \BooklyLite\Lib\Utils\Common::getTranslatedOption( 'ab_appearance_text_label_email' ) ?></th>
|
38 |
+
<th><?php _e( 'Notes', 'bookly' ) ?></th>
|
39 |
+
<th><?php _e( 'Last appointment', 'bookly' ) ?></th>
|
40 |
+
<th><?php _e( 'Total appointments', 'bookly' ) ?></th>
|
41 |
+
<th><?php _e( 'Payments', 'bookly' ) ?></th>
|
42 |
+
<th></th>
|
43 |
+
<th width="16"><input type="checkbox" id="bookly-check-all"></th>
|
44 |
+
</tr>
|
45 |
+
</thead>
|
46 |
+
</table>
|
47 |
+
|
48 |
+
<div class="text-right bookly-margin-top-lg">
|
49 |
+
<?php \BooklyLite\Lib\Utils\Common::deleteButton() ?>
|
50 |
+
</div>
|
51 |
+
</div>
|
52 |
+
</div>
|
53 |
+
|
54 |
+
<?php include '_import.php' ?>
|
55 |
+
<?php include '_export.php' ?>
|
56 |
+
|
57 |
+
<div id="bookly-delete-dialog" class="modal fade" tabindex=-1 role="dialog">
|
58 |
+
<div class="modal-dialog">
|
59 |
+
<div class="modal-content">
|
60 |
+
<div class="modal-header">
|
61 |
+
<button type="button" class="close" data-dismiss="modal"><span>×</span></button>
|
62 |
+
<div class="modal-title h2"><?php _e( 'Delete customers', 'bookly' ) ?></div>
|
63 |
+
</div>
|
64 |
+
<div class="modal-body">
|
65 |
+
<?php _e( 'You are about to delete customers which may have WordPress accounts associated to them. Do you want to delete those accounts too (if there are any)?', 'bookly' ) ?>
|
66 |
+
<div class="checkbox">
|
67 |
+
<label>
|
68 |
+
<input id="bookly-delete-remember-choice" type="checkbox" /><?php _e( 'Remember my choice', 'bookly' ) ?>
|
69 |
+
</label>
|
70 |
+
</div>
|
71 |
+
</div>
|
72 |
+
<div class="modal-footer">
|
73 |
+
<button type="button" class="btn btn-danger ladda-button" id="bookly-delete-no" data-spinner-size="40" data-style="zoom-in">
|
74 |
+
<span class="ladda-label"><i class="glyphicon glyphicon-trash"></i> <?php _e( 'No, delete just customers', 'bookly' ) ?></span>
|
75 |
+
</button>
|
76 |
+
<button type="button" class="btn btn-danger ladda-button" id="bookly-delete-yes" data-spinner-size="40" data-style="zoom-in">
|
77 |
+
<span class="ladda-label"><i class="glyphicon glyphicon-trash"></i> <?php _e( 'Yes', 'bookly' ) ?></span>
|
78 |
+
</button>
|
79 |
+
</div>
|
80 |
+
</div>
|
81 |
+
</div>
|
82 |
+
</div>
|
83 |
+
|
84 |
+
<div ng-app="customer" ng-controller="customerCtrl">
|
85 |
+
<div customer-dialog=saveCustomer(customer) customer="customer"></div>
|
86 |
+
<?php \BooklyLite\Backend\Modules\Customers\Components::getInstance()->renderCustomerDialog() ?>
|
87 |
+
</div>
|
88 |
+
</div>
|
89 |
+
</div>
|
backend/modules/debug/Controller.php
ADDED
@@ -0,0 +1,181 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace BooklyLite\Backend\Modules\Debug;
|
3 |
+
|
4 |
+
use BooklyLite\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Controller
|
8 |
+
* @package BooklyLite\Backend\Modules\Debug
|
9 |
+
*/
|
10 |
+
class Controller extends Lib\Base\Controller
|
11 |
+
{
|
12 |
+
|
13 |
+
const TABLE_STATUS_OK = 1;
|
14 |
+
const TABLE_STATUS_ERROR = 0;
|
15 |
+
const TABLE_STATUS_WARNING = 2;
|
16 |
+
|
17 |
+
/**
|
18 |
+
* Default action
|
19 |
+
*/
|
20 |
+
public function index()
|
21 |
+
{
|
22 |
+
$this->enqueueStyles( array(
|
23 |
+
'backend' => array( 'bootstrap/css/bootstrap-theme.min.css', ),
|
24 |
+
'module' => array( 'css/style.css' ),
|
25 |
+
) );
|
26 |
+
|
27 |
+
$this->enqueueScripts( array(
|
28 |
+
'backend' => array( 'bootstrap/js/bootstrap.min.js' => array( 'jquery' ) ),
|
29 |
+
'module' => array( 'js/debug.js' => array( 'jquery' ) ),
|
30 |
+
) );
|
31 |
+
|
32 |
+
$debug = array();
|
33 |
+
/** @var Lib\Base\Entity $entity */
|
34 |
+
foreach ( self::getEntities() as $entity ) {
|
35 |
+
$tableName = $entity::getTableName();
|
36 |
+
$debug[ $tableName ] = array(
|
37 |
+
'fields' => null,
|
38 |
+
'constraints' => null,
|
39 |
+
'status' => null,
|
40 |
+
);
|
41 |
+
if ( self::tableExists( $tableName ) ) {
|
42 |
+
$tableStructure = self::getTableStructure( $tableName );
|
43 |
+
$tableConstraints = self::getTableConstraints( $tableName );
|
44 |
+
$entitySchema = $entity::getSchema();
|
45 |
+
$entityConstraints = $entity::getConstraints();
|
46 |
+
$debug[ $tableName ]['status'] = self::TABLE_STATUS_OK;
|
47 |
+
$debug[ $tableName ]['fields'] = array();
|
48 |
+
|
49 |
+
// Comparing model schema with real DB schema
|
50 |
+
foreach ( $entitySchema as $field => $data ) {
|
51 |
+
if ( in_array( $field, $tableStructure ) ) {
|
52 |
+
$debug[ $tableName ]['fields'][ $field ] = 1;
|
53 |
+
} else {
|
54 |
+
$debug[ $tableName ]['fields'][ $field ] = 0;
|
55 |
+
$debug[ $tableName ]['status'] = self::TABLE_STATUS_WARNING;
|
56 |
+
}
|
57 |
+
}
|
58 |
+
|
59 |
+
// Comparing model constraints with real DB constraints
|
60 |
+
foreach ( $entityConstraints as $constraint ) {
|
61 |
+
$key = $constraint['column_name'] . $constraint['referenced_table_name'] . $constraint['referenced_column_name'];
|
62 |
+
$debug[ $tableName ]['constraints'][ $key ] = $constraint;
|
63 |
+
if ( array_key_exists ( $key, $tableConstraints ) ) {
|
64 |
+
$debug[ $tableName ]['constraints'][ $key ]['status'] = 1;
|
65 |
+
} else {
|
66 |
+
$debug[ $tableName ]['constraints'][ $key ]['status'] = 0;
|
67 |
+
$debug[ $tableName ]['status'] = self::TABLE_STATUS_WARNING;
|
68 |
+
}
|
69 |
+
}
|
70 |
+
|
71 |
+
} else {
|
72 |
+
$debug[ $tableName ]['status'] = self::TABLE_STATUS_ERROR;
|
73 |
+
}
|
74 |
+
}
|
75 |
+
$this->render( 'index', compact( 'debug', 'sql' ) );
|
76 |
+
}
|
77 |
+
|
78 |
+
/**
|
79 |
+
* Get entity class names.
|
80 |
+
*
|
81 |
+
* @return array
|
82 |
+
*/
|
83 |
+
public function getEntities()
|
84 |
+
{
|
85 |
+
$result = array();
|
86 |
+
foreach ( scandir( Lib\Plugin::getDirectory() . '/lib/entities' ) as $filename ) {
|
87 |
+
if ( $filename == '.' || $filename == '..' ) {
|
88 |
+
continue;
|
89 |
+
}
|
90 |
+
$result[] = '\\BooklyLite\\Lib\\Entities\\' . basename( $filename, '.php' );
|
91 |
+
}
|
92 |
+
|
93 |
+
return apply_filters( 'bookly_tables', $result );
|
94 |
+
}
|
95 |
+
|
96 |
+
/**
|
97 |
+
* Get table structure
|
98 |
+
*
|
99 |
+
* @param string $tableName
|
100 |
+
* @return array
|
101 |
+
*/
|
102 |
+
public static function getTableStructure( $tableName )
|
103 |
+
{
|
104 |
+
global $wpdb;
|
105 |
+
|
106 |
+
$tableStructure = array();
|
107 |
+
$results = $wpdb->get_results( 'DESCRIBE `' . $tableName . '`;' );
|
108 |
+
if ( $results ) {
|
109 |
+
foreach ( $results as $row ) {
|
110 |
+
$tableStructure[] = $row->Field;
|
111 |
+
}
|
112 |
+
}
|
113 |
+
|
114 |
+
return $tableStructure;
|
115 |
+
}
|
116 |
+
|
117 |
+
/**
|
118 |
+
* Get table constraints
|
119 |
+
*
|
120 |
+
* @param string $tableName
|
121 |
+
* @return array
|
122 |
+
*/
|
123 |
+
public static function getTableConstraints( $tableName )
|
124 |
+
{
|
125 |
+
global $wpdb;
|
126 |
+
|
127 |
+
$tableConstraints = array();
|
128 |
+
$results = $wpdb->get_results(
|
129 |
+
'SELECT
|
130 |
+
COLUMN_NAME,
|
131 |
+
CONSTRAINT_NAME,
|
132 |
+
REFERENCED_COLUMN_NAME,
|
133 |
+
REFERENCED_TABLE_NAME
|
134 |
+
FROM information_schema.KEY_COLUMN_USAGE
|
135 |
+
WHERE
|
136 |
+
TABLE_NAME = "' . $tableName . '"
|
137 |
+
AND CONSTRAINT_SCHEMA = SCHEMA()
|
138 |
+
AND CONSTRAINT_NAME <> "PRIMARY";'
|
139 |
+
);
|
140 |
+
if ( $results ) {
|
141 |
+
foreach ( $results as $row ) {
|
142 |
+
$constraint = array(
|
143 |
+
'column_name' => $row->COLUMN_NAME,
|
144 |
+
'referenced_table_name' => $row->REFERENCED_COLUMN_NAME,
|
145 |
+
'referenced_column_name' => $row->REFERENCED_TABLE_NAME,
|
146 |
+
);
|
147 |
+
$key = $row->COLUMN_NAME . $row->REFERENCED_TABLE_NAME . $row->REFERENCED_COLUMN_NAME;
|
148 |
+
$tableConstraints[ $key ] = $constraint;
|
149 |
+
}
|
150 |
+
}
|
151 |
+
|
152 |
+
return $tableConstraints;
|
153 |
+
}
|
154 |
+
|
155 |
+
/**
|
156 |
+
* Verifying if table exists
|
157 |
+
*
|
158 |
+
* @param string $tableName
|
159 |
+
* @return int
|
160 |
+
*/
|
161 |
+
public static function tableExists( $tableName )
|
162 |
+
{
|
163 |
+
global $wpdb;
|
164 |
+
|
165 |
+
return $wpdb->query( 'SHOW TABLES LIKE "' . $tableName . '"' );
|
166 |
+
}
|
167 |
+
|
168 |
+
// Protected methods.
|
169 |
+
|
170 |
+
/**
|
171 |
+
* Override parent method to add 'wp_ajax_ab_' prefix
|
172 |
+
* so current 'execute*' methods look nicer.
|
173 |
+
*
|
174 |
+
* @param string $prefix
|
175 |
+
*/
|
176 |
+
protected function registerWpActions( $prefix = '' )
|
177 |
+
{
|
178 |
+
parent::registerWpActions( 'wp_ajax_ab_' );
|
179 |
+
}
|
180 |
+
|
181 |
+
}
|
backend/modules/debug/resources/css/style.css
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.panel {
|
2 |
+
margin-right: 20px;
|
3 |
+
margin-top: 20px;
|
4 |
+
}
|
5 |
+
|
6 |
+
.panel-body .list-group{
|
7 |
+
margin-bottom: 0;
|
8 |
+
}
|
9 |
+
|
10 |
+
.list-group-item{
|
11 |
+
padding: 5px 10px;
|
12 |
+
}
|
backend/modules/debug/resources/js/debug.js
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
1 |
+
jQuery(function($) {
|
2 |
+
$('.collapse').collapse('hide');
|
3 |
+
});
|
backend/modules/debug/templates/index.php
ADDED
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
|
2 |
+
<div id="bookly-tbs" class="wrap">
|
3 |
+
<div class="bookly-tbs-body">
|
4 |
+
<div class="page-header text-right clearfix">
|
5 |
+
<div class="bookly-page-title">
|
6 |
+
Database Integrity
|
7 |
+
</div>
|
8 |
+
</div>
|
9 |
+
<div class="panel-group" id="accordion" role="tablist" aria-multiselectable="true">
|
10 |
+
<?php foreach ( $debug as $tableName => $table ) : ?>
|
11 |
+
<div class="panel <?php echo $table['status'] == 1 ? 'panel-success' : 'panel-danger' ?>">
|
12 |
+
<div class="panel-heading" role="tab" id="heading_<?php echo $tableName ?>">
|
13 |
+
<h4 class="panel-title">
|
14 |
+
<a class="collapsed" role="button" data-toggle="collapse" data-parent="#accordion" href="#<?php echo $tableName ?>" aria-expanded="true" aria-controls="<?php echo $tableName ?>">
|
15 |
+
<?php echo $tableName ?>
|
16 |
+
</a>
|
17 |
+
</h4>
|
18 |
+
</div>
|
19 |
+
<div id="<?php echo $tableName ?>" class="panel-collapse collapse" role="tabpanel" aria-labelledby="<?php echo $tableName ?>">
|
20 |
+
<div class="panel-body">
|
21 |
+
<?php if ( $table['status'] ) : ?>
|
22 |
+
<h4>Columns</h4>
|
23 |
+
<table class="table table-condensed">
|
24 |
+
<thead>
|
25 |
+
<tr>
|
26 |
+
<th>Column name</th>
|
27 |
+
<th width="50">Status</th>
|
28 |
+
</tr>
|
29 |
+
</thead>
|
30 |
+
<tbody>
|
31 |
+
<?php foreach ( $table['fields'] as $field => $status ) : ?>
|
32 |
+
<tr class="<?php echo $status ? 'default' : 'danger' ?>">
|
33 |
+
<td><?php echo $field ?></td>
|
34 |
+
<td><?php echo $status ? 'OK' : 'ERROR' ?></td>
|
35 |
+
</tr>
|
36 |
+
<?php endforeach ?>
|
37 |
+
</tbody>
|
38 |
+
</table>
|
39 |
+
<?php if ( $table['constraints'] ) : ?>
|
40 |
+
<h4>Constraints</h4>
|
41 |
+
<table class="table table-condensed">
|
42 |
+
<thead>
|
43 |
+
<tr>
|
44 |
+
<th>Column name</th>
|
45 |
+
<th>Referenced table name</th>
|
46 |
+
<th>Referenced column name</th>
|
47 |
+
<th width="50">Status</th>
|
48 |
+
</tr>
|
49 |
+
</thead>
|
50 |
+
<tbody>
|
51 |
+
<?php foreach ( $table['constraints'] as $key => $constraint ) : ?>
|
52 |
+
<tr class="<?php echo $constraint['status'] ? 'default' : 'danger' ?>">
|
53 |
+
<td><?php echo $constraint['column_name'] ?></td>
|
54 |
+
<td><?php echo $constraint['referenced_table_name'] ?></td>
|
55 |
+
<td><?php echo $constraint['referenced_column_name'] ?></td>
|
56 |
+
<td><?php echo $constraint['status'] ? 'OK' : 'ERROR' ?></td>
|
57 |
+
</tr>
|
58 |
+
<?php endforeach ?>
|
59 |
+
</tbody>
|
60 |
+
</table>
|
61 |
+
<?php endif ?>
|
62 |
+
<?php else: ?>
|
63 |
+
Table does not exist
|
64 |
+
<?php endif ?>
|
65 |
+
</div>
|
66 |
+
</div>
|
67 |
+
</div>
|
68 |
+
<?php endforeach ?>
|
69 |
+
</div>
|
70 |
+
</div>
|
71 |
+
</div>
|
backend/modules/notifications/AB_NotificationsController.php
DELETED
@@ -1,70 +0,0 @@
|
|
1 |
-
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
-
|
3 |
-
class AB_NotificationsController extends AB_Controller {
|
4 |
-
|
5 |
-
public function index()
|
6 |
-
{
|
7 |
-
$this->enqueueStyles( array(
|
8 |
-
'frontend' => array(
|
9 |
-
'css/ladda.min.css'
|
10 |
-
),
|
11 |
-
'module' => array(
|
12 |
-
'css/notifications.css'
|
13 |
-
),
|
14 |
-
'backend' => array(
|
15 |
-
'css/bookly.main-backend.css',
|
16 |
-
'bootstrap/css/bootstrap.min.css',
|
17 |
-
)
|
18 |
-
) );
|
19 |
-
|
20 |
-
$this->enqueueScripts( array(
|
21 |
-
'backend' => array(
|
22 |
-
'bootstrap/js/bootstrap.min.js' => array( 'jquery' ),
|
23 |
-
),
|
24 |
-
'module' => array(
|
25 |
-
'js/notification.js' => array( 'jquery' ),
|
26 |
-
),
|
27 |
-
'frontend' => array(
|
28 |
-
'js/spin.min.js' => array( 'jquery' ),
|
29 |
-
'js/ladda.min.js' => array( 'jquery' ),
|
30 |
-
)
|
31 |
-
) );
|
32 |
-
|
33 |
-
$this->form = new AB_NotificationsForm( 'email' );
|
34 |
-
$this->message = '';
|
35 |
-
// Save action.
|
36 |
-
if ( !empty ( $_POST ) ) {
|
37 |
-
$this->message = __( 'Notification settings were updated successfully.', 'bookly' );
|
38 |
-
// sender name
|
39 |
-
if ( $this->hasParameter( 'ab_settings_sender_name' ) ) {
|
40 |
-
update_option( 'ab_settings_sender_name', $this->getParameter( 'ab_settings_sender_name' ) );
|
41 |
-
}
|
42 |
-
// sender email
|
43 |
-
if ( $this->hasParameter( 'ab_settings_sender_email' ) ) {
|
44 |
-
update_option( 'ab_settings_sender_email', $this->getParameter( 'ab_settings_sender_email' ) );
|
45 |
-
}
|
46 |
-
if ( $this->hasParameter( 'ab_email_notification_reply_to_customers' ) ) {
|
47 |
-
update_option( 'ab_email_notification_reply_to_customers', $this->getParameter( 'ab_email_notification_reply_to_customers' ) );
|
48 |
-
}
|
49 |
-
if ( $this->hasParameter( 'ab_email_content_type' ) ) {
|
50 |
-
update_option( 'ab_email_content_type', $this->getParameter( 'ab_email_content_type' ) );
|
51 |
-
}
|
52 |
-
}
|
53 |
-
|
54 |
-
$this->render( 'index' );
|
55 |
-
}
|
56 |
-
|
57 |
-
// Protected methods.
|
58 |
-
|
59 |
-
/**
|
60 |
-
* Override parent method to add 'wp_ajax_ab_' prefix
|
61 |
-
* so current 'execute*' methods look nicer.
|
62 |
-
*
|
63 |
-
* @param string $prefix
|
64 |
-
*/
|
65 |
-
protected function registerWpActions( $prefix = '' )
|
66 |
-
{
|
67 |
-
parent::registerWpActions( 'wp_ajax_ab_' );
|
68 |
-
}
|
69 |
-
|
70 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/modules/notifications/Controller.php
ADDED
@@ -0,0 +1,108 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace BooklyLite\Backend\Modules\Notifications;
|
3 |
+
|
4 |
+
use \BooklyLite\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Controller
|
8 |
+
* @package BooklyLite\Backend\Modules\Notifications
|
9 |
+
*/
|
10 |
+
class Controller extends Lib\Base\Controller
|
11 |
+
{
|
12 |
+
public function index()
|
13 |
+
{
|
14 |
+
$this->enqueueStyles( array(
|
15 |
+
'frontend' => array( 'css/ladda.min.css' ),
|
16 |
+
'backend' => array( 'bootstrap/css/bootstrap-theme.min.css', ),
|
17 |
+
) );
|
18 |
+
|
19 |
+
$this->enqueueScripts( array(
|
20 |
+
'backend' => array(
|
21 |
+
'bootstrap/js/bootstrap.min.js' => array( 'jquery' ),
|
22 |
+
'js/angular.min.js',
|
23 |
+
'js/help.js' => array( 'jquery' ),
|
24 |
+
'js/alert.js' => array( 'jquery' ),
|
25 |
+
),
|
26 |
+
'module' => array(
|
27 |
+
'js/notification.js' => array( 'jquery' ),
|
28 |
+
'js/ng-app.js' => array( 'jquery', 'ab-angular.min.js' ),
|
29 |
+
),
|
30 |
+
'frontend' => array(
|
31 |
+
'js/spin.min.js' => array( 'jquery' ),
|
32 |
+
'js/ladda.min.js' => array( 'jquery' ),
|
33 |
+
)
|
34 |
+
) );
|
35 |
+
$cron_reminder = (array) get_option( 'ab_settings_cron_reminder' );
|
36 |
+
$form = new Forms\Notifications( 'email' );
|
37 |
+
$alert = array( 'success' => array() );
|
38 |
+
// Save action.
|
39 |
+
if ( ! empty ( $_POST ) ) {
|
40 |
+
$form->bind( $this->getPostParameters(), $_FILES );
|
41 |
+
$form->save();
|
42 |
+
$alert['success'][] = __( 'Settings saved.', 'bookly' );
|
43 |
+
// sender name
|
44 |
+
if ( $this->hasParameter( 'ab_settings_sender_name' ) ) {
|
45 |
+
update_option( 'ab_settings_sender_name', $this->getParameter( 'ab_settings_sender_name' ) );
|
46 |
+
}
|
47 |
+
// sender email
|
48 |
+
if ( $this->hasParameter( 'ab_settings_sender_email' ) ) {
|
49 |
+
update_option( 'ab_settings_sender_email', $this->getParameter( 'ab_settings_sender_email' ) );
|
50 |
+
}
|
51 |
+
if ( $this->hasParameter( 'ab_email_notification_reply_to_customers' ) ) {
|
52 |
+
update_option( 'ab_email_notification_reply_to_customers', $this->getParameter( 'ab_email_notification_reply_to_customers' ) );
|
53 |
+
}
|
54 |
+
if ( $this->hasParameter( 'ab_email_content_type' ) ) {
|
55 |
+
update_option( 'ab_email_content_type', $this->getParameter( 'ab_email_content_type' ) );
|
56 |
+
}
|
57 |
+
foreach ( array( 'staff_agenda', 'client_follow_up', 'client_reminder' ) as $type ) {
|
58 |
+
$cron_reminder[ $type ] = $this->getParameter( $type . '_cron_hour' );
|
59 |
+
}
|
60 |
+
update_option( 'ab_settings_cron_reminder', $cron_reminder );
|
61 |
+
}
|
62 |
+
$cron_path = realpath( Lib\Plugin::getDirectory() . '/lib/utils/send_notifications_cron.php' );
|
63 |
+
wp_localize_script( 'ab-alert.js', 'BooklyL10n', array(
|
64 |
+
'alert' => $alert,
|
65 |
+
'sent_successfully' => __( 'Sent successfully', 'bookly' ),
|
66 |
+
'limitations' => __( '<b class="h4">This function is disabled in the Lite version of Bookly.</b><br><br>If you find the plugin useful for your business please consider buying a licence for the full version.<br>It costs just $59 and for this money you will get many useful functions, lifetime free updates and excellent support!<br>More information can be found here', 'bookly' ) . ': <a href="http://booking-wp-plugin.com" target="_blank" class="alert-link">http://booking-wp-plugin.com</a>',
|
67 |
+
) );
|
68 |
+
$this->render( 'index', compact( 'form', 'cron_path', 'cron_reminder' ) );
|
69 |
+
}
|
70 |
+
|
71 |
+
public function executeGetEmailNotificationsData()
|
72 |
+
{
|
73 |
+
$result = array();
|
74 |
+
|
75 |
+
$form = new Forms\Notifications( 'email' );
|
76 |
+
|
77 |
+
$ab_settings_sender_name = get_option( 'ab_settings_sender_name' ) == '' ?
|
78 |
+
get_option( 'blogname' ) : get_option( 'ab_settings_sender_name' );
|
79 |
+
|
80 |
+
$ab_settings_sender_email = get_option( 'ab_settings_sender_email' ) == '' ?
|
81 |
+
get_option( 'admin_email' ) : get_option( 'ab_settings_sender_email' );
|
82 |
+
|
83 |
+
$result[ 'ab_notifications' ] = $form->getData();
|
84 |
+
$result[ 'ab_settings_sender_name' ] = $ab_settings_sender_name;
|
85 |
+
$result[ 'ab_settings_sender_email' ] = $ab_settings_sender_email;
|
86 |
+
$result[ 'ab_types' ] = $form->types;
|
87 |
+
|
88 |
+
wp_send_json_success( $result );
|
89 |
+
}
|
90 |
+
|
91 |
+
public function executeTestEmailNotifications()
|
92 |
+
{
|
93 |
+
}
|
94 |
+
|
95 |
+
// Protected methods.
|
96 |
+
|
97 |
+
/**
|
98 |
+
* Override parent method to add 'wp_ajax_ab_' prefix
|
99 |
+
* so current 'execute*' methods look nicer.
|
100 |
+
*
|
101 |
+
* @param string $prefix
|
102 |
+
*/
|
103 |
+
protected function registerWpActions( $prefix = '' )
|
104 |
+
{
|
105 |
+
parent::registerWpActions( 'wp_ajax_ab_' );
|
106 |
+
}
|
107 |
+
|
108 |
+
}
|
backend/modules/notifications/forms/AB_NotificationsForm.php
DELETED
@@ -1,166 +0,0 @@
|
|
1 |
-
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
-
|
3 |
-
class AB_NotificationsForm extends AB_Form {
|
4 |
-
|
5 |
-
public $types = array(
|
6 |
-
'client_new_appointment',
|
7 |
-
'staff_new_appointment',
|
8 |
-
'staff_cancelled_appointment',
|
9 |
-
'client_new_wp_user',
|
10 |
-
'client_reminder',
|
11 |
-
'client_follow_up',
|
12 |
-
'staff_agenda',
|
13 |
-
);
|
14 |
-
public $gateway;
|
15 |
-
|
16 |
-
public function __construct( $gateway = 'email' )
|
17 |
-
{
|
18 |
-
/*
|
19 |
-
* make Visual Mode as default (instead of Text Mode)
|
20 |
-
* allowed: tinymce - Visual Mode, html - Text Mode, test - no one Mode selected
|
21 |
-
*/
|
22 |
-
add_filter( 'wp_default_editor', create_function( '', 'return \'tinymce\';' ) );
|
23 |
-
$this->gateway = $gateway;
|
24 |
-
$this->setFields( array(
|
25 |
-
'active',
|
26 |
-
'subject',
|
27 |
-
'message',
|
28 |
-
'copy',
|
29 |
-
) );
|
30 |
-
|
31 |
-
$this->load();
|
32 |
-
}
|
33 |
-
|
34 |
-
public function bind( array $_post = array(), array $files = array() )
|
35 |
-
{
|
36 |
-
foreach ( $this->types as $type ) {
|
37 |
-
foreach ( $this->fields as $field ) {
|
38 |
-
if ( isset($_post[ $type ][ $field ] ) ) {
|
39 |
-
$this->data[ $type ][ $field ] = $_post[ $type ][ $field ];
|
40 |
-
}
|
41 |
-
}
|
42 |
-
}
|
43 |
-
}
|
44 |
-
|
45 |
-
/**
|
46 |
-
* @return bool|void
|
47 |
-
*/
|
48 |
-
public function save()
|
49 |
-
{
|
50 |
-
return;
|
51 |
-
}
|
52 |
-
|
53 |
-
public function load()
|
54 |
-
{
|
55 |
-
foreach ( $this->types as $type ) {
|
56 |
-
if ( ( $object = new AB_Notification() ) && $object->loadBy( array( 'type' => $type, 'gateway' => $this->gateway ) ) ) {
|
57 |
-
$this->data[ $type ][ 'active' ] = $object->get( 'active' );
|
58 |
-
$this->data[ $type ][ 'subject' ] = $object->get( 'subject' );
|
59 |
-
$this->data[ $type ][ 'message' ] = $object->get( 'message' );
|
60 |
-
$this->data[ $type ][ 'name' ] = $this->getNotificationName( $type );
|
61 |
-
if ( $type == 'staff_new_appointment' || $type == 'staff_cancelled_appointment' ) {
|
62 |
-
$this->data[ $type ][ 'copy' ] = $object->get( 'copy' );
|
63 |
-
}
|
64 |
-
}
|
65 |
-
}
|
66 |
-
}
|
67 |
-
|
68 |
-
/**
|
69 |
-
* @param $type
|
70 |
-
* @return mixed
|
71 |
-
*/
|
72 |
-
public function getNotificationName ( $type )
|
73 |
-
{
|
74 |
-
$notifications_name = array(
|
75 |
-
'client_new_appointment' => __( 'Notification to customer about appointment details', 'bookly' ),
|
76 |
-
'staff_new_appointment' => __( 'Notification to staff member about appointment details', 'bookly' ),
|
77 |
-
'staff_cancelled_appointment' => __( 'Notification to staff member about appointment cancellation', 'bookly' ),
|
78 |
-
'client_new_wp_user' => __( 'Notification to customer about their WordPress user login details', 'bookly' ),
|
79 |
-
'client_reminder' => __( 'Evening reminder to customer about next day appointment (requires cron setup)', 'bookly' ),
|
80 |
-
'client_follow_up' => __( 'Follow-up message in the same day after appointment (requires cron setup)', 'bookly' ),
|
81 |
-
'staff_agenda' => __( 'Evening notification with the next day agenda to staff member (requires cron setup)', 'bookly' ),
|
82 |
-
);
|
83 |
-
|
84 |
-
return $notifications_name[ $type ];
|
85 |
-
}
|
86 |
-
|
87 |
-
/**
|
88 |
-
* Render the "active" form
|
89 |
-
*
|
90 |
-
* @param $type
|
91 |
-
* @return string
|
92 |
-
*/
|
93 |
-
public function renderActive( $type )
|
94 |
-
{
|
95 |
-
$title = isset( $this->data[ $type ]['name'] ) ? $this->data[ $type ]['name'] : '';
|
96 |
-
|
97 |
-
return $title;
|
98 |
-
}
|
99 |
-
|
100 |
-
/**
|
101 |
-
* @param $type
|
102 |
-
* @return string
|
103 |
-
*/
|
104 |
-
public function renderSubject( $type )
|
105 |
-
{
|
106 |
-
$id = $type . '_subject';
|
107 |
-
$name = $type . '[subject]';
|
108 |
-
$value = isset( $this->data[ $type ]['subject'] ) ? $this->data[$type]['subject'] : '';
|
109 |
-
|
110 |
-
return "<label class='ab-form-label' for='{$id}' style='margin-top: 19px;'>" . __( 'Subject', 'bookly' ) . "</label><input type='text' size='70' id='{$id}' name='{$name}' value='{$value}' class='form-control' />";
|
111 |
-
}
|
112 |
-
|
113 |
-
/**
|
114 |
-
* @param $type
|
115 |
-
* @return string
|
116 |
-
*/
|
117 |
-
public function renderMessage( $type )
|
118 |
-
{
|
119 |
-
$id = $type.'_message';
|
120 |
-
$name = $type.'[message]';
|
121 |
-
$value = isset( $this->data[$type]['message'] ) ? $this->data[$type]['message'] : '';
|
122 |
-
|
123 |
-
if ( 'sms' == $this->gateway ) {
|
124 |
-
return "<textarea rows='6' id='{$id}' name='{$name}' class='ab-sms-message pull-left'>".esc_textarea( $value )."</textarea>";
|
125 |
-
} else {
|
126 |
-
$settings = array(
|
127 |
-
'textarea_name' => $name,
|
128 |
-
'media_buttons' => false,
|
129 |
-
'tinymce' => array(
|
130 |
-
'theme_advanced_buttons1' => 'formatselect,|,bold,italic,underline,|,'.
|
131 |
-
'bullist,blockquote,|,justifyleft,justifycenter'.
|
132 |
-
',justifyright,justifyfull,|,link,unlink,|'.
|
133 |
-
',spellchecker,wp_fullscreen,wp_adv'
|
134 |
-
)
|
135 |
-
);
|
136 |
-
|
137 |
-
wp_editor( $value, $id, $settings );
|
138 |
-
}
|
139 |
-
}
|
140 |
-
|
141 |
-
/**
|
142 |
-
* Render the "copy" form
|
143 |
-
*
|
144 |
-
* @param $type
|
145 |
-
* @return string
|
146 |
-
*/
|
147 |
-
public function renderCopy( $type )
|
148 |
-
{
|
149 |
-
$id = $type . '_copy';
|
150 |
-
$name = $type . '[copy]';
|
151 |
-
$checked = checked( ( isset( $this->data[ $type ]['copy'] ) && intval( $this->data[ $type ]['copy'] ) ), true, false );
|
152 |
-
$title = __( 'Send copy to administrators', 'bookly' );
|
153 |
-
|
154 |
-
return "<div class='ab-form-row'>
|
155 |
-
<label class='ab-form-label'></label>
|
156 |
-
<div>
|
157 |
-
<legend>
|
158 |
-
<input name='{$name}' type=hidden value=0 />
|
159 |
-
<input id='{$id}' name='{$name}' type=checkbox value=1 {$checked} />
|
160 |
-
<label for='{$id}'> {$title}</label>
|
161 |
-
</legend>
|
162 |
-
</div>
|
163 |
-
</div>";
|
164 |
-
}
|
165 |
-
|
166 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/modules/notifications/forms/Notifications.php
ADDED
@@ -0,0 +1,213 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace BooklyLite\Backend\Modules\Notifications\Forms;
|
3 |
+
|
4 |
+
use BooklyLite\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Notifications
|
8 |
+
* @package BooklyLite\Backend\Modules\Notifications\Forms
|
9 |
+
*/
|
10 |
+
class Notifications extends Lib\Base\Form
|
11 |
+
{
|
12 |
+
public $types = array(
|
13 |
+
'single' => array(
|
14 |
+
'client_pending_appointment',
|
15 |
+
'staff_pending_appointment',
|
16 |
+
'client_approved_appointment',
|
17 |
+
'staff_approved_appointment',
|
18 |
+
'client_cancelled_appointment',
|
19 |
+
'staff_cancelled_appointment',
|
20 |
+
'client_new_wp_user',
|
21 |
+
'client_reminder',
|
22 |
+
'client_follow_up',
|
23 |
+
'staff_agenda',
|
24 |
+
),
|
25 |
+
'combined' => array(
|
26 |
+
'client_pending_appointment_cart',
|
27 |
+
'client_approved_appointment_cart',
|
28 |
+
),
|
29 |
+
);
|
30 |
+
|
31 |
+
public $gateway;
|
32 |
+
|
33 |
+
/**
|
34 |
+
* Constructor.
|
35 |
+
*
|
36 |
+
* @param string $gateway
|
37 |
+
*/
|
38 |
+
public function __construct( $gateway = 'email' )
|
39 |
+
{
|
40 |
+
/*
|
41 |
+
* make Visual Mode as default (instead of Text Mode)
|
42 |
+
* allowed: tinymce - Visual Mode, html - Text Mode, test - no one Mode selected
|
43 |
+
*/
|
44 |
+
add_filter( 'wp_default_editor', create_function( '', 'return \'tinymce\';' ) );
|
45 |
+
$this->gateway = $gateway;
|
46 |
+
if ( ! Lib\Config::areCombinedNotificationsEnabled() ) {
|
47 |
+
$this->types['combined'] = array();
|
48 |
+
}
|
49 |
+
$this->setFields( array( 'active', 'subject', 'message', 'copy', ) );
|
50 |
+
$this->load();
|
51 |
+
}
|
52 |
+
|
53 |
+
public function bind( array $_post = array(), array $files = array() )
|
54 |
+
{
|
55 |
+
foreach ( $this->types as $group ) {
|
56 |
+
foreach ( $group as $type ) {
|
57 |
+
foreach ( $this->fields as $field ) {
|
58 |
+
if ( isset ( $_post[ $type ] [ $field ] ) ) {
|
59 |
+
$this->data[ $type ][ $field ] = $_post[ $type ][ $field ];
|
60 |
+
}
|
61 |
+
}
|
62 |
+
}
|
63 |
+
}
|
64 |
+
}
|
65 |
+
|
66 |
+
/**
|
67 |
+
* Save form.
|
68 |
+
*/
|
69 |
+
public function save()
|
70 |
+
{
|
71 |
+
/** @var Lib\Entities\Notification[] $notifications */
|
72 |
+
$notifications = Lib\Entities\Notification::query( 'n' )
|
73 |
+
->where( 'gateway', $this->gateway )
|
74 |
+
->indexBy( 'type' )
|
75 |
+
->find();
|
76 |
+
foreach ( $this->types as $group ) {
|
77 |
+
foreach ( $group as $type ) {
|
78 |
+
$notifications[ $type ]->setFields( $this->data[ $type ] );
|
79 |
+
$notifications[ $type ]->save();
|
80 |
+
}
|
81 |
+
}
|
82 |
+
}
|
83 |
+
|
84 |
+
public function load()
|
85 |
+
{
|
86 |
+
$notifications = Lib\Entities\Notification::query( 'n' )
|
87 |
+
->select( 'active, subject, message, copy, type' )
|
88 |
+
->where( 'gateway', $this->gateway )
|
89 |
+
->indexBy( 'type' )
|
90 |
+
->fetchArray();
|
91 |
+
foreach ( $this->types as $group ) {
|
92 |
+
foreach ( $group as $type ) {
|
93 |
+
$notifications[ $type ]['name'] = Lib\Entities\Notification::getName( $type );
|
94 |
+
$this->data[ $type ] = $notifications[ $type ];
|
95 |
+
}
|
96 |
+
}
|
97 |
+
}
|
98 |
+
|
99 |
+
/**
|
100 |
+
* Render subject.
|
101 |
+
*
|
102 |
+
* @param string $type
|
103 |
+
*/
|
104 |
+
public function renderSubject( $type )
|
105 |
+
{
|
106 |
+
printf(
|
107 |
+
'<div class="form-group">
|
108 |
+
<label for="%1$s">%2$s</label>
|
109 |
+
<input type="text" class="form-control" id="%1$s" name="%3$s" value="%4$s" />
|
110 |
+
</div>',
|
111 |
+
$type . '_subject',
|
112 |
+
__( 'Subject', 'bookly' ),
|
113 |
+
$type . '[subject]',
|
114 |
+
esc_attr( $this->data[ $type ]['subject'] )
|
115 |
+
);
|
116 |
+
}
|
117 |
+
|
118 |
+
/**
|
119 |
+
* Render message editor.
|
120 |
+
*
|
121 |
+
* @param string $type
|
122 |
+
*/
|
123 |
+
public function renderEditor( $type )
|
124 |
+
{
|
125 |
+
$id = $type . '_message';
|
126 |
+
$name = $type . '[message]';
|
127 |
+
$value = $this->data[ $type ]['message'];
|
128 |
+
|
129 |
+
if ( $this->gateway == 'sms' ) {
|
130 |
+
printf(
|
131 |
+
'<div class="form-group">
|
132 |
+
<label for="%1$s">%2$s</label>
|
133 |
+
<textarea rows="6" id="%1$s" name="%3$s" class="form-control">%4$s</textarea>
|
134 |
+
</div>',
|
135 |
+
$id,
|
136 |
+
__( 'Message', 'bookly' ),
|
137 |
+
$name,
|
138 |
+
esc_textarea( $value )
|
139 |
+
);
|
140 |
+
} else {
|
141 |
+
$settings = array(
|
142 |
+
'textarea_name' => $name,
|
143 |
+
'media_buttons' => false,
|
144 |
+
'editor_height' => 384,
|
145 |
+
'tinymce' => array(
|
146 |
+
'theme_advanced_buttons1' => 'formatselect,|,bold,italic,underline,|,'.
|
147 |
+
'bullist,blockquote,|,justifyleft,justifycenter'.
|
148 |
+
',justifyright,justifyfull,|,link,unlink,|'.
|
149 |
+
',spellchecker,wp_fullscreen,wp_adv'
|
150 |
+
)
|
151 |
+
);
|
152 |
+
|
153 |
+
echo '<div class="form-group">
|
154 |
+
<label>' . __( 'Message', 'bookly' ) . '</label>';
|
155 |
+
wp_editor( $value, $id, $settings );
|
156 |
+
echo '</div>';
|
157 |
+
}
|
158 |
+
}
|
159 |
+
|
160 |
+
/**
|
161 |
+
* Render copy.
|
162 |
+
*
|
163 |
+
* @param string $type
|
164 |
+
*/
|
165 |
+
public function renderCopy( $type )
|
166 |
+
{
|
167 |
+
if ( in_array( $type, array( 'staff_pending_appointment', 'staff_approved_appointment', 'staff_cancelled_appointment' ) ) ) {
|
168 |
+
printf(
|
169 |
+
'<div class="form-group">
|
170 |
+
<input name="%1$s" type="hidden" value="0">
|
171 |
+
<div class="checkbox"><label for="%2$s"><input id="%2$s" name="%1$s" type="checkbox" value="1" %3$s> %4$s</label></div>
|
172 |
+
</div>',
|
173 |
+
$type . '[copy]',
|
174 |
+
$type . '_copy',
|
175 |
+
checked( $this->data[ $type ]['copy'], true, false ),
|
176 |
+
__( 'Send copy to administrators', 'bookly' )
|
177 |
+
);
|
178 |
+
}
|
179 |
+
}
|
180 |
+
|
181 |
+
/**
|
182 |
+
* Render sending time.
|
183 |
+
*
|
184 |
+
* @param string $type
|
185 |
+
*/
|
186 |
+
public function renderSendingTime( $type )
|
187 |
+
{
|
188 |
+
if ( in_array( $type, array( 'staff_agenda', 'client_follow_up', 'client_reminder' ) ) ) {
|
189 |
+
$cron_reminder = (array) get_option( 'ab_settings_cron_reminder' );
|
190 |
+
printf(
|
191 |
+
'<div class="form-group">
|
192 |
+
<label for="%1$s">%2$s</label>
|
193 |
+
<p class="help-block">%3$s</p>
|
194 |
+
<select class="form-control ab-auto-w" name="%1$s" id="%1$s">
|
195 |
+
%4$s
|
196 |
+
</select>
|
197 |
+
</div>',
|
198 |
+
$type . '_cron_hour',
|
199 |
+
__( 'Sending time', 'bookly' ),
|
200 |
+
__( 'Set the time you want the notification to be sent.', 'bookly' ),
|
201 |
+
implode( '', array_map( function ( $hour ) use ( $type, $cron_reminder ) {
|
202 |
+
return sprintf(
|
203 |
+
'<option value="%s" %s>%s</option>',
|
204 |
+
$hour,
|
205 |
+
selected( $cron_reminder[ $type ], $hour, false ),
|
206 |
+
Lib\Utils\DateTime::buildTimeString( $hour * HOUR_IN_SECONDS, false )
|
207 |
+
);
|
208 |
+
}, range( 0, 23 ) ) )
|
209 |
+
);
|
210 |
+
}
|
211 |
+
}
|
212 |
+
|
213 |
+
}
|
backend/modules/notifications/resources/css/notifications.css
DELETED
@@ -1,29 +0,0 @@
|
|
1 |
-
/* notifications */
|
2 |
-
.ab-notifications tr td { padding-left: 10px; }
|
3 |
-
.ab-notifications input[type=text],
|
4 |
-
.ab-notifications select { margin-top: 10px; min-width: 250px; }
|
5 |
-
.ab-notifications .ab-form-field { padding-top: 10px; }
|
6 |
-
.ab-notifications .ab-toggle-arrow { position: absolute; right: 0; top: 9px; width: 24px; height: 24px; cursor: pointer; }
|
7 |
-
.ab-notifications .ab-form-label { display: block; float: left; margin-top: 3px; min-width: 80px; }
|
8 |
-
.ab-notifications .ab-form-row { padding: 3px 0; overflow: hidden; }
|
9 |
-
.ab-notifications .ab-form-row input[type="text"] { width: 700px!important; position: relative; z-index: 9; }
|
10 |
-
.ab-notifications #message_editor { margin-top: -20px; }
|
11 |
-
.ab-notifications legend { margin: 0; border: 0; line-height: normal; }
|
12 |
-
.ab-notifications legend input { margin: 0!important; }
|
13 |
-
.ab-notifications legend label { display: inline; font-size: 17px; }
|
14 |
-
.ab-notifications #message_editor .wp-editor-wrap { margin-left: 81px; }
|
15 |
-
.ab-notifications .panel-title > input { margin: 0 5px 0 0; }
|
16 |
-
.ab-notifications .ab-codes table tr td { padding: 0 20px 0 0; font-size: 12px;}
|
17 |
-
.ab-notifications .ab-codes table tr td input { cursor: pointer; border: none; width: 250px; }
|
18 |
-
.ab-notifications .wp-editor-tools { position: relative; top: 26px; }
|
19 |
-
.ab-notifications a[data-toggle="collapse"] {
|
20 |
-
outline: none;
|
21 |
-
box-shadow: none;
|
22 |
-
padding-right: 25px;
|
23 |
-
background: url("../../../../resources/images/notifications-arrow-up.png") 100% 50% no-repeat;
|
24 |
-
background-size: 17px 17px;
|
25 |
-
}
|
26 |
-
.ab-notifications a[data-toggle="collapse"].collapsed {
|
27 |
-
background: url("../../../../resources/images/notifications-arrow-down.png") 100% 50% no-repeat;
|
28 |
-
background-size: 17px 17px;
|
29 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/modules/notifications/resources/js/ng-app.js
ADDED
@@ -0,0 +1,96 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
;(function() {
|
2 |
+
var module = angular.module('notifications', []);
|
3 |
+
|
4 |
+
module.factory('dataSource', function($q, $rootScope) {
|
5 |
+
var ds = {
|
6 |
+
ab_settings_sender_name : '',
|
7 |
+
ab_settings_sender_email : '',
|
8 |
+
reply_to_customers : false,
|
9 |
+
content_type : 'html',
|
10 |
+
notifications : [],
|
11 |
+
loadData : function(params) {
|
12 |
+
var deferred = $q.defer();
|
13 |
+
jQuery.ajax({
|
14 |
+
url : ajaxurl,
|
15 |
+
type : 'POST',
|
16 |
+
data : jQuery.extend({ action : 'ab_get_email_notifications_data' }, params),
|
17 |
+
dataType : 'json',
|
18 |
+
success : function(response) {
|
19 |
+
if (response.success) {
|
20 |
+
ds.ab_settings_sender_name = response.data.ab_settings_sender_name;
|
21 |
+
ds.ab_settings_sender_email = response.data.ab_settings_sender_email;
|
22 |
+
ds.reply_to_customers = response.data.reply_to_customers;
|
23 |
+
ds.content_type = response.data.sender_email;
|
24 |
+
ds.notifications = response.data.ab_notifications;
|
25 |
+
}
|
26 |
+
$rootScope.$apply(deferred.resolve);
|
27 |
+
},
|
28 |
+
error : function() {
|
29 |
+
$rootScope.$apply(deferred.resolve);
|
30 |
+
}
|
31 |
+
});
|
32 |
+
|
33 |
+
return deferred.promise;
|
34 |
+
}
|
35 |
+
};
|
36 |
+
|
37 |
+
return ds;
|
38 |
+
});
|
39 |
+
|
40 |
+
module.controller('emailNotifications', function($scope, dataSource) {
|
41 |
+
$scope.showTestEmailNotificationDialog = function(){
|
42 |
+
showTestEmailNotificationDialog();
|
43 |
+
}
|
44 |
+
});
|
45 |
+
|
46 |
+
module.controller('testEmailNotificationsDialogCtrl', function($scope, dataSource, $timeout) {
|
47 |
+
$scope.loading = true;
|
48 |
+
$scope.mailSentAlert = false;
|
49 |
+
$scope.allNotifications = false;
|
50 |
+
$scope.toEmail = 'admin@example.com';
|
51 |
+
$scope.dataSource = dataSource;
|
52 |
+
|
53 |
+
dataSource.loadData().then(function(){
|
54 |
+
$scope.loading = false;
|
55 |
+
$scope.allNotificationsChecked();
|
56 |
+
});
|
57 |
+
|
58 |
+
$scope.$watch('notifications', function(newVal, oldVal){
|
59 |
+
$scope.allNotificationsChecked();
|
60 |
+
}, true);
|
61 |
+
|
62 |
+
$scope.toggleAllNotifications = function(){
|
63 |
+
var active = $scope.allNotifications ? '1' : '0';
|
64 |
+
angular.forEach($scope.dataSource.notifications, function(notification){
|
65 |
+
notification.active = active;
|
66 |
+
});
|
67 |
+
};
|
68 |
+
|
69 |
+
$scope.allNotificationsChecked = function(){
|
70 |
+
var count = $scope.selectedNotificationsCount();
|
71 |
+
var totalCount = Object.keys($scope.dataSource.notifications).length;
|
72 |
+
$scope.allNotifications = count===totalCount;
|
73 |
+
};
|
74 |
+
|
75 |
+
$scope.notificationChecked = function(){
|
76 |
+
$scope.allNotificationsChecked();
|
77 |
+
};
|
78 |
+
|
79 |
+
$scope.selectedNotificationsCount = function(){
|
80 |
+
var count = 0;
|
81 |
+
angular.forEach($scope.dataSource.notifications, function(notification){
|
82 |
+
count += notification.active==='1'?1:0;
|
83 |
+
});
|
84 |
+
return count;
|
85 |
+
};
|
86 |
+
|
87 |
+
$scope.testEmailNotifications = function(){
|
88 |
+
};
|
89 |
+
|
90 |
+
});
|
91 |
+
|
92 |
+
})();
|
93 |
+
|
94 |
+
var showTestEmailNotificationDialog = function () {
|
95 |
+
jQuery('#ab_test_email_notifications_dialog').modal('show');
|
96 |
+
};
|
backend/modules/notifications/resources/js/notification.js
CHANGED
@@ -6,25 +6,29 @@ jQuery(function($) {
|
|
6 |
$('#toplevel_page_ab-system > ul').css('margin-left', '0px');
|
7 |
|
8 |
/* exclude checkboxes in form */
|
9 |
-
var $checkboxes = $('.
|
10 |
|
11 |
$checkboxes.change(function () {
|
12 |
$(this).parents('.panel-heading').next().collapse(this.checked ? 'show' : 'hide');
|
13 |
-
$('#lite_notice').modal('show');
|
14 |
});
|
15 |
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
$('input.ab-sender').on('change', function() {
|
22 |
-
var $val = $(this).val();
|
23 |
-
$(this).val(escapeXSS($val));
|
24 |
});
|
25 |
|
26 |
-
|
27 |
-
|
|
|
|
|
|
|
|
|
|
|
28 |
});
|
29 |
|
|
|
|
|
|
|
30 |
});
|
6 |
$('#toplevel_page_ab-system > ul').css('margin-left', '0px');
|
7 |
|
8 |
/* exclude checkboxes in form */
|
9 |
+
var $checkboxes = $('.bookly-js-collapse .panel-title > input:checkbox');
|
10 |
|
11 |
$checkboxes.change(function () {
|
12 |
$(this).parents('.panel-heading').next().collapse(this.checked ? 'show' : 'hide');
|
|
|
13 |
});
|
14 |
|
15 |
+
$('[data-toggle="popover"]').popover({
|
16 |
+
html: true,
|
17 |
+
placement: 'top',
|
18 |
+
trigger: 'hover',
|
19 |
+
template: '<div class="popover bookly-font-xs" style="width: 220px" role="tooltip"><div class="popover-arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'
|
|
|
|
|
|
|
20 |
});
|
21 |
|
22 |
+
booklyAlert(BooklyL10n.alert);
|
23 |
+
|
24 |
+
$(':checkbox').on('change', function () {
|
25 |
+
if ($(this).prop('checked')) {
|
26 |
+
booklyAlert({error: [BooklyL10n.limitations]});
|
27 |
+
$(this).prop('checked', false).prop('readonly', true);
|
28 |
+
}
|
29 |
});
|
30 |
|
31 |
+
$('.ab-test-email-notifications').on('click',function () {
|
32 |
+
booklyAlert({error: [BooklyL10n.limitations]});
|
33 |
+
});
|
34 |
});
|
backend/modules/notifications/templates/_codes.php
CHANGED
@@ -1,24 +1,33 @@
|
|
1 |
-
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
+
$codes = array(
|
3 |
+
array( 'code' => 'appointment_date', 'description' => __( 'date of appointment', 'bookly' ) ),
|
4 |
+
array( 'code' => 'appointment_time', 'description' => __( 'time of appointment', 'bookly' ) ),
|
5 |
+
array( 'code' => 'booking_number', 'description' => __( 'booking number', 'bookly' ) ),
|
6 |
+
array( 'code' => 'approve_appointment_url', 'description' => esc_html__( 'URL of approve appointment link (to use inside <a> tag)', 'bookly' ) ),
|
7 |
+
array( 'code' => 'cancel_appointment', 'description' => __( 'cancel appointment link', 'bookly' ) ),
|
8 |
+
array( 'code' => 'cancel_appointment_url', 'description' => esc_html__( 'URL of cancel appointment link (to use inside <a> tag)', 'bookly' ) ),
|
9 |
+
array( 'code' => 'category_name', 'description' => __( 'name of category', 'bookly' ) ),
|
10 |
+
array( 'code' => 'client_email', 'description' => __( 'email of client', 'bookly' ) ),
|
11 |
+
array( 'code' => 'client_name', 'description' => __( 'name of client', 'bookly' ) ),
|
12 |
+
array( 'code' => 'client_phone', 'description' => __( 'phone of client', 'bookly' ) ),
|
13 |
+
array( 'code' => 'company_name', 'description' => __( 'name of company', 'bookly' ) ),
|
14 |
+
array( 'code' => 'company_logo', 'description' => __( 'company logo', 'bookly' ) ),
|
15 |
+
array( 'code' => 'company_address', 'description' => __( 'address of company', 'bookly' ) ),
|
16 |
+
array( 'code' => 'company_phone', 'description' => __( 'company phone', 'bookly' ) ),
|
17 |
+
array( 'code' => 'company_website', 'description' => __( 'company web-site address', 'bookly' ) ),
|
18 |
+
array( 'code' => 'custom_fields', 'description' => __( 'combined values of all custom fields', 'bookly' ) ),
|
19 |
+
array( 'code' => 'custom_fields_2c', 'description' => __( 'combined values of all custom fields (formatted in 2 columns)', 'bookly' ) ),
|
20 |
+
array( 'code' => 'google_calendar_url', 'description' => esc_html__( 'URL for adding event to client\'s Google Calendar (to use inside <a> tag)', 'bookly' ) ),
|
21 |
+
array( 'code' => 'number_of_persons', 'description' => __( 'number of persons', 'bookly' ) ),
|
22 |
+
array( 'code' => 'payment_type', 'description' => __( 'payment type', 'bookly' ) ),
|
23 |
+
array( 'code' => 'service_info', 'description' => __( 'info of service', 'bookly' ) ),
|
24 |
+
array( 'code' => 'service_name', 'description' => __( 'name of service', 'bookly' ) ),
|
25 |
+
array( 'code' => 'service_price', 'description' => __( 'price of service', 'bookly' ) ),
|
26 |
+
array( 'code' => 'staff_email', 'description' => __( 'email of staff', 'bookly' ) ),
|
27 |
+
array( 'code' => 'staff_info', 'description' => __( 'info of staff', 'bookly' ) ),
|
28 |
+
array( 'code' => 'staff_name', 'description' => __( 'name of staff', 'bookly' ) ),
|
29 |
+
array( 'code' => 'staff_phone', 'description' => __( 'phone of staff', 'bookly' ) ),
|
30 |
+
array( 'code' => 'staff_photo', 'description' => __( 'photo of staff', 'bookly' ) ),
|
31 |
+
array( 'code' => 'total_price', 'description' => __( 'total price of booking (sum of all cart items after applying coupon)', 'bookly' ) ),
|
32 |
+
);
|
33 |
+
\BooklyLite\Lib\Utils\Common::Codes( apply_filters( 'bookly_notification_short_codes', $codes ) );
|
backend/modules/notifications/templates/_codes_cart.php
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
+
$codes = array(
|
3 |
+
array( 'code' => 'cart_info', 'description' => __( 'cart information', 'bookly' ) ),
|
4 |
+
array( 'code' => 'cart_info_c', 'description' => __( 'cart information with cancel', 'bookly' ) ),
|
5 |
+
array( 'code' => 'client_email', 'description' => __( 'email of client', 'bookly' ) ),
|
6 |
+
array( 'code' => 'client_name', 'description' => __( 'name of client', 'bookly' ) ),
|
7 |
+
array( 'code' => 'client_phone', 'description' => __( 'phone of client', 'bookly' ) ),
|
8 |
+
array( 'code' => 'company_name', 'description' => __( 'name of company', 'bookly' ) ),
|
9 |
+
array( 'code' => 'company_logo', 'description' => __( 'company logo', 'bookly' ) ),
|
10 |
+
array( 'code' => 'company_address', 'description' => __( 'address of company', 'bookly' ) ),
|
11 |
+
array( 'code' => 'company_phone', 'description' => __( 'company phone', 'bookly' ) ),
|
12 |
+
array( 'code' => 'company_website', 'description' => __( 'company web-site address', 'bookly' ) ),
|
13 |
+
array( 'code' => 'payment_type', 'description' => __( 'payment type', 'bookly' ) ),
|
14 |
+
array( 'code' => 'total_price', 'description' => __( 'total price of booking (sum of all cart items after applying coupon)', 'bookly' ) ),
|
15 |
+
);
|
16 |
+
\BooklyLite\Lib\Utils\Common::Codes( apply_filters( 'bookly_deposit_notification_short_codes', $codes ) );
|
backend/modules/notifications/templates/_codes_client_new_wp_user.php
CHANGED
@@ -1,12 +1,15 @@
|
|
1 |
-
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
-
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
|
|
|
|
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
+
$codes = array(
|
3 |
+
array( 'code' => 'client_email', 'description' => __( 'email of client', 'bookly' ) ),
|
4 |
+
array( 'code' => 'client_name', 'description' => __( 'name of client', 'bookly' ) ),
|
5 |
+
array( 'code' => 'client_phone', 'description' => __( 'phone of client', 'bookly' ) ),
|
6 |
+
array( 'code' => 'company_name', 'description' => __( 'name of your company', 'bookly' ) ),
|
7 |
+
array( 'code' => 'company_logo', 'description' => __( 'your company logo', 'bookly' ) ),
|
8 |
+
array( 'code' => 'company_address', 'description' => __( 'address of your company', 'bookly' ) ),
|
9 |
+
array( 'code' => 'company_phone', 'description' => __( 'your company phone', 'bookly' ) ),
|
10 |
+
array( 'code' => 'company_website', 'description' => __( 'this web-site address', 'bookly' ) ),
|
11 |
+
array( 'code' => 'new_username', 'description' => __( 'customer new username', 'bookly' ) ),
|
12 |
+
array( 'code' => 'new_password', 'description' => __( 'customer new password', 'bookly' ) ),
|
13 |
+
array( 'code' => 'site_address', 'description' => __( 'site address', 'bookly' ) ),
|
14 |
+
);
|
15 |
+
\BooklyLite\Lib\Utils\Common::Codes( $codes );
|
backend/modules/notifications/templates/_codes_staff_agenda.php
CHANGED
@@ -1,4 +1,7 @@
|
|
1 |
-
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
-
|
3 |
-
|
4 |
-
|
|
|
|
|
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
+
$codes = array(
|
3 |
+
array( 'code' => 'tomorrow_date', 'description' => __( 'date of next day', 'bookly' ) ),
|
4 |
+
array( 'code' => 'next_day_agenda', 'description' => __( 'staff agenda for next day', 'bookly' ) ),
|
5 |
+
array( 'code' => 'staff_name', 'description' => __( 'name of staff', 'bookly' ) ),
|
6 |
+
);
|
7 |
+
\BooklyLite\Lib\Utils\Common::Codes( $codes );
|
backend/modules/notifications/templates/_test_email_notifications_modal.php
ADDED
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
|
2 |
+
<div ng-controller=testEmailNotificationsDialogCtrl>
|
3 |
+
<div id=ab_test_email_notifications_dialog class="modal fade" tabindex=-1 role="dialog">
|
4 |
+
<div class="modal-dialog">
|
5 |
+
<div class="modal-content">
|
6 |
+
<div ng-show=loading class="bookly-loading"></div>
|
7 |
+
|
8 |
+
<div ng-hide=loading>
|
9 |
+
<form ng-submit="testEmailNotifications()">
|
10 |
+
<div class="modal-header">
|
11 |
+
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
12 |
+
<div class="modal-title h2"><?php _e( 'Test Email Notifications', 'bookly' ) ?></div>
|
13 |
+
</div>
|
14 |
+
<div class="modal-body">
|
15 |
+
<div class="form-group">
|
16 |
+
<label for="ab_to_mail"><?php _e( 'To email', 'bookly' ) ?></label>
|
17 |
+
<input id="ab_to_mail" class="form-control" type="text" ng-model="toEmail"/>
|
18 |
+
</div>
|
19 |
+
<div class="row">
|
20 |
+
<div class="col-sm-6">
|
21 |
+
<div class="form-group">
|
22 |
+
<label for="ab_sender_name"><?php _e( 'Sender name', 'bookly' ) ?></label>
|
23 |
+
<input id="ab_sender_name" class="form-control" type="text" ng-model="dataSource.ab_settings_sender_name"/>
|
24 |
+
</div>
|
25 |
+
</div>
|
26 |
+
<div class="col-sm-6">
|
27 |
+
<div class="form-group">
|
28 |
+
<label for="ab_sender_email"><?php _e( 'Sender email', 'bookly' ) ?></label>
|
29 |
+
<input id="ab_sender_email" class="form-control" type="text" ng-model="dataSource.ab_settings_sender_email"/>
|
30 |
+
</div>
|
31 |
+
</div>
|
32 |
+
</div>
|
33 |
+
<div class="row">
|
34 |
+
<div class="col-sm-6">
|
35 |
+
<div class="form-group">
|
36 |
+
<label><?php _e( 'Reply directly to customers', 'bookly' ) ?></label>
|
37 |
+
<?php \BooklyLite\Lib\Utils\Common::optionToggle( 'ab_email_notification_reply_to_customers' ) ?>
|
38 |
+
</div>
|
39 |
+
</div>
|
40 |
+
<div class="col-sm-6">
|
41 |
+
<div class="form-group">
|
42 |
+
<label><?php _e( 'Send emails as', 'bookly' ) ?></label>
|
43 |
+
<?php \BooklyLite\Lib\Utils\Common::optionToggle( 'ab_email_content_type', array( 't' => array( 'html', __( 'HTML', 'bookly' ) ), 'f' => array( 'plain', __( 'Text', 'bookly' ) ) ) ) ?>
|
44 |
+
</div>
|
45 |
+
</div>
|
46 |
+
</div>
|
47 |
+
<div>
|
48 |
+
<div class="btn-group bookly-margin-bottom-lg">
|
49 |
+
<button class="btn btn-default btn-block dropdown-toggle bookly-flexbox" data-toggle="dropdown">
|
50 |
+
<div class="bookly-flex-cell text-left" style="width: 100%">
|
51 |
+
<?php _e( 'Notification templates', 'bookly' ) ?> ({{selectedNotificationsCount()}})
|
52 |
+
</div>
|
53 |
+
<div class="bookly-flex-cell"><div class="bookly-margin-left-md"><span class="caret"></span></div></div>
|
54 |
+
</button>
|
55 |
+
<ul class="dropdown-menu" style="width: 570px">
|
56 |
+
<li class="bookly-padding-horizontal-md">
|
57 |
+
<div class="checkbox">
|
58 |
+
<label>
|
59 |
+
<input type="checkbox" id="bookly-check-all-entities" ng-model="allNotifications" ng-change="toggleAllNotifications()">
|
60 |
+
<?php _e( 'All templates', 'bookly' ) ?>
|
61 |
+
</label>
|
62 |
+
</div>
|
63 |
+
</li>
|
64 |
+
<li role="separator" class="divider"></li>
|
65 |
+
|
66 |
+
<li class="bookly-padding-horizontal-md" ng-repeat="notification in dataSource.notifications">
|
67 |
+
<div class="checkbox">
|
68 |
+
<label>
|
69 |
+
<input type="checkbox" id="" value="0" data-staff_name="" class="bookly-js-check-entity"
|
70 |
+
ng-model="notification.active" ng-true-value="'1'" ng-false-value="'0'" ng-change="notificationChecked()"/>
|
71 |
+
{{notification.name}}
|
72 |
+
</label>
|
73 |
+
</div>
|
74 |
+
|
75 |
+
</li>
|
76 |
+
</ul>
|
77 |
+
</div>
|
78 |
+
</div>
|
79 |
+
</div>
|
80 |
+
<div class="modal-footer">
|
81 |
+
<?php \BooklyLite\Lib\Utils\Common::submitButton( '', '', __( 'Send', 'bookly' ) ) ?>
|
82 |
+
</div>
|
83 |
+
</form>
|
84 |
+
</div>
|
85 |
+
</div><!-- /.modal-content -->
|
86 |
+
</div><!-- /.modal-dialog -->
|
87 |
+
</div><!-- /.modal -->
|
88 |
+
|
89 |
+
<div class="modal fade" id="ab--modal" tabindex="-1" role="dialog">
|
90 |
+
<div class="modal-dialog" role="document">
|
91 |
+
<div class="modal-content">
|
92 |
+
<div class="modal-header">
|
93 |
+
<button type="button" class="close" data-dismiss="modal" aria-label="<?php esc_attr_e( 'Close', 'bookly' ) ?>"><span aria-hidden="true">×</span></button>
|
94 |
+
<div class="modal-title h2"></div>
|
95 |
+
</div>
|
96 |
+
<div class="modal-body"></div>
|
97 |
+
<div class="modal-footer">
|
98 |
+
<button type="button" class="btn btn-default" data-dismiss="modal">
|
99 |
+
<?php _e( 'Close', 'bookly' ) ?>
|
100 |
+
</button>
|
101 |
+
</div>
|
102 |
+
</div>
|
103 |
+
</div>
|
104 |
+
</div>
|
105 |
+
</div>
|
backend/modules/notifications/templates/index.php
CHANGED
@@ -3,135 +3,149 @@
|
|
3 |
get_option( 'blogname' ) : get_option( 'ab_settings_sender_name' );
|
4 |
$ab_settings_sender_email = get_option( 'ab_settings_sender_email' ) == '' ?
|
5 |
get_option( 'admin_email' ) : get_option( 'ab_settings_sender_email' );
|
|
|
|
|
6 |
?>
|
7 |
-
<
|
8 |
-
<div class="
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
<?php AB_Utils::notice( $message ) ?>
|
14 |
-
<div class="ab-notifications">
|
15 |
-
<table>
|
16 |
-
<tr><!-- sender name -->
|
17 |
-
<td>
|
18 |
-
<label for="ab_settings_sender_name" style="display: inline;"><?php _e( 'Sender name', 'bookly' ) ?></label>
|
19 |
-
</td>
|
20 |
-
<td>
|
21 |
-
<input id="ab_settings_sender_name" name="ab_settings_sender_name" class="form-control ab-inline-block ab-auto-w ab-sender" type="text" value="<?php echo esc_attr( $ab_settings_sender_name ) ?>"/>
|
22 |
-
</td>
|
23 |
-
<td></td>
|
24 |
-
</tr>
|
25 |
-
<tr><!-- sender email -->
|
26 |
-
<td>
|
27 |
-
<label for="ab_settings_sender_email" style="display: inline;"><?php _e( 'Sender email', 'bookly' ) ?></label>
|
28 |
-
</td>
|
29 |
-
<td>
|
30 |
-
<input id="ab_settings_sender_email" name="ab_settings_sender_email" class="form-control ab-inline-block ab-auto-w ab-sender" type="text" value="<?php echo esc_attr( $ab_settings_sender_email ) ?>"/>
|
31 |
-
</td>
|
32 |
-
<td></td>
|
33 |
-
</tr>
|
34 |
-
<tr>
|
35 |
-
<td>
|
36 |
-
<label for="ab_email_notification_reply_to_customers" style="display: inline;"><?php _e( 'Reply directly to customers', 'bookly' ) ?></label>
|
37 |
-
</td>
|
38 |
-
<td>
|
39 |
-
<?php AB_Utils::optionToggle( 'ab_email_notification_reply_to_customers' ) ?>
|
40 |
-
</td>
|
41 |
-
<td>
|
42 |
-
<?php AB_Utils::popover( __( 'If this option is enabled then the email address of the customer is used as a sender email address for notifications sent to staff members and administrators.', 'bookly' ) ) ?>
|
43 |
-
</td>
|
44 |
-
</tr>
|
45 |
-
<tr>
|
46 |
-
<td>
|
47 |
-
<label for="ab_email_content_type" style="display: inline;"><?php _e( 'Send emails as', 'bookly' ) ?></label>
|
48 |
-
</td>
|
49 |
-
<td>
|
50 |
-
<?php AB_Utils::optionToggle( 'ab_email_content_type', array( 't' => array( 'html', __( 'HTML', 'bookly' ) ), 'f' => array( 'plain', __( 'Text', 'bookly' ) ) ) ) ?>
|
51 |
-
</td>
|
52 |
-
<td>
|
53 |
-
<?php AB_Utils::popover( __( 'HTML allows formatting, colors, fonts, positioning, etc. With Text you must use Text mode of rich-text editors below. On some servers only text emails are sent successfully.', 'bookly' ) ) ?>
|
54 |
-
</td>
|
55 |
-
</tr>
|
56 |
-
</table>
|
57 |
</div>
|
58 |
-
<
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
<div class="panel-heading" role="tab" id="headingOne">
|
67 |
-
<h4 class="panel-title">
|
68 |
-
<input name="<?php echo $type ?>[active]" value="0" type="checkbox" checked="checked" class="hidden">
|
69 |
-
<input id="<?php echo $type ?>_active" name="<?php echo $type ?>[active]" value="1" type="checkbox" <?php checked( $active ) ?> />
|
70 |
-
<a class="collapsed" role="button" data-toggle="collapse" data-parent="#accordion" href="#collapse_<?php echo $notif_id ?>">
|
71 |
-
<?php echo $form->renderActive( $type ) ?>
|
72 |
-
</a>
|
73 |
-
</h4>
|
74 |
-
</div>
|
75 |
-
<div id="collapse_<?php echo $notif_id ?>" class="panel-collapse collapse">
|
76 |
-
<div class="panel-body">
|
77 |
-
<div class="ab-form-field">
|
78 |
-
<div class="ab-form-row">
|
79 |
-
<?php echo $form->renderSubject( $type ) ?>
|
80 |
</div>
|
81 |
-
|
82 |
-
|
83 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
84 |
</div>
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
102 |
</div>
|
103 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
104 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
105 |
</div>
|
106 |
</div>
|
107 |
</div>
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
<?php
|
112 |
-
echo '<i>' . __( 'To send scheduled notifications please execute the following script hourly with your cron:', 'bookly' ) . '</i><br />';
|
113 |
-
echo '<b>php -f ' . realpath( AB_PATH . '/lib/utils/send_notifications_cron.php' ) . '</b>';
|
114 |
-
?>
|
115 |
-
</div>
|
116 |
-
</div>
|
117 |
-
<div class="panel-footer">
|
118 |
-
<?php AB_Utils::submitButton() ?>
|
119 |
-
<?php AB_Utils::resetButton() ?>
|
120 |
</div>
|
121 |
-
</div>
|
122 |
-
</form>
|
123 |
-
<div class="modal fade" id="lite_notice" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
|
124 |
-
<div class="modal-dialog">
|
125 |
-
<div class="modal-content">
|
126 |
-
<div class="modal-header">
|
127 |
-
<h4 class="modal-title"><?php _e('Notice', 'bookly') ?></h4>
|
128 |
-
</div>
|
129 |
-
<div class="modal-body">
|
130 |
-
<?php _e('This function is disabled in the lite version of Bookly. If you find the plugin useful for your business please consider buying a licence for the full version. It costs just $46 and for this money you will get many useful functions, lifetime free update and excellent support! More information can be found here', 'bookly'); ?>: <a href="http://booking-wp-plugin.com" target="_blank">http://booking-wp-plugin.com</a>
|
131 |
-
</div>
|
132 |
-
<div class="modal-footer">
|
133 |
-
<button type="button" class="btn btn-default" data-dismiss="modal"><?php _e('Close', 'bookly') ?></button>
|
134 |
-
</div>
|
135 |
-
</div><!-- /.modal-content -->
|
136 |
-
</div><!-- /.modal-dialog -->
|
137 |
-
</div><!-- /.modal -->
|
3 |
get_option( 'blogname' ) : get_option( 'ab_settings_sender_name' );
|
4 |
$ab_settings_sender_email = get_option( 'ab_settings_sender_email' ) == '' ?
|
5 |
get_option( 'admin_email' ) : get_option( 'ab_settings_sender_email' );
|
6 |
+
$collapse_id = 0;
|
7 |
+
$form_data = $form->getData();
|
8 |
?>
|
9 |
+
<div id="bookly-tbs" class="wrap">
|
10 |
+
<div class="bookly-tbs-body" ng-app="notifications">
|
11 |
+
<div class="page-header text-right clearfix">
|
12 |
+
<div class="bookly-page-title">
|
13 |
+
<?php _e( 'Email Notifications', 'bookly' ) ?>
|
14 |
+
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
15 |
</div>
|
16 |
+
<form method="post" id="form1" action="">
|
17 |
+
<div class="panel panel-default bookly-main" ng-controller="emailNotifications">
|
18 |
+
<div class="panel-body">
|
19 |
+
<div class="row">
|
20 |
+
<div class="col-md-6">
|
21 |
+
<div class="form-group">
|
22 |
+
<label for="sender_name"><?php _e( 'Sender name', 'bookly' ) ?></label>
|
23 |
+
<input id="sender_name" name="ab_settings_sender_name" class="form-control" type="text" value="<?php echo esc_attr( $ab_settings_sender_name ) ?>">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
24 |
</div>
|
25 |
+
</div>
|
26 |
+
<div class="col-md-6">
|
27 |
+
<div class="form-group">
|
28 |
+
<label for="sender_email"><?php _e( 'Sender email', 'bookly' ) ?></label>
|
29 |
+
<input id="sender_email" name="ab_settings_sender_email" class="form-control ab-inline-block ab-auto-w ab-sender" type="text" value="<?php echo esc_attr( $ab_settings_sender_email ) ?>">
|
30 |
+
</div>
|
31 |
+
</div>
|
32 |
+
<div class="clearfix visible-md-block"></div>
|
33 |
+
<div class="col-md-6">
|
34 |
+
<div class="form-group">
|
35 |
+
<label for="ab_email_notification_reply_to_customers">
|
36 |
+
<?php _e( 'Reply directly to customers', 'bookly' ) ?>
|
37 |
+
</label>
|
38 |
+
<p class="help-block" style="display: none;"><?php _e( 'If this option is enabled then the email address of the customer is used as a sender email address for notifications sent to staff members and administrators.', 'bookly' ) ?></p>
|
39 |
+
<?php \BooklyLite\Lib\Utils\Common::optionToggle( 'ab_email_notification_reply_to_customers' ) ?>
|
40 |
</div>
|
41 |
+
</div>
|
42 |
+
<div class="col-md-6">
|
43 |
+
<div class="form-group">
|
44 |
+
<label for="ab_email_content_type">
|
45 |
+
<?php _e( 'Send emails as', 'bookly' ) ?>
|
46 |
+
</label>
|
47 |
+
<p class="help-block" style="display: none;"><?php _e( 'HTML allows formatting, colors, fonts, positioning, etc. With Text you must use Text mode of rich-text editors below. On some servers only text emails are sent successfully.', 'bookly' ) ?></p>
|
48 |
+
<?php \BooklyLite\Lib\Utils\Common::optionToggle( 'ab_email_content_type', array( 't' => array( 'html', __( 'HTML', 'bookly' ) ), 'f' => array( 'plain', __( 'Text', 'bookly' ) ) ) ) ?>
|
49 |
+
</div>
|
50 |
+
</div>
|
51 |
+
</div>
|
52 |
+
<?php if ( $form->types['combined'] ) : ?>
|
53 |
+
<h4 class="bookly-block-head bookly-color-gray"><?php _e( 'Single', 'bookly' ) ?></h4>
|
54 |
+
<?php endif ?>
|
55 |
+
<div class="panel-group bookly-margin-vertical-xlg" id="single">
|
56 |
+
<?php foreach ( $form->types['single'] as $type ) : ?>
|
57 |
+
<div class="panel panel-default bookly-js-collapse">
|
58 |
+
<div class="panel-heading" role="tab">
|
59 |
+
<div class="checkbox bookly-margin-remove">
|
60 |
+
<label>
|
61 |
+
<input name="<?php echo $type ?>[active]" value="0" type="checkbox" checked="checked" class="hidden">
|
62 |
+
<input id="<?php echo $type ?>_active" name="<?php echo $type ?>[active]" value="1" type="checkbox" <?php checked( $form_data[ $type ]['active'] ) ?>>
|
63 |
+
<a href="#collapse_<?php echo ++ $collapse_id ?>" class="collapsed panel-title" role="button" data-toggle="collapse" data-parent="#single">
|
64 |
+
<?php echo $form_data[ $type ]['name'] ?>
|
65 |
+
</a>
|
66 |
+
</label>
|
67 |
+
</div>
|
68 |
+
</div>
|
69 |
+
<div id="collapse_<?php echo $collapse_id ?>" class="panel-collapse collapse">
|
70 |
+
<div class="panel-body">
|
71 |
+
|
72 |
+
<?php $form->renderSendingTime( $type ) ?>
|
73 |
+
<?php $form->renderSubject( $type ) ?>
|
74 |
+
<?php $form->renderEditor( $type ) ?>
|
75 |
+
<?php $form->renderCopy( $type ) ?>
|
76 |
+
|
77 |
+
<div class="form-group">
|
78 |
+
<label><?php _e( 'Codes', 'bookly' ) ?></label>
|
79 |
+
<?php switch ( $type ) :
|
80 |
+
case 'staff_agenda': include '_codes_staff_agenda.php'; break;
|
81 |
+
case 'client_new_wp_user': include '_codes_client_new_wp_user.php'; break;
|
82 |
+
default: include '_codes.php';
|
83 |
+
endswitch ?>
|
84 |
+
</div>
|
85 |
+
|
86 |
+
</div>
|
87 |
</div>
|
88 |
</div>
|
89 |
+
<?php endforeach ?>
|
90 |
+
</div>
|
91 |
+
|
92 |
+
<?php if ( $form->types['combined'] ) : ?>
|
93 |
+
<h4 class="bookly-block-head bookly-color-gray"><?php _e( 'Combined', 'bookly' ) ?></h4>
|
94 |
+
<div class="panel-group bookly-margin-vertical-xlg" id="combined">
|
95 |
+
<?php foreach ( $form->types['combined'] as $type ) : ?>
|
96 |
+
<div class="panel panel-default bookly-js-collapse">
|
97 |
+
<div class="panel-heading" role="tab">
|
98 |
+
<div class="checkbox bookly-margin-remove">
|
99 |
+
<label>
|
100 |
+
<input name="<?php echo $type ?>[active]" value="0" type="checkbox" checked="checked" class="hidden">
|
101 |
+
<input id="<?php echo $type ?>_active" name="<?php echo $type ?>[active]" value="1" type="checkbox" <?php checked( $form_data[ $type ]['active'] ) ?>>
|
102 |
+
<a href="#collapse_<?php echo ++ $collapse_id ?>" class="collapsed panel-title" role="button" data-toggle="collapse" data-parent="#combined">
|
103 |
+
<?php echo $form_data[ $type ]['name'] ?>
|
104 |
+
</a>
|
105 |
+
</label>
|
106 |
+
</div>
|
107 |
+
</div>
|
108 |
+
<div id="collapse_<?php echo $collapse_id ?>" class="panel-collapse collapse">
|
109 |
+
<div class="panel-body">
|
110 |
+
<?php $form->renderSubject( $type ) ?>
|
111 |
+
<?php $form->renderEditor( $type ) ?>
|
112 |
+
<?php $form->renderCopy( $type ) ?>
|
113 |
+
|
114 |
+
<div class="form-group">
|
115 |
+
<label><?php _e( 'Codes', 'bookly' ) ?></label>
|
116 |
+
<?php include '_codes_cart.php' ?>
|
117 |
+
|
118 |
+
</div>
|
119 |
+
</div>
|
120 |
+
</div>
|
121 |
+
</div>
|
122 |
+
<?php endforeach ?>
|
123 |
</div>
|
124 |
+
<?php endif ?>
|
125 |
+
|
126 |
+
<div class="alert alert-info">
|
127 |
+
<?php if ( is_multisite() ) : ?>
|
128 |
+
<p><?php printf( __( 'To send scheduled notifications please refer to <a href="%1$s">Bookly Multisite</a> add-on <a href="%2$s">message</a>.', 'bookly' ), 'http://codecanyon.net/item/bookly-multisite-addon/13903524?ref=ladela', network_admin_url( 'admin.php?page=bookly-multisite-network' ) ) ?></p>
|
129 |
+
<?php else : ?>
|
130 |
+
<p><?php _e( 'To send scheduled notifications please execute the following script hourly with your cron:', 'bookly' ) ?></p><br />
|
131 |
+
<code class="bookly-text-wrap">php -f <?php echo $cron_path ?></code>
|
132 |
+
<?php endif ?>
|
133 |
+
</div>
|
134 |
+
</div>
|
135 |
+
|
136 |
+
<div class="panel-footer">
|
137 |
+
<?php \BooklyLite\Lib\Utils\Common::submitButton() ?>
|
138 |
+
<?php \BooklyLite\Lib\Utils\Common::resetButton() ?>
|
139 |
+
|
140 |
+
<div class="pull-left">
|
141 |
+
<button type="button" class="btn btn-default ab-test-email-notifications btn-lg">
|
142 |
+
<?php _e( 'Test Email Notifications', 'bookly' ) ?>
|
143 |
+
</button>
|
144 |
</div>
|
145 |
</div>
|
146 |
</div>
|
147 |
+
</form>
|
148 |
+
|
149 |
+
<?php include '_test_email_notifications_modal.php' ?>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
150 |
</div>
|
151 |
+
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/modules/payment/AB_PaymentController.php
DELETED
@@ -1,84 +0,0 @@
|
|
1 |
-
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
-
|
3 |
-
class AB_PaymentController extends AB_Controller {
|
4 |
-
|
5 |
-
public function index() {
|
6 |
-
/** @var WP_Locale $wp_locale */
|
7 |
-
global $wp_locale;
|
8 |
-
|
9 |
-
$this->enqueueStyles( array(
|
10 |
-
'backend' => array(
|
11 |
-
'css/bookly.main-backend.css',
|
12 |
-
'bootstrap/css/bootstrap.min.css',
|
13 |
-
'css/daterangepicker.css',
|
14 |
-
'css/bootstrap-select.min.css',
|
15 |
-
)
|
16 |
-
) );
|
17 |
-
|
18 |
-
$this->enqueueScripts( array(
|
19 |
-
'backend' => array(
|
20 |
-
'bootstrap/js/bootstrap.min.js' => array( 'jquery' ),
|
21 |
-
'js/moment.min.js',
|
22 |
-
'js/daterangepicker.js' => array( 'jquery' ),
|
23 |
-
'js/bootstrap-select.min.js',
|
24 |
-
)
|
25 |
-
) );
|
26 |
-
|
27 |
-
wp_localize_script( 'ab-daterangepicker.js', 'BooklyL10n', array(
|
28 |
-
'today' => __( 'Today', 'bookly' ),
|
29 |
-
'yesterday' => __( 'Yesterday', 'bookly' ),
|
30 |
-
'last_7' => __( 'Last 7 Days', 'bookly' ),
|
31 |
-
'last_30' => __( 'Last 30 Days', 'bookly' ),
|
32 |
-
'this_month' => __( 'This Month', 'bookly' ),
|
33 |
-
'last_month' => __( 'Last Month', 'bookly' ),
|
34 |
-
'custom_range' => __( 'Custom Range', 'bookly' ),
|
35 |
-
'apply' => __( 'Apply', 'bookly' ),
|
36 |
-
'cancel' => __( 'Cancel', 'bookly' ),
|
37 |
-
'to' => __( 'To', 'bookly' ),
|
38 |
-
'from' => __( 'From', 'bookly' ),
|
39 |
-
'months' => array_values( $wp_locale->month ),
|
40 |
-
'days' => array_values( $wp_locale->weekday_abbrev ),
|
41 |
-
'startOfWeek' => (int) get_option( 'start_of_week' ),
|
42 |
-
'mjsDateFormat' => AB_DateTimeUtils::convertFormat( 'date', AB_DateTimeUtils::FORMAT_MOMENT_JS ),
|
43 |
-
));
|
44 |
-
|
45 |
-
$this->collection = false;
|
46 |
-
|
47 |
-
$this->types = array();
|
48 |
-
$this->customers = array();
|
49 |
-
$this->providers = array();
|
50 |
-
$this->services = array();
|
51 |
-
|
52 |
-
$this->render( 'index' );
|
53 |
-
}
|
54 |
-
|
55 |
-
/**
|
56 |
-
* Sort payments.
|
57 |
-
*/
|
58 |
-
public function executeSortPayments()
|
59 |
-
{
|
60 |
-
$this->executeFilterPayments();
|
61 |
-
}
|
62 |
-
|
63 |
-
/**
|
64 |
-
* Filter payments.
|
65 |
-
*/
|
66 |
-
public function executeFilterPayments()
|
67 |
-
{
|
68 |
-
$this->render( '_body', array( 'collection' => false ) );
|
69 |
-
exit;
|
70 |
-
}
|
71 |
-
|
72 |
-
|
73 |
-
/**
|
74 |
-
* Override parent method to add 'wp_ajax_ab_' prefix
|
75 |
-
* so current 'execute*' methods look nicer.
|
76 |
-
*
|
77 |
-
* @param string $prefix
|
78 |
-
*/
|
79 |
-
protected function registerWpActions( $prefix = '' )
|
80 |
-
{
|
81 |
-
parent::registerWpActions( 'wp_ajax_ab_' );
|
82 |
-
}
|
83 |
-
|
84 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/modules/payment/templates/_alert.php
DELETED
@@ -1,4 +0,0 @@
|
|
1 |
-
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
|
2 |
-
<div class="alert alert-warning" >
|
3 |
-
<?php _e('This function is disabled in the lite version of Bookly. If you find the plugin useful for your business please consider buying a licence for the full version. It costs just $46 and for this money you will get many useful functions, lifetime free update and excellent support! More information can be found here', 'bookly'); ?>: <a href="http://booking-wp-plugin.com" target="_blank">http://booking-wp-plugin.com</a>
|
4 |
-
</div>
|
|
|
|
|
|
|
|
backend/modules/payment/templates/index.php
DELETED
@@ -1,148 +0,0 @@
|
|
1 |
-
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
-
/**
|
3 |
-
* @var array $customers
|
4 |
-
* @var array $types
|
5 |
-
* @var array $providers
|
6 |
-
* @var array $services
|
7 |
-
*/
|
8 |
-
?>
|
9 |
-
|
10 |
-
<div class="panel panel-default">
|
11 |
-
<div class="panel-heading">
|
12 |
-
<h3 class="panel-title"><?php _e( 'Payments', 'bookly' ) ?></h3>
|
13 |
-
</div>
|
14 |
-
<div class="panel-body">
|
15 |
-
<div class=ab-nav-payment>
|
16 |
-
<div class=row-fluid>
|
17 |
-
<div id=reportrange class="ab-reportrange ab-inline-block">
|
18 |
-
<i class="glyphicon glyphicon-calendar"></i>
|
19 |
-
<span data-date="<?php echo date( 'Y-m-d', strtotime( '-30 day' ) ) ?> - <?php echo date( 'Y-m-d' ) ?>"><?php echo date_i18n( get_option( 'date_format' ), strtotime( '-30 day' ) ) ?> - <?php echo date_i18n( get_option( 'date_format' ) ) ?></span> <b style="margin-top: 8px;" class=caret></b>
|
20 |
-
</div>
|
21 |
-
<div class=ab-inline-block>
|
22 |
-
<select id=ab-type-filter class=selectpicker>
|
23 |
-
<option value="-1"><?php _e( 'All payment types', 'bookly' ) ?></option>
|
24 |
-
<?php foreach ( $types as $type ): ?>
|
25 |
-
<option value="<?php echo esc_attr( $type ) ?>">
|
26 |
-
<?php
|
27 |
-
switch( $type ) {
|
28 |
-
case 'paypal':
|
29 |
-
echo 'PayPal'; break;
|
30 |
-
case 'authorizeNet':
|
31 |
-
echo 'authorizeNet'; break;
|
32 |
-
case 'stripe':
|
33 |
-
echo 'Stripe'; break;
|
34 |
-
default:
|
35 |
-
_e( 'Local', 'bookly' ); break;
|
36 |
-
}
|
37 |
-
?>
|
38 |
-
</option>
|
39 |
-
<?php endforeach ?>
|
40 |
-
</select>
|
41 |
-
<select id=ab-customer-filter class=selectpicker>
|
42 |
-
<option value="-1"><?php _e( 'All customers', 'bookly' ) ?></option>
|
43 |
-
<?php foreach ( $customers as $customer ): ?>
|
44 |
-
<option><?php echo esc_html( $customer ) ?></option>
|
45 |
-
<?php endforeach ?>
|
46 |
-
</select>
|
47 |
-
<select id=ab-provider-filter class=selectpicker>
|
48 |
-
<option value="-1"><?php _e( 'All providers', 'bookly' ) ?></option>
|
49 |
-
<?php foreach ( $providers as $provider ): ?>
|
50 |
-
<option><?php echo esc_html( $provider ) ?></option>
|
51 |
-
<?php endforeach ?>
|
52 |
-
</select>
|
53 |
-
<select id=ab-service-filter class=selectpicker>
|
54 |
-
<option value="-1"><?php _e( 'All services', 'bookly' ) ?></option>
|
55 |
-
<?php foreach ( $services as $service ): ?>
|
56 |
-
<option><?php echo esc_html( $service ) ?></option>
|
57 |
-
<?php endforeach ?>
|
58 |
-
</select>
|
59 |
-
<a id=ab-filter-submit href="#" class="btn btn-primary"><?php _e( 'Filter', 'bookly' ) ?></a>
|
60 |
-
</div>
|
61 |
-
</div>
|
62 |
-
</div>
|
63 |
-
<div id=ab-alert-div class=alert style="display: none"></div>
|
64 |
-
<?php include '_alert.php' ?>
|
65 |
-
<div style="display: none" class="loading-indicator">
|
66 |
-
<span class="ab-loader"></span>
|
67 |
-
</div>
|
68 |
-
</div>
|
69 |
-
</div>
|
70 |
-
<div class="modal fade" id="lite_notice" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
|
71 |
-
<div class="modal-dialog">
|
72 |
-
<div class="modal-content">
|
73 |
-
<div class="modal-header">
|
74 |
-
<h4 class="modal-title"><?php _e('Notice', 'bookly') ?></h4>
|
75 |
-
</div>
|
76 |
-
<div class="modal-body">
|
77 |
-
<?php _e('This function is disabled in the lite version of Bookly. If you find the plugin useful for your business please consider buying a licence for the full version. It costs just $46 and for this money you will get many useful functions, lifetime free update and excellent support! More information can be found here', 'bookly'); ?>: <a href="http://booking-wp-plugin.com" target="_blank">http://booking-wp-plugin.com</a>
|
78 |
-
</div>
|
79 |
-
<div class="modal-footer">
|
80 |
-
<button type="button" class="btn btn-default" data-dismiss="modal"><?php _e('Close', 'bookly') ?></button>
|
81 |
-
</div>
|
82 |
-
</div><!-- /.modal-content -->
|
83 |
-
</div><!-- /.modal-dialog -->
|
84 |
-
</div><!-- /.modal -->
|
85 |
-
|
86 |
-
<script type="text/javascript">
|
87 |
-
jQuery(function($) {
|
88 |
-
var data = {},
|
89 |
-
$report_range = $('#reportrange span'),
|
90 |
-
picker_ranges = {};
|
91 |
-
|
92 |
-
picker_ranges[BooklyL10n.today] = [moment(), moment()];
|
93 |
-
picker_ranges[BooklyL10n.yesterday] = [moment().subtract(1, 'days'), moment().subtract(1, 'days')];
|
94 |
-
picker_ranges[BooklyL10n.last_7] = [moment().subtract(7, 'days'), moment()];
|
95 |
-
picker_ranges[BooklyL10n.last_30] = [moment().subtract(30, 'days'), moment()];
|
96 |
-
picker_ranges[BooklyL10n.this_month] = [moment().startOf('month'), moment().endOf('month')];
|
97 |
-
picker_ranges[BooklyL10n.last_month] = [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')];
|
98 |
-
|
99 |
-
$('.selectpicker').selectpicker({style: 'btn-info', size: 5});
|
100 |
-
|
101 |
-
function ajaxData(object) {
|
102 |
-
data['customer'] = $('#ab-customer-filter').val();
|
103 |
-
data['provider'] = $('#ab-provider-filter').val();
|
104 |
-
data['service'] = $('#ab-service-filter').val();
|
105 |
-
data['range'] = $report_range.data('date'); //text();
|
106 |
-
data['type'] = $('#ab-type-filter').val();
|
107 |
-
data['key'] = $('#search_customers').val();
|
108 |
-
|
109 |
-
|
110 |
-
return data;
|
111 |
-
}
|
112 |
-
|
113 |
-
// sort order
|
114 |
-
$('#ab_payments_list th a').on('click', function() {
|
115 |
-
var data = { action:'ab_sort_payments', data: ajaxData(this) };
|
116 |
-
$('.loading-indicator').show();
|
117 |
-
$('#ab_payments_list tbody').load(ajaxurl, data, function() {$('.loading-indicator').hide();});
|
118 |
-
});
|
119 |
-
|
120 |
-
$('#reportrange').daterangepicker(
|
121 |
-
{
|
122 |
-
startDate: moment().subtract(30, 'days'), // by default selected is "Last 30 days"
|
123 |
-
ranges: picker_ranges,
|
124 |
-
locale: {
|
125 |
-
applyLabel: BooklyL10n.apply,
|
126 |
-
cancelLabel: BooklyL10n.cancel,
|
127 |
-
fromLabel: BooklyL10n.from,
|
128 |
-
toLabel: BooklyL10n.to,
|
129 |
-
customRangeLabel: BooklyL10n.custom_range,
|
130 |
-
daysOfWeek: BooklyL10n.days,
|
131 |
-
monthNames: BooklyL10n.months,
|
132 |
-
firstDay: parseInt(BooklyL10n.startOfWeek),
|
133 |
-
format: BooklyL10n.mjsDateFormat
|
134 |
-
}
|
135 |
-
},
|
136 |
-
function(start, end) {
|
137 |
-
var format = 'YYYY-MM-DD';
|
138 |
-
$report_range
|
139 |
-
.data('date', start.format(format) + ' - ' + end.format(format))
|
140 |
-
.html(start.format(BooklyL10n.mjsDateFormat) + ' - ' + end.format(BooklyL10n.mjsDateFormat));
|
141 |
-
}
|
142 |
-
);
|
143 |
-
|
144 |
-
$('#ab-filter-submit').on('click', function() {
|
145 |
-
$('#lite_notice').modal('show');
|
146 |
-
});
|
147 |
-
});
|
148 |
-
</script>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/modules/payments/Components.php
ADDED
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace BooklyLite\Backend\Modules\Payments;
|
3 |
+
|
4 |
+
use BooklyLite\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Components
|
8 |
+
* @package BooklyLite\Backend\Modules\Payments
|
9 |
+
*/
|
10 |
+
class Components extends Lib\Base\Components
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Render payment details dialog.
|
14 |
+
* @throws \Exception
|
15 |
+
*/
|
16 |
+
public function renderPaymentDetailsDialog()
|
17 |
+
{
|
18 |
+
$this->enqueueStyles( array(
|
19 |
+
'frontend' => array( 'css/ladda.min.css', ),
|
20 |
+
) );
|
21 |
+
|
22 |
+
$this->enqueueScripts( array(
|
23 |
+
'backend' => array( 'js/angular.min.js' => array( 'jquery' ), ),
|
24 |
+
'frontend' => array(
|
25 |
+
'js/spin.min.js' => array( 'jquery' ),
|
26 |
+
'js/ladda.min.js' => array( 'jquery' ),
|
27 |
+
),
|
28 |
+
'module' => array( 'js/ng-payment_details_dialog.js' => array( 'ab-angular.min.js' ), ),
|
29 |
+
) );
|
30 |
+
|
31 |
+
$this->render( '_payment_details_dialog' );
|
32 |
+
}
|
33 |
+
|
34 |
+
}
|
backend/modules/payments/Controller.php
ADDED
@@ -0,0 +1,263 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace BooklyLite\Backend\Modules\Payments;
|
3 |
+
|
4 |
+
use BooklyLite\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Controller
|
8 |
+
* @package BooklyLite\Backend\Modules\Payments
|
9 |
+
*/
|
10 |
+
class Controller extends Lib\Base\Controller
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* @return array
|
14 |
+
*/
|
15 |
+
protected function getPermissions()
|
16 |
+
{
|
17 |
+
return array(
|
18 |
+
'executeGetPaymentDetails' => 'user',
|
19 |
+
'executeCompletePayment' => 'user',
|
20 |
+
);
|
21 |
+
}
|
22 |
+
|
23 |
+
public function index()
|
24 |
+
{
|
25 |
+
/** @var \WP_Locale $wp_locale */
|
26 |
+
global $wp_locale;
|
27 |
+
|
28 |
+
$this->enqueueStyles( array(
|
29 |
+
'backend' => array(
|
30 |
+
'bootstrap/css/bootstrap-theme.min.css',
|
31 |
+
'css/daterangepicker.css',
|
32 |
+
),
|
33 |
+
'frontend' => array( 'css/ladda.min.css', ),
|
34 |
+
) );
|
35 |
+
|
36 |
+
$this->enqueueScripts( array(
|
37 |
+
'backend' => array(
|
38 |
+
'bootstrap/js/bootstrap.min.js' => array( 'jquery' ),
|
39 |
+
'js/datatables.min.js' => array( 'jquery' ),
|
40 |
+
'js/moment.min.js',
|
41 |
+
'js/daterangepicker.js' => array( 'jquery' ),
|
42 |
+
'js/chosen.jquery.min.js' => array( 'jquery' ),
|
43 |
+
),
|
44 |
+
'frontend' => array(
|
45 |
+
'js/spin.min.js' => array( 'jquery' ),
|
46 |
+
'js/ladda.min.js' => array( 'jquery' ),
|
47 |
+
),
|
48 |
+
'module' => array( 'js/payments.js' => array( 'ab-datatables.min.js', 'ab-ng-payment_details_dialog.js' ) ),
|
49 |
+
) );
|
50 |
+
|
51 |
+
wp_localize_script( 'ab-daterangepicker.js', 'BooklyL10n', array(
|
52 |
+
'today' => __( 'Today', 'bookly' ),
|
53 |
+
'yesterday' => __( 'Yesterday', 'bookly' ),
|
54 |
+
'last_7' => __( 'Last 7 Days', 'bookly' ),
|
55 |
+
'last_30' => __( 'Last 30 Days', 'bookly' ),
|
56 |
+
'this_month' => __( 'This Month', 'bookly' ),
|
57 |
+
'last_month' => __( 'Last Month', 'bookly' ),
|
58 |
+
'custom_range' => __( 'Custom Range', 'bookly' ),
|
59 |
+
'apply' => __( 'Apply', 'bookly' ),
|
60 |
+
'cancel' => __( 'Cancel', 'bookly' ),
|
61 |
+
'to' => __( 'To', 'bookly' ),
|
62 |
+
'from' => __( 'From', 'bookly' ),
|
63 |
+
'calendar' => array(
|
64 |
+
'longMonths' => array_values( $wp_locale->month ),
|
65 |
+
'shortMonths' => array_values( $wp_locale->month_abbrev ),
|
66 |
+
'longDays' => array_values( $wp_locale->weekday ),
|
67 |
+
'shortDays' => array_values( $wp_locale->weekday_abbrev ),
|
68 |
+
),
|
69 |
+
'startOfWeek' => (int) get_option( 'start_of_week' ),
|
70 |
+
'mjsDateFormat' => Lib\Utils\DateTime::convertFormat( 'date', Lib\Utils\DateTime::FORMAT_MOMENT_JS ),
|
71 |
+
'zeroRecords' => __( 'No payments for selected period and criteria.', 'bookly' ),
|
72 |
+
'processing' => __( 'Processing...', 'bookly' ),
|
73 |
+
'details' => __( 'Details', 'bookly' ),
|
74 |
+
'are_you_sure' => __( 'Are you sure?', 'bookly' ),
|
75 |
+
) );
|
76 |
+
|
77 |
+
$types = array(
|
78 |
+
Lib\Entities\Payment::TYPE_LOCAL,
|
79 |
+
Lib\Entities\Payment::TYPE_2CHECKOUT,
|
80 |
+
Lib\Entities\Payment::TYPE_PAYPAL,
|
81 |
+
Lib\Entities\Payment::TYPE_AUTHORIZENET,
|
82 |
+
Lib\Entities\Payment::TYPE_STRIPE,
|
83 |
+
Lib\Entities\Payment::TYPE_PAYULATAM,
|
84 |
+
Lib\Entities\Payment::TYPE_PAYSON,
|
85 |
+
Lib\Entities\Payment::TYPE_MOLLIE,
|
86 |
+
Lib\Entities\Payment::TYPE_COUPON,
|
87 |
+
);
|
88 |
+
$providers = Lib\Entities\Staff::query()->select( 'id, full_name' )->sortBy( 'full_name' )->fetchArray();
|
89 |
+
$services = Lib\Entities\Service::query()->select( 'id, title' )->sortBy( 'title' )->fetchArray();
|
90 |
+
|
91 |
+
$this->render( 'index', compact( 'types', 'providers', 'services' ) );
|
92 |
+
}
|
93 |
+
|
94 |
+
/**
|
95 |
+
* Get payments.
|
96 |
+
*/
|
97 |
+
public function executeGetPayments()
|
98 |
+
{
|
99 |
+
$columns = $this->getParameter( 'columns' );
|
100 |
+
$order = $this->getParameter( 'order' );
|
101 |
+
$filter = $this->getParameter( 'filter' );
|
102 |
+
|
103 |
+
$query = Lib\Entities\Payment::query( 'p' )
|
104 |
+
->select( 'p.id, p.created, p.type, p.paid, p.total, p.status, p.details, c.name customer, st.full_name provider, s.title service, a.start_date' )
|
105 |
+
->leftJoin( 'CustomerAppointment', 'ca', 'ca.payment_id = p.id' )
|
106 |
+
->leftJoin( 'Customer', 'c', 'c.id = ca.customer_id' )
|
107 |
+
->leftJoin( 'Appointment', 'a', 'a.id = ca.appointment_id' )
|
108 |
+
->leftJoin( 'Service', 's', 's.id = COALESCE(ca.compound_service_id, a.service_id)' )
|
109 |
+
->leftJoin( 'Staff', 'st', 'st.id = a.staff_id' )
|
110 |
+
->groupBy( 'p.id' );
|
111 |
+
|
112 |
+
// Filters.
|
113 |
+
list ( $start, $end ) = explode( ' - ', $filter['created'], 2 );
|
114 |
+
$end = date( 'Y-m-d', strtotime( '+1 day', strtotime( $end ) ) );
|
115 |
+
|
116 |
+
$query->whereBetween( 'p.created', $start, $end );
|
117 |
+
|
118 |
+
if ( $filter['type'] != -1 ) {
|
119 |
+
$query->where( 'p.type', $filter['type'] );
|
120 |
+
}
|
121 |
+
|
122 |
+
if ( $filter['staff'] != -1 ) {
|
123 |
+
$query->where( 'st.id', $filter['staff'] );
|
124 |
+
}
|
125 |
+
|
126 |
+
if ( $filter['service'] != -1 ) {
|
127 |
+
$query->where( 's.id', $filter['service'] );
|
128 |
+
}
|
129 |
+
|
130 |
+
foreach ( $order as $sort_by ) {
|
131 |
+
$query->sortBy( $columns[ $sort_by['column'] ]['data'] )
|
132 |
+
->order( $sort_by['dir'] == 'desc' ? Lib\Query::ORDER_DESCENDING : Lib\Query::ORDER_ASCENDING );
|
133 |
+
}
|
134 |
+
|
135 |
+
$payments = $query->fetchArray();
|
136 |
+
|
137 |
+
$data = array();
|
138 |
+
$total = 0;
|
139 |
+
foreach ( $payments as $payment ) {
|
140 |
+
$details = json_decode( $payment['details'], true );
|
141 |
+
$multiple = count( $details['items'] ) > 1
|
142 |
+
? ' <span class="glyphicon glyphicon-shopping-cart" title="' . esc_attr( __( 'See details for more items', 'bookly' ) ) . '"></span>'
|
143 |
+
: '' ;
|
144 |
+
|
145 |
+
$paid_title = Lib\Utils\Common::formatPrice( $payment['paid'] );
|
146 |
+
if ( $payment['paid'] != $payment['total'] ) {
|
147 |
+
$paid_title = sprintf( __( '%s of %s', 'bookly' ), $paid_title, Lib\Utils\Common::formatPrice( $payment['total'] ) );
|
148 |
+
}
|
149 |
+
|
150 |
+
$data[] = array(
|
151 |
+
'id' => $payment['id'],
|
152 |
+
'created' => Lib\Utils\DateTime::formatDateTime( $payment['created'] ),
|
153 |
+
'type' => Lib\Entities\Payment::typeToString( $payment['type'] ),
|
154 |
+
'customer' => $payment['customer'] ?: $details['customer'],
|
155 |
+
'provider' => ( $payment['provider'] ?: $details['items'][0]['staff_name'] ) . $multiple,
|
156 |
+
'service' => ( $payment['service'] ?: $details['items'][0]['service_name'] ) . $multiple,
|
157 |
+
'start_date' => ( $payment['start_date']
|
158 |
+
? Lib\Utils\DateTime::formatDateTime( $payment['start_date'] )
|
159 |
+
: Lib\Utils\DateTime::formatDateTime( $details['items'][0]['appointment_date'] ) ) . $multiple,
|
160 |
+
'paid' => $paid_title,
|
161 |
+
'status' => Lib\Entities\Payment::statusToString( $payment['status'] ),
|
162 |
+
|
163 |
+
);
|
164 |
+
|
165 |
+
$total += $payment['paid'];
|
166 |
+
}
|
167 |
+
|
168 |
+
wp_send_json( array(
|
169 |
+
'draw' => ( int ) $this->getParameter( 'draw' ),
|
170 |
+
'recordsTotal' => count( $data ),
|
171 |
+
'recordsFiltered' => count( $data ),
|
172 |
+
'data' => $data,
|
173 |
+
'total' => Lib\Utils\Common::formatPrice( $total ),
|
174 |
+
) );
|
175 |
+
}
|
176 |
+
|
177 |
+
/**
|
178 |
+
* Get payment details.
|
179 |
+
*
|
180 |
+
* @throws \Exception
|
181 |
+
*/
|
182 |
+
public function executeGetPaymentDetails()
|
183 |
+
{
|
184 |
+
$data = array();
|
185 |
+
$payment = Lib\Entities\Payment::query( 'p' )
|
186 |
+
->select( 'p.total,
|
187 |
+
p.status,
|
188 |
+
p.created AS created,
|
189 |
+
p.type,
|
190 |
+
p.details,
|
191 |
+
p.paid,
|
192 |
+
c.name AS customer' )
|
193 |
+
->leftJoin( 'CustomerAppointment', 'ca', 'ca.payment_id = p.id' )
|
194 |
+
->leftJoin( 'Customer', 'c', 'c.id = ca.customer_id' )
|
195 |
+
->where( 'p.id', $this->getParameter( 'payment_id' ) )
|
196 |
+
->fetchRow();
|
197 |
+
if ( $payment ) {
|
198 |
+
$details = json_decode( $payment['details'], true );
|
199 |
+
$data = array(
|
200 |
+
'payment' => array(
|
201 |
+
'status' => $payment['status'],
|
202 |
+
'type' => $payment['type'],
|
203 |
+
'coupon' => $details['coupon'],
|
204 |
+
'created' => $payment['created'],
|
205 |
+
'customer' => empty ( $payment['customer'] ) ? $details['customer'] : $payment['customer'],
|
206 |
+
'total' => $payment['total'],
|
207 |
+
'paid' => $payment['paid'],
|
208 |
+
),
|
209 |
+
'items' => $details['items'],
|
210 |
+
'deposit_enabled' => Lib\Config::depositEnabled()
|
211 |
+
);
|
212 |
+
}
|
213 |
+
|
214 |
+
wp_send_json_success( array( 'html' => $this->render( 'details', $data, false ) ) );
|
215 |
+
}
|
216 |
+
|
217 |
+
/**
|
218 |
+
* Delete payments.
|
219 |
+
*/
|
220 |
+
public function executeDeletePayments()
|
221 |
+
{
|
222 |
+
$payment_ids = array_map( 'intval', $this->getParameter( 'data', array() ) );
|
223 |
+
Lib\Entities\Payment::query()->delete()->whereIn( 'id', $payment_ids )->execute();
|
224 |
+
wp_send_json_success();
|
225 |
+
}
|
226 |
+
|
227 |
+
/**
|
228 |
+
* Complete payment.
|
229 |
+
*/
|
230 |
+
public function executeCompletePayment()
|
231 |
+
{
|
232 |
+
$payment = Lib\Entities\Payment::find( $this->getParameter( 'payment_id' ) );
|
233 |
+
$payment
|
234 |
+
->set( 'paid', $payment->get( 'total' ) )
|
235 |
+
->set( 'status', Lib\Entities\Payment::STATUS_COMPLETED )
|
236 |
+
->save();
|
237 |
+
|
238 |
+
$payment_title = Lib\Utils\Common::formatPrice( $payment->get( 'paid' ) );
|
239 |
+
if ( $payment->get( 'paid' ) != $payment->get( 'total' ) ) {
|
240 |
+
$payment_title = sprintf( __( '%s of %s', 'bookly' ), $payment_title, Lib\Utils\Common::formatPrice( $payment->get( 'total' ) ) );
|
241 |
+
}
|
242 |
+
$payment_title .= sprintf(
|
243 |
+
' %s <span%s>%s</span>',
|
244 |
+
Lib\Entities\Payment::typeToString( $payment->get( 'type' ) ),
|
245 |
+
$payment->get( 'status' ) == Lib\Entities\Payment::STATUS_PENDING ? ' class="text-danger"' : '',
|
246 |
+
Lib\Entities\Payment::statusToString( $payment->get( 'status' ) )
|
247 |
+
);
|
248 |
+
|
249 |
+
wp_send_json_success( array( 'payment_title' => $payment_title ) );
|
250 |
+
}
|
251 |
+
|
252 |
+
/**
|
253 |
+
* Override parent method to add 'wp_ajax_ab_' prefix
|
254 |
+
* so current 'execute*' methods look nicer.
|
255 |
+
*
|
256 |
+
* @param string $prefix
|
257 |
+
*/
|
258 |
+
protected function registerWpActions( $prefix = '' )
|
259 |
+
{
|
260 |
+
parent::registerWpActions( 'wp_ajax_ab_' );
|
261 |
+
}
|
262 |
+
|
263 |
+
}
|
backend/modules/payments/resources/js/ng-payment_details_dialog.js
ADDED
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
;(function() {
|
2 |
+
|
3 |
+
angular.module('paymentDetailsDialog', []).directive('paymentDetailsDialog', function() {
|
4 |
+
return {
|
5 |
+
restrict: 'A',
|
6 |
+
replace: true,
|
7 |
+
scope: {
|
8 |
+
callback: '&paymentDetailsDialog'
|
9 |
+
},
|
10 |
+
templateUrl: 'bookly-payment-details-dialog.tpl',
|
11 |
+
// The linking function will add behavior to the template.
|
12 |
+
link: function (scope, element, attrs) {
|
13 |
+
var $body = element.find('.modal-body'),
|
14 |
+
spinner = $body.html();
|
15 |
+
|
16 |
+
element
|
17 |
+
.on('show.bs.modal', function (e, payment_id) {
|
18 |
+
if (payment_id === undefined) {
|
19 |
+
payment_id = jQuery(e.relatedTarget).data('payment_id');
|
20 |
+
}
|
21 |
+
jQuery.ajax({
|
22 |
+
url: ajaxurl,
|
23 |
+
data: {action: 'ab_get_payment_details', payment_id: payment_id},
|
24 |
+
dataType: 'json',
|
25 |
+
success: function (response) {
|
26 |
+
if (response.success) {
|
27 |
+
$body.html(response.data.html);
|
28 |
+
$body.find('#bookly-complete-payment').on('click',function () {
|
29 |
+
var ladda = Ladda.create(this);
|
30 |
+
ladda.start();
|
31 |
+
jQuery.ajax({
|
32 |
+
url: ajaxurl,
|
33 |
+
data: {action: 'ab_complete_payment', payment_id: payment_id},
|
34 |
+
dataType: 'json',
|
35 |
+
type: 'POST',
|
36 |
+
success: function (response) {
|
37 |
+
if (response.success) {
|
38 |
+
element.trigger('show.bs.modal', [payment_id]);
|
39 |
+
if (scope.callback) {
|
40 |
+
scope.$apply(function ($scope) {
|
41 |
+
$scope.callback({
|
42 |
+
payment_id : payment_id,
|
43 |
+
payment_title : response.data.payment_title
|
44 |
+
});
|
45 |
+
});
|
46 |
+
}
|
47 |
+
// Reload DataTable.
|
48 |
+
var $table = jQuery(e.relatedTarget).closest('table.dataTable');
|
49 |
+
if ($table.length) {
|
50 |
+
$table.DataTable().ajax.reload();
|
51 |
+
}
|
52 |
+
}
|
53 |
+
}
|
54 |
+
});
|
55 |
+
});
|
56 |
+
}
|
57 |
+
}
|
58 |
+
});
|
59 |
+
})
|
60 |
+
.on('hidden.bs.modal', function () {
|
61 |
+
$body.html(spinner);
|
62 |
+
if ((jQuery("#bookly-appointment-dialog").data('bs.modal') || {isShown: false}).isShown) {
|
63 |
+
jQuery('body').addClass('modal-open');
|
64 |
+
}
|
65 |
+
});
|
66 |
+
}
|
67 |
+
}
|
68 |
+
});
|
69 |
+
})();
|
backend/modules/payments/resources/js/payments.js
ADDED
@@ -0,0 +1,184 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
jQuery(function($) {
|
2 |
+
|
3 |
+
var
|
4 |
+
$payments_list = $('#bookly-payments-list'),
|
5 |
+
$check_all_button = $('#bookly-check-all'),
|
6 |
+
$date_filter = $('#bookly-filter-date'),
|
7 |
+
$type_filter = $('#bookly-filter-type'),
|
8 |
+
$staff_filter = $('#bookly-filter-staff'),
|
9 |
+
$service_filter = $('#bookly-filter-service'),
|
10 |
+
$payment_total = $('#bookly-payment-total'),
|
11 |
+
$delete_button = $('#bookly-delete')
|
12 |
+
;
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Init DataTables.
|
16 |
+
*/
|
17 |
+
var dt = $payments_list.DataTable({
|
18 |
+
order: [[ 0, 'asc' ]],
|
19 |
+
paging: false,
|
20 |
+
info: false,
|
21 |
+
searching: false,
|
22 |
+
processing: true,
|
23 |
+
responsive: true,
|
24 |
+
serverSide: true,
|
25 |
+
ajax: {
|
26 |
+
url: ajaxurl,
|
27 |
+
data: function ( d ) {
|
28 |
+
return $.extend( {}, d, {
|
29 |
+
action: 'ab_get_payments',
|
30 |
+
filter: {
|
31 |
+
created: $date_filter.data('date'),
|
32 |
+
type: $type_filter.val(),
|
33 |
+
staff: $staff_filter.val(),
|
34 |
+
service: $service_filter.val()
|
35 |
+
}
|
36 |
+
} );
|
37 |
+
},
|
38 |
+
dataSrc: function (json) {
|
39 |
+
$payment_total.html(json.total);
|
40 |
+
|
41 |
+
return json.data;
|
42 |
+
}
|
43 |
+
},
|
44 |
+
columns: [
|
45 |
+
{ data: 'created' },
|
46 |
+
{ data: 'type' },
|
47 |
+
{ data: 'customer' },
|
48 |
+
{ data: 'provider' },
|
49 |
+
{ data: 'service' },
|
50 |
+
{ data: 'start_date' },
|
51 |
+
{ data: 'paid' },
|
52 |
+
{ data: 'status' },
|
53 |
+
{
|
54 |
+
responsivePriority: 1,
|
55 |
+
orderable: false,
|
56 |
+
searchable: false,
|
57 |
+
render: function ( data, type, row, meta ) {
|
58 |
+
return '<button type="button" class="btn btn-default" data-toggle="modal" data-target="#bookly-payment-details-modal" data-payment_id="' + row.id + '"><i class="glyphicon glyphicon-list-alt"></i> ' + BooklyL10n.details + '</a>';
|
59 |
+
}
|
60 |
+
},
|
61 |
+
{
|
62 |
+
responsivePriority: 1,
|
63 |
+
orderable: false,
|
64 |
+
searchable: false,
|
65 |
+
render: function ( data, type, row, meta ) {
|
66 |
+
return '<input type="checkbox" value="' + row.id + '">';
|
67 |
+
}
|
68 |
+
}
|
69 |
+
],
|
70 |
+
language: {
|
71 |
+
zeroRecords: BooklyL10n.zeroRecords,
|
72 |
+
processing: BooklyL10n.processing
|
73 |
+
}
|
74 |
+
});
|
75 |
+
|
76 |
+
/**
|
77 |
+
* Select all coupons.
|
78 |
+
*/
|
79 |
+
$check_all_button.on('change', function () {
|
80 |
+
$payments_list.find('tbody input:checkbox').prop('checked', this.checked);
|
81 |
+
});
|
82 |
+
|
83 |
+
/**
|
84 |
+
* On coupon select.
|
85 |
+
*/
|
86 |
+
$payments_list.on('change', 'tbody input:checkbox', function () {
|
87 |
+
$check_all_button.prop('checked', $payments_list.find('tbody input:not(:checked)').length == 0);
|
88 |
+
});
|
89 |
+
/**
|
90 |
+
* Init date range picker.
|
91 |
+
*/
|
92 |
+
moment.locale('en', {
|
93 |
+
months: BooklyL10n.calendar.longMonths,
|
94 |
+
monthsShort: BooklyL10n.calendar.shortMonths,
|
95 |
+
weekdays: BooklyL10n.calendar.longDays,
|
96 |
+
weekdaysShort: BooklyL10n.calendar.shortDays,
|
97 |
+
weekdaysMin: BooklyL10n.calendar.shortDays
|
98 |
+
});
|
99 |
+
|
100 |
+
var picker_ranges = {};
|
101 |
+
picker_ranges[BooklyL10n.yesterday] = [moment().subtract(1, 'days'), moment().subtract(1, 'days')];
|
102 |
+
picker_ranges[BooklyL10n.today] = [moment(), moment()];
|
103 |
+
picker_ranges[BooklyL10n.last_7] = [moment().subtract(7, 'days'), moment()];
|
104 |
+
picker_ranges[BooklyL10n.last_30] = [moment().subtract(30, 'days'), moment()];
|
105 |
+
picker_ranges[BooklyL10n.this_month] = [moment().startOf('month'), moment().endOf('month')];
|
106 |
+
picker_ranges[BooklyL10n.last_month] = [moment().subtract(1, 'month').startOf('month'), moment().subtract(1, 'month').endOf('month')];
|
107 |
+
|
108 |
+
$date_filter.daterangepicker(
|
109 |
+
{
|
110 |
+
parentEl: $date_filter.parent(),
|
111 |
+
startDate: moment().subtract(30, 'days'), // by default selected is "Last 30 days"
|
112 |
+
ranges: picker_ranges,
|
113 |
+
locale: {
|
114 |
+
applyLabel: BooklyL10n.apply,
|
115 |
+
cancelLabel: BooklyL10n.cancel,
|
116 |
+
fromLabel: BooklyL10n.from,
|
117 |
+
toLabel: BooklyL10n.to,
|
118 |
+
customRangeLabel: BooklyL10n.custom_range,
|
119 |
+
daysOfWeek: BooklyL10n.calendar.shortDays,
|
120 |
+
monthNames: BooklyL10n.calendar.longMonths,
|
121 |
+
firstDay: parseInt(BooklyL10n.startOfWeek),
|
122 |
+
format: BooklyL10n.mjsDateFormat
|
123 |
+
}
|
124 |
+
},
|
125 |
+
function(start, end) {
|
126 |
+
var format = 'YYYY-MM-DD';
|
127 |
+
$date_filter
|
128 |
+
.data('date', start.format(format) + ' - ' + end.format(format))
|
129 |
+
.find('span')
|
130 |
+
.html(start.format(BooklyL10n.mjsDateFormat) + ' - ' + end.format(BooklyL10n.mjsDateFormat));
|
131 |
+
}
|
132 |
+
);
|
133 |
+
|
134 |
+
/**
|
135 |
+
* On filters change.
|
136 |
+
*/
|
137 |
+
$('.bookly-js-chosen-select').chosen({
|
138 |
+
allow_single_deselect: true,
|
139 |
+
disable_search_threshold: 10
|
140 |
+
});
|
141 |
+
$date_filter.on('apply.daterangepicker', function () { dt.ajax.reload(); });
|
142 |
+
$type_filter.on('change', function () { dt.ajax.reload(); });
|
143 |
+
$staff_filter.on('change', function () { dt.ajax.reload(); });
|
144 |
+
$service_filter.on('change', function () { dt.ajax.reload(); });
|
145 |
+
|
146 |
+
/**
|
147 |
+
* Delete payments.
|
148 |
+
*/
|
149 |
+
$delete_button.on('click', function () {
|
150 |
+
if (confirm(BooklyL10n.are_you_sure)) {
|
151 |
+
var ladda = Ladda.create(this);
|
152 |
+
ladda.start();
|
153 |
+
|
154 |
+
var data = [];
|
155 |
+
var $checkboxes = $payments_list.find('tbody input:checked');
|
156 |
+
$checkboxes.each(function () {
|
157 |
+
data.push(this.value);
|
158 |
+
});
|
159 |
+
|
160 |
+
$.ajax({
|
161 |
+
url : ajaxurl,
|
162 |
+
type : 'POST',
|
163 |
+
data : {
|
164 |
+
action : 'ab_delete_payments',
|
165 |
+
data : data
|
166 |
+
},
|
167 |
+
dataType : 'json',
|
168 |
+
success : function(response) {
|
169 |
+
ladda.stop();
|
170 |
+
if (response.success) {
|
171 |
+
dt.rows($checkboxes.closest('td')).remove().draw();
|
172 |
+
} else {
|
173 |
+
alert(response.data.message);
|
174 |
+
}
|
175 |
+
}
|
176 |
+
});
|
177 |
+
}
|
178 |
+
});
|
179 |
+
});
|
180 |
+
|
181 |
+
(function() {
|
182 |
+
var module = angular.module('paymentDetails', ['paymentDetailsDialog']);
|
183 |
+
module.controller('paymentDetailsCtrl', function($scope) {});
|
184 |
+
})();
|
backend/modules/payments/templates/_payment_details_dialog.php
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
|
2 |
+
<script type="text/ng-template" id="bookly-payment-details-dialog.tpl">
|
3 |
+
<div class="modal fade" id="bookly-payment-details-modal" tabindex="-1" role="dialog">
|
4 |
+
<div class="modal-dialog" role="document">
|
5 |
+
<div class="modal-content">
|
6 |
+
<div class="modal-header">
|
7 |
+
<button type="button" class="close" data-dismiss="modal" aria-label="<?php esc_attr_e( 'Close', 'bookly' ) ?>"><span aria-hidden="true">×</span></button>
|
8 |
+
<div class="modal-title h2"><?php _e( 'Payment', 'bookly' ) ?></div>
|
9 |
+
</div>
|
10 |
+
<div class="modal-body">
|
11 |
+
<div class="bookly-loading"></div>
|
12 |
+
</div>
|
13 |
+
<div class="modal-footer">
|
14 |
+
<button type="button" class="btn btn-default" data-dismiss="modal">
|
15 |
+
<?php _e( 'Close', 'bookly' ) ?>
|
16 |
+
</button>
|
17 |
+
</div>
|
18 |
+
</div>
|
19 |
+
</div>
|
20 |
+
</div>
|
21 |
+
</script>
|
backend/modules/payments/templates/details.php
ADDED
@@ -0,0 +1,132 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
+
$subtotal = 0;
|
3 |
+
$subtotal_deposit = 0;
|
4 |
+
?>
|
5 |
+
<?php if ( $payment ) : ?>
|
6 |
+
<div class="table-responsive">
|
7 |
+
<table class="table table-bordered">
|
8 |
+
<thead>
|
9 |
+
<tr>
|
10 |
+
<th width="50%"><?php _e( 'Customer', 'bookly' ) ?></th>
|
11 |
+
<th width="50%"><?php _e( 'Payment', 'bookly' ) ?></th>
|
12 |
+
</tr>
|
13 |
+
</thead>
|
14 |
+
<tbody>
|
15 |
+
<tr>
|
16 |
+
<td><?php echo $payment['customer'] ?></td>
|
17 |
+
<td>
|
18 |
+
<div><?php _e( 'Date', 'bookly' ) ?>: <?php echo \BooklyLite\Lib\Utils\DateTime::formatDateTime( $payment['created'] ) ?></div>
|
19 |
+
<div><?php _e( 'Type', 'bookly' ) ?>: <?php echo \BooklyLite\Lib\Entities\Payment::typeToString( $payment['type'] ) ?></div>
|
20 |
+
<div><?php _e( 'Status', 'bookly' ) ?>: <?php echo \BooklyLite\Lib\Entities\Payment::statusToString( $payment['status'] ) ?></div>
|
21 |
+
</td>
|
22 |
+
</tr>
|
23 |
+
</tbody>
|
24 |
+
</table>
|
25 |
+
</div>
|
26 |
+
|
27 |
+
<div class="table-responsive">
|
28 |
+
<table class="table table-bordered">
|
29 |
+
<thead>
|
30 |
+
<tr>
|
31 |
+
<th><?php _e( 'Service', 'bookly' ) ?></th>
|
32 |
+
<th><?php _e( 'Date', 'bookly' ) ?></th>
|
33 |
+
<th><?php _e( 'Provider', 'bookly' ) ?></th>
|
34 |
+
<?php if ( $deposit_enabled ): ?>
|
35 |
+
<th class="text-right"><?php _e( 'Deposit', 'bookly' ) ?></th>
|
36 |
+
<?php endif ?>
|
37 |
+
<th class="text-right"><?php _e( 'Price', 'bookly' ) ?></th>
|
38 |
+
</tr>
|
39 |
+
</thead>
|
40 |
+
<tbody>
|
41 |
+
<?php foreach ( $items as $item ) :
|
42 |
+
$extras_price = 0; ?>
|
43 |
+
<tr>
|
44 |
+
<td>
|
45 |
+
<?php if ( $item['number_of_persons'] > 1 ) echo $item['number_of_persons'] . ' × ' ?><?php echo $item['service_name'] ?>
|
46 |
+
<?php if ( ! empty ( $item['extras'] ) ) : ?>
|
47 |
+
<ul class="bookly-list list-dots">
|
48 |
+
<?php foreach ( $item['extras'] as $extra ) : ?>
|
49 |
+
<li><?php if ( $extra['quantity'] > 1 ) echo $extra['quantity'] . ' × ' ?><?php echo $extra['title'] ?></li>
|
50 |
+
<?php $extras_price += $extra['price'] * $extra['quantity'] ?>
|
51 |
+
<?php endforeach ?>
|
52 |
+
</ul>
|
53 |
+
<?php endif ?>
|
54 |
+
</td>
|
55 |
+
<td><?php echo \BooklyLite\Lib\Utils\DateTime::formatDateTime( $item['appointment_date'] ) ?></td>
|
56 |
+
<td><?php echo $item['staff_name'] ?></td>
|
57 |
+
<?php $deposit = apply_filters( 'bookly_deposit_get_deposit_amount', $item['number_of_persons'] * ( $item['service_price'] + $extras_price ), $item['deposit'], $item['number_of_persons'] ) ?>
|
58 |
+
<?php if ( $deposit_enabled ) : ?>
|
59 |
+
<td class="text-right"><?php echo apply_filters( 'bookly_deposit_format_deposit', $deposit, $item['deposit'] ) ?></td>
|
60 |
+
<?php endif ?>
|
61 |
+
<td class="text-right">
|
62 |
+
<?php $service_price = \BooklyLite\Lib\Utils\Common::formatPrice( $item['service_price'] ) ?>
|
63 |
+
<?php if ( $item['number_of_persons'] > 1 ) $service_price = $item['number_of_persons'] . ' × ' . $service_price ?>
|
64 |
+
<?php echo $service_price ?>
|
65 |
+
<ul class="bookly-list">
|
66 |
+
<?php foreach ( $item['extras'] as $extra ) : ?>
|
67 |
+
<li>
|
68 |
+
<?php printf( '%s%s%s',
|
69 |
+
( $item['number_of_persons'] > 1 ) ? $item['number_of_persons'] . ' × ' : '',
|
70 |
+
( $extra['quantity'] > 1 ) ? $extra['quantity'] . ' × ' : '',
|
71 |
+
\BooklyLite\Lib\Utils\Common::formatPrice( $extra['price'] )
|
72 |
+
) ?>
|
73 |
+
</li>
|
74 |
+
<?php $subtotal += $item['number_of_persons'] * $extra['price'] * $extra['quantity'] ?>
|
75 |
+
<?php endforeach ?>
|
76 |
+
</ul>
|
77 |
+
</td>
|
78 |
+
</tr>
|
79 |
+
<?php $subtotal += $item['number_of_persons'] * $item['service_price'] ?>
|
80 |
+
<?php $subtotal_deposit += $deposit ?>
|
81 |
+
<?php endforeach ?>
|
82 |
+
</tbody>
|
83 |
+
<tfoot>
|
84 |
+
<tr>
|
85 |
+
<th rowspan="3" style="border-left-color: white; border-bottom-color: white;"></th>
|
86 |
+
<th colspan="2"><?php _e( 'Subtotal', 'bookly' ) ?></th>
|
87 |
+
<?php if ( $deposit_enabled ) : ?>
|
88 |
+
<th class="text-right"><?php echo \BooklyLite\Lib\Utils\Common::formatPrice( $subtotal_deposit ) ?></th>
|
89 |
+
<?php endif ?>
|
90 |
+
<th class="text-right"><?php echo \BooklyLite\Lib\Utils\Common::formatPrice( $subtotal ) ?></th>
|
91 |
+
</tr>
|
92 |
+
<tr>
|
93 |
+
<th colspan="<?php echo 2 + (int) $deposit_enabled ?>">
|
94 |
+
<?php _e( 'Discount', 'bookly' ) ?>
|
95 |
+
<?php if ( $payment['coupon'] ) : ?><div><small>(<?php echo $payment['coupon']['code'] ?>)</small></div><?php endif ?>
|
96 |
+
</th>
|
97 |
+
<th class="text-right">
|
98 |
+
<?php if ( $payment['coupon'] ) : ?>
|
99 |
+
<?php if ( $payment['coupon']['discount'] ) : ?>
|
100 |
+
<div>-<?php echo $payment['coupon']['discount'] ?>%</div>
|
101 |
+
<?php endif ?>
|
102 |
+
<?php if ( $payment['coupon']['deduction'] ) : ?>
|
103 |
+
<div><?php echo \BooklyLite\Lib\Utils\Common::formatPrice( - $payment['coupon']['deduction'] ) ?></div>
|
104 |
+
<?php endif ?>
|
105 |
+
<?php else : ?>
|
106 |
+
<?php echo \BooklyLite\Lib\Utils\Common::formatPrice( 0 ) ?>
|
107 |
+
<?php endif ?>
|
108 |
+
</th>
|
109 |
+
</tr>
|
110 |
+
<tr>
|
111 |
+
<th colspan="<?php echo 2 + (int) $deposit_enabled ?>"><?php _e( 'Total', 'bookly' ) ?></th>
|
112 |
+
<th class="text-right"><?php echo \BooklyLite\Lib\Utils\Common::formatPrice( $payment['total'] ) ?></th>
|
113 |
+
</tr>
|
114 |
+
<?php if ( $payment['total'] != $payment['paid'] ) : ?>
|
115 |
+
<tr>
|
116 |
+
<td rowspan="2" style="border-left-color:#fff;border-bottom-color:#fff;"></td>
|
117 |
+
<td colspan="<?php echo 2 + (int) $deposit_enabled ?>"><i><?php _e( 'Paid', 'bookly' ) ?></i></td>
|
118 |
+
<td class="text-right"><i><?php echo \BooklyLite\Lib\Utils\Common::formatPrice( $payment['paid'] ) ?></i></td>
|
119 |
+
</tr>
|
120 |
+
<tr>
|
121 |
+
<td colspan="<?php echo 2 + (int) $deposit_enabled ?>"><i><?php _e( 'Due', 'bookly' ) ?></i></td>
|
122 |
+
<td class="text-right"><i><?php echo \BooklyLite\Lib\Utils\Common::formatPrice( $payment['total'] - $payment['paid'] ) ?></i></td>
|
123 |
+
</tr>
|
124 |
+
<tr>
|
125 |
+
<td style="border-left-color:#fff;border-bottom-color:#fff;"></td>
|
126 |
+
<td colspan="<?php echo 3 + (int) $deposit_enabled ?>" class="text-right"><button type="button" class="btn btn-success ladda-button" id="bookly-complete-payment" data-spinner-size="40" data-style="zoom-in"><i><?php _e( 'Complete payment', 'bookly' ) ?></i></button></td>
|
127 |
+
</tr>
|
128 |
+
<?php endif ?>
|
129 |
+
</tfoot>
|
130 |
+
</table>
|
131 |
+
</div>
|
132 |
+
<?php endif ?>
|
backend/modules/payments/templates/index.php
ADDED
@@ -0,0 +1,89 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
|
2 |
+
<div id="bookly-tbs" class="wrap">
|
3 |
+
<div class="bookly-tbs-body">
|
4 |
+
<div class="page-header text-right clearfix">
|
5 |
+
<div class="bookly-page-title">
|
6 |
+
<?php _e( 'Payments', 'bookly' ) ?>
|
7 |
+
</div>
|
8 |
+
</div>
|
9 |
+
<div class="panel panel-default bookly-main">
|
10 |
+
<div class="panel-body">
|
11 |
+
<div class="row">
|
12 |
+
<div class="col-md-4 col-lg-3">
|
13 |
+
<div class="bookly-margin-bottom-lg bookly-relative">
|
14 |
+
<button type="button" class="btn btn-block btn-default" id="bookly-filter-date" data-date="<?php echo date( 'Y-m-d', strtotime( '-30 day' ) ) ?> - <?php echo date( 'Y-m-d' ) ?>">
|
15 |
+
<i class="dashicons dashicons-calendar-alt"></i>
|
16 |
+
<span>
|
17 |
+
<?php echo date_i18n( get_option( 'date_format' ), strtotime( '-30 day' ) ) ?> - <?php echo date_i18n( get_option( 'date_format' ) ) ?>
|
18 |
+
</span>
|
19 |
+
</button>
|
20 |
+
</div>
|
21 |
+
</div>
|
22 |
+
<div class="col-md-2 col-lg-2">
|
23 |
+
<div class="form-group">
|
24 |
+
<select id="bookly-filter-type" class="form-control bookly-js-chosen-select" data-placeholder="<?php esc_attr_e( 'Type', 'bookly' ) ?>">
|
25 |
+
<option value="-1"></option>
|
26 |
+
<?php foreach ( $types as $type ) : ?>
|
27 |
+
<option value="<?php echo esc_attr( $type ) ?>">
|
28 |
+
<?php echo \BooklyLite\Lib\Entities\Payment::typeToString( $type ) ?>
|
29 |
+
</option>
|
30 |
+
<?php endforeach ?>
|
31 |
+
</select>
|
32 |
+
</div>
|
33 |
+
</div>
|
34 |
+
<div class="col-md-3 col-lg-2">
|
35 |
+
<div class="form-group">
|
36 |
+
<select id="bookly-filter-staff" class="form-control bookly-js-chosen-select" data-placeholder="<?php esc_attr_e( 'Provider', 'bookly' ) ?>">
|
37 |
+
<option value="-1"></option>
|
38 |
+
<?php foreach ( $providers as $provider ) : ?>
|
39 |
+
<option value="<?php echo $provider['id'] ?>"><?php echo esc_html( $provider['full_name'] ) ?></option>
|
40 |
+
<?php endforeach ?>
|
41 |
+
</select>
|
42 |
+
</div>
|
43 |
+
</div>
|
44 |
+
<div class="col-md-3 col-lg-2">
|
45 |
+
<div class="form-group">
|
46 |
+
<select id="bookly-filter-service" class="form-control bookly-js-chosen-select" data-placeholder="<?php esc_attr_e( 'Service', 'bookly' ) ?>">
|
47 |
+
<option value="-1"></option>
|
48 |
+
<?php foreach ( $services as $service ) : ?>
|
49 |
+
<option value="<?php echo $service['id'] ?>"><?php echo esc_html( $service['title'] ) ?></option>
|
50 |
+
<?php endforeach ?>
|
51 |
+
</select>
|
52 |
+
</div>
|
53 |
+
</div>
|
54 |
+
</div>
|
55 |
+
|
56 |
+
<table id="bookly-payments-list" class="table table-striped" width="100%">
|
57 |
+
<thead>
|
58 |
+
<tr>
|
59 |
+
<th><?php _e( 'Date', 'bookly' ) ?></th>
|
60 |
+
<th><?php _e( 'Type', 'bookly' ) ?></th>
|
61 |
+
<th><?php _e( 'Customer', 'bookly' ) ?></th>
|
62 |
+
<th><?php _e( 'Provider', 'bookly' ) ?></th>
|
63 |
+
<th><?php _e( 'Service', 'bookly' ) ?></th>
|
64 |
+
<th><?php _e( 'Appointment Date', 'bookly' ) ?></th>
|
65 |
+
<th><?php _e( 'Amount', 'bookly' ) ?></th>
|
66 |
+
<th><?php _e( 'Status', 'bookly' ) ?></th>
|
67 |
+
<th></th>
|
68 |
+
<th width="16"><input type="checkbox" id="bookly-check-all"></th>
|
69 |
+
</tr>
|
70 |
+
</thead>
|
71 |
+
<tfoot>
|
72 |
+
<tr>
|
73 |
+
<th colspan="6"><div class="pull-right"><?php _e( 'Total', 'bookly' ) ?>:</div></th>
|
74 |
+
<th colspan="4"><span id="bookly-payment-total"></span></th>
|
75 |
+
</tr>
|
76 |
+
</tfoot>
|
77 |
+
</table>
|
78 |
+
<div class="text-right bookly-margin-top-lg">
|
79 |
+
<?php \BooklyLite\Lib\Utils\Common::deleteButton() ?>
|
80 |
+
</div>
|
81 |
+
</div>
|
82 |
+
</div>
|
83 |
+
|
84 |
+
<div ng-app="paymentDetails" ng-controller="paymentDetailsCtrl">
|
85 |
+
<div payment-details-dialog></div>
|
86 |
+
<?php \BooklyLite\Backend\Modules\Payments\Components::getInstance()->renderPaymentDetailsDialog() ?>
|
87 |
+
</div>
|
88 |
+
</div>
|
89 |
+
</div>
|
backend/modules/service/AB_ServiceController.php
DELETED
@@ -1,282 +0,0 @@
|
|
1 |
-
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Class AB_ServiceController
|
5 |
-
*/
|
6 |
-
class AB_ServiceController extends AB_Controller {
|
7 |
-
|
8 |
-
const page_slug = 'ab-services';
|
9 |
-
/**
|
10 |
-
* Index page.
|
11 |
-
*/
|
12 |
-
public function index()
|
13 |
-
{
|
14 |
-
$this->enqueueStyles( array(
|
15 |
-
'wp' => array(
|
16 |
-
'wp-color-picker',
|
17 |
-
),
|
18 |
-
'frontend' => array(
|
19 |
-
'css/ladda.min.css',
|
20 |
-
),
|
21 |
-
'backend' => array(
|
22 |
-
'css/bookly.main-backend.css',
|
23 |
-
'bootstrap/css/bootstrap.min.css',
|
24 |
-
),
|
25 |
-
'module' => array(
|
26 |
-
'css/service.css',
|
27 |
-
)
|
28 |
-
) );
|
29 |
-
|
30 |
-
$this->enqueueScripts( array(
|
31 |
-
'wp' => array(
|
32 |
-
'wp-color-picker',
|
33 |
-
),
|
34 |
-
'backend' => array(
|
35 |
-
'js/ab_popup.js' => array( 'jquery' ),
|
36 |
-
'bootstrap/js/bootstrap.min.js' => array( 'jquery' ),
|
37 |
-
),
|
38 |
-
'module' => array(
|
39 |
-
'js/service.js' => array( 'jquery-ui-sortable', 'jquery' ),
|
40 |
-
),
|
41 |
-
'frontend' => array(
|
42 |
-
'js/spin.min.js' => array( 'jquery' ),
|
43 |
-
'js/ladda.min.js' => array( 'ab-spin.min.js', 'jquery' ),
|
44 |
-
)
|
45 |
-
) );
|
46 |
-
|
47 |
-
wp_localize_script( 'ab-service.js', 'BooklyL10n', array(
|
48 |
-
'are_you_sure' => __( 'Are you sure?', 'bookly' ),
|
49 |
-
'no_staff_selected' => __( 'No staff selected', 'bookly' ),
|
50 |
-
'please_select_at_least_one_service' => __( 'Please select at least one service.', 'bookly' ),
|
51 |
-
) );
|
52 |
-
|
53 |
-
$this->staff_collection = $this->getStaffCollection();
|
54 |
-
$this->category_collection = $this->getCategoryCollection();
|
55 |
-
$this->service_collection = $this->getServiceCollection();
|
56 |
-
$this->render( 'index' );
|
57 |
-
}
|
58 |
-
|
59 |
-
/**
|
60 |
-
*
|
61 |
-
*/
|
62 |
-
public function executeCategoryServices()
|
63 |
-
{
|
64 |
-
$this->setDataForServiceList();
|
65 |
-
$this->render( '_list' );
|
66 |
-
exit;
|
67 |
-
}
|
68 |
-
|
69 |
-
/**
|
70 |
-
*
|
71 |
-
*/
|
72 |
-
public function executeCategoryForm()
|
73 |
-
{
|
74 |
-
$this->form = new AB_CategoryForm();
|
75 |
-
|
76 |
-
if ( ! empty ( $_POST ) ) {
|
77 |
-
$this->form->bind( $this->getPostParameters() );
|
78 |
-
if ( $category = $this->form->save() ) {
|
79 |
-
echo "<li class='ab-category-item' data-id='{$category->id}'>
|
80 |
-
<span class='ab-handle'><i class='ab-inner-handle glyphicon glyphicon-move'></i></span>
|
81 |
-
<span class='left displayed-value'>{$category->name}</span>
|
82 |
-
<a href='#' class='left ab-hidden ab-edit'></a>
|
83 |
-
<input class=value type=text name=name value='{$category->name}' style='display: none' />
|
84 |
-
<a href='#' class='left ab-hidden ab-delete'></a></li>";
|
85 |
-
// Register string for translate in WPML.
|
86 |
-
do_action( 'wpml_register_single_string', 'bookly', 'category_' . $category->id, $category->name );
|
87 |
-
exit;
|
88 |
-
}
|
89 |
-
}
|
90 |
-
exit;
|
91 |
-
}
|
92 |
-
|
93 |
-
/**
|
94 |
-
* Update category.
|
95 |
-
*/
|
96 |
-
public function executeUpdateCategory()
|
97 |
-
{
|
98 |
-
$form = new AB_CategoryForm();
|
99 |
-
$form->bind( $this->getPostParameters() );
|
100 |
-
$category = $form->save();
|
101 |
-
// Register string for translate in WPML.
|
102 |
-
do_action( 'wpml_register_single_string', 'bookly', 'category_' . $category->id, $category->name );
|
103 |
-
}
|
104 |
-
|
105 |
-
/**
|
106 |
-
* Update category position.
|
107 |
-
*/
|
108 |
-
public function executeUpdateCategoryPosition()
|
109 |
-
{
|
110 |
-
$category_sorts = $this->getParameter( 'position' );
|
111 |
-
foreach ( $category_sorts as $position => $category_id ) {
|
112 |
-
$category_sort = new AB_Category();
|
113 |
-
$category_sort->load( $category_id );
|
114 |
-
$category_sort->set( 'position', $position );
|
115 |
-
$category_sort->save();
|
116 |
-
}
|
117 |
-
}
|
118 |
-
|
119 |
-
/**
|
120 |
-
* Update services position.
|
121 |
-
*/
|
122 |
-
public function executeUpdateServicesPosition()
|
123 |
-
{
|
124 |
-
$services_sorts = $this->getParameter( 'position' );
|
125 |
-
foreach ( $services_sorts as $position => $service_ids ) {
|
126 |
-
$services_sort = new AB_Service();
|
127 |
-
$services_sort->load( $service_ids );
|
128 |
-
$services_sort->set( 'position', $position );
|
129 |
-
$services_sort->save();
|
130 |
-
}
|
131 |
-
}
|
132 |
-
|
133 |
-
/**
|
134 |
-
* Delete category.
|
135 |
-
*/
|
136 |
-
public function executeDeleteCategory()
|
137 |
-
{
|
138 |
-
$category = new AB_Category();
|
139 |
-
$category->set( 'id', $this->getParameter( 'id', 0 ) );
|
140 |
-
$category->delete();
|
141 |
-
}
|
142 |
-
|
143 |
-
public function executeAddService()
|
144 |
-
{
|
145 |
-
$form = new AB_ServiceForm();
|
146 |
-
$form->bind( $this->getPostParameters() );
|
147 |
-
$form->getObject()->set( 'duration', get_option( 'ab_settings_time_slot_length' ) * 60 );
|
148 |
-
$service = $form->save();
|
149 |
-
$this->setDataForServiceList( $service->get( 'category_id' ) );
|
150 |
-
wp_send_json_success( array( 'html' => $this->render( '_list', array(), false ), 'service_id' => $service->get( 'id' ) ) );
|
151 |
-
}
|
152 |
-
|
153 |
-
public function executeRemoveServices()
|
154 |
-
{
|
155 |
-
$service_ids = $this->getParameter( 'service_ids', array() );
|
156 |
-
if ( is_array( $service_ids ) && ! empty ( $service_ids ) ) {
|
157 |
-
AB_Service::query( 's' )->delete()->whereIn( 's.id', $service_ids )->execute();
|
158 |
-
}
|
159 |
-
}
|
160 |
-
|
161 |
-
public function executeAssignStaff()
|
162 |
-
{
|
163 |
-
$service_id = $this->getParameter( 'service_id', 0 );
|
164 |
-
$staff_ids = $this->getParameter( 'staff_ids', array() );
|
165 |
-
|
166 |
-
if ( $service_id ) {
|
167 |
-
AB_StaffService::query()->delete()->where( 'service_id', $service_id )->execute();
|
168 |
-
$service = new AB_Service();
|
169 |
-
if ( ! empty( $staff_ids ) && $service->load( $service_id ) ) {
|
170 |
-
foreach ( $staff_ids as $staff_id ) {
|
171 |
-
$staff_service = new AB_StaffService();
|
172 |
-
$staff_service->set( 'staff_id', $staff_id );
|
173 |
-
$staff_service->set( 'service_id', $service_id );
|
174 |
-
$staff_service->set( 'price', $service->get( 'price' ) );
|
175 |
-
$staff_service->save();
|
176 |
-
}
|
177 |
-
}
|
178 |
-
wp_send_json_success( count( $staff_ids ) );
|
179 |
-
}
|
180 |
-
wp_send_json_error();
|
181 |
-
}
|
182 |
-
|
183 |
-
public function executeUpdateServiceValue()
|
184 |
-
{
|
185 |
-
/** @var $wpdb wpdb */
|
186 |
-
global $wpdb;
|
187 |
-
|
188 |
-
$form = new AB_ServiceForm();
|
189 |
-
$form->bind( $this->getPostParameters() );
|
190 |
-
$service = $form->save();
|
191 |
-
|
192 |
-
if ( $this->getParameter( 'update_staff', false ) ) {
|
193 |
-
if ( $this->getParameter( 'price' ) ) {
|
194 |
-
$wpdb->update( AB_StaffService::getTableName(), array( 'price' => $this->getParameter( 'price' ) ), array( 'service_id' => $this->getParameter( 'id' ) ) );
|
195 |
-
}
|
196 |
-
if ( $this->getParameter( 'capacity' ) ) {
|
197 |
-
$wpdb->update( AB_StaffService::getTableName(), array( 'capacity' => $this->getParameter( 'capacity' ) ), array( 'service_id' => $this->getParameter( 'id' ) ) );
|
198 |
-
}
|
199 |
-
}
|
200 |
-
// Register string for translate in WPML.
|
201 |
-
if ( $this->hasParameter( 'title' ) ) {
|
202 |
-
do_action( 'wpml_register_single_string', 'bookly', 'service_' . $service->id, $service->title );
|
203 |
-
}
|
204 |
-
|
205 |
-
$staff_ids = $this->getParameter( 'staff_ids', array() );
|
206 |
-
if ( $service->id ) {
|
207 |
-
AB_StaffService::query()->delete()->where( 'service_id', $service->id )->execute();
|
208 |
-
if ( ! empty( $staff_ids ) ) {
|
209 |
-
foreach ( $staff_ids as $staff_id ) {
|
210 |
-
$staff_service = new AB_StaffService();
|
211 |
-
$staff_service->set( 'staff_id', $staff_id );
|
212 |
-
$staff_service->set( 'service_id', $service->id );
|
213 |
-
$staff_service->set( 'price', $service->get( 'price' ) );
|
214 |
-
$staff_service->save();
|
215 |
-
}
|
216 |
-
}
|
217 |
-
}
|
218 |
-
|
219 |
-
wp_send_json_success( array( 'title' => $service->title, 'price' => AB_Utils::formatPrice( $service->price ), 'color' => $service->color, 'nice_duration' => AB_DateTimeUtils::secondsToInterval( $service->duration ) ) );
|
220 |
-
}
|
221 |
-
|
222 |
-
/**
|
223 |
-
* @param int $category_id
|
224 |
-
*/
|
225 |
-
private function setDataForServiceList( $category_id = 0 )
|
226 |
-
{
|
227 |
-
if ( ! $category_id ) {
|
228 |
-
$category_id = $this->getParameter( 'category_id', 0 );
|
229 |
-
}
|
230 |
-
|
231 |
-
$this->service_collection = $this->getServiceCollection( $category_id );
|
232 |
-
$this->staff_collection = $this->getStaffCollection();
|
233 |
-
$this->category_collection = $this->getCategoryCollection();
|
234 |
-
}
|
235 |
-
|
236 |
-
/**
|
237 |
-
* @return mixed
|
238 |
-
*/
|
239 |
-
private function getCategoryCollection()
|
240 |
-
{
|
241 |
-
return AB_Category::query()->sortBy( 'position' )->fetchArray();
|
242 |
-
}
|
243 |
-
|
244 |
-
/**
|
245 |
-
* @return mixed
|
246 |
-
*/
|
247 |
-
private function getStaffCollection() {
|
248 |
-
|
249 |
-
return AB_Staff::query()->fetchArray();
|
250 |
-
}
|
251 |
-
|
252 |
-
/**
|
253 |
-
* @param int $id
|
254 |
-
* @return mixed
|
255 |
-
*/
|
256 |
-
private function getServiceCollection( $id = 0 )
|
257 |
-
{
|
258 |
-
$services = AB_Service::query( 's' )
|
259 |
-
->select( 's.*, COUNT(staff.id) AS total_staff, GROUP_CONCAT(DISTINCT staff.id) AS staff_ids' )
|
260 |
-
->leftJoin( 'AB_StaffService', 'ss', 'ss.service_id = s.id' )
|
261 |
-
->leftJoin( 'AB_Staff', 'staff', 'staff.id = ss.staff_id' )
|
262 |
-
->whereRaw( 's.category_id = %d OR !%d', array( $id, $id ) )
|
263 |
-
->groupBy( 's.id' )
|
264 |
-
->sortBy( 'ISNULL(s.position), s.position' );
|
265 |
-
|
266 |
-
return $services->fetchArray();
|
267 |
-
}
|
268 |
-
|
269 |
-
// Protected methods.
|
270 |
-
|
271 |
-
/**
|
272 |
-
* Override parent method to add 'wp_ajax_ab_' prefix
|
273 |
-
* so current 'execute*' methods look nicer.
|
274 |
-
*
|
275 |
-
* @param string $prefix
|
276 |
-
*/
|
277 |
-
protected function registerWpActions( $prefix = '' )
|
278 |
-
{
|
279 |
-
parent::registerWpActions( 'wp_ajax_ab_' );
|
280 |
-
}
|
281 |
-
|
282 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/modules/service/forms/AB_CategoryForm.php
DELETED
@@ -1,25 +0,0 @@
|
|
1 |
-
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Class AB_CategoryForm
|
5 |
-
*/
|
6 |
-
class AB_CategoryForm extends AB_Form {
|
7 |
-
|
8 |
-
/**
|
9 |
-
* Constructor.
|
10 |
-
*/
|
11 |
-
public function __construct()
|
12 |
-
{
|
13 |
-
parent::$entity_class = 'AB_Category';
|
14 |
-
parent::__construct();
|
15 |
-
}
|
16 |
-
|
17 |
-
/**
|
18 |
-
* Configure the form.
|
19 |
-
*/
|
20 |
-
public function configure()
|
21 |
-
{
|
22 |
-
$this->setFields( array( 'name' ) );
|
23 |
-
}
|
24 |
-
|
25 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/modules/service/forms/AB_ServiceForm.php
DELETED
@@ -1,47 +0,0 @@
|
|
1 |
-
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Class AB_ServiceForm
|
5 |
-
*/
|
6 |
-
class AB_ServiceForm extends AB_Form
|
7 |
-
{
|
8 |
-
/**
|
9 |
-
* Constructor.
|
10 |
-
*/
|
11 |
-
public function __construct()
|
12 |
-
{
|
13 |
-
parent::$entity_class = 'AB_Service';
|
14 |
-
parent::__construct();
|
15 |
-
}
|
16 |
-
|
17 |
-
public function configure()
|
18 |
-
{
|
19 |
-
$this->setFields( array( 'id', 'title', 'duration', 'price', 'category_id', 'color', 'capacity', 'padding_left', 'padding_right' ) );
|
20 |
-
}
|
21 |
-
|
22 |
-
/**
|
23 |
-
* Bind values to form.
|
24 |
-
*
|
25 |
-
* @param array $post
|
26 |
-
* @param array $files
|
27 |
-
*/
|
28 |
-
public function bind( array $post, array $files = array() )
|
29 |
-
{
|
30 |
-
if ( array_key_exists( 'category_id', $post ) && !$post['category_id'] ) {
|
31 |
-
$post['category_id'] = null;
|
32 |
-
}
|
33 |
-
parent::bind( $post, $files );
|
34 |
-
}
|
35 |
-
|
36 |
-
public function save()
|
37 |
-
{
|
38 |
-
if ( $this->isNew() ) {
|
39 |
-
// When adding new service - set its color randomly.
|
40 |
-
$this->data['color'] = sprintf( '#%06X', mt_rand( 0, 0x64FFFF ) );
|
41 |
-
}
|
42 |
-
$this->data['capacity'] = 1;
|
43 |
-
|
44 |
-
return parent::save();
|
45 |
-
}
|
46 |
-
|
47 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/modules/service/resources/css/service.css
DELETED
@@ -1,144 +0,0 @@
|
|
1 |
-
#ab_services_wrapper .table-responsive { overflow: visible!important; }
|
2 |
-
@media screen and (max-width: 767px) {
|
3 |
-
#ab_services_wrapper .table-responsive {
|
4 |
-
overflow-y: visible!important;
|
5 |
-
overflow-x: auto!important;
|
6 |
-
}
|
7 |
-
.ab-col-responsive {
|
8 |
-
margin-bottom: 15px;
|
9 |
-
}
|
10 |
-
#services_list .panel.panel-default {
|
11 |
-
margin-right: 0;
|
12 |
-
}
|
13 |
-
}
|
14 |
-
.ab-category-item {
|
15 |
-
padding: 10px;
|
16 |
-
padding-left: 50px;
|
17 |
-
vertical-align: middle;
|
18 |
-
border-radius: 5px;
|
19 |
-
cursor: pointer;
|
20 |
-
}
|
21 |
-
.ab-main-category-item {
|
22 |
-
padding-left: 40px;
|
23 |
-
font-size: 17px;
|
24 |
-
background-image: url("../../../../resources/images/box-big.png");
|
25 |
-
background-position: 5px 50%;
|
26 |
-
background-repeat: no-repeat;
|
27 |
-
}
|
28 |
-
.ab-active {
|
29 |
-
background-color: #ccc;
|
30 |
-
}
|
31 |
-
|
32 |
-
.wp-picker-holder { margin-top: 10px; left: -268px; z-index: 2; position: absolute; }
|
33 |
-
|
34 |
-
#services_list .service-color-wrapper .wp-picker-open + .wp-picker-input-wrap {
|
35 |
-
display: block;
|
36 |
-
margin-left: 50px;
|
37 |
-
margin-top: 20px;
|
38 |
-
}
|
39 |
-
.service-color-wrapper {
|
40 |
-
height: 24px;
|
41 |
-
width: 27px;
|
42 |
-
}
|
43 |
-
|
44 |
-
.wp-picker-container, .wp-picker-container:active {
|
45 |
-
display: inline-block;
|
46 |
-
outline: 0 none;
|
47 |
-
position: absolute;
|
48 |
-
}
|
49 |
-
|
50 |
-
/** Services list **/
|
51 |
-
#ab_services_wrapper .ab-right-content .ab-category-title { margin: 10px 0 15px 0; font-weight: normal; }
|
52 |
-
#ab_services_wrapper .list-wrapper { max-width: 575px; }
|
53 |
-
#ab_services_wrapper .list-wrapper .list-actions { overflow: hidden; margin-top: 10px; }
|
54 |
-
#ab_services_wrapper .list-wrapper .list-actions .add-service { float: left }
|
55 |
-
#ab_services_wrapper .list-wrapper .list-actions .delete { float: right }
|
56 |
-
|
57 |
-
#services_list td.last,
|
58 |
-
#services_list th.last { width: 16px; }
|
59 |
-
#services_list td .btn-group { min-width: 85px; }
|
60 |
-
#services_list td,
|
61 |
-
#services_list th { vertical-align: middle; }
|
62 |
-
#services_list .service-color-cell { width: 28px; }
|
63 |
-
#services_list .service-color-wrapper .wp-color-result { padding-left: 25px; margin: 0; top: auto; }
|
64 |
-
#services_list .service-color-wrapper .wp-color-result.wp-picker-open { top: auto }
|
65 |
-
#services_list .service-color-wrapper .wp-color-result:after { top: auto; content: none; }
|
66 |
-
#services_list .service-color-wrapper .wp-color-result.wp-picker-open:after { content: none }
|
67 |
-
#services_list .service-color-wrapper .wp-picker-open + .wp-picker-input-wrap { display: block; visibility: hidden; margin-top: -25px; margin-left: 34px; }
|
68 |
-
#services_list .service-color-wrapper .button { margin-left: 0 }
|
69 |
-
#services_list .panel.panel-default { margin-right: 0; }
|
70 |
-
#services_list .panel.panel-default.service .panel-heading { padding: 10px; overflow: hidden; }
|
71 |
-
#services_list .panel.panel-default.service .panel-collapse { width: 100%!important; }
|
72 |
-
#services_list .panel.panel-default.service .panel-heading .badge { margin: 0 5px; margin-top: -3px; }
|
73 |
-
#services_list .ab-padding-before-after select { width: 46%!important; }
|
74 |
-
#services_list a[data-toggle="collapse"] {
|
75 |
-
outline: none;
|
76 |
-
box-shadow: none;
|
77 |
-
padding-right: 25px;
|
78 |
-
background: url("../../../../resources/images/notifications-arrow-up.png") 100% 50% no-repeat;
|
79 |
-
background-size: 17px 17px;
|
80 |
-
}
|
81 |
-
#services_list a[data-toggle="collapse"].collapsed {
|
82 |
-
background: url("../../../../resources/images/notifications-arrow-down.png") 100% 50% no-repeat;
|
83 |
-
background-size: 17px 17px;
|
84 |
-
}
|
85 |
-
|
86 |
-
.table tbody > tr td:first-child { vertical-align: middle; }
|
87 |
-
|
88 |
-
#ab-services-list,
|
89 |
-
#ab-services-list li { margin: 0; }
|
90 |
-
#ab-services-list .table { margin: 0; }
|
91 |
-
#ab-services-list .table .ab-handle { cursor: move; }
|
92 |
-
#ab-services-list .ab-popover { margin: 29px 0 0 -20px; }
|
93 |
-
#ab-services-list .service-color-wrapper { margin: 5px 0 0 -20px; }
|
94 |
-
|
95 |
-
#new_category_popup .ab-popup { width: 187px }
|
96 |
-
.staff-popup-wrapper .staff-row { white-space: nowrap; margin: 0; }
|
97 |
-
|
98 |
-
#ab-category-item-list { padding: 0; margin: 20px 0; }
|
99 |
-
#ab-category-item-list .ab-category-item {
|
100 |
-
width: auto;
|
101 |
-
height: auto;
|
102 |
-
padding: 5px;
|
103 |
-
margin-top: 5px;
|
104 |
-
position: relative;
|
105 |
-
white-space: nowrap;
|
106 |
-
overflow: hidden;
|
107 |
-
padding-left: 45px;
|
108 |
-
background-image: url("../../../../resources/images/box-small.png");
|
109 |
-
background-position: 22px 50%;
|
110 |
-
background-repeat: no-repeat;
|
111 |
-
}
|
112 |
-
#ab-category-item-list .ab-category-item .ab-handle {
|
113 |
-
display: block;
|
114 |
-
width: 20px;
|
115 |
-
position: absolute;
|
116 |
-
top: 0;
|
117 |
-
left: 0;
|
118 |
-
bottom: 0;
|
119 |
-
z-index: 999;
|
120 |
-
text-align: center;
|
121 |
-
padding: 5px 0;
|
122 |
-
cursor: move;
|
123 |
-
}
|
124 |
-
#ab-category-item-list .ab-category-item > span { padding-right: 5px; }
|
125 |
-
#ab-category-item-list .ab-category-item:hover span { text-decoration: underline; }
|
126 |
-
#ab-category-item-list .ab-category-item .ab-edit,
|
127 |
-
#ab-category-item-list .ab-category-item .ab-delete {
|
128 |
-
display: inline-block;
|
129 |
-
width: 21px;
|
130 |
-
height: 16px;
|
131 |
-
background: url("../../../../resources/images/edit.png") 5px 0 no-repeat;
|
132 |
-
padding: 0 5px;
|
133 |
-
visibility: hidden;
|
134 |
-
}
|
135 |
-
#ab-category-item-list .ab-category-item:hover .ab-edit,
|
136 |
-
#ab-category-item-list .ab-category-item:hover .ab-delete { visibility: visible; }
|
137 |
-
#ab-category-item-list .ab-category-item:hover .ab-delete { background: url("../../../../resources/images/delete.png") 5px 0 no-repeat; padding-left: 5px; }
|
138 |
-
#ab-category-item-list .ab-category-item .ab-edit { padding-right: 0; }
|
139 |
-
|
140 |
-
@media only screen and (max-width: 640px) {
|
141 |
-
.wp-picker-container,
|
142 |
-
.wp-picker-container:active { position: static; }
|
143 |
-
}
|
144 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/modules/service/resources/js/service.js
DELETED
@@ -1,344 +0,0 @@
|
|
1 |
-
jQuery(function($) {
|
2 |
-
var $no_result = $('#ab_services_wrapper .no-result');
|
3 |
-
// Remember user choice in the modal dialog.
|
4 |
-
var update_staff_choice = null;
|
5 |
-
|
6 |
-
// On new category form submit.
|
7 |
-
$('#new-category-form').on('submit', function(event) {
|
8 |
-
var data = $(this).serialize();
|
9 |
-
$.post(ajaxurl, data, function(response) {
|
10 |
-
$('.ab-category-item-list').append(response);
|
11 |
-
$('#new_category_popup').ab_popup('close');
|
12 |
-
// add created category to services
|
13 |
-
$.each($('#services_list').find('select[name="category_id"]'), function(key, value) {
|
14 |
-
var $new_category = $('.ab-category-item:last');
|
15 |
-
$(value).append('<option value="' + $new_category.data('id') + '">'
|
16 |
-
+ $new_category.find('input').val() + ' </option>');
|
17 |
-
});
|
18 |
-
});
|
19 |
-
return false;
|
20 |
-
});
|
21 |
-
|
22 |
-
// Preventing multiple creation of new category by pressing Enter-key
|
23 |
-
$('input[value="ab_category_form"]').parent().find('input:first').one('keypress', function(e) {
|
24 |
-
var code = (e.keyCode ? e.keyCode : e.which);
|
25 |
-
if (code == 13) {
|
26 |
-
$(this).trigger(e).blur();
|
27 |
-
}
|
28 |
-
});
|
29 |
-
|
30 |
-
// Categories list delegated events.
|
31 |
-
$('#ab-categories-list')
|
32 |
-
|
33 |
-
// On category item click.
|
34 |
-
.on('click', '.ab-category-item', function() {
|
35 |
-
var $clicked = $(this);
|
36 |
-
$.get(ajaxurl, {action:'ab_category_services', category_id: $clicked.data('id')}, function(response) {
|
37 |
-
$('.ab-category-item').not($clicked).removeClass('ab-active');
|
38 |
-
$('.ab-category-title').text($clicked.text());
|
39 |
-
$clicked.addClass('ab-active');
|
40 |
-
refreshList(response,0);
|
41 |
-
});
|
42 |
-
})
|
43 |
-
|
44 |
-
// On edit category click.
|
45 |
-
.on('click', '.ab-category-item .ab-edit', function(e) {
|
46 |
-
// Keep category item click from being executed.
|
47 |
-
e.stopPropagation();
|
48 |
-
// Prevent navigating to '#'.
|
49 |
-
e.preventDefault();
|
50 |
-
// Hide edit button.
|
51 |
-
$(this).hide()
|
52 |
-
// Hide displayed category name and delete button.
|
53 |
-
.siblings('.displayed-value, .ab-delete').hide().end()
|
54 |
-
// Show input field.
|
55 |
-
.nextAll('.value').show().focus();
|
56 |
-
})
|
57 |
-
|
58 |
-
// On blur of category edit input.
|
59 |
-
.on('blur', '.ab-category-item input.value', function() {
|
60 |
-
var $this = $(this),
|
61 |
-
$item = $this.closest('.ab-category-item'),
|
62 |
-
field = $this.attr('name'),
|
63 |
-
value = $this.attr('value'),
|
64 |
-
id = $item.data('id');
|
65 |
-
if (value) {
|
66 |
-
var data = { action: 'ab_update_category', id: id };
|
67 |
-
data[field] = value;
|
68 |
-
$.post(ajaxurl, data, function(response) {
|
69 |
-
// Hide input field.
|
70 |
-
$this.hide()
|
71 |
-
// Show modified category name.
|
72 |
-
.prevAll('.displayed-value').text(value).show().end()
|
73 |
-
// Show edit and delete buttons.
|
74 |
-
.siblings('.ab-edit, .ab-delete').show();
|
75 |
-
// update edited category's name for services
|
76 |
-
$.each($('#services_list').find('select[name="category_id"]'), function(k, v) {
|
77 |
-
$(v).find('option:selected[value="' + id + '"]').text(value);
|
78 |
-
});
|
79 |
-
});
|
80 |
-
}
|
81 |
-
})
|
82 |
-
|
83 |
-
// On delete category click.
|
84 |
-
.on('click', '.ab-category-item .ab-delete', function(e) {
|
85 |
-
// Keep category item click from being executed.
|
86 |
-
e.stopPropagation();
|
87 |
-
// Prevent navigating to '#'.
|
88 |
-
e.preventDefault();
|
89 |
-
// Ask user if he is sure.
|
90 |
-
if (confirm(BooklyL10n.are_you_sure)) {
|
91 |
-
var $item = $(this).closest('.ab-category-item');
|
92 |
-
var data = { action: 'ab_delete_category', id: $item.data('id') };
|
93 |
-
$.post(ajaxurl, data, function(response) {
|
94 |
-
// Remove category item from Services
|
95 |
-
$.each($('#services_list').find('select[name="category_id"]'), function(key, value) {
|
96 |
-
$(value).find('option[value="' + $item.data('id') + '"]').remove();
|
97 |
-
});
|
98 |
-
// Remove category item from DOM.
|
99 |
-
$item.remove();
|
100 |
-
if ($item.is('.ab-active')) {
|
101 |
-
location.reload(true);
|
102 |
-
}
|
103 |
-
});
|
104 |
-
}
|
105 |
-
});
|
106 |
-
|
107 |
-
// Services list delegated events.
|
108 |
-
$('#ab_services_wrapper')
|
109 |
-
// On click on 'Add Service' button.
|
110 |
-
.on('click', 'a.add-service', function(e) {
|
111 |
-
e.preventDefault();
|
112 |
-
var selected_category_id = $('#ab-categories-list .ab-active').data('id'),
|
113 |
-
data = { action: 'ab_add_service' };
|
114 |
-
if (selected_category_id) {
|
115 |
-
data['category_id'] = selected_category_id;
|
116 |
-
}
|
117 |
-
$.post(ajaxurl, data, function(response) {
|
118 |
-
refreshList(response.data.html,response.data.service_id);
|
119 |
-
});
|
120 |
-
})
|
121 |
-
// On click on 'Delete' button.
|
122 |
-
.on('click', 'a.delete', function(e){
|
123 |
-
e.preventDefault();
|
124 |
-
|
125 |
-
var for_delete = $('input:checkbox.service-checker:checked'),
|
126 |
-
data = { action: 'ab_remove_services' },
|
127 |
-
services = [],
|
128 |
-
$panels = [];
|
129 |
-
|
130 |
-
if (!for_delete.length) {
|
131 |
-
alert(BooklyL10n.please_select_at_least_one_service);
|
132 |
-
return false;
|
133 |
-
}
|
134 |
-
|
135 |
-
for_delete.each(function(){
|
136 |
-
var panel = $(this).parents('.panel.service');
|
137 |
-
$panels.push(panel);
|
138 |
-
services.push(panel.data('service_id'));
|
139 |
-
});
|
140 |
-
data['service_ids[]'] = services;
|
141 |
-
$.post(ajaxurl, data, function() {
|
142 |
-
$.each($panels, function () {
|
143 |
-
$(this).fadeOut(200, function () {
|
144 |
-
$(this).remove();
|
145 |
-
});
|
146 |
-
});
|
147 |
-
});
|
148 |
-
})
|
149 |
-
|
150 |
-
.on('change', 'input.all-staff, input.staff', function(){
|
151 |
-
var $panel = $(this).parents('.panel.service');
|
152 |
-
if ($(this).hasClass('all-staff')) {
|
153 |
-
$panel.find('.staff').prop('checked', $(this).prop('checked'));
|
154 |
-
} else {
|
155 |
-
$panel.find('.all-staff').prop('checked', $panel.find('.staff:not(:checked)').length == 0);
|
156 |
-
}
|
157 |
-
updateStaffButton($panel);
|
158 |
-
});
|
159 |
-
|
160 |
-
// Modal window events.
|
161 |
-
var $modal = $('#ab-staff-update');
|
162 |
-
$modal
|
163 |
-
.on('click', '.ab-yes', function() {
|
164 |
-
$modal.modal('hide');
|
165 |
-
if ( $('#ab-remember-my-choice').prop('checked') ) {
|
166 |
-
update_staff_choice = true;
|
167 |
-
}
|
168 |
-
submitServiceFrom($modal.data('input'),true);
|
169 |
-
})
|
170 |
-
.on('click', '.ab-no', function() {
|
171 |
-
if ( $('#ab-remember-my-choice').prop('checked') ) {
|
172 |
-
update_staff_choice = false;
|
173 |
-
}
|
174 |
-
submitServiceFrom($modal.data('input'),false);
|
175 |
-
});
|
176 |
-
|
177 |
-
function refreshList(response,service_id) {
|
178 |
-
var $list = $('#ab-services-list');
|
179 |
-
$list.html(response);
|
180 |
-
makeServicesSortable();
|
181 |
-
doNotCloseDropDowns();
|
182 |
-
initColorPicker($list.find('.service-color'));
|
183 |
-
initPopovers();
|
184 |
-
initServiceFormButtons();
|
185 |
-
|
186 |
-
if (response.indexOf('panel') >= 0) {
|
187 |
-
$no_result.hide();
|
188 |
-
} else {
|
189 |
-
$no_result.show();
|
190 |
-
}
|
191 |
-
$('#service_' + service_id).collapse('show');
|
192 |
-
$('#service_' + service_id).find('input[name=title]').focus();
|
193 |
-
}
|
194 |
-
|
195 |
-
function initColorPicker($jquery_collection) {
|
196 |
-
$jquery_collection.each(function(){
|
197 |
-
$(this).data('last_color', $(this).val());
|
198 |
-
});
|
199 |
-
$jquery_collection.wpColorPicker();
|
200 |
-
}
|
201 |
-
|
202 |
-
function doNotCloseDropDowns() {
|
203 |
-
$('#ab-services-list .dropdown-menu').on('click', function(e) {
|
204 |
-
e.stopPropagation();
|
205 |
-
});
|
206 |
-
}
|
207 |
-
|
208 |
-
function initPopovers() {
|
209 |
-
// Popovers initialization.
|
210 |
-
$('.ab-popover').popover({
|
211 |
-
trigger : 'hover'
|
212 |
-
});
|
213 |
-
}
|
214 |
-
|
215 |
-
function submitServiceFrom($form, update_staff) {
|
216 |
-
$form.find('input[name=update_staff]').val( update_staff ? 1 : 0 );
|
217 |
-
var ladda = Ladda.create( $form.find('button[type=submit]').get(0) );
|
218 |
-
ladda.start();
|
219 |
-
$.post(ajaxurl, $form.serialize(), function (response) {
|
220 |
-
if (response.success) {
|
221 |
-
var $panel = $form.parents('.panel.service'),
|
222 |
-
$price = $form.find('[name=price]'),
|
223 |
-
$capacity = $form.find('[name=capacity]');
|
224 |
-
$panel.find('span.badge').css('background-color', response.data.color);
|
225 |
-
$panel.find('.panel-title a').html(response.data.title);
|
226 |
-
$panel.find('.panel-title .ab-right-nav small').html(response.data.nice_duration + '<div class="ab-inline-block" style="width: 75px;text-align: right">' + response.data.price + '</div>');
|
227 |
-
$price.data('last_value', $price.val());
|
228 |
-
$capacity.data('last_value', $capacity.val());
|
229 |
-
$('.notice-dismiss').unbind('click.wp-dismiss-notice').on('click', function(){
|
230 |
-
$(this).parents('.notice').fadeOut();
|
231 |
-
});
|
232 |
-
$('.notice-success').show();
|
233 |
-
}
|
234 |
-
}, 'json').always(function() {
|
235 |
-
ladda.stop();
|
236 |
-
} );
|
237 |
-
}
|
238 |
-
|
239 |
-
function updateStaffButton($panel) {
|
240 |
-
var staff_checked = $panel.find('.staff:checked').length;
|
241 |
-
if (staff_checked == 0) {
|
242 |
-
$panel.find('.staff-count').text(BooklyL10n.no_staff_selected);
|
243 |
-
} else if (staff_checked == 1) {
|
244 |
-
$panel.find('.staff-count').text($panel.find('.staff:checked').data('staff_name'));
|
245 |
-
} else {
|
246 |
-
$panel.find('.staff-count').text(staff_checked + '/' + $panel.find('.staff').length);
|
247 |
-
}
|
248 |
-
}
|
249 |
-
|
250 |
-
function initServiceFormButtons() {
|
251 |
-
$('.ajax-service-send').on('click', function (e) {
|
252 |
-
e.preventDefault();
|
253 |
-
var $form = $(this).parents('form'),
|
254 |
-
show_modal = false;
|
255 |
-
if(update_staff_choice === null) {
|
256 |
-
$('.ab-question', $form).each(function () {
|
257 |
-
if ($(this).data('last_value') != $(this).val()) {
|
258 |
-
show_modal = true;
|
259 |
-
}
|
260 |
-
});
|
261 |
-
}
|
262 |
-
if(show_modal){
|
263 |
-
$modal.data('input', $form).modal('show');
|
264 |
-
}else{
|
265 |
-
submitServiceFrom($form, update_staff_choice);
|
266 |
-
}
|
267 |
-
});
|
268 |
-
$('.js-reset').on('click', function (e) {
|
269 |
-
$(this).parents('form').trigger('reset');
|
270 |
-
var $color = $(this).parents('form').find('.service-color'),
|
271 |
-
$panel = $(this).parents('.panel.service');
|
272 |
-
$color.val($color.data('last_color')).trigger('change');
|
273 |
-
updateStaffButton($panel);
|
274 |
-
});
|
275 |
-
$('.ab-question').each( function(){
|
276 |
-
$(this).data('last_value', $(this).val());
|
277 |
-
});
|
278 |
-
}
|
279 |
-
|
280 |
-
|
281 |
-
doNotCloseDropDowns();
|
282 |
-
initColorPicker($('.service-color'));
|
283 |
-
initPopovers();
|
284 |
-
initServiceFormButtons();
|
285 |
-
|
286 |
-
var $category = $('ul#ab-category-item-list');
|
287 |
-
$category.sortable({
|
288 |
-
axis : 'y',
|
289 |
-
handle : '.ab-handle',
|
290 |
-
update : function( event, ui ) {
|
291 |
-
var data = [];
|
292 |
-
$category.children('li').each(function() {
|
293 |
-
var $this = $(this);
|
294 |
-
var position = $this.data('id');
|
295 |
-
data.push(position);
|
296 |
-
});
|
297 |
-
$.ajax({
|
298 |
-
type : 'POST',
|
299 |
-
url : ajaxurl,
|
300 |
-
data : { action: 'ab_update_category_position', position: data }
|
301 |
-
});
|
302 |
-
}
|
303 |
-
});
|
304 |
-
|
305 |
-
function makeServicesSortable() {
|
306 |
-
if ($('.ab-main-category-item').hasClass('ab-active')) {
|
307 |
-
var $services = $('#services_list'),
|
308 |
-
fixHelper = function(e, ui) {
|
309 |
-
ui.children().each(function() {
|
310 |
-
$(this).width($(this).width());
|
311 |
-
});
|
312 |
-
return ui;
|
313 |
-
};
|
314 |
-
$services.sortable({
|
315 |
-
helper : fixHelper,
|
316 |
-
axis : 'y',
|
317 |
-
handle : '.ab-handle',
|
318 |
-
update : function( event, ui ) {
|
319 |
-
var data = [];
|
320 |
-
$services.children('div').each(function() {
|
321 |
-
data.push($(this).data('service_id'));
|
322 |
-
});
|
323 |
-
$.ajax({
|
324 |
-
type : 'POST',
|
325 |
-
url : ajaxurl,
|
326 |
-
data : { action: 'ab_update_services_position', position: data }
|
327 |
-
});
|
328 |
-
}
|
329 |
-
});
|
330 |
-
} else {
|
331 |
-
$('#services_list .ab-handle').hide();
|
332 |
-
}
|
333 |
-
}
|
334 |
-
makeServicesSortable();
|
335 |
-
$('.panel.service').each(function(){
|
336 |
-
updateStaffButton($(this));
|
337 |
-
});
|
338 |
-
$('[name=capacity]').on('change',function(){
|
339 |
-
if ($(this).val() > 1) {
|
340 |
-
$('#lite_notice').modal('show');
|
341 |
-
$(this).val(1) ;
|
342 |
-
}
|
343 |
-
});
|
344 |
-
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/modules/service/templates/_list.php
DELETED
@@ -1,177 +0,0 @@
|
|
1 |
-
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
-
$time_interval = get_option( 'ab_settings_time_slot_length' );
|
3 |
-
?>
|
4 |
-
<?php AB_Utils::notice( __( 'Settings saved.', 'bookly' ), 'notice-success', false ) ?>
|
5 |
-
<?php if ( ! empty( $service_collection ) ) : ?>
|
6 |
-
<div class="panel-group" id="services_list" role="tablist" aria-multiselectable="true">
|
7 |
-
<?php foreach ( $service_collection as $i => $service ) : ?>
|
8 |
-
<?php $service_id = $service['id'];
|
9 |
-
$assigned_staff_ids = $service['staff_ids'] ? explode( ',', $service['staff_ids'] ) : array();
|
10 |
-
$all_staff_selected = count( $assigned_staff_ids ) == count( $staff_collection );
|
11 |
-
?>
|
12 |
-
<div class="panel panel-default service" data-service_id="<?php echo $service_id ?>">
|
13 |
-
<div class="panel-heading" role="tab" id="s_<?php echo esc_html( $service_id ) ?>">
|
14 |
-
<h4 class="panel-title">
|
15 |
-
<span class="ab-handle ab-move" title="<?php esc_attr_e( 'Reorder', 'bookly' ) ?>"><i class="ab-inner-handle glyphicon glyphicon-align-justify"></i></span>
|
16 |
-
<span class="badge" style="background-color: <?php echo esc_attr( $service['color'] ) ?>"> </span>
|
17 |
-
<a role="button" class="collapsed" data-toggle="collapse" data-parent="#services_list" href="#service_<?php echo esc_html( $service_id ) ?>" aria-expanded="false" aria-controls="service_<?php echo esc_html( $service_id ) ?>">
|
18 |
-
<?php echo esc_html( $service['title'] ) ?>
|
19 |
-
</a>
|
20 |
-
<div class="pull-right">
|
21 |
-
<div class="ab-right-nav ab-inline-block">
|
22 |
-
<small>
|
23 |
-
<?php echo AB_DateTimeUtils::secondsToInterval( $service['duration'] ) ?>
|
24 |
-
<div class="ab-inline-block" style="width: 75px;text-align: right">
|
25 |
-
<?php echo AB_Utils::formatPrice( $service['price'] ) ?>
|
26 |
-
</div>
|
27 |
-
</small>
|
28 |
-
</div>
|
29 |
-
|
30 |
-
<input style="margin: 0 0 0 5px" type="checkbox" class="service-checker"/>
|
31 |
-
</div>
|
32 |
-
</h4>
|
33 |
-
</div>
|
34 |
-
<div id="service_<?php echo esc_html( $service_id ) ?>" class="panel-collapse collapse" role="tabpanel" aria-labelledby="s_<?php echo esc_html( $service_id ) ?>" style="height: 0px;">
|
35 |
-
<div class="panel-body">
|
36 |
-
<form method="post">
|
37 |
-
<div class="form-group">
|
38 |
-
<label for="title_<?php echo $service_id ?>"><?php _e( 'Title', 'bookly' ) ?></label>
|
39 |
-
<div class="row">
|
40 |
-
<div class="col-sm-11 col-xs-10">
|
41 |
-
<input id="title_<?php echo $service_id ?>" class="form-control" type="text" name="title" value="<?php echo esc_attr( $service['title'] ) ?>" />
|
42 |
-
</div>
|
43 |
-
<div class="col-sm-1 col-xs-2">
|
44 |
-
<div class="service-color-wrapper">
|
45 |
-
<input type="hidden" class="service-color" name="color" value="<?php echo esc_attr( $service['color'] ) ?>" data-last_color='' />
|
46 |
-
</div>
|
47 |
-
</div>
|
48 |
-
</div>
|
49 |
-
</div>
|
50 |
-
<div class="form-group">
|
51 |
-
<div class="row">
|
52 |
-
<div class="col-sm-5 col-xs-10 ab-col-responsive">
|
53 |
-
<label for="duration_<?php echo $service_id ?>"><?php _e( 'Duration', 'bookly' ) ?></label>
|
54 |
-
<select id="duration_<?php echo $service_id ?>" class="form-control" name="duration">
|
55 |
-
<?php for ( $j = $time_interval; $j <= 720; $j += $time_interval ) : ?>
|
56 |
-
<?php if ( $service['duration'] / 60 > $j - $time_interval && $service['duration'] / 60 < $j ): ?>
|
57 |
-
<option value="<?php echo esc_attr( $service['duration'] ) ?>" selected>
|
58 |
-
<?php echo AB_DateTimeUtils::secondsToInterval( $service['duration'] ) ?>
|
59 |
-
</option>
|
60 |
-
<?php endif ?>
|
61 |
-
<option value="<?php echo $j * 60 ?>" <?php selected( $service['duration'], $j * 60 ) ?>>
|
62 |
-
<?php echo AB_DateTimeUtils::secondsToInterval( $j * 60 ) ?>
|
63 |
-
</option>
|
64 |
-
<?php endfor ?>
|
65 |
-
<option value="86400" <?php selected( $service['duration'], DAY_IN_SECONDS ) ?>>
|
66 |
-
<?php _e( 'All day', 'bookly' ) ?>
|
67 |
-
</option>
|
68 |
-
</select>
|
69 |
-
</div>
|
70 |
-
<div class="col-sm-6 col-xs-10 ab-padding-before-after">
|
71 |
-
<label for="padding_left_<?php echo $service_id ?>"><?php _e( 'Padding time (before and after)', 'bookly' ) ?></label>
|
72 |
-
<div style="clear: both;"></div>
|
73 |
-
<select id="padding_left_<?php echo $service_id ?>" class="form-control ab-auto-w pull-left" name="padding_left">
|
74 |
-
<option value="0"><?php _e( 'OFF', 'bookly' ) ?></option>
|
75 |
-
<?php for ( $j = $time_interval; $j <= 720; $j += $time_interval ) : ?>
|
76 |
-
<?php if ( $service['duration'] / 60 > $j - $time_interval && $service['duration'] / 60 < $j ): ?>
|
77 |
-
<option value="<?php echo esc_attr( $service['duration'] ) ?>" selected>
|
78 |
-
<?php echo AB_DateTimeUtils::secondsToInterval( $service['duration'] ) ?>
|
79 |
-
</option>
|
80 |
-
<?php endif ?>
|
81 |
-
<option value="<?php echo $j * 60 ?>" <?php selected( $service['padding_left'], $j * 60 ) ?>>
|
82 |
-
<?php echo AB_DateTimeUtils::secondsToInterval( $j * 60 ) ?>
|
83 |
-
</option>
|
84 |
-
<?php endfor ?>
|
85 |
-
</select>
|
86 |
-
<select id="padding_right_<?php echo $service_id ?>" class="form-control ab-auto-w pull-right" name="padding_right">
|
87 |
-
<option value="0"><?php _e( 'OFF', 'bookly' ) ?></option>
|
88 |
-
<?php for ( $j = $time_interval; $j <= 720; $j += $time_interval ) : ?>
|
89 |
-
<?php if ( $service['duration'] / 60 > $j - $time_interval && $service['duration'] / 60 < $j ): ?>
|
90 |
-
<option value="<?php echo esc_attr( $service['duration'] ) ?>" selected>
|
91 |
-
<?php echo AB_DateTimeUtils::secondsToInterval( $service['duration'] ) ?>
|
92 |
-
</option>
|
93 |
-
<?php endif ?>
|
94 |
-
<option value="<?php echo $j * 60 ?>" <?php selected( $service['padding_right'], $j * 60 ) ?>>
|
95 |
-
<?php echo AB_DateTimeUtils::secondsToInterval( $j * 60 ) ?>
|
96 |
-
</option>
|
97 |
-
<?php endfor ?>
|
98 |
-
</select>
|
99 |
-
</div>
|
100 |
-
<div class="col-sm-1 col-xs-2">
|
101 |
-
<?php AB_Utils::popover( __( 'Set padding time before and/or after an appointment. For example, if you require 15 minutes to prepare for the next appointment then you should set "padding before" to 15 min. If there is an appointment from 8:00 to 9:00 then the next available time slot will be 9:15 rather than 9:00.', 'bookly' ) ) ?>
|
102 |
-
</div>
|
103 |
-
</div>
|
104 |
-
</div>
|
105 |
-
<div class="form-group">
|
106 |
-
<div class="row">
|
107 |
-
<div class="col-xs-5">
|
108 |
-
<label for="price_<?php echo $service_id ?>"><?php _e( 'Price', 'bookly' ) ?></label>
|
109 |
-
<input id="price_<?php echo $service_id ?>" class="form-control ab-question" type="number" min="0.00" step="any" name="price" value="<?php echo esc_attr( $service['price'] ) ?>"/>
|
110 |
-
</div>
|
111 |
-
<div class="col-sm-3 col-xs-5">
|
112 |
-
<label for="capacity_<?php echo $service_id ?>"><?php _e( 'Capacity', 'bookly' ) ?></label>
|
113 |
-
<input id="capacity_<?php echo $service_id ?>" class="form-control ab-question" type="number" min="1" step="1" name="capacity" value="<?php echo esc_attr( $service['capacity'] ) ?>"/>
|
114 |
-
</div>
|
115 |
-
<div class="col-xs-1">
|
116 |
-
<?php AB_Utils::popover( __( 'The maximum number of customers allowed to book the service for the certain time period.', 'bookly' ) ) ?>
|
117 |
-
</div>
|
118 |
-
</div>
|
119 |
-
</div>
|
120 |
-
<div class="form-group">
|
121 |
-
<div class="row">
|
122 |
-
<div class="col-xs-5">
|
123 |
-
<label for="category_<?php echo $service_id ?>"><?php _e( 'Category', 'bookly' ) ?></label>
|
124 |
-
<select id="category_<?php echo $service_id ?>" class="form-control" name="category_id">
|
125 |
-
<option value="0"></option>
|
126 |
-
<?php foreach ( $category_collection as $category ) : ?>
|
127 |
-
<option value="<?php echo $category['id'] ?>" <?php selected( $category['id'], $service['category_id'] ) ?>>
|
128 |
-
<?php echo esc_html( $category['name'] ) ?>
|
129 |
-
</option>
|
130 |
-
<?php endforeach ?>
|
131 |
-
</select>
|
132 |
-
</div>
|
133 |
-
<div class="col-xs-7">
|
134 |
-
<label><?php _e( 'Providers', 'bookly' ) ?></label>
|
135 |
-
<div style="clear: both"></div>
|
136 |
-
<div class="btn-group">
|
137 |
-
<button class="btn btn-info" data-toggle="dropdown"><i class="glyphicon glyphicon-user"></i> <span class=staff-count><?php echo $service['total_staff'] ?></span>
|
138 |
-
</button>
|
139 |
-
<button class="btn btn-info dropdown-toggle" data-toggle="dropdown">
|
140 |
-
<span class="caret"></span>
|
141 |
-
</button>
|
142 |
-
<ul class="dropdown-menu staff-list">
|
143 |
-
<li>
|
144 |
-
<a href="javascript:void(0)">
|
145 |
-
<input type="checkbox" id="service_<?php echo $service_id ?>_all_staff" class="all-staff" <?php checked( $all_staff_selected ) ?>"/>
|
146 |
-
<label class="ab-inline" for="service_<?php echo $service_id ?>_all_staff"><?php _e( 'All staff', 'bookly' ) ?></label>
|
147 |
-
</a>
|
148 |
-
</li>
|
149 |
-
<?php foreach ( $staff_collection as $i => $staff ) : ?>
|
150 |
-
<li>
|
151 |
-
<a href="javascript:void(0)" style="padding-left: 30px">
|
152 |
-
<input type="checkbox" name="staff_ids[]" class="staff" value="<?php echo $staff['id'] ?>" <?php checked( in_array( $staff['id'], $assigned_staff_ids ) ) ?> data-staff_name="<?php echo esc_attr( $staff['full_name'] ) ?>" />
|
153 |
-
<label class="ab-inline" for="service_<?php echo $service_id . '_staff_' . $i ?>">
|
154 |
-
<?php echo esc_html( $staff['full_name'] ) ?>
|
155 |
-
</label>
|
156 |
-
</a>
|
157 |
-
</li>
|
158 |
-
<?php endforeach ?>
|
159 |
-
</ul>
|
160 |
-
</div>
|
161 |
-
</div>
|
162 |
-
</div>
|
163 |
-
</div>
|
164 |
-
<div class="form-group">
|
165 |
-
<input type="hidden" name="action" value="ab_update_service_value">
|
166 |
-
<input type="hidden" name="id" value="<?php echo esc_html( $service_id ) ?>">
|
167 |
-
<input type="hidden" name="update_staff" value="0">
|
168 |
-
<?php AB_Utils::submitButton( null, 'ajax-service-send' ) ?>
|
169 |
-
<?php AB_Utils::resetButton( null, 'js-reset' ) ?>
|
170 |
-
</div>
|
171 |
-
</form>
|
172 |
-
</div>
|
173 |
-
</div>
|
174 |
-
</div>
|
175 |
-
<?php endforeach ?>
|
176 |
-
</div>
|
177 |
-
<?php endif ?>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/modules/service/templates/index.php
DELETED
@@ -1,105 +0,0 @@
|
|
1 |
-
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
|
2 |
-
|
3 |
-
<div class="panel panel-default">
|
4 |
-
<div class="panel-heading">
|
5 |
-
<h3 class="panel-title"><?php _e( 'Services', 'bookly' ) ?></h3>
|
6 |
-
</div>
|
7 |
-
<div class="panel-body">
|
8 |
-
<div class="ab-wrapper-container">
|
9 |
-
<div class="row">
|
10 |
-
<div class="ab-left-bar col-md-3 col-sm-3 col-xs-12 col-lg-3">
|
11 |
-
<div id="ab-categories-list">
|
12 |
-
<div class="ab-category-item ab-active ab-main-category-item" data-id=""><?php _e( 'All Services', 'bookly' ) ?></div>
|
13 |
-
<ul id="ab-category-item-list" class="ab-category-item-list">
|
14 |
-
<?php foreach ( $category_collection as $category ): ?>
|
15 |
-
<li class="ab-category-item" data-id="<?php echo $category['id'] ?>">
|
16 |
-
<span class="ab-handle" title="<?php esc_attr_e( 'Reorder', 'bookly' ) ?>">
|
17 |
-
<i class="ab-inner-handle glyphicon glyphicon-align-justify"></i>
|
18 |
-
</span>
|
19 |
-
<span class="left displayed-value"><?php echo esc_html( $category['name'] ) ?></span>
|
20 |
-
<a href="#" class="left ab-hidden ab-edit"></a>
|
21 |
-
<input class="form-control value ab-value" type="text" name="name" value="<?php echo esc_attr( $category['name'] ) ?>" style="display: none" />
|
22 |
-
<a href="#" class="left ab-hidden ab-delete"></a>
|
23 |
-
</li>
|
24 |
-
<?php endforeach ?>
|
25 |
-
</ul>
|
26 |
-
</div>
|
27 |
-
<input type="hidden" id="color" />
|
28 |
-
<div id="new_category_popup" class="ab-popup-wrapper">
|
29 |
-
<input class="btn btn-info ab-popup-trigger" type="submit" value="<?php _e( 'New Category', 'bookly' ) ?>" />
|
30 |
-
<div class="ab-popup" style="display: none; margin-top: 10px;">
|
31 |
-
<div class="ab-arrow"></div>
|
32 |
-
<div class="ab-content">
|
33 |
-
<form method="post" id="new-category-form">
|
34 |
-
<table class="form-horizontal">
|
35 |
-
<tr>
|
36 |
-
<td>
|
37 |
-
<input class="form-control ab-clear-text" style="width: 170px" type="text" name="name" />
|
38 |
-
<input type="hidden" name="action" value="ab_category_form" />
|
39 |
-
</td>
|
40 |
-
</tr>
|
41 |
-
<tr>
|
42 |
-
<td>
|
43 |
-
<?php AB_Utils::submitButton() ?>
|
44 |
-
<a class="ab-popup-close" href="#"><?php _e( 'Cancel', 'bookly' ) ?></a>
|
45 |
-
</td>
|
46 |
-
</tr>
|
47 |
-
</table>
|
48 |
-
<a class="ab-popup-close ab-popup-close-icon" href="#"></a>
|
49 |
-
</form>
|
50 |
-
</div>
|
51 |
-
</div>
|
52 |
-
</div>
|
53 |
-
</div>
|
54 |
-
<div class="ab-right-content col-md-9 col-sm-9 col-xs-12 col-lg-9" id="ab_services_wrapper">
|
55 |
-
<h2 class="ab-category-title"><?php _e( 'All Services', 'bookly' ) ?></h2>
|
56 |
-
<div class="no-result"<?php if ( ! empty ( $service_collection ) ) : ?> style="display: none"<?php endif ?>><?php _e( 'No services found. Please add services.', 'bookly' ) ?></div>
|
57 |
-
<div class="list-wrapper">
|
58 |
-
<div id="ab-services-list">
|
59 |
-
<?php include '_list.php' ?>
|
60 |
-
</div>
|
61 |
-
<div class="list-actions">
|
62 |
-
<a class="add-service btn btn-info" href="#"><?php _e( 'Add Service', 'bookly' ) ?></a>
|
63 |
-
<a class="delete btn btn-info" href="#"><?php _e( 'Delete', 'bookly' ) ?></a>
|
64 |
-
</div>
|
65 |
-
</div>
|
66 |
-
</div>
|
67 |
-
</div>
|
68 |
-
<div id="ab-staff-update" class="modal fade">
|
69 |
-
<div class="modal-dialog">
|
70 |
-
<div class="modal-content">
|
71 |
-
<div class="modal-header">
|
72 |
-
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
73 |
-
<h4 class="modal-title"><?php _e( 'Update service setting', 'bookly' ) ?></h4>
|
74 |
-
</div>
|
75 |
-
<div class="modal-body" style="white-space: normal">
|
76 |
-
<span class="help-block"><?php _e( 'You are about to change a service setting which is also configured separately for each staff member. Do you want to update it in staff settings too?', 'bookly' ) ?></span>
|
77 |
-
<label>
|
78 |
-
<input style="margin: 0" id="ab-remember-my-choice" type="checkbox" /> <?php _e( 'Remember my choice', 'bookly' ) ?>
|
79 |
-
</label>
|
80 |
-
</div>
|
81 |
-
<div class="modal-footer">
|
82 |
-
<button type="reset" class="btn btn-default ab-no" data-dismiss="modal" aria-hidden="true"><?php _e( 'No, update just here in services', 'bookly' ) ?></button>
|
83 |
-
<button type="submit" class="btn btn-primary ab-yes"><?php _e( 'Yes', 'bookly' ) ?></button>
|
84 |
-
</div>
|
85 |
-
</div><!-- /.modal-content -->
|
86 |
-
</div><!-- /.modal-dialog -->
|
87 |
-
</div><!-- /.modal -->
|
88 |
-
</div>
|
89 |
-
</div>
|
90 |
-
</div>
|
91 |
-
<div class="modal fade" id="lite_notice" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
|
92 |
-
<div class="modal-dialog">
|
93 |
-
<div class="modal-content">
|
94 |
-
<div class="modal-header">
|
95 |
-
<h4 class="modal-title"><?php _e('Notice', 'bookly') ?></h4>
|
96 |
-
</div>
|
97 |
-
<div class="modal-body">
|
98 |
-
<?php _e('This function is disabled in the lite version of Bookly. If you find the plugin useful for your business please consider buying a licence for the full version. It costs just $46 and for this money you will get many useful functions, lifetime free update and excellent support! More information can be found here', 'bookly'); ?>: <a href="http://booking-wp-plugin.com" target="_blank">http://booking-wp-plugin.com</a>
|
99 |
-
</div>
|
100 |
-
<div class="modal-footer">
|
101 |
-
<button type="button" class="btn btn-default" data-dismiss="modal"><?php _e('Close', 'bookly') ?></button>
|
102 |
-
</div>
|
103 |
-
</div><!-- /.modal-content -->
|
104 |
-
</div><!-- /.modal-dialog -->
|
105 |
-
</div><!-- /.modal -->
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/modules/services/Controller.php
ADDED
@@ -0,0 +1,288 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace BooklyLite\Backend\Modules\Services;
|
3 |
+
|
4 |
+
use BooklyLite\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Controller
|
8 |
+
* @package BooklyLite\Backend\Modules\Services
|
9 |
+
*/
|
10 |
+
class Controller extends Lib\Base\Controller
|
11 |
+
{
|
12 |
+
const page_slug = 'ab-services';
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Index page.
|
16 |
+
*/
|
17 |
+
public function index()
|
18 |
+
{
|
19 |
+
wp_enqueue_media();
|
20 |
+
$this->enqueueStyles( array(
|
21 |
+
'wp' => array( 'wp-color-picker' ),
|
22 |
+
'frontend' => array( 'css/ladda.min.css' ),
|
23 |
+
'backend' => array( 'bootstrap/css/bootstrap-theme.min.css' ),
|
24 |
+
) );
|
25 |
+
|
26 |
+
$this->enqueueScripts( array(
|
27 |
+
'wp' => array( 'wp-color-picker' ),
|
28 |
+
'backend' => array(
|
29 |
+
'bootstrap/js/bootstrap.min.js' => array( 'jquery' ),
|
30 |
+
'js/help.js' => array( 'jquery' ),
|
31 |
+
'js/alert.js' => array( 'jquery' ),
|
32 |
+
),
|
33 |
+
'module' => array( 'js/service.js' => array( 'jquery-ui-sortable', 'jquery' ) ),
|
34 |
+
'frontend' => array(
|
35 |
+
'js/spin.min.js' => array( 'jquery' ),
|
36 |
+
'js/ladda.min.js' => array( 'ab-spin.min.js', 'jquery' ),
|
37 |
+
)
|
38 |
+
) );
|
39 |
+
|
40 |
+
wp_localize_script( 'ab-service.js', 'BooklyL10n', array(
|
41 |
+
'are_you_sure' => __( 'Are you sure?', 'bookly' ),
|
42 |
+
'saved' => __( 'Settings saved.', 'bookly' ),
|
43 |
+
'selector' => array( 'nothing_selected' => __( 'No staff selected', 'bookly' ), ),
|
44 |
+
'limitations' => __( '<b class="h4">This function is disabled in the Lite version of Bookly.</b><br><br>If you find the plugin useful for your business please consider buying a licence for the full version.<br>It costs just $59 and for this money you will get many useful functions, lifetime free updates and excellent support!<br>More information can be found here', 'bookly' ) . ': <a href="http://booking-wp-plugin.com" target="_blank" class="alert-link">http://booking-wp-plugin.com</a>',
|
45 |
+
) );
|
46 |
+
|
47 |
+
$staff_collection = $this->getStaffCollection();
|
48 |
+
$category_collection = $this->getCategoryCollection();
|
49 |
+
$service_collection = $this->getServiceCollection();
|
50 |
+
$this->render( 'index', compact( 'staff_collection', 'category_collection', 'service_collection' ) );
|
51 |
+
}
|
52 |
+
|
53 |
+
/**
|
54 |
+
*
|
55 |
+
*/
|
56 |
+
public function executeGetCategoryServices()
|
57 |
+
{
|
58 |
+
$this->setDataForServiceList();
|
59 |
+
wp_send_json_success( $this->render( '_list', array(), false ) );
|
60 |
+
}
|
61 |
+
|
62 |
+
/**
|
63 |
+
*
|
64 |
+
*/
|
65 |
+
public function executeCategoryForm()
|
66 |
+
{
|
67 |
+
if ( ! empty ( $_POST ) ) {
|
68 |
+
$form = new Forms\Category();
|
69 |
+
$form->bind( $this->getPostParameters() );
|
70 |
+
if ( $category = $form->save() ) {
|
71 |
+
$this->render( '_category_item', array( 'category' => $category->getFields() ) );
|
72 |
+
}
|
73 |
+
}
|
74 |
+
exit;
|
75 |
+
}
|
76 |
+
|
77 |
+
/**
|
78 |
+
* Update category.
|
79 |
+
*/
|
80 |
+
public function executeUpdateCategory()
|
81 |
+
{
|
82 |
+
$form = new Forms\Category();
|
83 |
+
$form->bind( $this->getPostParameters() );
|
84 |
+
$form->save();
|
85 |
+
}
|
86 |
+
|
87 |
+
/**
|
88 |
+
* Update category position.
|
89 |
+
*/
|
90 |
+
public function executeUpdateCategoryPosition()
|
91 |
+
{
|
92 |
+
$category_sorts = $this->getParameter( 'position' );
|
93 |
+
foreach ( $category_sorts as $position => $category_id ) {
|
94 |
+
$category_sort = new Lib\Entities\Category();
|
95 |
+
$category_sort->load( $category_id );
|
96 |
+
$category_sort->set( 'position', $position );
|
97 |
+
$category_sort->save();
|
98 |
+
}
|
99 |
+
}
|
100 |
+
|
101 |
+
/**
|
102 |
+
* Update services position.
|
103 |
+
*/
|
104 |
+
public function executeUpdateServicesPosition()
|
105 |
+
{
|
106 |
+
$services_sorts = $this->getParameter( 'position' );
|
107 |
+
foreach ( $services_sorts as $position => $service_ids ) {
|
108 |
+
$services_sort = new Lib\Entities\Service();
|
109 |
+
$services_sort->load( $service_ids );
|
110 |
+
$services_sort->set( 'position', $position );
|
111 |
+
$services_sort->save();
|
112 |
+
}
|
113 |
+
}
|
114 |
+
|
115 |
+
/**
|
116 |
+
* Delete category.
|
117 |
+
*/
|
118 |
+
public function executeDeleteCategory()
|
119 |
+
{
|
120 |
+
$category = new Lib\Entities\Category();
|
121 |
+
$category->set( 'id', $this->getParameter( 'id', 0 ) );
|
122 |
+
$category->delete();
|
123 |
+
}
|
124 |
+
|
125 |
+
public function executeAddService()
|
126 |
+
{
|
127 |
+
$form = new Forms\Service();
|
128 |
+
$form->bind( $this->getPostParameters() );
|
129 |
+
$form->getObject()->set( 'duration', Lib\Config::getTimeSlotLength() );
|
130 |
+
$service = $form->save();
|
131 |
+
$this->setDataForServiceList( $service->get( 'category_id' ) );
|
132 |
+
wp_send_json_success( array( 'html' => $this->render( '_list', array(), false ), 'service_id' => $service->get( 'id' ) ) );
|
133 |
+
}
|
134 |
+
|
135 |
+
public function executeRemoveServices()
|
136 |
+
{
|
137 |
+
$service_ids = $this->getParameter( 'service_ids', array() );
|
138 |
+
if ( is_array( $service_ids ) && ! empty ( $service_ids ) ) {
|
139 |
+
Lib\Entities\Service::query( 's' )->delete()->whereIn( 's.id', $service_ids )->execute();
|
140 |
+
}
|
141 |
+
}
|
142 |
+
|
143 |
+
/**
|
144 |
+
* Update service parameters and assign staff
|
145 |
+
*/
|
146 |
+
public function executeUpdateService()
|
147 |
+
{
|
148 |
+
/** @var \wpdb $wpdb */
|
149 |
+
global $wpdb;
|
150 |
+
|
151 |
+
$form = new Forms\Service();
|
152 |
+
$form->bind( $this->getPostParameters() );
|
153 |
+
$service = $form->save();
|
154 |
+
|
155 |
+
$staff_ids = $this->getParameter( 'staff_ids', array() );
|
156 |
+
if ( empty( $staff_ids ) ) {
|
157 |
+
Lib\Entities\StaffService::query()->delete()->where( 'service_id', $service->get( 'id' ) )->execute();
|
158 |
+
} else {
|
159 |
+
Lib\Entities\StaffService::query()->delete()->where( 'service_id', $service->get( 'id' ) )->whereNotIn( 'staff_id', $staff_ids )->execute();
|
160 |
+
if ( $this->getParameter( 'update_staff', false ) ) {
|
161 |
+
$wpdb->update( Lib\Entities\StaffService::getTableName(), array( 'price' => $this->getParameter( 'price' ) ), array( 'service_id' => $this->getParameter( 'id' ) ) );
|
162 |
+
$wpdb->update( Lib\Entities\StaffService::getTableName(), array( 'capacity' => 1 ), array( 'service_id' => $this->getParameter( 'id' ) ) );
|
163 |
+
}
|
164 |
+
$service_staff_exists = Lib\Entities\StaffService::query()->select( 'staff_id' )->where( 'service_id', $service->get( 'id' ) )->fetchArray();
|
165 |
+
$service_staff = array();
|
166 |
+
foreach ( $service_staff_exists as $staff ) {
|
167 |
+
$service_staff[] = $staff['staff_id'];
|
168 |
+
}
|
169 |
+
foreach ( $staff_ids as $staff_id ) {
|
170 |
+
if ( ! in_array( $staff_id, $service_staff ) ) {
|
171 |
+
$staff_service = new Lib\Entities\StaffService();
|
172 |
+
$staff_service->set( 'staff_id', $staff_id );
|
173 |
+
$staff_service->set( 'service_id', $service->get( 'id' ) );
|
174 |
+
$staff_service->set( 'price', $service->get( 'price' ) );
|
175 |
+
$staff_service->set( 'capacity', 1 );
|
176 |
+
$staff_service->save();
|
177 |
+
}
|
178 |
+
}
|
179 |
+
}
|
180 |
+
|
181 |
+
do_action( 'bookly_update_service', $service, $this->getPostParameters() );
|
182 |
+
|
183 |
+
/** @var \BooklyServiceExtras\Lib\Entities\ServiceExtra[] $service_extras
|
184 |
+
* @var \BooklyServiceExtras\Lib\Entities\ServiceExtra[] $db_extras
|
185 |
+
*/
|
186 |
+
$service_extras = apply_filters( 'bookly_extras_find_by_service_id', array(), $service->get( 'id' ) );
|
187 |
+
$db_extras = array();
|
188 |
+
foreach ( $service_extras as $extra ) {
|
189 |
+
$db_extras[ $extra->get( 'id' ) ] = $extra;
|
190 |
+
}
|
191 |
+
$new_extras = array();
|
192 |
+
// Find id for new extras.
|
193 |
+
foreach ( $this->getParameter( 'extras', array() ) as $_post_id => $_post_extra ) {
|
194 |
+
if ( isset( $db_extras[ $_post_id ] ) ) {
|
195 |
+
unset( $db_extras[ $_post_id ] );
|
196 |
+
} else {
|
197 |
+
foreach ( $db_extras as $id => $extra ) {
|
198 |
+
if ( $extra->get( 'title' ) == $_post_extra['title']
|
199 |
+
&& $extra->get( 'price' ) == $_post_extra['price']
|
200 |
+
&& $extra->get( 'duration' ) == $_post_extra['duration']
|
201 |
+
) {
|
202 |
+
$new_extras[ $_post_id ] = $id;
|
203 |
+
unset( $db_extras[ $id ] );
|
204 |
+
break;
|
205 |
+
}
|
206 |
+
}
|
207 |
+
}
|
208 |
+
}
|
209 |
+
|
210 |
+
$price = Lib\Utils\Common::formatPrice( $service->get( 'price' ) );
|
211 |
+
if ( $service->get( 'type' ) == Lib\Entities\Service::TYPE_SIMPLE ) {
|
212 |
+
$nice_duration = Lib\Utils\DateTime::secondsToInterval( $service->get( 'duration' ) );
|
213 |
+
} else {
|
214 |
+
$nice_duration = sprintf( _n( '%d service', '%d services', count( json_decode( $service->get( 'sub_services' ), true ) ), 'bookly' ), count( json_decode( $service->get( 'sub_services' ), true ) ) );
|
215 |
+
}
|
216 |
+
|
217 |
+
wp_send_json_success( array( 'title' => $service->get( 'title' ), 'price' => $price, 'color' => $service->get( 'color' ), 'nice_duration' => $nice_duration, 'new_extras' => $new_extras ) );
|
218 |
+
}
|
219 |
+
|
220 |
+
/**
|
221 |
+
* @param int $category_id
|
222 |
+
*/
|
223 |
+
private function setDataForServiceList( $category_id = 0 )
|
224 |
+
{
|
225 |
+
if ( ! $category_id ) {
|
226 |
+
$category_id = $this->getParameter( 'category_id', 0 );
|
227 |
+
}
|
228 |
+
|
229 |
+
$this->service_collection = $this->getServiceCollection( $category_id );
|
230 |
+
$this->staff_collection = $this->getStaffCollection();
|
231 |
+
$this->category_collection = $this->getCategoryCollection();
|
232 |
+
}
|
233 |
+
|
234 |
+
/**
|
235 |
+
* @return array
|
236 |
+
*/
|
237 |
+
private function getCategoryCollection()
|
238 |
+
{
|
239 |
+
return Lib\Entities\Category::query()->sortBy( 'position' )->fetchArray();
|
240 |
+
}
|
241 |
+
|
242 |
+
/**
|
243 |
+
* @return array
|
244 |
+
*/
|
245 |
+
private function getStaffCollection()
|
246 |
+
{
|
247 |
+
return Lib\Entities\Staff::query()->fetchArray();
|
248 |
+
}
|
249 |
+
|
250 |
+
/**
|
251 |
+
* @param int $id
|
252 |
+
* @return array
|
253 |
+
*/
|
254 |
+
private function getServiceCollection( $id = 0 )
|
255 |
+
{
|
256 |
+
$services = Lib\Entities\Service::query( 's' )
|
257 |
+
->select( 's.*, COUNT(staff.id) AS total_staff, GROUP_CONCAT(DISTINCT staff.id) AS staff_ids' )
|
258 |
+
->leftJoin( 'StaffService', 'ss', 'ss.service_id = s.id' )
|
259 |
+
->leftJoin( 'Staff', 'staff', 'staff.id = ss.staff_id' )
|
260 |
+
->whereRaw( 's.category_id = %d OR !%d', array( $id, $id ) )
|
261 |
+
->groupBy( 's.id' )
|
262 |
+
->indexBy( 'id' )
|
263 |
+
->sortBy( 's.position' );
|
264 |
+
|
265 |
+
return $services->fetchArray();
|
266 |
+
}
|
267 |
+
|
268 |
+
public function executeUpdateExtraPosition()
|
269 |
+
{
|
270 |
+
do_action( 'bookly_extras_reorder', $this->getParameter( 'position' ) );
|
271 |
+
|
272 |
+
wp_send_json_success();
|
273 |
+
}
|
274 |
+
|
275 |
+
// Protected methods.
|
276 |
+
|
277 |
+
/**
|
278 |
+
* Override parent method to add 'wp_ajax_ab_' prefix
|
279 |
+
* so current 'execute*' methods look nicer.
|
280 |
+
*
|
281 |
+
* @param string $prefix
|
282 |
+
*/
|
283 |
+
protected function registerWpActions( $prefix = '' )
|
284 |
+
{
|
285 |
+
parent::registerWpActions( 'wp_ajax_ab_' );
|
286 |
+
}
|
287 |
+
|
288 |
+
}
|
backend/modules/services/forms/Category.php
ADDED
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace BooklyLite\Backend\Modules\Services\Forms;
|
3 |
+
|
4 |
+
use \BooklyLite\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Category
|
8 |
+
* @package BooklyLite\Backend\Modules\Services\Forms
|
9 |
+
*/
|
10 |
+
class Category extends Lib\Base\Form
|
11 |
+
{
|
12 |
+
protected static $entity_class = 'Category';
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Configure the form.
|
16 |
+
*/
|
17 |
+
public function configure()
|
18 |
+
{
|
19 |
+
$this->setFields( array( 'name' ) );
|
20 |
+
}
|
21 |
+
|
22 |
+
}
|
backend/modules/services/forms/Service.php
ADDED
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace BooklyLite\Backend\Modules\Services\Forms;
|
3 |
+
|
4 |
+
use BooklyLite\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Service
|
8 |
+
* @package BooklyLite\Backend\Modules\Services\Forms
|
9 |
+
*/
|
10 |
+
class Service extends Lib\Base\Form
|
11 |
+
{
|
12 |
+
protected static $entity_class = 'Service';
|
13 |
+
|
14 |
+
public function configure()
|
15 |
+
{
|
16 |
+
$this->setFields( array( 'id', 'title', 'duration', 'price', 'category_id', 'color', 'capacity', 'info', 'type', 'sub_services', 'visibility' ) );
|
17 |
+
}
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Bind values to form.
|
21 |
+
*
|
22 |
+
* @param array $_post
|
23 |
+
* @param array $files
|
24 |
+
*/
|
25 |
+
public function bind( array $_post, array $files = array() )
|
26 |
+
{
|
27 |
+
if ( array_key_exists( 'category_id', $_post ) && ! $_post['category_id'] ) {
|
28 |
+
$_post['category_id'] = null;
|
29 |
+
}
|
30 |
+
parent::bind( $_post, $files );
|
31 |
+
}
|
32 |
+
|
33 |
+
/**
|
34 |
+
* @return \BooklyLite\Lib\Entities\Service
|
35 |
+
*/
|
36 |
+
public function save()
|
37 |
+
{
|
38 |
+
if ( $this->isNew() ) {
|
39 |
+
// When adding new service - set its color randomly.
|
40 |
+
$this->data['color'] = sprintf( '#%06X', mt_rand( 0, 0x64FFFF ) );
|
41 |
+
}
|
42 |
+
|
43 |
+
if ( $this->data['type'] == Lib\Entities\Service::TYPE_SIMPLE || ! array_key_exists( 'sub_services', $this->data ) || empty( $this->data['sub_services'] ) ) {
|
44 |
+
$this->data['sub_services'] = '[]';
|
45 |
+
} elseif ( is_array( $this->data['sub_services'] ) ) {
|
46 |
+
$this->data['sub_services'] = json_encode( (array) $this->data['sub_services'] );
|
47 |
+
}
|
48 |
+
|
49 |
+
return parent::save();
|
50 |
+
}
|
51 |
+
|
52 |
+
}
|
backend/modules/services/resources/js/service.js
ADDED
@@ -0,0 +1,511 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
jQuery(function($) {
|
2 |
+
var $no_result = $('#bookly-services-wrapper .no-result');
|
3 |
+
// Remember user choice in the modal dialog.
|
4 |
+
var update_staff_choice = null,
|
5 |
+
$new_category_popover = $('#bookly-new-category'),
|
6 |
+
$new_category_form = $('#new-category-form'),
|
7 |
+
$new_category_name = $('#bookly-category-name');
|
8 |
+
|
9 |
+
$new_category_popover.popover({
|
10 |
+
html: true,
|
11 |
+
placement: 'bottom',
|
12 |
+
template: '<div class="popover" role="tooltip"><div class="popover-arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>',
|
13 |
+
content: $new_category_form.show().detach(),
|
14 |
+
trigger: 'manual'
|
15 |
+
}).on('click', function () {
|
16 |
+
$(this).popover('toggle');
|
17 |
+
}).on('shown.bs.popover', function () {
|
18 |
+
// focus input
|
19 |
+
$new_category_name.focus();
|
20 |
+
}).on('hidden.bs.popover', function (e) {
|
21 |
+
//clear input
|
22 |
+
$new_category_name.val('');
|
23 |
+
});
|
24 |
+
|
25 |
+
// Save new category.
|
26 |
+
$new_category_form.on('submit', function() {
|
27 |
+
var data = $(this).serialize();
|
28 |
+
|
29 |
+
$.post(ajaxurl, data, function(response) {
|
30 |
+
$('#bookly-category-item-list').append(response);
|
31 |
+
var $new_category = $('.bookly-category-item:last');
|
32 |
+
// add created category to services
|
33 |
+
$('select[name="category_id"]').append('<option value="' + $new_category.data('category-id') + '">' + $new_category.find('input').val() + '</option>');
|
34 |
+
});
|
35 |
+
$new_category_popover.popover('hide');
|
36 |
+
return false;
|
37 |
+
});
|
38 |
+
|
39 |
+
// Cancel button.
|
40 |
+
$new_category_form.on('click', 'button[type="button"]', function (e) {
|
41 |
+
$new_category_popover.popover('hide');
|
42 |
+
});
|
43 |
+
|
44 |
+
// Categories list delegated events.
|
45 |
+
$('#bookly-categories-list')
|
46 |
+
|
47 |
+
// On category item click.
|
48 |
+
.on('click', '.bookly-category-item', function(e) {
|
49 |
+
if ($(e.target).is('.bookly-js-handle')) return;
|
50 |
+
$('#ab-services-list').html('<div class="bookly-loading"></div>');
|
51 |
+
var $clicked = $(this);
|
52 |
+
|
53 |
+
$.get(ajaxurl, {action:'ab_get_category_services', category_id: $clicked.data('category-id')}, function(response) {
|
54 |
+
if ( response.success ) {
|
55 |
+
$('.bookly-category-item').not($clicked).removeClass('active');
|
56 |
+
$clicked.addClass('active');
|
57 |
+
$('.bookly-category-title').text($clicked.text());
|
58 |
+
refreshList(response.data, 0);
|
59 |
+
}
|
60 |
+
});
|
61 |
+
})
|
62 |
+
|
63 |
+
// On edit category click.
|
64 |
+
.on('click', '.bookly-js-edit', function(e) {
|
65 |
+
// Keep category item click from being executed.
|
66 |
+
e.stopPropagation();
|
67 |
+
// Prevent navigating to '#'.
|
68 |
+
e.preventDefault();
|
69 |
+
var $this = $(this).closest('.bookly-category-item');
|
70 |
+
$this.find('.displayed-value').hide();
|
71 |
+
$this.find('input').show().focus();
|
72 |
+
})
|
73 |
+
|
74 |
+
// On blur save changes.
|
75 |
+
.on('blur', 'input', function() {
|
76 |
+
var $this = $(this),
|
77 |
+
$item = $this.closest('.bookly-category-item'),
|
78 |
+
field = $this.attr('name'),
|
79 |
+
value = $this.val(),
|
80 |
+
id = $item.data('category-id'),
|
81 |
+
data = { action: 'ab_update_category', id: id };
|
82 |
+
data[field] = value;
|
83 |
+
$.post(ajaxurl, data, function(response) {
|
84 |
+
// Hide input field.
|
85 |
+
$item.find('input').hide();
|
86 |
+
$item.find('.displayed-value').show();
|
87 |
+
// Show modified category name.
|
88 |
+
$item.find('.displayed-value').text(value);
|
89 |
+
// update edited category's name for services
|
90 |
+
$('select[name="category_id"] option[value="' + id + '"]').text(value);
|
91 |
+
});
|
92 |
+
})
|
93 |
+
|
94 |
+
// On press Enter save changes.
|
95 |
+
.on('keypress', 'input', function (e) {
|
96 |
+
var code = e.keyCode || e.which;
|
97 |
+
if (code == 13) {
|
98 |
+
$(this).blur();
|
99 |
+
}
|
100 |
+
})
|
101 |
+
|
102 |
+
// On delete category click.
|
103 |
+
.on('click', '.bookly-js-delete', function(e) {
|
104 |
+
// Keep category item click from being executed.
|
105 |
+
e.stopPropagation();
|
106 |
+
// Prevent navigating to '#'.
|
107 |
+
e.preventDefault();
|
108 |
+
// Ask user if he is sure.
|
109 |
+
if (confirm(BooklyL10n.are_you_sure)) {
|
110 |
+
var $item = $(this).closest('.bookly-category-item');
|
111 |
+
var data = { action: 'ab_delete_category', id: $item.data('category-id') };
|
112 |
+
$.post(ajaxurl, data, function(response) {
|
113 |
+
// Remove category item from Services
|
114 |
+
$('select[name="category_id"] option[value="' + $item.data('category-id') + '"]').remove();
|
115 |
+
// Remove category item from DOM.
|
116 |
+
$item.remove();
|
117 |
+
if ($item.is('.active')) {
|
118 |
+
$('.bookly-js-all-services').click();
|
119 |
+
}
|
120 |
+
});
|
121 |
+
}
|
122 |
+
})
|
123 |
+
|
124 |
+
.on('click', 'input', function(e) {
|
125 |
+
e.stopPropagation();
|
126 |
+
});
|
127 |
+
|
128 |
+
// Services list delegated events.
|
129 |
+
$('#bookly-services-wrapper')
|
130 |
+
// On click on 'Add Service' button.
|
131 |
+
.on('click', '.add-service', function(e) {
|
132 |
+
e.preventDefault();
|
133 |
+
var ladda = Ladda.create(this);
|
134 |
+
ladda.start();
|
135 |
+
var selected_category_id = $('#bookly-categories-list .active').data('category-id'),
|
136 |
+
data = { action: 'ab_add_service' };
|
137 |
+
if (selected_category_id) {
|
138 |
+
data['category_id'] = selected_category_id;
|
139 |
+
}
|
140 |
+
$.post(ajaxurl, data, function(response) {
|
141 |
+
refreshList(response.data.html, response.data.service_id);
|
142 |
+
ladda.stop();
|
143 |
+
});
|
144 |
+
})
|
145 |
+
// On click on 'Delete' button.
|
146 |
+
.on('click', '#bookly-delete', function(e) {
|
147 |
+
if (confirm(BooklyL10n.are_you_sure)) {
|
148 |
+
var ladda = Ladda.create(this);
|
149 |
+
ladda.start();
|
150 |
+
|
151 |
+
var $for_delete = $('.service-checker:checked'),
|
152 |
+
data = { action: 'ab_remove_services' },
|
153 |
+
services = [],
|
154 |
+
$panels = [];
|
155 |
+
|
156 |
+
$for_delete.each(function(){
|
157 |
+
var panel = $(this).parents('.bookly-js-collapse');
|
158 |
+
$panels.push(panel);
|
159 |
+
services.push(this.value);
|
160 |
+
});
|
161 |
+
data['service_ids[]'] = services;
|
162 |
+
$.post(ajaxurl, data, function() {
|
163 |
+
ladda.stop();
|
164 |
+
$.each($panels.reverse(), function (index) {
|
165 |
+
$(this).delay(500 * index).fadeOut(200, function () {
|
166 |
+
$(this).remove();
|
167 |
+
});
|
168 |
+
});
|
169 |
+
});
|
170 |
+
}
|
171 |
+
})
|
172 |
+
|
173 |
+
.on('change', 'input.bookly-check-all-entities, input.bookly-js-check-entity', function () {
|
174 |
+
var $panel = $(this).parents('.bookly-js-collapse');
|
175 |
+
if ($(this).hasClass('bookly-check-all-entities')) {
|
176 |
+
$panel.find('.bookly-js-check-entity').prop('checked', $(this).prop('checked'));
|
177 |
+
} else {
|
178 |
+
$panel.find('.bookly-check-all-entities').prop('checked', $panel.find('.bookly-js-check-entity:not(:checked)').length == 0);
|
179 |
+
}
|
180 |
+
updateStaffButton($panel);
|
181 |
+
});
|
182 |
+
|
183 |
+
// Modal window events.
|
184 |
+
var $modal = $('#ab-staff-update');
|
185 |
+
$modal
|
186 |
+
.on('click', '.ab-yes', function() {
|
187 |
+
$modal.modal('hide');
|
188 |
+
if ( $('#ab-remember-my-choice').prop('checked') ) {
|
189 |
+
update_staff_choice = true;
|
190 |
+
}
|
191 |
+
submitServiceFrom($modal.data('input'),true);
|
192 |
+
})
|
193 |
+
.on('click', '.ab-no', function() {
|
194 |
+
if ( $('#ab-remember-my-choice').prop('checked') ) {
|
195 |
+
update_staff_choice = false;
|
196 |
+
}
|
197 |
+
submitServiceFrom($modal.data('input'),false);
|
198 |
+
});
|
199 |
+
|
200 |
+
function refreshList(response,service_id) {
|
201 |
+
var $list = $('#ab-services-list');
|
202 |
+
$list.html(response);
|
203 |
+
if (response.indexOf('panel') >= 0) {
|
204 |
+
$no_result.hide();
|
205 |
+
makeServicesSortable();
|
206 |
+
onCollapseInitChildren();
|
207 |
+
$list.booklyHelp();
|
208 |
+
} else {
|
209 |
+
$no_result.show();
|
210 |
+
}
|
211 |
+
$('#service_' + service_id).collapse('show');
|
212 |
+
$('#service_' + service_id).find('input[name=title]').focus();
|
213 |
+
}
|
214 |
+
|
215 |
+
function initColorPicker($jquery_collection) {
|
216 |
+
$jquery_collection.each(function(){
|
217 |
+
$(this).data('last-color', $(this).val());
|
218 |
+
});
|
219 |
+
$jquery_collection.wpColorPicker({
|
220 |
+
width: 200
|
221 |
+
});
|
222 |
+
}
|
223 |
+
|
224 |
+
$('#ab-services-list').on('change', '[name=capacity]', function(){
|
225 |
+
if ($(this).val() > 1) {
|
226 |
+
booklyAlert({error: [BooklyL10n.limitations]});
|
227 |
+
$(this).val('1').prop('readonly',true);
|
228 |
+
}
|
229 |
+
}).on('change', '[name=padding_left],[name=padding_right]', function(){
|
230 |
+
if ($(this).val() > 0) {
|
231 |
+
booklyAlert({error: [BooklyL10n.limitations]});
|
232 |
+
$(this).val('0').prop('readonly', true);
|
233 |
+
$(this).find('option:gt(0)').prop('disabled', true);
|
234 |
+
}
|
235 |
+
});
|
236 |
+
|
237 |
+
function submitServiceFrom($form, update_staff) {
|
238 |
+
$form.find('input[name=update_staff]').val(update_staff ? 1 : 0);
|
239 |
+
var ladda = Ladda.create($form.find('button[type=submit]').get(0)),
|
240 |
+
data = $form.serializeArray();
|
241 |
+
ladda.start();
|
242 |
+
if ($form.find('input[name=type]:checked').val() == 'compound') {
|
243 |
+
$form.find('li[data-sub-service-id]').each(function () {
|
244 |
+
data.push({name: 'sub_services[]', value: $(this).data('sub-service-id')});
|
245 |
+
});
|
246 |
+
} else {
|
247 |
+
data.push({name: 'type', value: 'simple'});
|
248 |
+
data.push({name: 'sub_services[]', value: false});
|
249 |
+
}
|
250 |
+
$.post(ajaxurl, data, function (response) {
|
251 |
+
if (response.success) {
|
252 |
+
var $panel = $form.parents('.bookly-js-collapse'),
|
253 |
+
$price = $form.find('[name=price]'),
|
254 |
+
$capacity = $form.find('[name=capacity]');
|
255 |
+
$panel.find('.bookly-js-service-color').css('background-color', response.data.color);
|
256 |
+
$panel.find('.bookly-js-service-title').html(response.data.title);
|
257 |
+
$panel.find('.bookly-js-service-duration').html(response.data.nice_duration);
|
258 |
+
$panel.find('.bookly-js-service-price').html(response.data.price);
|
259 |
+
|
260 |
+
|
261 |
+
$price.data('last_value', $price.val());
|
262 |
+
$capacity.data('last_value', $capacity.val());
|
263 |
+
booklyAlert({success : [BooklyL10n.saved]});
|
264 |
+
$.each(response.data.new_extras, function (front_id, real_id) {
|
265 |
+
var $li = $('li.extra.new[data-extra-id="' + front_id + '"]', $form);
|
266 |
+
$('[name^="extras"]', $li).each(function () {
|
267 |
+
var name = $(this).attr('name');
|
268 |
+
name = name.replace('[' + front_id + ']', '[' + real_id + ']');
|
269 |
+
$(this).attr('name', name);
|
270 |
+
});
|
271 |
+
$li.data('extra-id', real_id).removeClass('new');
|
272 |
+
$li.append('<input type="hidden" value="' + real_id + '" name="extras[' + real_id + '][id]">');
|
273 |
+
});
|
274 |
+
} else {
|
275 |
+
booklyAlert({error: [response.data.message]});
|
276 |
+
}
|
277 |
+
}, 'json').always(function() {
|
278 |
+
ladda.stop();
|
279 |
+
});
|
280 |
+
}
|
281 |
+
|
282 |
+
function updateStaffButton($panel) {
|
283 |
+
var staff_checked = $panel.find('.bookly-js-check-entity:checked').length;
|
284 |
+
if (staff_checked == 0) {
|
285 |
+
$panel.find('.bookly-entity-counter').text(BooklyL10n.selector.nothing_selected);
|
286 |
+
} else if (staff_checked == 1) {
|
287 |
+
$panel.find('.bookly-entity-counter').text($panel.find('.bookly-js-check-entity:checked').data('staff_name'));
|
288 |
+
} else {
|
289 |
+
$panel.find('.bookly-entity-counter').text(staff_checked + '/' + $panel.find('.bookly-js-check-entity').length);
|
290 |
+
}
|
291 |
+
}
|
292 |
+
|
293 |
+
var $category = $('#bookly-category-item-list');
|
294 |
+
$category.sortable({
|
295 |
+
axis : 'y',
|
296 |
+
handle : '.bookly-js-handle',
|
297 |
+
update : function( event, ui ) {
|
298 |
+
var data = [];
|
299 |
+
$category.children('li').each(function() {
|
300 |
+
var $this = $(this);
|
301 |
+
var position = $this.data('category-id');
|
302 |
+
data.push(position);
|
303 |
+
});
|
304 |
+
$.ajax({
|
305 |
+
type : 'POST',
|
306 |
+
url : ajaxurl,
|
307 |
+
data : { action: 'ab_update_category_position', position: data }
|
308 |
+
});
|
309 |
+
}
|
310 |
+
});
|
311 |
+
|
312 |
+
function makeServicesSortable() {
|
313 |
+
if ($('.bookly-js-all-services').hasClass('active')) {
|
314 |
+
var $services = $('#services_list'),
|
315 |
+
fixHelper = function(e, ui) {
|
316 |
+
ui.children().each(function() {
|
317 |
+
$(this).width($(this).width());
|
318 |
+
});
|
319 |
+
return ui;
|
320 |
+
};
|
321 |
+
$services.sortable({
|
322 |
+
helper : fixHelper,
|
323 |
+
axis : 'y',
|
324 |
+
handle : '.bookly-js-handle',
|
325 |
+
update : function( event, ui ) {
|
326 |
+
var data = [];
|
327 |
+
$services.children('div').each(function() {
|
328 |
+
data.push($(this).data('service-id'));
|
329 |
+
});
|
330 |
+
$.ajax({
|
331 |
+
type : 'POST',
|
332 |
+
url : ajaxurl,
|
333 |
+
data : { action: 'ab_update_services_position', position: data }
|
334 |
+
});
|
335 |
+
}
|
336 |
+
});
|
337 |
+
} else {
|
338 |
+
$('#services_list .bookly-js-handle').hide();
|
339 |
+
}
|
340 |
+
}
|
341 |
+
|
342 |
+
function onCollapseInitChildren() {
|
343 |
+
$('.panel-collapse').on('show.bs.collapse.bookly', function () {
|
344 |
+
var $panel = $(this);
|
345 |
+
var $sub_services = $('.ab--service-list', $panel);
|
346 |
+
initColorPicker($panel.find('.bookly-js-color-picker'));
|
347 |
+
$('input[name=type]', $panel).on( 'click', function(){
|
348 |
+
if ($(this).val() == 'simple') {
|
349 |
+
$('.ab--for-simple', $panel).show();
|
350 |
+
$('.ab--for-compound', $panel).hide();
|
351 |
+
} else {
|
352 |
+
$('.ab--for-simple', $panel).hide();
|
353 |
+
$('.ab--for-compound', $panel).show();
|
354 |
+
}
|
355 |
+
});
|
356 |
+
$('input[name=type]:checked', $panel).trigger('click');
|
357 |
+
|
358 |
+
$('[data-toggle="popover"]').popover({
|
359 |
+
html: true,
|
360 |
+
placement: 'top',
|
361 |
+
trigger: 'hover',
|
362 |
+
template: '<div class="popover bookly-font-xs" style="width: 220px" role="tooltip"><div class="popover-arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>'
|
363 |
+
});
|
364 |
+
|
365 |
+
var initSubServicesLi = function ($li) {
|
366 |
+
$("option[value=" + $li.data('sub-service-id') + "]", $sub_services).prop('disabled', true);
|
367 |
+
$('.ab--sub-service-remove', $li).click(function () {
|
368 |
+
$('li.list-group-item[data-sub-service-id="' + $li.data('sub-service-id') + '"]', $panel).remove();
|
369 |
+
$("option[value=" + $li.data('sub-service-id') + "]", $sub_services).prop('disabled', false);
|
370 |
+
});
|
371 |
+
};
|
372 |
+
|
373 |
+
$('.ab--sub-services li.list-group-item[data-sub-service-id]', $panel).each(function () {
|
374 |
+
initSubServicesLi($(this));
|
375 |
+
});
|
376 |
+
|
377 |
+
$sub_services.on('change', function () {
|
378 |
+
if ($(this).val()) {
|
379 |
+
var $li = $('.ab--templates.services .template_' + $sub_services.val() + ' li').clone();
|
380 |
+
$li.insertBefore($(this).parents('li'));
|
381 |
+
initSubServicesLi($li);
|
382 |
+
$(this).val(0);
|
383 |
+
}
|
384 |
+
});
|
385 |
+
|
386 |
+
$('.ab--sub-services', $panel).sortable({axis: 'y', items: "[data-sub-service-id]"});
|
387 |
+
|
388 |
+
updateStaffButton($(this).parents('.bookly-js-collapse'));
|
389 |
+
|
390 |
+
$panel.find('.ajax-service-send').on('click', function (e) {
|
391 |
+
e.preventDefault();
|
392 |
+
var $form = $(this).parents('form'),
|
393 |
+
show_modal = false;
|
394 |
+
if(update_staff_choice === null) {
|
395 |
+
$('.ab-question', $form).each(function () {
|
396 |
+
if ($(this).data('last_value') != $(this).val()) {
|
397 |
+
show_modal = true;
|
398 |
+
}
|
399 |
+
});
|
400 |
+
}
|
401 |
+
if (show_modal) {
|
402 |
+
$modal.data('input', $form).modal('show');
|
403 |
+
} else {
|
404 |
+
submitServiceFrom($form, update_staff_choice);
|
405 |
+
}
|
406 |
+
});
|
407 |
+
|
408 |
+
$panel.find('.js-reset').on('click', function () {
|
409 |
+
$(this).parents('form').trigger('reset');
|
410 |
+
var $color = $(this).parents('form').find('.wp-color-picker'),
|
411 |
+
$panel = $(this).parents('.bookly-js-collapse');
|
412 |
+
$color.val($color.data('last-color')).trigger('change');
|
413 |
+
updateStaffButton($panel);
|
414 |
+
});
|
415 |
+
$panel.find('.ab-question').each(function () {
|
416 |
+
$(this).data('last_value', $(this).val());
|
417 |
+
});
|
418 |
+
$panel.unbind('show.bs.collapse.bookly');
|
419 |
+
});
|
420 |
+
}
|
421 |
+
makeServicesSortable();
|
422 |
+
onCollapseInitChildren();
|
423 |
+
|
424 |
+
/*<Extras>*/
|
425 |
+
$('.extras-container').sortable({
|
426 |
+
axis : 'y',
|
427 |
+
handle : '.bookly-js-handle',
|
428 |
+
update : function( event, ui ) {
|
429 |
+
var data = [];
|
430 |
+
$(this).find('.extra').each(function() {
|
431 |
+
data.push($(this).data('extra-id'));
|
432 |
+
});
|
433 |
+
$.ajax({
|
434 |
+
type : 'POST',
|
435 |
+
url : ajaxurl,
|
436 |
+
data : { action: 'ab_update_extra_position', position: data }
|
437 |
+
});
|
438 |
+
}
|
439 |
+
});
|
440 |
+
|
441 |
+
$(document).on('click', '.bookly-js-collapse .extra-new', function (e) {
|
442 |
+
e.preventDefault();
|
443 |
+
e.stopPropagation();
|
444 |
+
var children = $('.extras-container li');
|
445 |
+
|
446 |
+
var id = 1;
|
447 |
+
children.each(function (i, el) {
|
448 |
+
var elId = parseInt($(el).data('extra-id'));
|
449 |
+
id = (elId >= id) ? elId + 1 : id;
|
450 |
+
});
|
451 |
+
var template = $('.ab--templates.extras').html();
|
452 |
+
var $container = $(this).parents('.bookly-js-collapse').find('.extras-container');
|
453 |
+
id++;
|
454 |
+
$container.append(
|
455 |
+
template.replace(/%id%/g, id)
|
456 |
+
);
|
457 |
+
$('#title_' + id).focus();
|
458 |
+
});
|
459 |
+
|
460 |
+
$(document).on('click', '.bookly-js-collapse .extra-attachment', function (e) {
|
461 |
+
e.preventDefault();
|
462 |
+
e.stopPropagation();
|
463 |
+
var extra = $(this).parents('.extra');
|
464 |
+
var frame = wp.media({
|
465 |
+
library: {type: 'image'},
|
466 |
+
multiple: false
|
467 |
+
});
|
468 |
+
frame.on('select', function () {
|
469 |
+
var selection = frame.state().get('selection').toJSON(),
|
470 |
+
img_src
|
471 |
+
;
|
472 |
+
if (selection.length) {
|
473 |
+
if (selection[0].sizes['thumbnail'] !== undefined) {
|
474 |
+
img_src = selection[0].sizes['thumbnail'].url;
|
475 |
+
} else {
|
476 |
+
img_src = selection[0].url;
|
477 |
+
}
|
478 |
+
extra.find("[name='extras[" + extra.data('extra-id') + "][attachment_id]']").val(selection[0].id);
|
479 |
+
extra.find('.extra-attachment-image').css({'background-image': 'url(' + img_src + ')', 'background-size': 'cover'});
|
480 |
+
extra.find('.bookly-js-remove-attachment').show();
|
481 |
+
$(this).hide();
|
482 |
+
}
|
483 |
+
});
|
484 |
+
|
485 |
+
frame.open();
|
486 |
+
});
|
487 |
+
|
488 |
+
$(document).on('click', '.bookly-js-collapse .bookly-js-remove-attachment', function (e) {
|
489 |
+
e.preventDefault();
|
490 |
+
e.stopPropagation();
|
491 |
+
$(this).hide();
|
492 |
+
var extra = $(this).parents('.extra');
|
493 |
+
extra.find("[name='extras[" + extra.data('extra-id') + "][attachment_id]']").attr('value', '');
|
494 |
+
extra.find('.extra-attachment-image').attr('style', '');
|
495 |
+
extra.find('.extra-attachment').show();
|
496 |
+
});
|
497 |
+
|
498 |
+
$(document).on('click', '.bookly-js-collapse .extra-delete', function (e) {
|
499 |
+
e.preventDefault();
|
500 |
+
e.stopPropagation();
|
501 |
+
if (confirm(BooklyL10n.are_you_sure)) {
|
502 |
+
var extra = $(this).parents('.extra');
|
503 |
+
if (!extra.hasClass('new')) {
|
504 |
+
$.post(ajaxurl, {action: 'bookly_service_extras_delete_service_extra', id: extra.data('extra-id')}, function () {
|
505 |
+
});
|
506 |
+
}
|
507 |
+
extra.remove();
|
508 |
+
}
|
509 |
+
});
|
510 |
+
/*</Extras>*/
|
511 |
+
});
|
backend/modules/services/templates/_category_item.php
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
|
2 |
+
<li class="bookly-nav-item bookly-category-item" data-category-id="<?php echo $category['id'] ?>">
|
3 |
+
<div class="bookly-flexbox">
|
4 |
+
<div class="bookly-flex-cell bookly-vertical-middle" style="width: 1%">
|
5 |
+
<i class="bookly-js-handle bookly-icon bookly-icon-draghandle bookly-margin-right-sm bookly-cursor-move" title="<?php esc_attr_e( 'Reorder', 'bookly' ) ?>"></i>
|
6 |
+
</div>
|
7 |
+
<div class="bookly-flex-cell bookly-vertical-middle">
|
8 |
+
<span class="displayed-value"><?php echo esc_html( $category['name'] ) ?></span>
|
9 |
+
<input class="form-control input-lg" type="text" name="name" value="<?php echo esc_attr( $category['name'] ) ?>" style="display: none"/>
|
10 |
+
</div>
|
11 |
+
<div class="bookly-flex-cell bookly-vertical-middle" style="width: 1%">
|
12 |
+
<a href="#" class="glyphicon glyphicon-edit bookly-margin-horizontal-xs bookly-js-edit" title="<?php esc_attr_e( 'Edit', 'bookly' ) ?>"></a>
|
13 |
+
</div>
|
14 |
+
<div class="bookly-flex-cell bookly-vertical-middle" style="width: 1%">
|
15 |
+
<a href="#" class="glyphicon glyphicon-trash text-danger bookly-js-delete" title="<?php esc_attr_e( 'Delete', 'bookly' ) ?>"></a>
|
16 |
+
</div>
|
17 |
+
</div>
|
18 |
+
</li>
|
backend/modules/services/templates/_list.php
ADDED
@@ -0,0 +1,311 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
+
$time_interval = get_option( 'ab_settings_time_slot_length' );
|
3 |
+
?>
|
4 |
+
<?php if ( ! empty( $service_collection ) ) : ?>
|
5 |
+
<div class="panel-group" id="services_list" role="tablist" aria-multiselectable="true">
|
6 |
+
<?php foreach ( $service_collection as $service ) : ?>
|
7 |
+
<?php $service_id = $service['id'];
|
8 |
+
$assigned_staff_ids = $service['staff_ids'] ? explode( ',', $service['staff_ids'] ) : array();
|
9 |
+
$all_staff_selected = count( $assigned_staff_ids ) == count( $staff_collection );
|
10 |
+
?>
|
11 |
+
<div class="panel panel-default bookly-js-collapse" data-service-id="<?php echo $service_id ?>">
|
12 |
+
<div class="panel-heading" role="tab" id="s_<?php echo $service_id ?>">
|
13 |
+
<div class="row">
|
14 |
+
<div class="col-sm-8 col-xs-10">
|
15 |
+
<div class="bookly-flexbox">
|
16 |
+
<div class="bookly-flex-cell bookly-vertical-middle" style="width: 1%">
|
17 |
+
<i class="bookly-js-handle bookly-icon bookly-icon-draghandle bookly-margin-right-sm bookly-cursor-move"
|
18 |
+
title="<?php esc_attr_e( 'Reorder', 'bookly' ) ?>"></i>
|
19 |
+
</div>
|
20 |
+
<div class="bookly-flex-cell bookly-vertical-middle" style="width: 1%">
|
21 |
+
<span class="bookly-service-color bookly-margin-right-sm bookly-js-service-color"
|
22 |
+
style="background-color: <?php echo esc_attr( $service['color'] ) ?>"> </span>
|
23 |
+
</div>
|
24 |
+
<div class="bookly-flex-cell bookly-vertical-middle">
|
25 |
+
<a role="button" class="panel-title collapsed bookly-js-service-title" data-toggle="collapse"
|
26 |
+
data-parent="#services_list" href="#service_<?php echo $service_id ?>"
|
27 |
+
aria-expanded="false" aria-controls="service_<?php echo $service_id ?>">
|
28 |
+
<?php echo esc_html( $service['title'] ) ?>
|
29 |
+
</a>
|
30 |
+
</div>
|
31 |
+
</div>
|
32 |
+
</div>
|
33 |
+
<div class="col-sm-4 col-xs-2">
|
34 |
+
<div class="bookly-flexbox">
|
35 |
+
<div class="bookly-flex-cell bookly-vertical-middle hidden-xs" style="width: 60%">
|
36 |
+
<span class="bookly-js-service-duration">
|
37 |
+
<?php echo( $service['type'] == \BooklyLite\Lib\Entities\Service::TYPE_SIMPLE ? \BooklyLite\Lib\Utils\DateTime::secondsToInterval( $service['duration'] ) : sprintf( _n( '%d service', '%d services', count( json_decode( $service['sub_services'], true ) ), 'bookly' ), count( json_decode( $service['sub_services'], true ) ) ) ) ?>
|
38 |
+
</span>
|
39 |
+
</div>
|
40 |
+
<div class="bookly-flex-cell bookly-vertical-middle hidden-xs" style="width: 30%">
|
41 |
+
<span class="bookly-js-service-price">
|
42 |
+
<?php echo \BooklyLite\Lib\Utils\Common::formatPrice( $service['price'] ) ?>
|
43 |
+
</span>
|
44 |
+
</div>
|
45 |
+
<div class="bookly-flex-cell bookly-vertical-middle text-right" style="width: 10%">
|
46 |
+
<div class="checkbox bookly-margin-remove">
|
47 |
+
<label><input type="checkbox" class="service-checker" value="<?php echo $service_id ?>"/></label>
|
48 |
+
</div>
|
49 |
+
</div>
|
50 |
+
</div>
|
51 |
+
</div>
|
52 |
+
</div>
|
53 |
+
</div>
|
54 |
+
|
55 |
+
<div id="service_<?php echo $service_id ?>" class="panel-collapse collapse" role="tabpanel" style="height: 0">
|
56 |
+
<div class="panel-body">
|
57 |
+
<form method="post">
|
58 |
+
<?php do_action( 'bookly_render_service_form_contents', $service ) ?>
|
59 |
+
<div class="row">
|
60 |
+
<div class="col-md-9 col-sm-6">
|
61 |
+
<div class="form-group">
|
62 |
+
<label for="title_<?php echo $service_id ?>"><?php _e( 'Title', 'bookly' ) ?></label>
|
63 |
+
<input name="title" value="<?php echo esc_attr( $service['title'] ) ?>" id="title_<?php echo $service_id ?>" class="form-control" type="text">
|
64 |
+
</div>
|
65 |
+
</div>
|
66 |
+
<div class="col-md-3 col-sm-6">
|
67 |
+
<div class="form-group">
|
68 |
+
<label><?php _e( 'Color', 'bookly' ) ?></label>
|
69 |
+
<div class="bookly-color-picker-wrapper">
|
70 |
+
<input name="color" value="<?php echo esc_attr( $service['color'] ) ?>" class="bookly-js-color-picker" data-last-color="<?php echo esc_attr( $service['color'] ) ?>" type="hidden">
|
71 |
+
</div>
|
72 |
+
</div>
|
73 |
+
</div>
|
74 |
+
</div>
|
75 |
+
<div class="row">
|
76 |
+
<div class="col-sm-4">
|
77 |
+
<div class="form-group">
|
78 |
+
<label for="visibility_<?php echo $service_id ?>"><?php _e( 'Visibility', 'bookly' ) ?></label>
|
79 |
+
<p class="help-block"><?php _e( 'To make service invisible to your customers set the visibility to "Private".', 'bookly' ) ?></p>
|
80 |
+
<select name="visibility" class="form-control" id="visibility_<?php echo $service_id ?>">
|
81 |
+
<option value="public" <?php selected( $service['visibility'], 'public' ) ?>><?php _e( 'Public', 'bookly' ) ?></option>
|
82 |
+
<option value="private" <?php selected( $service['visibility'], 'private' ) ?>><?php _e( 'Private', 'bookly' ) ?></option>
|
83 |
+
</select>
|
84 |
+
</div>
|
85 |
+
</div>
|
86 |
+
<div class="col-sm-4">
|
87 |
+
<div class="form-group">
|
88 |
+
<label for="price_<?php echo $service_id ?>"><?php _e( 'Price', 'bookly' ) ?></label>
|
89 |
+
<input id="price_<?php echo $service_id ?>" class="form-control ab-question" type="number" min="0.00" step="any" name="price" value="<?php echo esc_attr( $service['price'] ) ?>">
|
90 |
+
</div>
|
91 |
+
</div>
|
92 |
+
<div class="col-sm-4 ab--for-simple">
|
93 |
+
<div class="form-group">
|
94 |
+
<label for="capacity_<?php echo $service_id ?>"><?php _e( 'Capacity', 'bookly' ) ?></label>
|
95 |
+
<p class="help-block"><?php _e( 'The maximum number of customers allowed to book the service for the certain time period.', 'bookly' ) ?></p>
|
96 |
+
<input id="capacity_<?php echo $service_id ?>" class="form-control ab-question" type="number" min="1" step="1" name="capacity" value="<?php echo esc_attr( $service['capacity'] ) ?>">
|
97 |
+
</div>
|
98 |
+
</div>
|
99 |
+
</div>
|
100 |
+
|
101 |
+
<div class="ab--for-simple">
|
102 |
+
<div class="row">
|
103 |
+
<div class="col-sm-4">
|
104 |
+
<div class="form-group">
|
105 |
+
<label for="duration_<?php echo $service_id ?>">
|
106 |
+
<?php _e( 'Duration', 'bookly' ) ?>
|
107 |
+
</label>
|
108 |
+
<select id="duration_<?php echo $service_id ?>" class="form-control" name="duration">
|
109 |
+
<?php for ( $j = $time_interval; $j <= 720; $j += $time_interval ) : ?><?php if ( $service['duration'] / 60 > $j - $time_interval && $service['duration'] / 60 < $j ) : ?><option value="<?php echo esc_attr( $service['duration'] ) ?>" selected><?php echo \BooklyLite\Lib\Utils\DateTime::secondsToInterval( $service['duration'] ) ?></option><?php endif ?><option value="<?php echo $j * 60 ?>" <?php selected( $service['duration'], $j * 60 ) ?>><?php echo \BooklyLite\Lib\Utils\DateTime::secondsToInterval( $j * 60 ) ?></option><?php endfor ?>
|
110 |
+
<option value="86400" <?php selected( $service['duration'], DAY_IN_SECONDS ) ?>><?php _e( 'All day', 'bookly' ) ?></option>
|
111 |
+
</select>
|
112 |
+
</div>
|
113 |
+
</div>
|
114 |
+
<div class="col-sm-8">
|
115 |
+
<div class="form-group">
|
116 |
+
<label for="padding_left_<?php echo $service_id ?>">
|
117 |
+
<?php _e( 'Padding time (before and after)', 'bookly' ) ?>
|
118 |
+
</label>
|
119 |
+
<p class="help-block"><?php _e( 'Set padding time before and/or after an appointment. For example, if you require 15 minutes to prepare for the next appointment then you should set "padding before" to 15 min. If there is an appointment from 8:00 to 9:00 then the next available time slot will be 9:15 rather than 9:00.', 'bookly' ) ?></p>
|
120 |
+
<div class="row">
|
121 |
+
<div class="col-xs-6">
|
122 |
+
<select id="padding_left_<?php echo $service_id ?>" class="form-control" name="padding_left">
|
123 |
+
<option value="0"><?php _e( 'OFF', 'bookly' ) ?></option>
|
124 |
+
<?php for ( $j = $time_interval; $j <= 720; $j += $time_interval ) : ?><?php if ( $service['padding_left'] > 0 && $service['padding_left'] / 60 > $j - $time_interval && $service['padding_left'] / 60 < $j ) : ?><option value="<?php echo esc_attr( $service['padding_left'] ) ?>" selected><?php echo \BooklyLite\Lib\Utils\DateTime::secondsToInterval( $service['padding_left'] ) ?></option><?php endif ?><option value="<?php echo $j * 60 ?>" <?php selected( $service['padding_left'], $j * 60 ) ?>><?php echo \BooklyLite\Lib\Utils\DateTime::secondsToInterval( $j * 60 ) ?></option><?php endfor ?>
|
125 |
+
</select>
|
126 |
+
</div>
|
127 |
+
<div class="col-xs-6">
|
128 |
+
<select id="padding_right_<?php echo $service_id ?>" class="form-control" name="padding_right">
|
129 |
+
<option value="0"><?php _e( 'OFF', 'bookly' ) ?></option>
|
130 |
+
<?php for ( $j = $time_interval; $j <= 720; $j += $time_interval ) : ?><?php if ( $service['padding_right'] > 0 && $service['padding_right'] / 60 > $j - $time_interval && $service['padding_right'] / 60 < $j ) : ?><option value="<?php echo esc_attr( $service['padding_right'] ) ?>" selected><?php echo \BooklyLite\Lib\Utils\DateTime::secondsToInterval( $service['padding_right'] ) ?></option><?php endif ?><option value="<?php echo $j * 60 ?>" <?php selected( $service['padding_right'], $j * 60 ) ?>><?php echo \BooklyLite\Lib\Utils\DateTime::secondsToInterval( $j * 60 ) ?></option><?php endfor ?>
|
131 |
+
</select>
|
132 |
+
</div>
|
133 |
+
</div>
|
134 |
+
</div>
|
135 |
+
</div>
|
136 |
+
</div>
|
137 |
+
</div>
|
138 |
+
|
139 |
+
<div class="row">
|
140 |
+
<div class="col-sm-6">
|
141 |
+
<div class="form-group">
|
142 |
+
<label for="category_<?php echo $service_id ?>"><?php _e( 'Category', 'bookly' ) ?></label>
|
143 |
+
<select id="category_<?php echo $service_id ?>" class="form-control" name="category_id"><option value="0"></option>
|
144 |
+
<?php foreach ( $category_collection as $category ) : ?>
|
145 |
+
<option value="<?php echo $category['id'] ?>" <?php selected( $category['id'], $service['category_id'] ) ?>><?php echo esc_html( $category['name'] ) ?></option>
|
146 |
+
<?php endforeach ?>
|
147 |
+
</select>
|
148 |
+
</div>
|
149 |
+
</div>
|
150 |
+
<div class="col-sm-6 ab--for-simple">
|
151 |
+
<div class="form-group">
|
152 |
+
<label><?php _e( 'Providers', 'bookly' ) ?></label><br>
|
153 |
+
<div class="btn-group">
|
154 |
+
<button class="btn btn-default btn-block dropdown-toggle bookly-flexbox" data-toggle="dropdown">
|
155 |
+
<div class="bookly-flex-cell">
|
156 |
+
<i class="dashicons dashicons-admin-users bookly-margin-right-md"></i>
|
157 |
+
</div>
|
158 |
+
<div class="bookly-flex-cell text-left" style="width: 100%">
|
159 |
+
<span class=bookly-entity-counter><?php echo $service['total_staff'] ?></span>
|
160 |
+
</div>
|
161 |
+
<div class="bookly-flex-cell"><div class="bookly-margin-left-md"><span class="caret"></span></div></div>
|
162 |
+
</button>
|
163 |
+
<ul class="dropdown-menu bookly-entity-selector">
|
164 |
+
<li>
|
165 |
+
<a class="checkbox" href="javascript:void(0)">
|
166 |
+
<label>
|
167 |
+
<input type="checkbox" id="service_<?php echo $service_id ?>_all_bookly-js-check-entity" class="bookly-check-all-entities" <?php checked( $all_staff_selected ) ?>">
|
168 |
+
<?php _e( 'All staff', 'bookly' ) ?>
|
169 |
+
</label>
|
170 |
+
</a>
|
171 |
+
</li>
|
172 |
+
<?php foreach ( $staff_collection as $i => $staff ) : ?>
|
173 |
+
<li>
|
174 |
+
<a class="checkbox" href="javascript:void(0)">
|
175 |
+
<label>
|
176 |
+
<input type="checkbox" name="staff_ids[]" class="bookly-js-check-entity" value="<?php echo $staff['id'] ?>" <?php checked( in_array( $staff['id'], $assigned_staff_ids ) ) ?> data-staff_name="<?php echo esc_attr( $staff['full_name'] ) ?>">
|
177 |
+
<?php echo esc_html( $staff['full_name'] ) ?>
|
178 |
+
</label>
|
179 |
+
</a>
|
180 |
+
</li>
|
181 |
+
<?php endforeach ?>
|
182 |
+
</ul>
|
183 |
+
</div>
|
184 |
+
</div>
|
185 |
+
</div>
|
186 |
+
</div>
|
187 |
+
|
188 |
+
<div class="form-group">
|
189 |
+
<label for="info_<?php echo $service_id ?>">
|
190 |
+
<?php _e( 'Info', 'bookly' ) ?>
|
191 |
+
</label>
|
192 |
+
<p class="help-block">
|
193 |
+
<?php printf( __( 'This text can be inserted into notifications with %s code.', 'bookly' ), '{service_info}' ) ?>
|
194 |
+
</p>
|
195 |
+
<textarea class="form-control" id="info_<?php echo $service_id ?>" name="info" rows="3" type="text"><?php echo esc_textarea( $service['info'] ) ?></textarea>
|
196 |
+
</div>
|
197 |
+
|
198 |
+
<?php do_action( 'bookly_compound_render_sub_services', $service, $service_collection, $service['sub_services'] ) ?>
|
199 |
+
<?php if ( has_filter( 'bookly_extras_find_by_service_id' ) ) : ?>
|
200 |
+
<div class="ab--for-simple">
|
201 |
+
<a class="h3" href="#ab--container_<?php echo $service_id ?>" data-toggle="collapse" role="button">
|
202 |
+
<?php echo get_option( 'ab_appearance_text_step_extras' ) ?>
|
203 |
+
</a>
|
204 |
+
<div id="ab--container_<?php echo $service_id ?>" class="bookly-margin-top-lg collapse in">
|
205 |
+
<ul class="list-group extras-container" data-service="<?php echo $service_id ?>">
|
206 |
+
<div class="form-group text-right">
|
207 |
+
<button class="btn btn-lg btn-success-outline extra-new">
|
208 |
+
<i class="dashicons dashicons-plus-alt"></i>
|
209 |
+
<?php _e( 'New Item', 'bookly' ) ?>
|
210 |
+
</button>
|
211 |
+
</div>
|
212 |
+
<?php foreach ( apply_filters( 'bookly_extras_find_by_service_id', array(), $service_id ) as $extra ) : ?>
|
213 |
+
<li class="list-group-item extra" data-extra-id="<?php echo $extra->get( 'id' ) ?>">
|
214 |
+
<div class="row">
|
215 |
+
<div class="col-lg-3">
|
216 |
+
<div class="bookly-flexbox">
|
217 |
+
<div class="bookly-flex-cell bookly-vertical-top">
|
218 |
+
<i class="bookly-js-handle bookly-icon bookly-icon-draghandle bookly-margin-right-sm bookly-cursor-move" title="<?php esc_attr_e( 'Reorder', 'bookly' ) ?>"></i>
|
219 |
+
</div>
|
220 |
+
<div class="bookly-flex-cell" style="width: 100%">
|
221 |
+
<div class="form-group">
|
222 |
+
<input name="extras[<?php echo $extra->get( 'id' ) ?>][id]"
|
223 |
+
value="<?php echo $extra->get( 'id' ) ?>" type="hidden">
|
224 |
+
<input name="extras[<?php echo $extra->get( 'id' ) ?>][attachment_id]"
|
225 |
+
value="<?php echo $extra->get( 'attachment_id' ) ?>" type="hidden">
|
226 |
+
|
227 |
+
<?php $img = wp_get_attachment_image_src( $extra->get( 'attachment_id' ), 'thumbnail' ) ?>
|
228 |
+
|
229 |
+
<div class="extra-attachment-image bookly-thumb bookly-thumb-lg bookly-margin-right-lg"
|
230 |
+
<?php echo $img ? 'style="background-image: url(' . $img[0] . '); background-size: cover;"' : '' ?>
|
231 |
+
>
|
232 |
+
<a class="bookly-js-remove-attachment dashicons dashicons-trash text-danger bookly-thumb-delete" href="javascript:void(0)" title="<?php _e( 'Delete', 'bookly' ) ?>"
|
233 |
+
<?php if ( !$img ) : ?>style="display: none;"<?php endif ?>>
|
234 |
+
</a>
|
235 |
+
<div class="bookly-thumb-edit extra-attachment" <?php if ( $img ) : ?>style="display: none;"<?php endif ?> >
|
236 |
+
<div class="bookly-pretty">
|
237 |
+
<label class="bookly-pretty-indicator bookly-thumb-edit-btn"><?php _e( 'Image', 'bookly' ) ?></label>
|
238 |
+
</div>
|
239 |
+
</div>
|
240 |
+
</div>
|
241 |
+
</div>
|
242 |
+
</div>
|
243 |
+
</div>
|
244 |
+
</div>
|
245 |
+
|
246 |
+
<div class="col-lg-9">
|
247 |
+
<div class="form-group">
|
248 |
+
<label for="title_<?php echo $extra->get( 'id' ) ?>">
|
249 |
+
<?php _e( 'Title', 'bookly' ) ?>
|
250 |
+
</label>
|
251 |
+
<input name="extras[<?php echo $extra->get( 'id' ) ?>][title]" class="form-control" type="text" id="title_<?php echo $extra->get( 'id' ) ?>" value="<?php echo $extra->get( 'title' ) ?>">
|
252 |
+
</div>
|
253 |
+
|
254 |
+
<div class="row">
|
255 |
+
<div class="col-sm-4">
|
256 |
+
<div class="form-group">
|
257 |
+
<label for="price_<?php echo $extra->get( 'id' ) ?>">
|
258 |
+
<?php _e( 'Price', 'bookly' ) ?>
|
259 |
+
</label>
|
260 |
+
<input name="extras[<?php echo $extra->get( 'id' ) ?>][price]" class="form-control" type="number" step="0.01" id="price_<?php echo $extra->get( 'id' ) ?>" min="0.00" value="<?php echo $extra->get( 'price' ) ?>">
|
261 |
+
</div>
|
262 |
+
</div>
|
263 |
+
|
264 |
+
<div class="col-sm-4">
|
265 |
+
<div class="form-group">
|
266 |
+
<label for="duration_<?php echo $extra->get( 'id' ) ?>">
|
267 |
+
<?php _e( 'Duration', 'bookly' ) ?>
|
268 |
+
</label>
|
269 |
+
<select name="extras[<?php echo $extra->get( 'id' ) ?>][duration]" id="duration_<?php echo $extra->get( 'id' ) ?>" class="form-control">
|
270 |
+
<option value="0"><?php _e( 'OFF', 'bookly' ) ?></option>
|
271 |
+
<?php for ( $j = $time_interval; $j <= 720; $j += $time_interval ) : ?><?php if ( $extra->get( 'duration' ) > 0 && $extra->get( 'duration' ) / 60 > $j - $time_interval && $extra->get( 'duration' ) / 60 < $j ) : ?><option value="<?php echo esc_attr( $extra->get( 'duration' ) ) ?>" selected><?php echo BooklyLite\Lib\Utils\DateTime::secondsToInterval( $extra->get( 'duration' ) ) ?></option><?php endif ?><option value="<?php echo $j * 60 ?>" <?php selected( $extra->get( 'duration' ), $j * 60 ) ?>><?php echo BooklyLite\Lib\Utils\DateTime::secondsToInterval( $j * 60 ) ?></option><?php endfor ?>
|
272 |
+
</select>
|
273 |
+
</div>
|
274 |
+
</div>
|
275 |
+
|
276 |
+
<div class="col-sm-4">
|
277 |
+
<div class="form-group">
|
278 |
+
<label for="max_quantity_<?php echo $extra->get( 'id' ) ?>">
|
279 |
+
<?php _e( 'Max quantity', 'bookly' ) ?>
|
280 |
+
</label>
|
281 |
+
<input name="extras[<?php echo $extra->get( 'id' ) ?>][max_quantity]" class="form-control" type="number" step="1" id="max_quantity_<?php echo $extra->get( 'id' ) ?>" min="1" value="<?php echo $extra->get( 'max_quantity' ) ?>">
|
282 |
+
</div>
|
283 |
+
</div>
|
284 |
+
</div>
|
285 |
+
|
286 |
+
<div class="form-group text-right">
|
287 |
+
<?php \BooklyLite\Lib\Utils\Common::deleteButton( null, 'extra-delete' ) ?>
|
288 |
+
</div>
|
289 |
+
</div>
|
290 |
+
</div>
|
291 |
+
</li>
|
292 |
+
<?php endforeach ?>
|
293 |
+
</ul>
|
294 |
+
</div>
|
295 |
+
</div>
|
296 |
+
<?php endif ?>
|
297 |
+
|
298 |
+
<div class="panel-footer">
|
299 |
+
<input type="hidden" name="action" value="ab_update_service">
|
300 |
+
<input type="hidden" name="id" value="<?php echo esc_html( $service_id ) ?>">
|
301 |
+
<input type="hidden" name="update_staff" value="0">
|
302 |
+
<?php \BooklyLite\Lib\Utils\Common::submitButton( null, 'ajax-service-send' ) ?>
|
303 |
+
<?php \BooklyLite\Lib\Utils\Common::resetButton( null, 'js-reset' ) ?>
|
304 |
+
</div>
|
305 |
+
</form>
|
306 |
+
</div>
|
307 |
+
</div>
|
308 |
+
</div>
|
309 |
+
<?php endforeach ?>
|
310 |
+
</div>
|
311 |
+
<?php endif ?>
|
backend/modules/services/templates/index.php
ADDED
@@ -0,0 +1,104 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
|
2 |
+
<div id="bookly-tbs" class="wrap">
|
3 |
+
<div class="bookly-tbs-body">
|
4 |
+
<div class="page-header text-right clearfix">
|
5 |
+
<div class="bookly-page-title">
|
6 |
+
<?php _e( 'Services', 'bookly' ) ?>
|
7 |
+
</div>
|
8 |
+
</div>
|
9 |
+
<div class="row">
|
10 |
+
<div id="bookly-sidebar" class="col-sm-4">
|
11 |
+
<div id="bookly-categories-list" class="bookly-nav">
|
12 |
+
<div class="bookly-nav-item active bookly-category-item bookly-js-all-services">
|
13 |
+
<div class="bookly-padding-vertical-xs"><?php _e( 'All Services', 'bookly' ) ?></div>
|
14 |
+
</div>
|
15 |
+
<ul id="bookly-category-item-list">
|
16 |
+
<?php foreach ( $category_collection as $category ) include '_category_item.php'; ?>
|
17 |
+
</ul>
|
18 |
+
</div>
|
19 |
+
|
20 |
+
<div class="form-group">
|
21 |
+
<button id="bookly-new-category" type="button"
|
22 |
+
class="btn btn-xlg btn-block btn-success-outline">
|
23 |
+
<i class="dashicons dashicons-plus-alt"></i>
|
24 |
+
<?php _e( 'New Category', 'bookly' ) ?>
|
25 |
+
</button>
|
26 |
+
</div>
|
27 |
+
|
28 |
+
<form method="post" id="new-category-form" style="display: none">
|
29 |
+
<div class="form-group bookly-margin-bottom-md">
|
30 |
+
<div class="form-field form-required">
|
31 |
+
<label for="bookly-category-name"><?php _e( 'Name', 'bookly' ) ?></label>
|
32 |
+
<input class="form-control" id="bookly-category-name" type="text" name="name" />
|
33 |
+
<input type="hidden" name="action" value="ab_category_form" />
|
34 |
+
</div>
|
35 |
+
</div>
|
36 |
+
|
37 |
+
<hr />
|
38 |
+
<div class="text-right">
|
39 |
+
<button type="submit" class="btn btn-success">
|
40 |
+
<?php _e( 'Save', 'bookly' ) ?>
|
41 |
+
</button>
|
42 |
+
<button type="button" class="btn btn-default">
|
43 |
+
<?php _e( 'Cancel', 'bookly' ) ?>
|
44 |
+
</button>
|
45 |
+
</div>
|
46 |
+
</form>
|
47 |
+
</div>
|
48 |
+
|
49 |
+
<div id="bookly-services-wrapper" class="col-sm-8">
|
50 |
+
<div class="panel panel-default bookly-main">
|
51 |
+
<div class="panel-body">
|
52 |
+
<h4 class="bookly-block-head">
|
53 |
+
<span class="bookly-category-title"><?php _e( 'All Services', 'bookly' ) ?></span>
|
54 |
+
<button type="button" class="add-service ladda-button pull-right btn btn-lg btn-success" data-spinner-size="40" data-style="zoom-in">
|
55 |
+
<span class="ladda-label"><i class="glyphicon glyphicon-plus"></i> <?php _e( 'Add Service', 'bookly' ) ?></span>
|
56 |
+
</button>
|
57 |
+
</h4>
|
58 |
+
|
59 |
+
<p class="bookly-margin-top-xlg no-result" <?php if ( ! empty ( $service_collection ) ) : ?>style="display: none;"<?php endif ?>>
|
60 |
+
<?php _e( 'No services found. Please add services.', 'bookly' ) ?>
|
61 |
+
</p>
|
62 |
+
|
63 |
+
<div class="bookly-margin-top-xlg" id="ab-services-list">
|
64 |
+
<?php include '_list.php' ?>
|
65 |
+
</div>
|
66 |
+
<div class="text-right">
|
67 |
+
<?php \BooklyLite\Lib\Utils\Common::deleteButton() ?>
|
68 |
+
</div>
|
69 |
+
</div>
|
70 |
+
</div>
|
71 |
+
</div>
|
72 |
+
</div>
|
73 |
+
</div>
|
74 |
+
|
75 |
+
<div id="ab-staff-update" class="modal fade" tabindex=-1 role="dialog">
|
76 |
+
<div class="modal-dialog">
|
77 |
+
<div class="modal-content">
|
78 |
+
<div class="modal-header">
|
79 |
+
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
80 |
+
<div class="modal-title h2"><?php _e( 'Update service setting', 'bookly' ) ?></div>
|
81 |
+
</div>
|
82 |
+
<div class="modal-body">
|
83 |
+
<p><?php _e( 'You are about to change a service setting which is also configured separately for each staff member. Do you want to update it in staff settings too?', 'bookly' ) ?></p>
|
84 |
+
<div class="checkbox">
|
85 |
+
<label>
|
86 |
+
<input id="ab-remember-my-choice" type="checkbox">
|
87 |
+
<?php _e( 'Remember my choice', 'bookly' ) ?>
|
88 |
+
</label>
|
89 |
+
</div>
|
90 |
+
</div>
|
91 |
+
<div class="modal-footer">
|
92 |
+
<button type="reset" class="btn btn-default ab-no" data-dismiss="modal" aria-hidden="true">
|
93 |
+
<?php _e( 'No, update just here in services', 'bookly' ) ?>
|
94 |
+
</button>
|
95 |
+
<button type="submit" class="btn btn-success ab-yes"><?php _e( 'Yes', 'bookly' ) ?></button>
|
96 |
+
</div>
|
97 |
+
</div>
|
98 |
+
</div>
|
99 |
+
</div>
|
100 |
+
</div>
|
101 |
+
|
102 |
+
<div class="hidden">
|
103 |
+
<?php do_action( 'bookly_render_after_service_list', $service_collection ) ?>
|
104 |
+
</div>
|
backend/modules/settings/AB_SettingsController.php
DELETED
@@ -1,197 +0,0 @@
|
|
1 |
-
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Class AB_SettingsController
|
5 |
-
*/
|
6 |
-
class AB_SettingsController extends AB_Controller {
|
7 |
-
|
8 |
-
const page_slug = 'ab-settings';
|
9 |
-
|
10 |
-
public function index()
|
11 |
-
{
|
12 |
-
/** @var WP_Locale $wp_locale */
|
13 |
-
global $wp_locale;
|
14 |
-
|
15 |
-
$this->enqueueStyles( array(
|
16 |
-
'frontend' => array(
|
17 |
-
'css/ladda.min.css'
|
18 |
-
),
|
19 |
-
'backend' => array(
|
20 |
-
'css/bookly.main-backend.css',
|
21 |
-
'bootstrap/css/bootstrap.min.css',
|
22 |
-
'css/jCal.css',
|
23 |
-
)
|
24 |
-
) );
|
25 |
-
|
26 |
-
$this->enqueueScripts( array(
|
27 |
-
'backend' => array(
|
28 |
-
'bootstrap/js/bootstrap.min.js' => array( 'jquery' ),
|
29 |
-
'js/jCal.js' => array( 'jquery' ),
|
30 |
-
),
|
31 |
-
'module' => array(
|
32 |
-
'js/settings.js' => array( 'jquery', 'ab-intlTelInput.min.js' ),
|
33 |
-
),
|
34 |
-
'frontend' => array(
|
35 |
-
'js/intlTelInput.min.js' => array( 'jquery' ),
|
36 |
-
'js/spin.min.js' => array( 'jquery' ),
|
37 |
-
'js/ladda.min.js' => array( 'jquery' ),
|
38 |
-
)
|
39 |
-
) );
|
40 |
-
|
41 |
-
wp_localize_script( 'ab-jCal.js', 'BooklyL10n', array(
|
42 |
-
'we_are_not_working' => __( 'We are not working on this day', 'bookly' ),
|
43 |
-
'repeat' => __( 'Repeat every year', 'bookly' ),
|
44 |
-
'months' => array_values( $wp_locale->month ),
|
45 |
-
'days' => array_values( $wp_locale->weekday_abbrev )
|
46 |
-
) );
|
47 |
-
$this->message = '';
|
48 |
-
// Save the settings.
|
49 |
-
if ( ! empty ( $_POST ) ) {
|
50 |
-
switch ( $this->getParameter( 'type' ) ) {
|
51 |
-
case '_payments': // Payments form.
|
52 |
-
update_option( 'ab_settings_pay_locally', (int)$this->getParameter( 'ab_settings_pay_locally' ) );
|
53 |
-
break;
|
54 |
-
case '_hours': // Business hours form.
|
55 |
-
$this->form = new AB_BusinessHoursForm();
|
56 |
-
break;
|
57 |
-
case '_general': // General form.
|
58 |
-
$ab_settings_time_slot_length = $this->getParameter( 'ab_settings_time_slot_length' );
|
59 |
-
if ( in_array( $ab_settings_time_slot_length, array( 5, 10, 12, 15, 20, 30, 60, 90, 120, 180, 240, 360 ) ) ) {
|
60 |
-
update_option( 'ab_settings_time_slot_length', $ab_settings_time_slot_length );
|
61 |
-
}
|
62 |
-
update_option( 'ab_settings_minimum_time_prior_booking', (int)$this->getParameter( 'ab_settings_minimum_time_prior_booking' ) );
|
63 |
-
update_option( 'ab_settings_maximum_available_days_for_booking', (int)$this->getParameter( 'ab_settings_maximum_available_days_for_booking' ) );
|
64 |
-
update_option( 'ab_settings_use_client_time_zone', (int)$this->getParameter( 'ab_settings_use_client_time_zone' ) );
|
65 |
-
update_option( 'ab_settings_cancel_page_url', $this->getParameter( 'ab_settings_cancel_page_url' ) );
|
66 |
-
update_option( 'ab_settings_final_step_url', $this->getParameter( 'ab_settings_final_step_url' ) );
|
67 |
-
update_option( 'ab_settings_allow_staff_members_edit_profile', (int)$this->getParameter( 'ab_settings_allow_staff_members_edit_profile' ) );
|
68 |
-
update_option( 'ab_settings_link_assets_method', $this->getParameter( 'ab_settings_link_assets_method' ) );
|
69 |
-
$this->message = __( 'Settings saved.', 'bookly' );
|
70 |
-
break;
|
71 |
-
case '_google_calendar': // Google calendar form.
|
72 |
-
$this->message = __( 'Settings saved.', 'bookly' );
|
73 |
-
break;
|
74 |
-
case '_holidays': // Holidays form.
|
75 |
-
// Company form.
|
76 |
-
break;
|
77 |
-
case '_customers': // Customers form.
|
78 |
-
update_option( 'ab_settings_create_account', (int)$this->getParameter( 'ab_settings_create_account' ) );
|
79 |
-
update_option( 'ab_settings_phone_default_country', $this->getParameter( 'ab_settings_phone_default_country' ) );
|
80 |
-
update_option( 'ab_sms_default_country_code', $this->getParameter( 'ab_sms_default_country_code' ) );
|
81 |
-
$this->message = __( 'Settings saved.', 'bookly' );
|
82 |
-
break;
|
83 |
-
case '_woocommerce': // WooCommerce form.
|
84 |
-
$this->message = __( 'Settings saved.', 'bookly' );
|
85 |
-
break;
|
86 |
-
case '_company': // Company form.
|
87 |
-
$this->form = new AB_CompanyForm();
|
88 |
-
break;
|
89 |
-
}
|
90 |
-
if ( in_array( $this->getParameter( 'type' ), array ( '_hours', '_company' ) ) ) {
|
91 |
-
$this->form->bind( $this->getPostParameters(), $_FILES );
|
92 |
-
$this->form->save();
|
93 |
-
$this->message = __( 'Settings saved.', 'bookly' );
|
94 |
-
}
|
95 |
-
}
|
96 |
-
|
97 |
-
// Get holidays.
|
98 |
-
$this->holidays = $this->getHolidays();
|
99 |
-
$this->candidates = $this->getCandidatesBooklyProduct();
|
100 |
-
|
101 |
-
$this->render( 'index' );
|
102 |
-
} // index
|
103 |
-
|
104 |
-
/**
|
105 |
-
* Ajax request for Holidays calendar
|
106 |
-
*/
|
107 |
-
public function executeSettingsHoliday()
|
108 |
-
{
|
109 |
-
$id = $this->getParameter( 'id', false );
|
110 |
-
$holiday = $this->getParameter( 'holiday' ) == 'true';
|
111 |
-
$repeat = $this->getParameter( 'repeat' ) == 'true';
|
112 |
-
$day = $this->getParameter( 'day', false );
|
113 |
-
|
114 |
-
// update or delete the event
|
115 |
-
if ( $id ) {
|
116 |
-
if ( $holiday ) {
|
117 |
-
$this->getWpdb()->update( AB_Holiday::getTableName(), array( 'repeat_event' => intval( $repeat ) ), array( 'id' => $id ), array( '%d' ) );
|
118 |
-
$this->getWpdb()->update( AB_Holiday::getTableName(), array( 'repeat_event' => intval( $repeat ) ), array( 'parent_id' => $id ), array( '%d' ) );
|
119 |
-
} else {
|
120 |
-
AB_Holiday::query()->delete()->where( 'id', $id )->where( 'parent_id', $id, 'OR' )->execute();
|
121 |
-
}
|
122 |
-
// add the new event
|
123 |
-
} elseif ( $holiday && $day ) {
|
124 |
-
$holiday = new AB_Holiday( array( 'date' => $day, 'repeat_event' => intval( $repeat ) ) );
|
125 |
-
$holiday->save();
|
126 |
-
foreach ( AB_Staff::query()->fetchArray() as $employee ) {
|
127 |
-
$staff_holiday = new AB_Holiday( array( 'date' => $day, 'repeat_event' => intval( $repeat ), 'staff_id' => $employee['id'], 'parent_id' => $holiday->get( 'id' ) ) );
|
128 |
-
$staff_holiday->save();
|
129 |
-
}
|
130 |
-
}
|
131 |
-
|
132 |
-
// and return refreshed events
|
133 |
-
echo $this->getHolidays();
|
134 |
-
exit;
|
135 |
-
}
|
136 |
-
|
137 |
-
/**
|
138 |
-
* @return mixed|string|void
|
139 |
-
*/
|
140 |
-
protected function getHolidays()
|
141 |
-
{
|
142 |
-
$collection = AB_Holiday::query()->where( 'staff_id', null )->fetchArray();
|
143 |
-
$holidays = array();
|
144 |
-
if ( count( $collection ) ) {
|
145 |
-
foreach ( $collection as $holiday ) {
|
146 |
-
$holidays[ $holiday['id'] ] = array(
|
147 |
-
'm' => intval( date( 'm', strtotime( $holiday['date'] ) ) ),
|
148 |
-
'd' => intval( date( 'd', strtotime( $holiday['date'] ) ) ),
|
149 |
-
'title' => $holiday['title'],
|
150 |
-
);
|
151 |
-
// if not repeated holiday, add the year
|
152 |
-
if ( ! $holiday['repeat_event'] ) {
|
153 |
-
$holidays[ $holiday['id'] ]['y'] = intval( date( 'Y', strtotime( $holiday['date'] ) ) );
|
154 |
-
}
|
155 |
-
}
|
156 |
-
}
|
157 |
-
|
158 |
-
return json_encode( $holidays );
|
159 |
-
}
|
160 |
-
|
161 |
-
protected function getCandidatesBooklyProduct()
|
162 |
-
{
|
163 |
-
$goods = array( array( 'id' => 0, 'name' => __( 'Select product', 'bookly' ) ) );
|
164 |
-
$args = array(
|
165 |
-
'numberposts' => 0,
|
166 |
-
'post_type' => 'product',
|
167 |
-
'suppress_filters' => true
|
168 |
-
);
|
169 |
-
$collection = get_posts( $args );
|
170 |
-
foreach ( $collection as $item ) {
|
171 |
-
$goods[] = array( 'id' => $item->ID, 'name' => $item->post_title );
|
172 |
-
}
|
173 |
-
wp_reset_postdata();
|
174 |
-
|
175 |
-
return $goods;
|
176 |
-
}
|
177 |
-
|
178 |
-
/**
|
179 |
-
* Ajax request to dismiss admin notice for current user.
|
180 |
-
*/
|
181 |
-
public function executeDismissAdminNotice()
|
182 |
-
{
|
183 |
-
update_user_meta( get_current_user_id(), 'ab_dismiss_admin_notice', 1 );
|
184 |
-
}
|
185 |
-
|
186 |
-
/**
|
187 |
-
* Override parent method to add 'wp_ajax_ab_' prefix
|
188 |
-
* so current 'execute*' methods look nicer.
|
189 |
-
*
|
190 |
-
* @param string $prefix
|
191 |
-
*/
|
192 |
-
protected function registerWpActions( $prefix = '' )
|
193 |
-
{
|
194 |
-
parent::registerWpActions( 'wp_ajax_ab_' );
|
195 |
-
}
|
196 |
-
|
197 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/modules/settings/Controller.php
ADDED
@@ -0,0 +1,241 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace BooklyLite\Backend\Modules\Settings;
|
3 |
+
|
4 |
+
use BooklyLite\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Controller
|
8 |
+
* @package BooklyLite\Backend\Modules\Settings
|
9 |
+
*/
|
10 |
+
class Controller extends Lib\Base\Controller
|
11 |
+
{
|
12 |
+
const page_slug = 'ab-settings';
|
13 |
+
|
14 |
+
public function index()
|
15 |
+
{
|
16 |
+
/** @var \WP_Locale $wp_locale */
|
17 |
+
global $wp_locale;
|
18 |
+
|
19 |
+
wp_enqueue_media();
|
20 |
+
$this->enqueueStyles( array(
|
21 |
+
'frontend' => array( 'css/ladda.min.css' ),
|
22 |
+
'backend' => array( 'bootstrap/css/bootstrap-theme.min.css', )
|
23 |
+
) );
|
24 |
+
|
25 |
+
$this->enqueueScripts( array(
|
26 |
+
'backend' => array(
|
27 |
+
'bootstrap/js/bootstrap.min.js' => array( 'jquery' ),
|
28 |
+
'js/jCal.js' => array( 'jquery' ),
|
29 |
+
'js/alert.js' => array( 'jquery' ),
|
30 |
+
),
|
31 |
+
'module' => array( 'js/settings.js' => array( 'jquery', 'ab-intlTelInput.min.js', 'jquery-ui-sortable' ) ),
|
32 |
+
'frontend' => array(
|
33 |
+
'js/intlTelInput.min.js' => array( 'jquery' ),
|
34 |
+
'js/spin.min.js' => array( 'jquery' ),
|
35 |
+
'js/ladda.min.js' => array( 'jquery' ),
|
36 |
+
)
|
37 |
+
) );
|
38 |
+
|
39 |
+
$current_tab = $this->hasParameter( 'tab' ) ? $this->getParameter( 'tab' ) : 'general';
|
40 |
+
$alert = array( 'success' => array(), 'error' => array() );
|
41 |
+
|
42 |
+
// Save the settings.
|
43 |
+
if ( ! empty ( $_POST ) ) {
|
44 |
+
switch ( $this->getParameter( 'tab' ) ) {
|
45 |
+
case 'payments': // Payments form.
|
46 |
+
$form = new Forms\Payments();
|
47 |
+
break;
|
48 |
+
case 'business_hours': // Business hours form.
|
49 |
+
$form = new Forms\BusinessHours();
|
50 |
+
break;
|
51 |
+
case 'purchase_code': // Purchase Code form.
|
52 |
+
break;
|
53 |
+
case 'general': // General form.
|
54 |
+
$ab_settings_time_slot_length = $this->getParameter( 'ab_settings_time_slot_length' );
|
55 |
+
if ( in_array( $ab_settings_time_slot_length, array( 5, 10, 12, 15, 20, 30, 45, 60, 90, 120, 180, 240, 360 ) ) ) {
|
56 |
+
update_option( 'ab_settings_time_slot_length', $ab_settings_time_slot_length );
|
57 |
+
}
|
58 |
+
update_option( 'ab_settings_allow_staff_members_edit_profile', (int) $this->getParameter( 'ab_settings_allow_staff_members_edit_profile' ) );
|
59 |
+
update_option( 'ab_settings_approve_page_url', $this->getParameter( 'ab_settings_approve_page_url' ) );
|
60 |
+
update_option( 'ab_settings_cancel_denied_page_url', $this->getParameter( 'ab_settings_cancel_denied_page_url' ) );
|
61 |
+
update_option( 'ab_settings_cancel_page_url', $this->getParameter( 'ab_settings_cancel_page_url' ) );
|
62 |
+
update_option( 'ab_settings_default_appointment_status', $this->getParameter( 'ab_settings_default_appointment_status' ) );
|
63 |
+
update_option( 'ab_settings_final_step_url', '' );
|
64 |
+
update_option( 'ab_settings_link_assets_method', $this->getParameter( 'ab_settings_link_assets_method' ) );
|
65 |
+
update_option( 'ab_settings_maximum_available_days_for_booking', (int) $this->getParameter( 'ab_settings_maximum_available_days_for_booking' ) );
|
66 |
+
update_option( 'ab_settings_minimum_time_prior_booking', (int) $this->getParameter( 'ab_settings_minimum_time_prior_booking' ) );
|
67 |
+
update_option( 'ab_settings_minimum_time_prior_cancel', $this->getParameter( 'ab_settings_minimum_time_prior_cancel' ) );
|
68 |
+
update_option( 'ab_settings_use_client_time_zone', (int) $this->getParameter( 'ab_settings_use_client_time_zone' ) );
|
69 |
+
update_option( 'ab_lite_uninstall_remove_bookly_data', (int) $this->getParameter( 'ab_lite_uninstall_remove_bookly_data' ) );
|
70 |
+
$alert['success'][] = __( 'Settings saved.', 'bookly' );
|
71 |
+
break;
|
72 |
+
case 'google_calendar': // Google calendar form.
|
73 |
+
break;
|
74 |
+
case 'customers': // Customers form.
|
75 |
+
update_option( 'ab_settings_client_cancel_appointment_action', $this->getParameter( 'ab_settings_client_cancel_appointment_action' ) );
|
76 |
+
update_option( 'ab_settings_create_account', (int) $this->getParameter( 'ab_settings_create_account' ) );
|
77 |
+
update_option( 'ab_settings_phone_default_country', $this->getParameter( 'ab_settings_phone_default_country' ) );
|
78 |
+
update_option( 'ab_sms_default_country_code', $this->getParameter( 'ab_sms_default_country_code' ) );
|
79 |
+
$alert['success'][] = __( 'Settings saved.', 'bookly' );
|
80 |
+
break;
|
81 |
+
case 'woocommerce': // WooCommerce form.
|
82 |
+
break;
|
83 |
+
case 'cart': // Cart form.
|
84 |
+
update_option( 'ab_cart_show_columns', $this->getParameter( 'ab_cart_show_columns', array() ) );
|
85 |
+
update_option( 'ab_settings_cart_notifications_combined', $this->getParameter( 'ab_settings_cart_notifications_combined' ) );
|
86 |
+
update_option( 'ab_settings_step_cart_enabled', '0' );
|
87 |
+
$alert['success'][] = __( 'Settings saved.', 'bookly' );
|
88 |
+
break;
|
89 |
+
case 'company': // Company form.
|
90 |
+
update_option( 'ab_settings_company_address', $this->getParameter( 'ab_settings_company_address' ) );
|
91 |
+
update_option( 'ab_settings_company_logo_attachment_id', $this->getParameter( 'ab_settings_company_logo_attachment_id' ) );
|
92 |
+
update_option( 'ab_settings_company_name', $this->getParameter( 'ab_settings_company_name' ) );
|
93 |
+
update_option( 'ab_settings_company_phone', $this->getParameter( 'ab_settings_company_phone' ) );
|
94 |
+
update_option( 'ab_settings_company_website', $this->getParameter( 'ab_settings_company_website' ) );
|
95 |
+
$alert['success'][] = __( 'Settings saved.', 'bookly' );
|
96 |
+
break;
|
97 |
+
default:
|
98 |
+
// Let add-ons save their settings.
|
99 |
+
$alert = apply_filters( 'bookly_save_settings', $alert, $this->getParameter( 'tab' ), $this->getPostParameters() );
|
100 |
+
}
|
101 |
+
|
102 |
+
if ( in_array( $this->getParameter( 'tab' ), array ( 'payments', 'business_hours' ) ) ) {
|
103 |
+
$form->bind( $this->getPostParameters(), $_FILES );
|
104 |
+
$form->save();
|
105 |
+
|
106 |
+
$alert['success'][] = __( 'Settings saved.', 'bookly' );
|
107 |
+
}
|
108 |
+
}
|
109 |
+
|
110 |
+
$holidays = $this->getHolidays();
|
111 |
+
$candidates = $this->getCandidatesBooklyProduct();
|
112 |
+
|
113 |
+
// Check if WooCommerce cart exists.
|
114 |
+
if ( get_option( 'ab_woocommerce_enabled' ) && class_exists( 'WooCommerce', false ) ) {
|
115 |
+
$post = get_post( wc_get_page_id( 'cart' ) );
|
116 |
+
if ( $post === null || $post->post_status != 'publish' ) {
|
117 |
+
$alert['error'][] = sprintf(
|
118 |
+
__( 'WooCommerce cart is not set up. Follow the <a href="%s">link</a> to correct this problem.', 'bookly' ),
|
119 |
+
Lib\Utils\Common::escAdminUrl( 'wc-status', array( 'tab' => 'tools' ) )
|
120 |
+
);
|
121 |
+
}
|
122 |
+
}
|
123 |
+
$cart_columns = array(
|
124 |
+
'service' => Lib\Utils\Common::getTranslatedOption( 'ab_appearance_text_label_service' ),
|
125 |
+
'date' => __( 'Date', 'bookly' ),
|
126 |
+
'time' => __( 'Time', 'bookly' ),
|
127 |
+
'employee' => Lib\Utils\Common::getTranslatedOption( 'ab_appearance_text_label_employee' ),
|
128 |
+
'price' => __( 'Price', 'bookly' ),
|
129 |
+
);
|
130 |
+
|
131 |
+
$cart_columns = apply_filters( 'bookly_settings_cart_columns', $cart_columns );
|
132 |
+
|
133 |
+
wp_localize_script( 'ab-jCal.js', 'BooklyL10n', array(
|
134 |
+
'alert' => $alert,
|
135 |
+
'close' => __( 'Close', 'bookly' ),
|
136 |
+
'current_tab' => $current_tab,
|
137 |
+
'days' => array_values( $wp_locale->weekday_abbrev ),
|
138 |
+
'months' => array_values( $wp_locale->month ),
|
139 |
+
'repeat' => __( 'Repeat every year', 'bookly' ),
|
140 |
+
'we_are_not_working' => __( 'We are not working on this day', 'bookly' ),
|
141 |
+
'limitations' => __( '<b class="h4">This function is disabled in the Lite version of Bookly.</b><br><br>If you find the plugin useful for your business please consider buying a licence for the full version.<br>It costs just $59 and for this money you will get many useful functions, lifetime free updates and excellent support!<br>More information can be found here', 'bookly' ) . ': <a href="http://booking-wp-plugin.com" target="_blank" class="alert-link">http://booking-wp-plugin.com</a>',
|
142 |
+
) );
|
143 |
+
|
144 |
+
$this->render( 'index', compact( 'holidays', 'candidates', 'cart_columns' ) );
|
145 |
+
}
|
146 |
+
|
147 |
+
/**
|
148 |
+
* Ajax request for Holidays calendar
|
149 |
+
*/
|
150 |
+
public function executeSettingsHoliday()
|
151 |
+
{
|
152 |
+
global $wpdb;
|
153 |
+
|
154 |
+
$id = $this->getParameter( 'id', false );
|
155 |
+
$day = $this->getParameter( 'day', false );
|
156 |
+
$holiday = $this->getParameter( 'holiday' ) == 'true';
|
157 |
+
$repeat = $this->getParameter( 'repeat' ) == 'true';
|
158 |
+
|
159 |
+
// update or delete the event
|
160 |
+
if ( $id ) {
|
161 |
+
if ( $holiday ) {
|
162 |
+
$wpdb->update( Lib\Entities\Holiday::getTableName(), array( 'repeat_event' => intval( $repeat ) ), array( 'id' => $id ), array( '%d' ) );
|
163 |
+
$wpdb->update( Lib\Entities\Holiday::getTableName(), array( 'repeat_event' => intval( $repeat ) ), array( 'parent_id' => $id ), array( '%d' ) );
|
164 |
+
} else {
|
165 |
+
Lib\Entities\Holiday::query()->delete()->where( 'id', $id )->where( 'parent_id', $id, 'OR' )->execute();
|
166 |
+
}
|
167 |
+
// add the new event
|
168 |
+
} elseif ( $holiday && $day ) {
|
169 |
+
$holiday = new Lib\Entities\Holiday( array( 'date' => $day, 'repeat_event' => intval( $repeat ) ) );
|
170 |
+
$holiday->save();
|
171 |
+
foreach ( Lib\Entities\Staff::query()->fetchArray() as $employee ) {
|
172 |
+
$staff_holiday = new Lib\Entities\Holiday( array( 'date' => $day, 'repeat_event' => intval( $repeat ), 'staff_id' => $employee['id'], 'parent_id' => $holiday->get( 'id' ) ) );
|
173 |
+
$staff_holiday->save();
|
174 |
+
}
|
175 |
+
}
|
176 |
+
|
177 |
+
// and return refreshed events
|
178 |
+
echo $this->getHolidays();
|
179 |
+
exit;
|
180 |
+
}
|
181 |
+
|
182 |
+
/**
|
183 |
+
* @return mixed|string|void
|
184 |
+
*/
|
185 |
+
protected function getHolidays()
|
186 |
+
{
|
187 |
+
$collection = Lib\Entities\Holiday::query()->where( 'staff_id', null )->fetchArray();
|
188 |
+
$holidays = array();
|
189 |
+
if ( count( $collection ) ) {
|
190 |
+
foreach ( $collection as $holiday ) {
|
191 |
+
$holidays[ $holiday['id'] ] = array(
|
192 |
+
'm' => intval( date( 'm', strtotime( $holiday['date'] ) ) ),
|
193 |
+
'd' => intval( date( 'd', strtotime( $holiday['date'] ) ) ),
|
194 |
+
);
|
195 |
+
// If not repeated holiday, add the year
|
196 |
+
if ( ! $holiday['repeat_event'] ) {
|
197 |
+
$holidays[ $holiday['id'] ]['y'] = intval( date( 'Y', strtotime( $holiday['date'] ) ) );
|
198 |
+
}
|
199 |
+
}
|
200 |
+
}
|
201 |
+
|
202 |
+
return json_encode( $holidays );
|
203 |
+
}
|
204 |
+
|
205 |
+
protected function getCandidatesBooklyProduct()
|
206 |
+
{
|
207 |
+
$goods = array( array( 'id' => 0, 'name' => __( 'Select product', 'bookly' ) ) );
|
208 |
+
$args = array(
|
209 |
+
'numberposts' => -1,
|
210 |
+
'post_type' => 'product',
|
211 |
+
'suppress_filters' => true
|
212 |
+
);
|
213 |
+
$collection = get_posts( $args );
|
214 |
+
foreach ( $collection as $item ) {
|
215 |
+
$goods[] = array( 'id' => $item->ID, 'name' => $item->post_title );
|
216 |
+
}
|
217 |
+
wp_reset_postdata();
|
218 |
+
|
219 |
+
return $goods;
|
220 |
+
}
|
221 |
+
|
222 |
+
/**
|
223 |
+
* Ajax request to dismiss admin notice for current user.
|
224 |
+
*/
|
225 |
+
public function executeDismissAdminNotice()
|
226 |
+
{
|
227 |
+
update_user_meta( get_current_user_id(), $this->getParameter( 'prefix' ) . 'dismiss_admin_notice', 1 );
|
228 |
+
}
|
229 |
+
|
230 |
+
/**
|
231 |
+
* Override parent method to add 'wp_ajax_ab_' prefix
|
232 |
+
* so current 'execute*' methods look nicer.
|
233 |
+
*
|
234 |
+
* @param string $prefix
|
235 |
+
*/
|
236 |
+
protected function registerWpActions( $prefix = '' )
|
237 |
+
{
|
238 |
+
parent::registerWpActions( 'wp_ajax_ab_' );
|
239 |
+
}
|
240 |
+
|
241 |
+
}
|
backend/modules/settings/forms/AB_BusinessHoursForm.php
DELETED
@@ -1,66 +0,0 @@
|
|
1 |
-
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Class AB_BusinessHoursForm
|
5 |
-
*/
|
6 |
-
class AB_BusinessHoursForm extends AB_Form {
|
7 |
-
|
8 |
-
public function __construct()
|
9 |
-
{
|
10 |
-
$this->setFields(array(
|
11 |
-
'ab_settings_monday_start',
|
12 |
-
'ab_settings_monday_end',
|
13 |
-
'ab_settings_tuesday_start',
|
14 |
-
'ab_settings_tuesday_end',
|
15 |
-
'ab_settings_wednesday_start',
|
16 |
-
'ab_settings_wednesday_end',
|
17 |
-
'ab_settings_thursday_start',
|
18 |
-
'ab_settings_thursday_end',
|
19 |
-
'ab_settings_friday_start',
|
20 |
-
'ab_settings_friday_end',
|
21 |
-
'ab_settings_saturday_start',
|
22 |
-
'ab_settings_saturday_end',
|
23 |
-
'ab_settings_sunday_start',
|
24 |
-
'ab_settings_sunday_end',
|
25 |
-
));
|
26 |
-
}
|
27 |
-
|
28 |
-
public function save()
|
29 |
-
{
|
30 |
-
foreach ( $this->data as $field => $value ) {
|
31 |
-
update_option( $field, $value );
|
32 |
-
}
|
33 |
-
}
|
34 |
-
|
35 |
-
/**
|
36 |
-
* @param string $field_name
|
37 |
-
* @param bool $is_start
|
38 |
-
* @return string
|
39 |
-
*/
|
40 |
-
public function renderField( $field_name = 'ab_settings_monday', $is_start = true )
|
41 |
-
{
|
42 |
-
$ts_length = AB_Config::getTimeSlotLength();
|
43 |
-
$time_output = AB_StaffScheduleItem::WORKING_START_TIME;
|
44 |
-
$time_end = AB_StaffScheduleItem::WORKING_END_TIME;
|
45 |
-
$option_name = $field_name . ( $is_start ? '_start' : '_end' );
|
46 |
-
$class_name = $is_start ? 'select_start' : 'select_end';
|
47 |
-
$selected_value = get_option( $option_name );
|
48 |
-
$output = "<select class='form-control ab-auto-w {$class_name}' name={$option_name}>";
|
49 |
-
|
50 |
-
if ( $is_start ) {
|
51 |
-
$output .= "<option value=''>" . __( 'OFF', 'bookly' ) . "</option>";
|
52 |
-
$time_end -= $ts_length;
|
53 |
-
}
|
54 |
-
|
55 |
-
while ( $time_output <= $time_end ) {
|
56 |
-
$value = AB_DateTimeUtils::buildTimeString( $time_output, false );
|
57 |
-
$op_name = AB_DateTimeUtils::formatTime( $time_output );
|
58 |
-
$output .= "<option value='{$value}'".selected( $value, $selected_value, false ).">{$op_name}</option>";
|
59 |
-
$time_output += $ts_length;
|
60 |
-
}
|
61 |
-
|
62 |
-
$output .= '</select>';
|
63 |
-
|
64 |
-
return $output;
|
65 |
-
}
|
66 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/modules/settings/forms/AB_CompanyForm.php
DELETED
@@ -1,64 +0,0 @@
|
|
1 |
-
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Class AB_CompanyForm
|
5 |
-
*/
|
6 |
-
class AB_CompanyForm extends AB_Form {
|
7 |
-
|
8 |
-
public function __construct()
|
9 |
-
{
|
10 |
-
$this->setFields(array(
|
11 |
-
'ab_settings_company_name',
|
12 |
-
'ab_settings_company_logo',
|
13 |
-
'ab_settings_company_address',
|
14 |
-
'ab_settings_company_phone',
|
15 |
-
'ab_settings_company_website',
|
16 |
-
'ab_settings_company_logo_path',
|
17 |
-
'ab_settings_company_logo_url',
|
18 |
-
));
|
19 |
-
}
|
20 |
-
|
21 |
-
/**
|
22 |
-
* @param array $post
|
23 |
-
* @param array $files
|
24 |
-
*/
|
25 |
-
public function bind( array $post, array $files = array() )
|
26 |
-
{
|
27 |
-
parent::bind( $post, $files );
|
28 |
-
|
29 |
-
// Remove the old image.
|
30 |
-
if ( isset( $post['ab_remove_logo'] ) && file_exists( get_option( 'ab_settings_company_logo_path' ) ) ) {
|
31 |
-
unlink( get_option( 'ab_settings_company_logo_path' ) );
|
32 |
-
update_option( 'ab_settings_company_logo_path', '' );
|
33 |
-
update_option( 'ab_settings_company_logo_url', '' );
|
34 |
-
}
|
35 |
-
|
36 |
-
// And add new.
|
37 |
-
if ( isset ( $files['ab_settings_company_logo'] ) && $files['ab_settings_company_logo']['tmp_name'] ) {
|
38 |
-
|
39 |
-
if ( in_array( $files['ab_settings_company_logo']['type'], array( 'image/gif', 'image/jpeg', 'image/png' ) ) ) {
|
40 |
-
$uploaded = wp_handle_upload( $files['ab_settings_company_logo'], array( 'test_form' => false ) );
|
41 |
-
if ( $uploaded ) {
|
42 |
-
$editor = wp_get_image_editor( $uploaded['file'] );
|
43 |
-
$editor->resize( 200, 200 );
|
44 |
-
$editor->save( $uploaded['file'] );
|
45 |
-
|
46 |
-
$this->data['ab_settings_company_logo_path'] = $uploaded['file'];
|
47 |
-
$this->data['ab_settings_company_logo_url'] = $uploaded['url'];
|
48 |
-
|
49 |
-
// Remove old image.
|
50 |
-
if ( file_exists( get_option( 'ab_settings_company_logo_path' ) ) ) {
|
51 |
-
unlink( get_option( 'ab_settings_company_logo_path' ) );
|
52 |
-
}
|
53 |
-
}
|
54 |
-
}
|
55 |
-
}
|
56 |
-
}
|
57 |
-
|
58 |
-
public function save()
|
59 |
-
{
|
60 |
-
foreach ( $this->data as $field => $value ) {
|
61 |
-
update_option( $field, $value );
|
62 |
-
}
|
63 |
-
}
|
64 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
backend/modules/settings/forms/BusinessHours.php
ADDED
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace BooklyLite\Backend\Modules\Settings\Forms;
|
3 |
+
|
4 |
+
use BooklyLite\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class BusinessHours
|
8 |
+
* @package BooklyLite\Backend\Modules\Settings\Forms
|
9 |
+
*/
|
10 |
+
class BusinessHours extends Lib\Base\Form
|
11 |
+
{
|
12 |
+
public function __construct()
|
13 |
+
{
|
14 |
+
$this->setFields( array(
|
15 |
+
'ab_settings_monday_start',
|
16 |
+
'ab_settings_monday_end',
|
17 |
+
'ab_settings_tuesday_start',
|
18 |
+
'ab_settings_tuesday_end',
|
19 |
+
'ab_settings_wednesday_start',
|
20 |
+
'ab_settings_wednesday_end',
|
21 |
+
'ab_settings_thursday_start',
|
22 |
+
'ab_settings_thursday_end',
|
23 |
+
'ab_settings_friday_start',
|
24 |
+
'ab_settings_friday_end',
|
25 |
+
'ab_settings_saturday_start',
|
26 |
+
'ab_settings_saturday_end',
|
27 |
+
'ab_settings_sunday_start',
|
28 |
+
'ab_settings_sunday_end',
|
29 |
+
) );
|
30 |
+
}
|
31 |
+
|
32 |
+
public function save()
|
33 |
+
{
|
34 |
+
foreach ( $this->data as $field => $value ) {
|
35 |
+
update_option( $field, $value );
|
36 |
+
}
|
37 |
+
}
|
38 |
+
|
39 |
+
/**
|
40 |
+
* @param string $field_name
|
41 |
+
* @param bool $is_start
|
42 |
+
* @return string
|
43 |
+
*/
|
44 |
+
public function renderField( $field_name = 'ab_settings_monday', $is_start = true )
|
45 |
+
{
|
46 |
+
$ts_length = Lib\Config::getTimeSlotLength();
|
47 |
+
$time_output = Lib\Entities\StaffScheduleItem::WORKING_START_TIME;
|
48 |
+
$time_end = Lib\Entities\StaffScheduleItem::WORKING_END_TIME;
|
49 |
+
$option_name = $field_name . ( $is_start ? '_start' : '_end' );
|
50 |
+
$class_name = $is_start ? 'select_start' : 'select_end hide-on-non-working-day';
|
51 |
+
$selected_value = get_option( $option_name );
|
52 |
+
$selected_seconds = Lib\Utils\DateTime::timeToSeconds( $selected_value );
|
53 |
+
$output = "<select style='display:inline-block' class='form-control ab-auto-w {$class_name}' name={$option_name}>";
|
54 |
+
|
55 |
+
if ( $is_start ) {
|
56 |
+
$output .= '<option value="">' . __( 'OFF', 'bookly' ) . '</option>';
|
57 |
+
$time_end -= $ts_length;
|
58 |
+
}
|
59 |
+
$value_added = false;
|
60 |
+
while ( $time_output <= $time_end ) {
|
61 |
+
if ( $value_added === false ) {
|
62 |
+
if ( $selected_seconds == $time_output ) {
|
63 |
+
$value_added = true;
|
64 |
+
} elseif ( $selected_seconds < $time_output ) {
|
65 |
+
$output .= "<option value='{$selected_value}' selected='selected'>{$selected_value}</option>";
|
66 |
+
$value_added = true;
|
67 |
+
}
|
68 |
+
}
|
69 |
+
|
70 |
+
$value = Lib\Utils\DateTime::buildTimeString( $time_output, false );
|
71 |
+
$op_name = Lib\Utils\DateTime::formatTime( $time_output );
|
72 |
+
$output .= "<option value='{$value}'" . selected( $value, $selected_value, false ) . ">{$op_name}</option>";
|
73 |
+
$time_output += $ts_length;
|
74 |
+
}
|
75 |
+
|
76 |
+
$output .= '</select>';
|
77 |
+
|
78 |
+
return $output;
|
79 |
+
}
|
80 |
+
|
81 |
+
}
|
backend/modules/settings/forms/Payments.php
ADDED
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace BooklyLite\Backend\Modules\Settings\Forms;
|
3 |
+
|
4 |
+
use \BooklyLite\Lib;
|
5 |
+
|
6 |
+
/**
|
7 |
+
* Class Payments
|
8 |
+
* @package BooklyLite\Backend\Modules\Settings
|
9 |
+
*/
|
10 |
+
class Payments extends Lib\Base\Form
|
11 |
+
{
|
12 |
+
public function __construct()
|
13 |
+
{
|
14 |
+
$this->setFields( array(
|
15 |
+
'ab_currency',
|
16 |
+
'ab_settings_pay_locally',
|
17 |
+
) );
|
18 |
+
}
|
19 |
+
|
20 |
+
public function save()
|
21 |
+
{
|
22 |
+
foreach ( $this->data as $field => $value ) {
|
23 |
+
update_option( $field, $value );
|
24 |
+
}
|
25 |
+
}
|
26 |
+
|
27 |
+
}
|
backend/modules/settings/resources/js/settings.js
CHANGED
@@ -1,20 +1,36 @@
|
|
1 |
jQuery(function ($) {
|
2 |
var $form = $('#business-hours'),
|
3 |
-
$all_tabs = $('#ab_settings_company, #ab_settings_payments, #ab_settings_hours, #ab_settings_holidays, #ab_settings_purchase_code, #ab_settings_google_calendar, #ab_settings_general, #ab_settings_woocommerce, #ab_settings_customers'),
|
4 |
-
$all_forms = $('#company-form, #payments-form, #hours-form, #holidays-form, #purchase-code-form, #general-form, #google-calendar-form, #woocommerce-form, #customers-form'),
|
5 |
$final_step_url = $('input[name=ab_settings_final_step_url]'),
|
6 |
-
$final_step_url_mode = $('#ab_settings_final_step_url_mode')
|
|
|
7 |
|
8 |
-
|
9 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
10 |
$('.select_start', $form).on('change', function () {
|
11 |
-
var $
|
12 |
-
$end_select = $('.select_end', $
|
13 |
$start_select = $(this);
|
14 |
|
15 |
if ($start_select.val()) {
|
16 |
-
$
|
17 |
-
$('span', $row).show();
|
18 |
|
19 |
var start_time = $start_select.val();
|
20 |
|
@@ -25,7 +41,7 @@ jQuery(function ($) {
|
|
25 |
// Hides end time options with value less than in the start time
|
26 |
$('option', $end_select).each(function () {
|
27 |
if ($(this).val() <= start_time) {
|
28 |
-
$(this).wrap(
|
29 |
}
|
30 |
});
|
31 |
|
@@ -33,8 +49,7 @@ jQuery(function ($) {
|
|
33 |
$('option:visible:first', $end_select).attr('selected', true);
|
34 |
}
|
35 |
} else { // OFF
|
36 |
-
$
|
37 |
-
$('span', $row).hide();
|
38 |
}
|
39 |
}).each(function () {
|
40 |
var $row = $(this).parent(),
|
@@ -65,102 +80,145 @@ jQuery(function ($) {
|
|
65 |
$('.select_start', $form).trigger('change');
|
66 |
});
|
67 |
|
68 |
-
// Tabs Onclick Handlers.
|
69 |
-
$all_tabs.on('click', function() {
|
70 |
-
$('.ab-active').removeClass('ab-active');
|
71 |
-
$(this).addClass('ab-active');
|
72 |
-
});
|
73 |
-
$('#ab_settings_company').on('click', function() {
|
74 |
-
$all_forms.addClass('hidden');
|
75 |
-
$('#company-form').removeClass('hidden');
|
76 |
-
});
|
77 |
-
$('#ab_settings_payments').on('click', function() {
|
78 |
-
$all_forms.addClass('hidden');
|
79 |
-
$('#payments-form').removeClass('hidden');
|
80 |
-
});
|
81 |
-
$('#ab_settings_hours').on('click', function() {
|
82 |
-
$all_forms.addClass('hidden');
|
83 |
-
$('#hours-form').removeClass('hidden');
|
84 |
-
});
|
85 |
-
$('#ab_settings_holidays').on('click', function() {
|
86 |
-
$all_forms.addClass('hidden');
|
87 |
-
$('#holidays-form').removeClass('hidden');
|
88 |
-
});
|
89 |
-
$('#ab_settings_purchase_code').on('click', function() {
|
90 |
-
$all_forms.addClass('hidden');
|
91 |
-
$('#purchase-code-form').removeClass('hidden');
|
92 |
-
});
|
93 |
-
$('#ab_settings_general').on('click', function() {
|
94 |
-
$all_forms.addClass('hidden');
|
95 |
-
$('#general-form').removeClass('hidden');
|
96 |
-
});
|
97 |
-
$('#ab_settings_woocommerce').on('click', function() {
|
98 |
-
$all_forms.addClass('hidden');
|
99 |
-
$('#woocommerce-form').removeClass('hidden');
|
100 |
-
});
|
101 |
-
$('#ab_settings_google_calendar').on('click', function() {
|
102 |
-
$all_forms.addClass('hidden');
|
103 |
-
$('#google-calendar-form').removeClass('hidden');
|
104 |
-
});
|
105 |
-
|
106 |
// Customers Tab
|
107 |
-
var $
|
108 |
-
$default_country = $('#ab_settings_phone_default_country'),
|
109 |
$default_country_code = $('#ab_sms_default_country_code');
|
110 |
|
111 |
-
|
112 |
-
$
|
113 |
-
$('#customers-form').removeClass('hidden');
|
114 |
-
}).on('click.fill', function() {
|
115 |
-
$.each($.fn.intlTelInput.getCountryData(), function( index, value ) {
|
116 |
-
$default_country.append('<option value="'+value.iso2 + '" data-code="' + value.dialCode + '">'+ value.name + ' +' + value.dialCode + '</option>' );
|
117 |
-
});
|
118 |
-
$(this).unbind('click.fill');
|
119 |
-
$default_country.val( $default_country.data('country') );
|
120 |
});
|
|
|
121 |
|
122 |
-
$default_country.on('change', function() {
|
123 |
$default_country_code.val($default_country.find('option:selected').data('code'));
|
124 |
});
|
125 |
|
126 |
-
if ($customers_tab.hasClass('ab-active')){
|
127 |
-
$customers_tab.trigger('click.fill');
|
128 |
-
}
|
129 |
-
|
130 |
// Company Tab
|
131 |
-
$('#ab-
|
132 |
-
$('#
|
|
|
|
|
|
|
133 |
});
|
134 |
-
$('#ab_settings_company_logo').on('change', function() { $('#ab-remove-logo').remove(); });
|
135 |
|
136 |
-
|
137 |
-
|
138 |
-
|
|
|
139 |
});
|
140 |
|
141 |
-
|
142 |
-
|
143 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
144 |
|
145 |
-
$('
|
146 |
-
|
|
|
|
|
147 |
});
|
148 |
|
149 |
-
$('
|
150 |
-
|
151 |
-
return $('#' + $(this).data('ext_id')).html();
|
152 |
-
},
|
153 |
-
html: true
|
154 |
});
|
155 |
|
156 |
if ($final_step_url.val()) { $final_step_url_mode.val(1); }
|
157 |
-
$final_step_url_mode.change(function(){
|
|
|
|
|
|
|
|
|
|
|
158 |
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
164 |
});
|
165 |
|
|
|
|
|
|
|
|
|
|
|
166 |
});
|
1 |
jQuery(function ($) {
|
2 |
var $form = $('#business-hours'),
|
|
|
|
|
3 |
$final_step_url = $('input[name=ab_settings_final_step_url]'),
|
4 |
+
$final_step_url_mode = $('#ab_settings_final_step_url_mode')
|
5 |
+
;
|
6 |
|
7 |
+
booklyAlert(BooklyL10n.alert);
|
8 |
|
9 |
+
Ladda.bind('button[type=submit]', {timeout: 2000});
|
10 |
+
|
11 |
+
$('.bookly-limitation').on('click', function (e) {
|
12 |
+
e.preventDefault();
|
13 |
+
Ladda.stopAll();
|
14 |
+
booklyAlert({error: [BooklyL10n.limitations]});
|
15 |
+
$(this).prop('disabled', true);
|
16 |
+
});
|
17 |
+
$('#ab_settings_step_cart_enabled,#ab_woocommerce_enabled,#ab_settings_coupons').on('change', function (e) {
|
18 |
+
$(this).val('0');
|
19 |
+
booklyAlert({error: [BooklyL10n.limitations]});
|
20 |
+
$(this).find('option:gt(0)').prop('disabled', true);
|
21 |
+
});
|
22 |
+
|
23 |
+
$('#ab_settings_google_client_id,#ab_settings_google_client_secret,#ab_settings_google_two_way_sync,#ab_settings_google_limit_events,#ab_settings_google_event_title').on('focus', function () {
|
24 |
+
$(this).prop('disabled',true);
|
25 |
+
booklyAlert({error: [BooklyL10n.limitations]});
|
26 |
+
});
|
27 |
$('.select_start', $form).on('change', function () {
|
28 |
+
var $flexbox = $(this).parents('.bookly-flexbox'),
|
29 |
+
$end_select = $('.select_end', $flexbox),
|
30 |
$start_select = $(this);
|
31 |
|
32 |
if ($start_select.val()) {
|
33 |
+
$flexbox.find('.hide-on-non-working-day').show();
|
|
|
34 |
|
35 |
var start_time = $start_select.val();
|
36 |
|
41 |
// Hides end time options with value less than in the start time
|
42 |
$('option', $end_select).each(function () {
|
43 |
if ($(this).val() <= start_time) {
|
44 |
+
$(this).wrap('<span>').parent().hide();
|
45 |
}
|
46 |
});
|
47 |
|
49 |
$('option:visible:first', $end_select).attr('selected', true);
|
50 |
}
|
51 |
} else { // OFF
|
52 |
+
$flexbox.find('.hide-on-non-working-day').hide();
|
|
|
53 |
}
|
54 |
}).each(function () {
|
55 |
var $row = $(this).parent(),
|
80 |
$('.select_start', $form).trigger('change');
|
81 |
});
|
82 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
83 |
// Customers Tab
|
84 |
+
var $default_country = $('#ab_settings_phone_default_country'),
|
|
|
85 |
$default_country_code = $('#ab_sms_default_country_code');
|
86 |
|
87 |
+
$.each($.fn.intlTelInput.getCountryData(), function (index, value) {
|
88 |
+
$default_country.append('<option value="' + value.iso2 + '" data-code="' + value.dialCode + '">' + value.name + ' +' + value.dialCode + '</option>');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
89 |
});
|
90 |
+
$default_country.val($default_country.data('country'));
|
91 |
|
92 |
+
$default_country.on('change', function () {
|
93 |
$default_country_code.val($default_country.find('option:selected').data('code'));
|
94 |
});
|
95 |
|
|
|
|
|
|
|
|
|
96 |
// Company Tab
|
97 |
+
$('#ab-settings-company-reset').on('click', function () {
|
98 |
+
var $div = $('#bookly-js-logo .bookly-js-image'),
|
99 |
+
$input = $('[name=ab_settings_company_logo_attachment_id]');
|
100 |
+
$div.attr('style', $div.data('style'));
|
101 |
+
$input.val($input.data('default'));
|
102 |
});
|
|
|
103 |
|
104 |
+
// Cart Tab
|
105 |
+
$('#ab_cart_show_columns').sortable({
|
106 |
+
axis : 'y',
|
107 |
+
handle : '.bookly-js-handle'
|
108 |
});
|
109 |
|
110 |
+
// Payment Tab
|
111 |
+
$('#ab_paypal_type').change(function () {
|
112 |
+
if (this.value != 'disabled') {
|
113 |
+
$(this).val('disabled');
|
114 |
+
booklyAlert({error: [BooklyL10n.limitations]});
|
115 |
+
$(this).find('option:gt(0)').prop('disabled', true);
|
116 |
+
}
|
117 |
+
$('.ab-paypal-ec').toggle(this.value != 'disabled');
|
118 |
+
}).change();
|
119 |
+
|
120 |
+
$('#ab_authorizenet_type').change(function () {
|
121 |
+
if (this.value != 'disabled') {
|
122 |
+
$(this).val('disabled');
|
123 |
+
booklyAlert({error: [BooklyL10n.limitations]});
|
124 |
+
$(this).find('option:gt(0)').prop('disabled', true);
|
125 |
+
}
|
126 |
+
$('.authorizenet').toggle(this.value != 'disabled');
|
127 |
+
}).change();
|
128 |
+
|
129 |
+
$('#ab_stripe').change(function () {
|
130 |
+
if (this.value != 'disabled') {
|
131 |
+
$(this).val('disabled');
|
132 |
+
booklyAlert({error: [BooklyL10n.limitations]});
|
133 |
+
$(this).find('option:gt(0)').prop('disabled', true);
|
134 |
+
}
|
135 |
+
$('.ab-stripe').toggle(this.value == 1);
|
136 |
+
}).change();
|
137 |
+
|
138 |
+
$('#ab_2checkout').change(function () {
|
139 |
+
if (this.value != 'disabled') {
|
140 |
+
$(this).val('disabled');
|
141 |
+
booklyAlert({error: [BooklyL10n.limitations]});
|
142 |
+
$(this).find('option:gt(0)').prop('disabled', true);
|
143 |
+
}
|
144 |
+
$('.ab-2checkout').toggle(this.value != 'disabled');
|
145 |
+
}).change();
|
146 |
+
|
147 |
+
$('#ab_payulatam').change(function () {
|
148 |
+
if (this.value != 'disabled') {
|
149 |
+
$(this).val('disabled');
|
150 |
+
booklyAlert({error: [BooklyL10n.limitations]});
|
151 |
+
$(this).find('option:gt(0)').prop('disabled', true);
|
152 |
+
}
|
153 |
+
$('.ab-payulatam').toggle(this.value != 'disabled');
|
154 |
+
}).change();
|
155 |
+
|
156 |
+
$('#ab_payson').change(function () {
|
157 |
+
if (this.value != 'disabled') {
|
158 |
+
$(this).val('disabled');
|
159 |
+
booklyAlert({error: [BooklyL10n.limitations]});
|
160 |
+
$(this).find('option:gt(0)').prop('disabled', true);
|
161 |
+
}
|
162 |
+
$('.ab-payson').toggle(this.value != 'disabled');
|
163 |
+
}).change();
|
164 |
+
|
165 |
+
$('#ab_mollie').change(function () {
|
166 |
+
if (this.value != 'disabled') {
|
167 |
+
$(this).val('disabled');
|
168 |
+
booklyAlert({error: [BooklyL10n.limitations]});
|
169 |
+
$(this).find('option:gt(0)').prop('disabled', true);
|
170 |
+
}
|
171 |
+
$('.ab-mollie').toggle(this.value != 'disabled');
|
172 |
+
}).change();
|
173 |
|
174 |
+
$('#ab-payments-reset').on('click', function (event) {
|
175 |
+
setTimeout(function () {
|
176 |
+
$('#ab_paypal_type,#ab_authorizenet_type,#ab_stripe,#ab_2checkout,#ab_payulatam,#ab_payson,#ab_mollie').change();
|
177 |
+
}, 50);
|
178 |
});
|
179 |
|
180 |
+
$('#ab-customer-reset').on('click', function (event) {
|
181 |
+
$default_country.val($default_country.data('country'));
|
|
|
|
|
|
|
182 |
});
|
183 |
|
184 |
if ($final_step_url.val()) { $final_step_url_mode.val(1); }
|
185 |
+
$final_step_url_mode.change(function () {
|
186 |
+
$(this).val(0);
|
187 |
+
booklyAlert({error: [BooklyL10n.limitations]});
|
188 |
+
$(this).find('option:gt(0)').prop('disabled', true);
|
189 |
+
$final_step_url.hide().val('');
|
190 |
+
});
|
191 |
|
192 |
+
$('li[data-target="#ab_settings_' + BooklyL10n.current_tab + '"]').tab('show');
|
193 |
+
|
194 |
+
$('#bookly-js-logo .bookly-pretty-indicator').on('click', function(){
|
195 |
+
var frame = wp.media({
|
196 |
+
library: {type: 'image'},
|
197 |
+
multiple: false
|
198 |
+
});
|
199 |
+
frame.on('select', function () {
|
200 |
+
var selection = frame.state().get('selection').toJSON(),
|
201 |
+
img_src
|
202 |
+
;
|
203 |
+
if (selection.length) {
|
204 |
+
if (selection[0].sizes['thumbnail'] !== undefined) {
|
205 |
+
img_src = selection[0].sizes['thumbnail'].url;
|
206 |
+
} else {
|
207 |
+
img_src = selection[0].url;
|
208 |
+
}
|
209 |
+
$('[name=ab_settings_company_logo_attachment_id]').val(selection[0].id);
|
210 |
+
$('#bookly-js-logo .bookly-js-image').css({'background-image': 'url(' + img_src + ')', 'background-size': 'cover'});
|
211 |
+
$('#bookly-js-logo .bookly-thumb-delete').show();
|
212 |
+
$(this).hide();
|
213 |
+
}
|
214 |
+
});
|
215 |
+
|
216 |
+
frame.open();
|
217 |
});
|
218 |
|
219 |
+
$('#bookly-js-logo .bookly-thumb-delete').on('click', function () {
|
220 |
+
var $thumb = $(this).parents('.bookly-js-image');
|
221 |
+
$thumb.attr('style', '');
|
222 |
+
$('[name=ab_settings_company_logo_attachment_id]').val('');
|
223 |
+
});
|
224 |
});
|
backend/modules/settings/templates/_cartForm.php
ADDED
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
|
2 |
+
<form method="post" action="<?php echo esc_url( add_query_arg( 'tab', 'cart' ) ) ?>"
|
3 |
+
class="ab-settings-form">
|
4 |
+
<div class="form-group">
|
5 |
+
<label for="ab_settings_step_cart_enabled"><?php _e( 'Cart', 'bookly' ) ?></label>
|
6 |
+
<p class="help-block"><?php _e( 'If cart is enabled then your clients will be able to book several appointments at once. Please note that WooCommerce integration must be disabled.', 'bookly' ) ?></p>
|
7 |
+
<?php \BooklyLite\Lib\Utils\Common::optionToggle( 'ab_settings_step_cart_enabled' ) ?>
|
8 |
+
</div>
|
9 |
+
<div class="form-group">
|
10 |
+
<label for="ab_cart_show_columns"><?php _e('Columns', 'bookly') ?></label><br/>
|
11 |
+
<div class="ab-flags" id="ab_cart_show_columns">
|
12 |
+
<?php foreach ( (array) get_option( 'ab_cart_show_columns' ) as $column => $attr ) : ?>
|
13 |
+
<div class="bookly-flexbox"<?php if ( $column == 'deposit' && ! \BooklyLite\Lib\Utils\Common::isPluginActive( 'bookly-addon-deposit-payments/main.php' ) ): ?> style="display:none"<?php endif ?>>
|
14 |
+
<div class="bookly-flex-cell">
|
15 |
+
<i class="bookly-js-handle bookly-margin-right-sm bookly-icon bookly-icon-draghandle bookly-cursor-move" title="<?php esc_attr_e( 'Reorder', 'bookly' ) ?>"></i>
|
16 |
+
</div>
|
17 |
+
<div class="bookly-flex-cell" style="width: 100%">
|
18 |
+
<div class="checkbox">
|
19 |
+
<label>
|
20 |
+
<input type="hidden" name="ab_cart_show_columns[<?php echo $column ?>][show]" value="0">
|
21 |
+
<input type="checkbox"
|
22 |
+
name="ab_cart_show_columns[<?php echo $column ?>][show]"
|
23 |
+
value="1" <?php checked($attr['show'], true) ?>>
|
24 |
+
<?php echo $cart_columns[$column] ?>
|
25 |
+
</label>
|
26 |
+
</div>
|
27 |
+
</div>
|
28 |
+
</div>
|
29 |
+
<?php endforeach ?>
|
30 |
+
</div>
|
31 |
+
</div>
|
32 |
+
<div class="form-group">
|
33 |
+
<label for="ab_settings_cart_notifications_combined"><?php _e( 'Combined notifications', 'bookly' ) ?></label>
|
34 |
+
<p class="help-block"><?php _e( 'If combined notifications are enabled then your clients will receive single notification for whole order instead of separate notification per each cart item. You will need to edit corresponding templates in Email and SMS Notifications.', 'bookly' ) ?></p>
|
35 |
+
<?php \BooklyLite\Lib\Utils\Common::optionToggle( 'ab_settings_cart_notifications_combined' ) ?>
|
36 |
+
</div>
|
37 |
+
|
38 |
+
<div class="panel-footer">
|
39 |
+
<?php \BooklyLite\Lib\Utils\Common::submitButton() ?>
|
40 |
+
<?php \BooklyLite\Lib\Utils\Common::resetButton() ?>
|
41 |
+
</div>
|
42 |
+
</form>
|
backend/modules/settings/templates/_companyForm.php
CHANGED
@@ -1,51 +1,58 @@
|
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
|
2 |
-
<form method="post" action="<?php echo esc_url(
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
20 |
</div>
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
<td><input id="ab_settings_company_website" class="form-control" type="text" name="ab_settings_company_website" value="<?php echo esc_attr( get_option( 'ab_settings_company_website' ) ) ?>" /></td>
|
42 |
-
</tr>
|
43 |
-
<tr>
|
44 |
-
<td></td>
|
45 |
-
<td>
|
46 |
-
<?php AB_Utils::submitButton() ?>
|
47 |
-
<?php AB_Utils::resetButton( 'ab-settings-company-reset' ) ?>
|
48 |
-
</td>
|
49 |
-
</tr>
|
50 |
-
</table>
|
1 |
<?php if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly ?>
|
2 |
+
<form method="post" action="<?php echo esc_url(add_query_arg('tab', 'company')) ?>" enctype="multipart/form-data"
|
3 |
+
class="ab-settings-form">
|
4 |
+
<div class="row">
|
5 |
+
<div class="col-xs-3 col-lg-2">
|
6 |
+
<div class="bookly-flexbox">
|
7 |
+
<div id="bookly-js-logo" class="bookly-thumb bookly-thumb-lg bookly-margin-right-lg">
|
8 |
+
<input type="hidden" name="ab_settings_company_logo_attachment_id" data-default="<?php echo esc_attr( get_option( 'ab_settings_company_logo_attachment_id' ) ) ?>"
|
9 |
+
value="<?php echo esc_attr( get_option( 'ab_settings_company_logo_attachment_id' ) ) ?>">
|
10 |
+
<div class="bookly-flex-cell">
|
11 |
+
<div class="form-group">
|
12 |
+
<?php $img = wp_get_attachment_image_src( get_option( 'ab_settings_company_logo_attachment_id' ), 'thumbnail' ) ?>
|
13 |
+
<div class="bookly-js-image bookly-thumb bookly-thumb-lg bookly-margin-right-lg"
|
14 |
+
data-style="<?php echo $img ? 'background-image: url(' . $img[0] . '); background-size: cover;' : '' ?>"
|
15 |
+
<?php echo $img ? 'style="background-image: url(' . $img[0] . '); background-size: cover;"' : '' ?>
|
16 |
+
>
|
17 |
+
<a class="dashicons dashicons-trash text-danger bookly-thumb-delete"
|
18 |
+
href="javascript:void(0)"
|
19 |
+
title="<?php _e( 'Delete', 'bookly' ) ?>"
|
20 |
+
<?php if ( ! $img ) : ?>style="display: none;"<?php endif ?>>
|
21 |
+
</a>
|
22 |
+
<div class="bookly-thumb-edit">
|
23 |
+
<div class="bookly-pretty">
|
24 |
+
<label class="bookly-pretty-indicator bookly-thumb-edit-btn">
|
25 |
+
<?php _e( 'Image', 'bookly' ) ?>
|
26 |
+
</label>
|
27 |
+
</div>
|
28 |
+
</div>
|
29 |
+
</div>
|
30 |
+
</div>
|
31 |
</div>
|
32 |
+
</div>
|
33 |
+
</div>
|
34 |
+
</div>
|
35 |
+
<div class="col-xs-9 col-lg-10">
|
36 |
+
<div class="bookly-flex-cell bookly-vertical-middle">
|
37 |
+
<?php \BooklyLite\Lib\Utils\Common::optionText( __( 'Company name', 'bookly' ), 'ab_settings_company_name' ) ?>
|
38 |
+
</div>
|
39 |
+
</div>
|
40 |
+
</div>
|
41 |
+
|
42 |
+
<div class="form-group">
|
43 |
+
<label for="ab_settings_company_address"><?php _e( 'Address', 'bookly' ) ?></label>
|
44 |
+
<textarea id="ab_settings_company_address" class="form-control" rows="5"
|
45 |
+
name="ab_settings_company_address"><?php echo esc_attr( get_option( 'ab_settings_company_address' ) ) ?></textarea>
|
46 |
+
</div>
|
47 |
+
<div class="form-group">
|
48 |
+
<?php \BooklyLite\Lib\Utils\Common::optionText( __( 'Phone', 'bookly' ), 'ab_settings_company_phone' ) ?>
|
49 |
+
</div>
|
50 |
+
<div class="form-group">
|
51 |
+
<?php \BooklyLite\Lib\Utils\Common::optionText( __( 'Website', 'bookly'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|