services = new ServicesRepository(); $this->locations = new LocationsRepository(); } public function render_form_admin(): void { if ( ! current_user_can( 'abientot_manage_settings' ) ) { wp_die( esc_html__( 'You do not have permission to manage the booking form.', 'abientot' ) ); } $form_id = BookingForms::resolve( \Abientot\Support\Request::view( 'form', 'key' ) ?? 0 ); $forms = BookingForms::names(); $settings = BookingForms::settings( $form_id ); $services = $this->public_services(); // Extension-owned preview context is opaque to the shared editor. $editor_context = apply_filters( 'abientot_form_editor_context', array(), $settings ); $editor_context = is_array( $editor_context ) ? $editor_context : array(); $location = $this->locations->default_location(); $staff = ( new StaffRepository() )->default_staff(); $timezone = DateTimeHelper::business_timezone(); foreach ( array( $staff['timezone'] ?? '', $location['timezone'] ?? '' ) as $candidate ) { if ( '' === $candidate ) { continue; } try { $timezone = new \DateTimeZone( $candidate ); break; } catch ( \Exception $error ) { continue; } } ?>

render_form_switcher( $forms, $form_id ); ?>

render_general_section( $settings, $forms[ $form_id ] ); ?>

render_style_section( $settings, $location ); ?>

render_fields_section( $settings ); ?>

card_open( 'yes-alt', __( 'After booking', 'abientot' ), __( 'Message shown once the booking is sent', 'abientot' ) ); ?>
setting_textarea( 'confirmation_message', __( 'Confirmation message', 'abientot' ), (string) $settings['confirmation_message'] ); ?>
card_close(); ?> render_policy_section( $settings ); ?>

card_open( 'visibility', __( 'Display', 'abientot' ), __( 'How the form opens on your pages', 'abientot' ) ); ?>
setting_select( 'form_display_mode', __( 'Default mode', 'abientot' ), array( 'embedded' => __( 'Embedded', 'abientot' ), 'popup' => __( 'Popup', 'abientot' ) ), (string) $settings['form_display_mode'] ); ?> setting_text( 'form_button_label', __( 'Popup button label', 'abientot' ), (string) $settings['form_button_label'] ); ?>
card_close(); ?> card_open( 'shortcode', __( 'Add to your website', 'abientot' ), __( 'Copy a shortcode or use the block', 'abientot' ), true, 'abientot-form-publish' ); ?>

card_close(); ?>

appearance_css( $settings ) ); ?>

>

render_preview_services( $services, $settings ); ?>
> */ private function public_services(): array { return array_values( array_filter( $this->services->list_for_admin(), static function ( array $service ): bool { return 'active' === (string) $service['status'] && 'public' === (string) $service['visibility']; } ) ); } /** * @param array $settings Settings. */ private function render_general_section( array $settings, string $form_name ): void { $this->card_open( 'edit', __( 'Form content', 'abientot' ), __( 'Title and introduction shown above the steps', 'abientot' ) ); ?>

setting_text( 'form_title', __( 'Title', 'abientot' ), (string) $settings['form_title'], true ); ?> setting_textarea( 'form_intro', __( 'Intro text', 'abientot' ), (string) $settings['form_intro'] ); ?>
card_close(); } /** * @param array $settings Settings. */ private function render_fields_section( array $settings ): void { $fields = Settings::normalize_form_fields( $settings['form_fields'] ?? array() ); $this->card_open( 'id', __( 'Customer details', 'abientot' ), __( 'Choose the information customers provide', 'abientot' ) ); ?>

$field ) : ?> render_field_builder_row( $field, $index ); ?>
card_close(); } /** * Appearance: a palette, then one card per part of the form. * * @param array $settings Settings. * @param array|null $location Default location. */ private function render_style_section( array $settings, ?array $location ): void { $looks = FormLooks::all(); $current = (string) ( $settings['form_look'] ?? '' ); $data = array(); foreach ( $looks as $key => $look ) { $data[ $key ] = $look['settings']; } $fonts = array( 'system' => '-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif', 'serif' => 'Georgia,"Times New Roman",serif', 'rounded' => '"Trebuchet MS","Segoe UI",sans-serif', 'mono' => '"SFMono-Regular",Consolas,monospace' ); $this->card_open( 'admin-appearance', __( 'Start with a look', 'abientot' ), __( 'Each look sets colors, shapes, type and depth together. Adjust anything afterwards.', 'abientot' ), true, 'abientot-design-presets' ); ?>
$look ) : $l = $look['settings']; ?>

