dashboard_manager = new DashboardManager(); $this->business_manager = new BusinessManager(); $this->bookings_manager = new BookingsManager(); $this->calendar_manager = new CalendarManager(); $this->customers_manager = new CustomersManager(); $this->form_manager = new FormManager(); $this->communication_manager = new CommunicationManager(); $this->payment_methods_manager = new PaymentMethodsManager(); $this->data_tools_manager = new DataToolsManager(); $this->admin_data_manager = new AdminDataManager(); $this->renderer = new PageRenderer( $this->dashboard_manager, $this->business_manager, $this->bookings_manager, $this->calendar_manager, $this->customers_manager, $this->form_manager, $this->communication_manager, $this->payment_methods_manager, $this->data_tools_manager ); $this->settings = new Settings(); } /** * Registers admin hooks. */ public function register(): void { add_action( 'admin_init', array( $this->settings, 'register' ) ); add_action( 'admin_init', array( $this->renderer, 'redirect_settings_module' ), 20, 0 ); $this->business_manager->register(); $this->bookings_manager->register(); $this->calendar_manager->register(); $this->customers_manager->register(); $this->communication_manager->register(); $this->data_tools_manager->register(); $this->admin_data_manager->register(); RecordPicker::register(); ReviewRequest::register(); add_action( 'admin_post_abientot_save_settings', array( $this->settings, 'save' ) ); add_action( 'admin_post_abientot_form_create', array( $this->form_manager, 'create_form' ) ); add_action( 'admin_post_abientot_form_delete', array( $this->form_manager, 'delete_form' ) ); add_action( 'admin_menu', array( $this, 'register_menu' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_assets' ) ); add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_design_assets' ), 150 ); add_filter( 'admin_body_class', array( $this, 'admin_body_class' ) ); add_filter( 'option_page_capability_' . Settings::GROUP, array( $this, 'settings_capability' ) ); } /** * Registers the Abientot menu tree. */ public function register_menu(): void { $modules = $this->renderer->modules(); $modules = apply_filters( 'abientot_admin_menu_items', $modules ); add_menu_page( __( 'Abientot', 'abientot' ), __( 'Abientot', 'abientot' ), current_user_can( 'abientot_manage_bookings' ) ? 'abientot_manage_bookings' : 'abientot_view_reports', 'abientot', function (): void { $report_filters = array_filter( array( 'date_from', 'date_to', 'currency' ), static fn( string $key ): bool => null !== \Abientot\Support\Request::view( $key, 'list' ) || null !== \Abientot\Support\Request::view( $key ) ); $this->renderer->render_module( current_user_can( 'abientot_manage_bookings' ) && ! $report_filters ? 'today' : 'dashboard' ); }, 'dashicons-calendar-alt', 56 ); foreach ( $modules as $slug => $module ) { $page_slug = 'abientot-' . $slug; $callback = 'settings' === $slug ? array( $this->renderer, 'render_settings' ) : function () use ( $slug ): void { $this->renderer->render_module( $slug ); }; add_submenu_page( 'abientot', $module['title'], $module['menu_title'], $module['capability'], $page_slug, $callback ); } } /** * Loads local admin assets only on Abientot pages. * * @param string $hook_suffix Current admin hook suffix. */ public function enqueue_assets( string $hook_suffix ): void { if ( false === strpos( $hook_suffix, 'abientot' ) && ! $this->is_abientot_admin_page() ) { return; } wp_enqueue_style( 'abientot-admin', ABIENTOT_PLUGIN_URL . 'assets/admin/admin.css', array(), $this->asset_version( 'assets/admin/admin.css' ) ); wp_enqueue_style( 'abientot-schedule', ABIENTOT_PLUGIN_URL . 'assets/admin/schedule.css', array( 'abientot-admin' ), $this->asset_version( 'assets/admin/schedule.css' ) ); wp_enqueue_script( 'abientot-schedule', ABIENTOT_PLUGIN_URL . 'assets/admin/schedule.js', array(), $this->asset_version( 'assets/admin/schedule.js' ), true ); wp_enqueue_style( 'abientot-navigation', ABIENTOT_PLUGIN_URL . 'assets/admin/navigation.css', array( 'abientot-admin' ), $this->asset_version( 'assets/admin/navigation.css' ) ); wp_enqueue_style( 'abientot-workspace', ABIENTOT_PLUGIN_URL . 'assets/admin/workspace.css', array( 'abientot-admin' ), $this->asset_version( 'assets/admin/workspace.css' ) ); wp_enqueue_script( 'abientot-workspace', ABIENTOT_PLUGIN_URL . 'assets/admin/workspace.js', array( 'abientot-admin' ), $this->asset_version( 'assets/admin/workspace.js' ), true ); wp_enqueue_script( 'abientot-qrcode', ABIENTOT_PLUGIN_URL . 'assets/admin/vendor/qrcode-generator/qrcode.js', array(), '1.4.4', true ); wp_enqueue_script( 'abientot-share-kit', ABIENTOT_PLUGIN_URL . 'assets/admin/share-kit.js', array( 'abientot-qrcode' ), $this->asset_version( 'assets/admin/share-kit.js' ), true ); wp_enqueue_style( 'abientot-frontend', ABIENTOT_PLUGIN_URL . 'assets/frontend/booking-form.css', array(), $this->asset_version( 'assets/frontend/booking-form.css' ) ); wp_enqueue_style( 'abientot-journey', ABIENTOT_PLUGIN_URL . 'assets/frontend/journey.css', array( 'abientot-frontend' ), $this->asset_version( 'assets/frontend/journey.css' ) ); wp_enqueue_style( 'abientot-compact', ABIENTOT_PLUGIN_URL . 'assets/frontend/compact.css', array( 'abientot-journey' ), $this->asset_version( 'assets/frontend/compact.css' ) ); // The booking form editor is a section of every Abientot page; the script exits when it is absent. wp_enqueue_script( 'abientot-form-editor-preview', ABIENTOT_PLUGIN_URL . 'assets/admin/form-editor-preview.js', array(), $this->asset_version( 'assets/admin/form-editor-preview.js' ), true ); $page = \Abientot\Support\Request::page(); wp_register_script( 'abientot-contrast', ABIENTOT_PLUGIN_URL . 'assets/frontend/contrast.js', array(), $this->asset_version( 'assets/frontend/contrast.js' ), true ); wp_enqueue_script( 'abientot-admin', ABIENTOT_PLUGIN_URL . 'assets/admin/admin.js', array( 'jquery', 'abientot-localization', 'wp-i18n', 'abientot-contrast' ), $this->asset_version( 'assets/admin/admin.js' ), true ); if ( 'abientot-form' === $page ) { wp_enqueue_style( 'abientot-form-builder', ABIENTOT_PLUGIN_URL . 'assets/admin/form-builder.css', array( 'abientot-admin', 'abientot-journey', 'abientot-compact', 'abientot-design' ), $this->asset_version( 'assets/admin/form-builder.css' ) ); wp_enqueue_script( 'abientot-form-builder', ABIENTOT_PLUGIN_URL . 'assets/admin/form-builder.js', array( 'abientot-admin' ), $this->asset_version( 'assets/admin/form-builder.js' ), true ); } wp_set_script_translations( 'abientot-admin', 'abientot', \Abientot\Support\Translations::bundled_path() ); wp_localize_script( 'abientot-admin', 'abientotAdmin', array( 'labels' => array( 'Time' => __( 'Time', 'abientot' ), 'Customer' => __( 'Customer', 'abientot' ), 'Booking detail' => __( 'Booking detail', 'abientot' ), 'When' => __( 'When', 'abientot' ), 'Notes' => __( 'Notes', 'abientot' ), 'Service' => __( 'Service', 'abientot' ), 'Status' => __( 'Status', 'abientot' ), 'Payment' => __( 'Payment', 'abientot' ), 'Total' => __( 'Total', 'abientot' ), 'Actions' => __( 'Actions', 'abientot' ), 'Contact' => __( 'Contact', 'abientot' ), 'Bookings' => __( 'Bookings', 'abientot' ), 'Total spent' => __( 'Total spent', 'abientot' ), 'Average spend' => __( 'Average spend', 'abientot' ), 'Issues' => __( 'Issues', 'abientot' ), 'Paid' => __( 'Paid', 'abientot' ), 'Unpaid' => __( 'Unpaid', 'abientot' ), 'Not required' => __( 'Not required', 'abientot' ), 'Booking service' => __( 'Booking service', 'abientot' ), 'Booking status' => __( 'Booking status', 'abientot' ), 'Payment status' => __( 'Payment status', 'abientot' ), 'No customers found.' => __( 'No customers found.', 'abientot' ), 'Possible duplicate' => __( 'Possible duplicate', 'abientot' ), 'Edit' => __( 'Edit', 'abientot' ), 'Create booking' => __( 'Create booking', 'abientot' ), 'Previous' => __( 'Previous', 'abientot' ), 'Next' => __( 'Next', 'abientot' ), 'Pagination' => __( 'Pagination', 'abientot' ), '%1$d–%2$d of %3$d' => /* translators: 1: first displayed row, 2: last displayed row, 3: total rows. */ __( '%1$d–%2$d of %3$d', 'abientot' ), '%1$d cancelled, %2$d no-show' => /* translators: 1: cancelled booking count, 2: no-show booking count. */ __( '%1$d cancelled, %2$d no-show', 'abientot' ), ), 'ajaxUrl' => admin_url( 'admin-ajax.php' ), 'calendarNonce' => wp_create_nonce( 'abientot_calendar_events' ), 'bookingNonce' => wp_create_nonce( 'abientot_admin_booking_slots' ), 'customerAutocompleteNonce' => wp_create_nonce( 'abientot_customer_autocomplete' ), 'quickEditNonce' => wp_create_nonce( 'abientot_quick_update_booking' ), 'rescheduleNonce' => wp_create_nonce( 'abientot_booking_change_confirmation' ), 'dataNonce' => current_user_can( 'abientot_manage_bookings' ) || current_user_can( 'abientot_manage_customers' ) ? wp_create_nonce( 'abientot_admin_data' ) : '', 'adminUrl' => admin_url( 'admin.php' ), 'displayFormats' => array( 'date' => DateTimeHelper::date_display_format(), 'time' => DateTimeHelper::time_display_format(), 'monthYear' => DateTimeHelper::month_year_format(), ), 'dateNames' => \Abientot\Support\Locale::date_names(), 'locale' => \Abientot\Support\Locale::config(), 'strings' => array( 'loading' => __( 'Loading calendar...', 'abientot' ), 'empty' => __( 'No bookings in this range.', 'abientot' ), 'error' => __( 'Calendar data could not be loaded.', 'abientot' ), 'syncing' => __( 'Syncing admin data...', 'abientot' ), 'loadingSlots' => __( 'Loading available times...', 'abientot' ), 'noSlots' => __( 'No available times remain for this date with the selected service, staff and location. Choose another date or change your selection in this form.', 'abientot' ), 'slotError' => __( 'Available times could not be loaded.', 'abientot' ), 'edit' => __( 'Edit', 'abientot' ), 'editBooking' => __( 'Edit booking', 'abientot' ), 'updateBooking' => __( 'Update booking', 'abientot' ), 'create' => __( 'Create booking', 'abientot' ), 'add' => __( 'Add', 'abientot' ), 'close' => __( 'Close', 'abientot' ), 'remove' => __( 'Remove', 'abientot' ), 'more' => __( 'more', 'abientot' ), 'calendarRegion' => __( 'Bookings calendar', 'abientot' ), /* translators: %s: number of guests of a table or room booking. */ 'guests' => __( 'Guests: %s', 'abientot' ), /* translators: %s: number of bookings hidden behind a +N button (button label for screen readers). */ 'guestMore' => __( 'More bookings: %s', 'abientot' ), 'guestLess' => __( 'Show fewer bookings', 'abientot' ), 'servicesLabel' => __( 'Services', 'abientot' ), 'viewDay' => __( 'View day', 'abientot' ), 'addBooking' => __( 'Add booking', 'abientot' ), 'timeChangeBookings' => __( 'Bookings during a time change', 'abientot' ), 'closed' => __( 'Closed', 'abientot' ), 'today' => __( 'Today', 'abientot' ), 'dayLoad' => __( 'Select a date to see that day.', 'abientot' ), 'dayEmpty' => __( 'No other bookings for this day.', 'abientot' ), 'quickSaved' => __( 'Saved', 'abientot' ), 'quickError' => __( 'The booking could not be updated.', 'abientot' ), 'saving' => __( 'Saving...', 'abientot' ), 'saveError' => __( 'The booking could not be saved.', 'abientot' ), 'customerLoadError' => __( 'The customer profile could not be loaded.', 'abientot' ), 'customerCreateError' => __( 'The customer could not be created.', 'abientot' ), 'customerUpdateError' => __( 'The customer could not be updated.', 'abientot' ), 'customerSaved' => __( 'Customer saved.', 'abientot' ), 'customerMatches' => __( 'Existing customers', 'abientot' ), 'customerMatchesHint' => __( 'Select a customer to fill all contact details.', 'abientot' ), 'customerSelected' => __( 'Customer selected', 'abientot' ), 'customerUndo' => __( 'Undo', 'abientot' ), 'customerNoMatch' => __( 'No matching customer. A new customer will be created.', 'abientot' ), 'confirmChangeTitle' => __( 'Confirm customer message', 'abientot' ), 'confirmReschedule' => __( 'Confirm reschedule', 'abientot' ), 'confirmCancellation' => __( 'Confirm cancellation', 'abientot' ), 'contactCustomer' => __( 'Contact the customer before confirming if needed.', 'abientot' ), 'subject' => __( 'Subject', 'abientot' ), 'messageToCustomer' => __( 'Message to customer', 'abientot' ), 'sendAndUpdate' => __( 'Update booking and send message', 'abientot' ), 'keepEditing' => __( 'Cancel rescheduling', 'abientot' ), 'previewError' => __( 'The confirmation message could not be prepared.', 'abientot' ), 'timeOffConflict' => __( 'This employee already has bookings on the selected day. Edit or cancel those bookings before saving time off.', 'abientot' ), 'locationClosureConflict' => __( 'This location already has bookings on the selected day. Edit or cancel those bookings before saving the closure.', 'abientot' ), 'manageBooking' => __( 'Manage booking', 'abientot' ), 'previous' => __( 'Previous', 'abientot' ), 'next' => __( 'Next', 'abientot' ), 'week' => __( 'Week', 'abientot' ), 'weekdays' => array( esc_html_x( 'Mon', 'weekday abbreviation', 'abientot' ), esc_html_x( 'Tue', 'weekday abbreviation', 'abientot' ), esc_html_x( 'Wed', 'weekday abbreviation', 'abientot' ), esc_html_x( 'Thu', 'weekday abbreviation', 'abientot' ), esc_html_x( 'Fri', 'weekday abbreviation', 'abientot' ), esc_html_x( 'Sat', 'weekday abbreviation', 'abientot' ), esc_html_x( 'Sun', 'weekday abbreviation', 'abientot' ), ), ), ) ); wp_enqueue_script( 'abientot-admin-feedback', ABIENTOT_PLUGIN_URL . 'assets/admin/feedback.js', array( 'abientot-admin' ), $this->asset_version( 'assets/admin/feedback.js' ), true ); wp_enqueue_style( 'abientot-admin-feedback', ABIENTOT_PLUGIN_URL . 'assets/admin/feedback.css', array( 'abientot-admin' ), $this->asset_version( 'assets/admin/feedback.css' ) ); wp_localize_script( 'abientot-admin-feedback', 'abientotAdminFeedback', array( 'ajaxUrl' => admin_url( 'admin-ajax.php' ), 'deleteWarning' => __( 'Delete this data? This action cannot be undone.', 'abientot' ), 'deleteLabel' => __( 'Delete', 'abientot' ), 'cancelLabel' => __( 'Cancel', 'abientot' ), 'error' => __( 'The action could not be completed. Check your entries and try again.', 'abientot' ), 'session' => __( 'Your session expired. Reload the page and try again.', 'abientot' ), 'uncertain' => __( 'The result could not be confirmed. Your entries are still here. Check whether the change was saved before trying again.', 'abientot' ), ) ); wp_enqueue_media(); wp_enqueue_editor(); } /** * Allows Abientot settings managers to save the option through options.php. */ public function settings_capability(): string { return 'abientot_manage_settings'; } public function enqueue_design_assets(): void { if ( ! $this->is_abientot_admin_page() ) { return; } wp_enqueue_style( 'abientot-design', ABIENTOT_PLUGIN_URL . 'assets/admin/design.css', array( 'abientot-admin', 'abientot-workspace' ), $this->asset_version( 'assets/admin/design.css' ) ); wp_enqueue_style( 'abientot-calendar-design', ABIENTOT_PLUGIN_URL . 'assets/admin/calendar-design.css', array( 'abientot-design' ), $this->asset_version( 'assets/admin/calendar-design.css' ) ); wp_enqueue_style( 'abientot-drawers', ABIENTOT_PLUGIN_URL . 'assets/admin/drawers.css', array( 'abientot-design', 'abientot-schedule' ), $this->asset_version( 'assets/admin/drawers.css' ) ); wp_enqueue_style( 'abientot-mobile', ABIENTOT_PLUGIN_URL . 'assets/admin/mobile.css', array( 'abientot-drawers', 'abientot-calendar-design', 'abientot-activity-center' ), $this->asset_version( 'assets/admin/mobile.css' ) ); wp_enqueue_style( 'abientot-settings-workspace', ABIENTOT_PLUGIN_URL . 'assets/admin/settings-workspace.css', array( 'abientot-mobile' ), $this->asset_version( 'assets/admin/settings-workspace.css' ) ); wp_enqueue_script( 'abientot-mobile', ABIENTOT_PLUGIN_URL . 'assets/admin/mobile.js', array( 'abientot-admin' ), $this->asset_version( 'assets/admin/mobile.js' ), true ); wp_localize_script( 'abientot-mobile', 'abientotMobile', array( 'filters' => __( 'Filters', 'abientot' ) ) ); wp_enqueue_script( 'abientot-design', ABIENTOT_PLUGIN_URL . 'assets/admin/design.js', array( 'abientot-admin' ), $this->asset_version( 'assets/admin/design.js' ), true ); wp_enqueue_style( 'abientot-activity-center', ABIENTOT_PLUGIN_URL . 'assets/admin/activity-center.css', array( 'abientot-design' ), $this->asset_version( 'assets/admin/activity-center.css' ) ); wp_enqueue_script( 'abientot-activity-center', ABIENTOT_PLUGIN_URL . 'assets/admin/activity-center.js', array( 'abientot-admin' ), $this->asset_version( 'assets/admin/activity-center.js' ), true ); wp_enqueue_style( 'abientot-reports', ABIENTOT_PLUGIN_URL . 'assets/admin/reports.css', array( 'abientot-settings-workspace' ), $this->asset_version( 'assets/admin/reports.css' ) ); wp_enqueue_script( 'abientot-reports', ABIENTOT_PLUGIN_URL . 'assets/admin/reports.js', array(), $this->asset_version( 'assets/admin/reports.js' ), true ); } /** * Marks Abientot admin pages so the app can use a focused full-screen layout. */ public function admin_body_class( string $classes ): string { if ( $this->is_abientot_admin_page() ) { $classes .= ' abientot-fullscreen-admin'; } return $classes; } private function is_abientot_admin_page(): bool { $page = \Abientot\Support\Request::page(); return 'abientot' === $page || 0 === strpos( $page, 'abientot-' ); } private function asset_version( string $relative_path ): string { $path = ABIENTOT_PLUGIN_DIR . ltrim( $relative_path, '/' ); return file_exists( $path ) ? (string) filemtime( $path ) : ABIENTOT_VERSION; } }