card_close(); $this->card_open( 'art', __( 'Colors', 'abientot' ), __( 'Brand, background and text colors', 'abientot' ) ); ?>
setting_color( 'form_primary_color', __( 'Primary color', 'abientot' ), (string) $settings['form_primary_color'] ); ?> setting_color( 'form_background_color', __( 'Background color', 'abientot' ), (string) $settings['form_background_color'] ); ?> setting_color( 'form_text_color', __( 'Text color', 'abientot' ), (string) $settings['form_text_color'] ); ?>
setting_range( 'form_background_opacity', __( 'Background visibility', 'abientot' ), (int) $settings['form_background_opacity'], 0, 100, '%' ); ?> setting_toggle( 'form_auto_text_color', __( 'Automatic text color', 'abientot' ), ! empty( $settings['form_auto_text_color'] ), __( 'Keeps your text color but adjusts its shade on each background (form, cards, fields) so the text always keeps a good contrast and stays easy to read.', 'abientot' ) ); ?>
card_close(); $this->card_open( 'editor-textcolor', __( 'Text & spacing', 'abientot' ), __( 'Font, density and text size', 'abientot' ) ); ?>
setting_select( 'form_font_family', __( 'Font', 'abientot' ), array( 'system' => __( 'Modern system', 'abientot' ), 'serif' => __( 'Editorial serif', 'abientot' ), 'rounded' => __( 'Friendly rounded', 'abientot' ), 'mono' => __( 'Technical mono', 'abientot' ) ), (string) $settings['form_font_family'] ); ?> setting_select( 'form_density', __( 'Density', 'abientot' ), array( 'compact' => __( 'Compact', 'abientot' ), 'comfortable' => __( 'Comfortable', 'abientot' ) ), (string) $settings['form_density'], __( 'Compact uses less space on your page. Comfortable uses larger buttons and spacing.', 'abientot' ) ); ?> setting_range( 'form_font_size', __( 'Text size', 'abientot' ), (int) $settings['form_font_size'], 13, 19, 'px' ); ?>
card_close(); $this->card_open( 'editor-expand', __( 'Form frame', 'abientot' ), __( 'Width, padding, border and shadow', 'abientot' ), false ); ?>
setting_range( 'form_max_width', __( 'Form width', 'abientot' ), (int) $settings['form_max_width'], 360, 1400, 'px' ); ?> setting_range( 'form_padding', __( 'Form padding', 'abientot' ), (int) $settings['form_padding'], 8, 64, 'px' ); ?> setting_range( 'form_container_border_radius', __( 'Border radius', 'abientot' ), (int) $settings['form_container_border_radius'], 0, 32, 'px' ); ?> setting_range( 'form_border_width', __( 'Border width', 'abientot' ), (int) $settings['form_border_width'], 0, 8, 'px' ); ?> setting_color( 'form_border_color', __( 'Border color', 'abientot' ), (string) $settings['form_border_color'] ); ?> setting_select( 'form_shadow', __( 'Shadow', 'abientot' ), array( 'none' => __( 'Flat', 'abientot' ), 'soft' => __( 'Soft', 'abientot' ), 'lifted' => __( 'Lifted', 'abientot' ) ), (string) $settings['form_shadow'] ); ?>
card_close(); ?>
render_style_services_subsection( $settings ); ?>
render_style_categories_subsection( $settings ); ?>
render_style_hours_subsection( $settings ); $this->render_style_fields_subsection( $settings ); $this->render_style_images_subsection( $settings ); $this->render_style_location_subsection( $settings, $location ); } /** * @param array $settings Settings. */ private function render_style_services_subsection( array $settings ): void { $this->card_open( 'screenoptions', __( 'Service cards', 'abientot' ), __( 'Layout, grouping and service cards', 'abientot' ), false ); ?>
setting_toggle( 'form_group_services', __( 'Group services by category', 'abientot' ), ! empty( $settings['form_group_services'] ) ); ?> setting_toggle( 'form_show_service_images', __( 'Show service images', 'abientot' ), ! empty( $settings['form_show_service_images'] ) ); ?> setting_select( 'form_service_layout', __( 'Layout', 'abientot' ), array( 'list' => __( 'List', 'abientot' ), 'grid' => __( 'Grid', 'abientot' ), 'compact' => __( 'Compact', 'abientot' ) ), (string) $settings['form_service_layout'] ); ?> setting_range( 'form_border_radius', __( 'Card radius', 'abientot' ), (int) $settings['form_border_radius'], 0, 32, 'px' ); ?> setting_range( 'form_service_border_width', __( 'Card border width', 'abientot' ), (int) $settings['form_service_border_width'], 0, 8, 'px' ); ?>
setting_color_mode( 'form_service_border_color_mode', 'form_service_border_color', __( 'Card border color', 'abientot' ), (string) $settings['form_service_border_color_mode'], (string) $settings['form_service_border_color'] ); ?> setting_color_mode( 'form_service_background_color_mode', 'form_service_background_color', __( 'Card background color', 'abientot' ), (string) $settings['form_service_background_color_mode'], (string) $settings['form_service_background_color'] ); ?>
card_close(); } /** * @param array $settings Settings. */ private function render_style_categories_subsection( array $settings ): void { $this->card_open( 'category', __( 'Categories', 'abientot' ), __( 'How categories open and look', 'abientot' ), false ); ?>
setting_select( 'form_category_behavior', __( 'Behavior', 'abientot' ), array( 'expanded' => __( 'Open by default', 'abientot' ), 'collapsed' => __( 'Open on click', 'abientot' ) ), (string) $settings['form_category_behavior'] ); ?> setting_color( 'form_category_background_color', __( 'Background', 'abientot' ), (string) $settings['form_category_background_color'] ); ?> setting_toggle( 'form_show_category_images', __( 'Show category images', 'abientot' ), ! empty( $settings['form_show_category_images'] ) ); ?>
card_close(); } /** * @param array $settings Settings. */ private function render_style_hours_subsection( array $settings ): void { $this->card_open( 'clock', __( 'Available times', 'abientot' ), __( 'Shape of the time buttons', 'abientot' ), false ); ?>
setting_range( 'form_hours_border_radius', __( 'Availability radius', 'abientot' ), (int) $settings['form_hours_border_radius'], 0, 32, 'px' ); ?>
card_close(); } /** * @param array $settings Settings. */ private function render_style_fields_subsection( array $settings ): void { $this->card_open( 'feedback', __( 'Fields', 'abientot' ), __( 'Colors and shape of input fields', 'abientot' ), false ); ?>
setting_color( 'form_field_text_color', __( 'Text color', 'abientot' ), (string) $settings['form_field_text_color'] ); ?> setting_color( 'form_field_background_color', __( 'Background color', 'abientot' ), (string) $settings['form_field_background_color'] ); ?> setting_color( 'form_field_border_color', __( 'Border color', 'abientot' ), (string) $settings['form_field_border_color'] ); ?>
setting_range( 'form_field_border_radius', __( 'Border radius', 'abientot' ), (int) $settings['form_field_border_radius'], 0, 32, 'px' ); ?> setting_range( 'form_field_border_width', __( 'Border width', 'abientot' ), (int) $settings['form_field_border_width'], 0, 8, 'px' ); ?>
card_close(); } /** * @param array $settings Settings. */ private function render_style_images_subsection( array $settings ): void { $this->card_open( 'format-image', __( 'Images', 'abientot' ), __( 'Size and framing of photos', 'abientot' ), false ); ?>
setting_range( 'form_image_size', __( 'Image size', 'abientot' ), (int) $settings['form_image_size'], 32, 240, 'px' ); ?> setting_range( 'form_image_radius', __( 'Image radius', 'abientot' ), (int) $settings['form_image_radius'], 0, 48, 'px' ); ?> setting_range( 'form_image_border_width', __( 'Image border width', 'abientot' ), (int) $settings['form_image_border_width'], 0, 8, 'px' ); ?> setting_select( 'form_image_fit', __( 'Image fit', 'abientot' ), array( 'cover' => __( 'Crop to fill', 'abientot' ), 'contain' => __( 'Fit inside', 'abientot' ) ), (string) $settings['form_image_fit'] ); ?>
card_close(); } /** * @param array $settings Settings. * @param array|null $location Default location. */ private function render_style_location_subsection( array $settings, ?array $location ): void { $this->card_open( 'location', __( 'Location', 'abientot' ), __( 'Map below the form', 'abientot' ), false ); ?>
setting_toggle( 'form_show_map', __( 'Show location map when the default location has a map URL', 'abientot' ), ! empty( $settings['form_show_map'] ), empty( $location['map_url'] ) ? __( 'Add a map URL on the Locations page to display a map.', 'abientot' ) : '' ); ?> setting_range( 'form_map_height', __( 'Map height', 'abientot' ), (int) $settings['form_map_height'], 120, 520, 'px' ); ?>
card_close(); } /** * @param array $settings Settings. */ private function render_policy_section( array $settings ): void { $this->card_open( 'shield', __( 'Consent & policies', 'abientot' ), __( 'Set the agreement required before booking', 'abientot' ) ); ?>
setting_toggle( 'require_policy_checkbox', __( 'Require before booking', 'abientot' ), ! empty( $settings['require_policy_checkbox'] ) ); ?> setting_text( 'policy_checkbox_text', __( 'Policy text', 'abientot' ), (string) $settings['policy_checkbox_text'], true ); ?>
card_close(); } /** * @param array $field Field definition. * @param int|string $index Row index. */ private function render_field_builder_row( array $field, $index ): void { $key = (string) ( $field['key'] ?? '' ); $label = (string) ( $field['label'] ?? '' ); $type = (string) ( $field['type'] ?? 'text' ); $required = ! empty( $field['required'] ); ?>
0 ) : ?>

0 ? array( 'page' => 'abientot-form', 'form' => $id ) : array( 'page' => 'abientot-form', 'abientot_form_limit' => '1' ); wp_safe_redirect( add_query_arg( $args, admin_url( 'admin.php' ) ) ); exit; } public function delete_form(): void { check_admin_referer( 'abientot_form_delete' ); if ( ! current_user_can( 'abientot_manage_settings' ) ) { wp_die( esc_html__( 'You do not have permission to manage the booking form.', 'abientot' ), 403 ); } $id = \Abientot\Support\Input::post( 'abientot_form_delete' )->int( 'form' ); \Abientot\Repositories\BookingLock::require_configuration_lock(); BookingForms::delete( $id ); wp_safe_redirect( add_query_arg( array( 'page' => 'abientot-form' ), admin_url( 'admin.php' ) ) ); exit; } /** Opens a builder card: icon, title, one-line summary; the body is collapsible. */ private function card_open( string $icon, string $title, string $summary, bool $open = true, string $class = '' ): void { ?>
>
'; } private function setting_text( string $key, string $label, string $value, bool $wide = false ): void { ?>
$options Value => label. */ private function setting_select( string $key, string $label, array $options, string $value, string $hint = '' ): void { ?>

> $services Public services. * @param array $settings Settings. */ private function render_preview_services( array $services, array $settings ): void { if ( ! $services ) { echo '

' . esc_html__( 'No public services are available yet.', 'abientot' ) . '

'; return; } $groups = array(); foreach ( $services as $service ) { $key = ! empty( $service['category_id'] ) ? 'cat-' . (int) $service['category_id'] : sanitize_key( (string) ( $service['category_name'] ?? 'featured' ) ); if ( empty( $groups[ $key ] ) ) { $groups[ $key ] = array( 'name' => ! empty( $service['category_name'] ) ? (string) $service['category_name'] : __( 'Other services', 'abientot' ), 'description' => (string) ( $service['category_description'] ?? '' ), 'image_id' => (int) ( $service['category_image_id'] ?? 0 ), 'services' => array(), ); } $groups[ $key ]['services'][] = $service; } ?>
> >preview_image( (int) $group['image_id'] ) ); ?>
$service ) : ?> render_preview_service_card( $service, $settings ); ?>
> $service ) : ?> render_preview_service_card( $service, $settings ); ?>
$settings Settings. */ private function render_preview_fields( array $settings ): void { $fields = Settings::normalize_form_fields( $settings['form_fields'] ?? array() ); foreach ( $fields as $field ) { $key = (string) $field['key']; $label = (string) $field['label']; $type = (string) $field['type']; $required = ! empty( $field['required'] ); $value = ''; ?> class="abientot-booking__wide" data-abientot-preview-custom-field data-abientot-preview-field-key=""> > */ /** * @param array $service Service row. */ private function render_preview_service_card( array $service, array $settings ): void { $color = \Abientot\Support\BrandPalette::color( (string) sanitize_hex_color( (string) ( $service['color'] ?? '#172033' ) ) ) ?: '#172033'; ?> auto_text_card_style( $settings, \Abientot\Support\BrandPalette::color( (string) $background_color ) ); } private function preview_image( int $attachment_id ): string { if ( $attachment_id > 0 ) { return (string) wp_get_attachment_image( $attachment_id, 'thumbnail', false, array( 'class' => 'abientot-booking__image' ) ); } return ''; } }