# booking/11.9/includes/page-form-builder/legacy-import/bfb-import-simple-form.php

Booking Calendar, version 11.9. 1,207 lines.

- Page: https://pluginprobe.com/plugins/booking/11.9/code/includes/page-form-builder/legacy-import/bfb-import-simple-form.php
- Raw: https://pluginprobe.com/plugins/booking/11.9/raw/includes/page-form-builder/legacy-import/bfb-import-simple-form.php
- Modified: 2026-07-13T07:52:42+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/booking/11.9/code/includes/page-form-builder/legacy-import/bfb-import-simple-form.php#L10-L20`.

```php
<?php
/**
 * Booking Calendar – Form Builder.  Export "Simple booking form" -> BFB structure.
 *
 *
 *
 * This file replaces the old "form_simple__export_to_bfb.php" logic and
 * is dedicated to exporting the legacy Simple booking form "visual structure"
 * into the new BFB structure used by the Form Builder.
 *
 * Responsibilities:
 * - Load visual Simple Form structure from DB.
 * - Group fields into rows/columns (same logic as the legacy visual editor).
 * - Build a BFB structure array:
 *      [
 *        {
 *          "page": 1,
 *          "content": [
 *            { "type": "section", "data": { "columns": [ { "width": "...", "items": [ { "type": "field", "data": {...} } ] } ] } }
 *          ]
 *        }
 *      ]
 * - Optionally store the result as JSON in an option for later import into BFB.
 *
 * @since 10.14.0
 *
 * @package Booking_Calendar
 * @file: ../includes/page-form-builder/save-load/bfb-import-simple-form.php
 */

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}


/**
 * Export current Simple Form visual structure into a BFB structure array.
 *
 * @param array|false $visual_form_structure Optional preloaded Simple Form structure.
 *
 * @return array BFB structure ready to JSON-encode.
 */
function wpbc_simple_form__export_to_bfb_structure( $visual_form_structure = false ) {

	// 1) Get visual structure from DB if not passed.
	if ( empty( $visual_form_structure ) ) {
		if ( function_exists( 'wpbc_simple_form__db__get_visual_form_structure' ) ) {
			$visual_form_structure = wpbc_simple_form__db__get_visual_form_structure();
		} else {
			$visual_form_structure = array();
		}
	}
	$visual_form_structure = maybe_unserialize( $visual_form_structure );

	if ( ! is_array( $visual_form_structure ) ) {
		$visual_form_structure = array();
	}

	// 2) Layout meta.
	$layout_type = function_exists( 'get_bk_option' ) ? get_bk_option( 'booking_form_structure_type' ) : '';
	if ( empty( $layout_type ) ) {
		$layout_type = 'vertical';
	}

	$max_cols = function_exists( 'get_bk_option' ) ? get_bk_option( 'booking_form_layout_max_cols' ) : 1;
	$max_cols = max( 1, intval( $max_cols ) );

	$is_timepicker = false;
	if ( function_exists( 'get_bk_option' ) ) {
		$is_timepicker = ( 'On' === get_bk_option( 'booking_timeslot_picker' ) );
	}

	// 3) Normalize fields & filter inactive non-obligatory.
	$normalized_fields = array();
	foreach ( $visual_form_structure as $form_field ) {

		$defaults   = array(
			'type'              => 'text',
			'name'              => 'unique_name',
			'obligatory'        => 'Off',
			'if_exist_required' => 'Off',
			'active'            => 'On',
			'required'          => 'Off',
			'label'             => '',
			'value'             => '',
			'placeholder'       => '',
			'cssclass'          => '',
			'html_id'           => '',
		);
		$form_field = wp_parse_args( $form_field, $defaults );

		// Skip inactive non-obligatory fields.
		if ( ( 'Off' === $form_field['active'] ) && ( 'On' !== $form_field['obligatory'] ) ) {
			continue;
		}

		$normalized_fields[] = $form_field;
	}

	// 3.1) Ensure at least one calendar (Simple Form has a fallback).
	$has_calendar = false;
	foreach ( $normalized_fields as $f ) {
		if ( 'calendar' === $f['type'] || 'calendar' === $f['name'] ) {
			$has_calendar = true;
			break;
		}
	}
	if ( ! $has_calendar ) {
		// Prepend a default calendar field (similar to Simple Form fallback: [calendar] at top).
		$normalized_fields = array_merge(
			array(
				array(
					'type'              => 'calendar',
					'name'              => 'calendar',
					'label'             => __( 'Select Date', 'booking' ),
					'value'             => '',
					'placeholder'       => '',
					'cssclass'          => '',
					'html_id'           => '',
					'obligatory'        => 'On',
					'if_exist_required' => 'Off',
					'required'          => 'On',
					'active'            => 'On',
				),
			),
			$normalized_fields
		);
	}

	$builder_structure = array();
	$field_seq         = 0;
	$section_seq       = 0;

	// 4) Wizard layouts -> multi-page structure.
	if ( in_array( $layout_type, array( 'wizard_2columns', 'wizard_services_a' ), true ) ) {

		$builder_structure = wpbc_simple_form__export_wizard_to_bfb_structure(
			$normalized_fields,
			$layout_type,
			$max_cols,
			$is_timepicker,
			$field_seq,
			$section_seq
		);

	} else {
		// 5) Standard layouts (vertical, form_center, form_right) -> single page.
		$sections = wpbc_simple_form__build_bfb_sections_from_fields(
			$normalized_fields,
			$max_cols,
			$is_timepicker,
			$field_seq,
			$section_seq
		);

		if ( ! empty( $sections ) ) {
			$builder_structure[] = array(
				'page'        => 1,
				'layout_type' => $layout_type,   // meta only, safe to ignore.
				'max_cols'    => $max_cols,      // meta only, safe to ignore.
				'content'     => $sections,
			);
		}
	}

	return $builder_structure;
}

// ---------------------------------------------------------------------------------------------------------------------
// == Helpers ==
// ---------------------------------------------------------------------------------------------------------------------
/**
 * Export Simple Form "wizard" layouts into a multi-page BFB structure.
 *
 * @param array  $fields        Normalized fields (active/obligatory only).
 * @param string $layout_type   'wizard_2columns' | 'wizard_services_a'.
 * @param int    $max_cols      booking_form_layout_max_cols.
 * @param bool   $is_timepicker timeslot picker On/Off.
 * @param int    $field_seq     Sequence for unique field IDs (by reference).
 * @param int    $section_seq   Sequence for unique section IDs (by reference).
 *
 * @return array
 */
function wpbc_simple_form__export_wizard_to_bfb_structure( $fields, $layout_type, $max_cols, $is_timepicker, &$field_seq, &$section_seq ) {

	$calendar_field   = null;
	$duration_field   = null;
	$time_fields_map  = array(); // name => field.
	$submit_field     = null;
	$other_fields     = array();

	// Split fields into special buckets + "other".
	foreach ( $fields as $f ) {

		$type = isset( $f['type'] ) ? $f['type'] : '';
		$name = isset( $f['name'] ) ? $f['name'] : '';

		if ( 'calendar' === $type || 'calendar' === $name ) {
			if ( null === $calendar_field ) {
				$calendar_field = $f;
				continue;
			}
		}

		if ( 'durationtime' === $name && null === $duration_field ) {
			$duration_field = $f;
			continue;
		}

		if ( in_array( $name, array( 'rangetime', 'starttime', 'endtime' ), true ) ) {
			// Keep first occurrence only, preserve order later.
			if ( ! isset( $time_fields_map[ $name ] ) ) {
				$time_fields_map[ $name ] = $f;
			}
			continue;
		}

		if ( 'submit' === $type && null === $submit_field ) {
			$submit_field = $f;
			continue;
		}

		$other_fields[] = $f;
	}

	$builder_structure    = array();
	$has_duration         = ( null !== $duration_field );
	$wizard_steps_count   = $has_duration ? 3 : 2;
	$page_index           = 1;
	$is_services_timeline = ( 'wizard_services_a' === $layout_type );

	// ---------------------------------------------------------------------
	// STEP 1: if durationtime exists -> only this field + Next.
	// ---------------------------------------------------------------------
	if ( $has_duration ) {

		$page_content = array();

		if ( $is_services_timeline ) {
			$page_content[] = wpbc_simple_form__build_steps_timeline_section(
				$wizard_steps_count,
				$page_index,
				$field_seq,
				$section_seq
			);
		}

		$sections = wpbc_simple_form__build_bfb_sections_from_fields(
			array( $duration_field ),
			$max_cols,
			$is_timepicker,
			$field_seq,
			$section_seq
		);

		$page_content = array_merge( $page_content, $sections );

		if ( ! empty( $page_content ) ) {

			$step_index    = $page_index;
			$is_last_step  = ( $step_index === $wizard_steps_count );
			$has_back      = false;               // First step: no Back button.
			$include_next  = ! $is_last_step;     // Here always true (duration -> 3 steps).
			$submit_for_nav = null;              // Submit only on last step.

			$page_content[] = wpbc_simple_form__build_wizard_nav_section(
				$step_index,
				$wizard_steps_count,
				$has_back,
				$include_next,
				$submit_for_nav,
				$field_seq,
				$section_seq
			);

			$builder_structure[] = array(
				'page'        => $step_index,
				'layout_type' => $layout_type,
				'wizard_step' => $step_index,
				'content'     => $page_content,
			);

			++$page_index;
		}
	}

	// ---------------------------------------------------------------------
	// STEP 1 (no duration) or STEP 2 (with duration): calendar + time fields.
	// ---------------------------------------------------------------------
	$has_calendar_or_time = ( null !== $calendar_field ) || ! empty( $time_fields_map );

	if ( $has_calendar_or_time ) {

		$page_content = array();

		if ( $is_services_timeline ) {
			$page_content[] = wpbc_simple_form__build_steps_timeline_section(
				$wizard_steps_count,
				$page_index,
				$field_seq,
				$section_seq
			);
		}

		$has_time_fields = ! empty( $time_fields_map );

		// Special wizard layout: calendar + vertical divider + time fields in the right column.
		if ( ( null !== $calendar_field ) && $has_time_fields ) {

			$page_content[] = wpbc_simple_form__build_wizard_calendar_times_section(
				$calendar_field,
				$time_fields_map,
				$field_seq,
				$section_seq
			);

		} else {
			// Fallback: generic grouping (e.g. calendar only or time fields only).
			if ( null !== $calendar_field ) {
				$sections = wpbc_simple_form__build_bfb_sections_from_fields(
					array( $calendar_field ),
					$max_cols,
					$is_timepicker,
					$field_seq,
					$section_seq
				);
				$page_content = array_merge( $page_content, $sections );
			}

			foreach ( array( 'rangetime', 'starttime', 'endtime' ) as $time_name ) {
				if ( isset( $time_fields_map[ $time_name ] ) ) {
					$sections = wpbc_simple_form__build_bfb_sections_from_fields(
						array( $time_fields_map[ $time_name ] ),
						$max_cols,
						$is_timepicker,
						$field_seq,
						$section_seq
					);
					$page_content = array_merge( $page_content, $sections );
				}
			}
		}

		if ( ! empty( $page_content ) ) {

			$step_index     = $page_index;
			$is_last_step   = ( $step_index === $wizard_steps_count );
			$has_back       = ( $step_index > 1 );
			$include_next   = ! $is_last_step;   // If 2-step wizard -> step 1; if 3-step wizard -> step 2.
			$submit_for_nav = null;              // Submit only on last step.

			$page_content[] = wpbc_simple_form__build_wizard_nav_section(
				$step_index,
				$wizard_steps_count,
				$has_back,
				$include_next,
				$submit_for_nav,
				$field_seq,
				$section_seq
			);

			$builder_structure[] = array(
				'page'        => $step_index,
				'layout_type' => $layout_type,
				'wizard_step' => $step_index,
				'content'     => $page_content,
			);

			++$page_index;
		}
	}

	// ---------------------------------------------------------------------
	// Last STEP: remaining fields + final submit (if exists).
	//   - If duration -> this is step 3.
	//   - If no duration -> this is step 2.
	// ---------------------------------------------------------------------
	if ( ! empty( $other_fields ) || null !== $submit_field ) {

		$page_content = array();

		if ( $is_services_timeline ) {
			$page_content[] = wpbc_simple_form__build_steps_timeline_section(
				$wizard_steps_count,
				$page_index,
				$field_seq,
				$section_seq
			);
		}

		// Other fields in the same grid logic as classic Simple Form.
		if ( ! empty( $other_fields ) ) {
			$sections = wpbc_simple_form__build_bfb_sections_from_fields(
				$other_fields,
				$max_cols,
				$is_timepicker,
				$field_seq,
				$section_seq
			);
			$page_content = array_merge( $page_content, $sections );
		}

		if ( ! empty( $page_content ) ) {

			$step_index     = $page_index;
			$is_last_step   = true;
			$has_back       = ( $step_index > 1 );
			$include_next   = false;             // Last step: no Next.
			$submit_for_nav = $submit_field;     // Submit goes together with Back + divider.

			$page_content[] = wpbc_simple_form__build_wizard_nav_section(
				$step_index,
				$wizard_steps_count,
				$has_back,
				$include_next,
				$submit_for_nav,
				$field_seq,
				$section_seq
			);

			$builder_structure[] = array(
				'page'        => $step_index,
				'layout_type' => $layout_type,
				'wizard_step' => $step_index,
				'content'     => $page_content,
			);
		}
	}

	return $builder_structure;
}

/**
 * Group fields into rows (exact same logic as Simple Form)
 *
 * @param array $fields        Normalized fields.
 * @param int   $max_cols      booking_form_layout_max_cols.
 * @param bool  $is_timepicker Whether timeslot picker is enabled for 'rangetime'.
 *
 * @return array Array of rows; each row is an array of $form_field arrays.
 */
function wpbc_simple_form__group_fields_into_rows_for_bfb( $fields, $max_cols, $is_timepicker ) {

	$rows     = array();
	$curr_row = 0;
	$rows[ $curr_row ] = array();

	foreach ( $fields as $form_field ) {

		$type = isset( $form_field['type'] ) ? $form_field['type'] : '';
		$name = isset( $form_field['name'] ) ? $form_field['name'] : '';

		$is_full_width_type = in_array( $type, array( 'textarea', 'calendar', 'submit' ), true );
		$is_full_width_time = ( ( 'rangetime' === $name ) && $is_timepicker );

		if (
			( count( $rows[ $curr_row ] ) >= $max_cols ) ||
			$is_full_width_type ||
			$is_full_width_time
		) {
			++$curr_row;
			$rows[ $curr_row ] = array();
		}

		$rows[ $curr_row ][] = $form_field;

		if ( $is_full_width_type || $is_full_width_time ) {
			++$curr_row;
			$rows[ $curr_row ] = array();
		}
	}

	return $rows;
}

/**
 * Convert rows into BFB “section” structures.
 *
 * Each row becomes one "section", with N equally sized columns.
 *
 * @param array $fields        Normalized fields.
 * @param int   $max_cols      booking_form_layout_max_cols.
 * @param bool  $is_timepicker Whether timeslot picker is enabled.
 * @param int   $field_seq     Field ID sequence (by reference).
 * @param int   $section_seq   Section ID sequence (by reference).
 *
 * @return array List of BFB sections.
 */
function wpbc_simple_form__build_bfb_sections_from_fields( $fields, $max_cols, $is_timepicker, &$field_seq, &$section_seq ) {

	$sections = array();
	$rows     = wpbc_simple_form__group_fields_into_rows_for_bfb( $fields, $max_cols, $is_timepicker );

	foreach ( $rows as $row_fields ) {

		if ( empty( $row_fields ) ) {
			continue;
		}

		$cols_count = count( $row_fields );
		$cols_count = max( 1, $cols_count );
		$col_width  = round( 100 / $cols_count, 4 );

		$columns = array();

		foreach ( $row_fields as $form_field ) {

			++$field_seq;
			$item = wpbc_simple_form__map_field_to_bfb_item( $form_field, $field_seq );

			// Skip if mapping failed.
			if ( empty( $item ) || ! is_array( $item ) ) {
				continue;
			}

			$columns[] = array(
				'width' => sprintf( '%.4f%%', $col_width ),
				'items' => array( $item ),
			);
		}

		if ( empty( $columns ) ) {
			continue;
		}

		++$section_seq;
		$section_id = 'section-simple-' . $section_seq;

		$sections[] = array(
			'type' => 'section',
			'data' => array(
				'id'        => $section_id,
				'label'     => __( 'Section', 'booking' ),
				'html_id'   => '',
				'cssclass'  => '',
				// Leave col_styles empty – BFB Column Styles will apply defaults.
				'col_styles'=> '',
				'columns'   => $columns,
			),
		);
	}

	return $sections;
}

/**
 *  Helper for Steps Timeline (only used for wizard_services_a)
 *
 * @param int $steps_count  Total number of wizard steps (2 or 3).
 * @param int $active_step  Step index for this page (1-based).
 * @param int $field_seq    Field ID sequence (by reference).
 * @param int $section_seq  Section ID sequence (by reference).
 *
 * @return array BFB section with one "steps_timeline" field.
 */
function wpbc_simple_form__build_steps_timeline_section( $steps_count, $active_step, &$field_seq, &$section_seq ) {

	++$field_seq;
	$field_id = 'steps_timeline_' . $field_seq;

	$item = array(
		'type' => 'field',
		'data' => array(
			'id'          => $field_id,
			'type'        => 'steps_timeline',
			'usage_key'   => 'steps_timeline',
			'label'       => '',
			'html_id'     => '',
			'cssclass'    => '',
			'steps_count' => intval( $steps_count ),
			'active_step' => intval( $active_step ),
		),
	);

	++$section_seq;
	$section_id = 'section-wizard-timeline-' . $section_seq;

	return array(
		'type' => 'section',
		'data' => array(
			'id'        => $section_id,
			'label'     => __( 'Step header', 'booking' ),
			'html_id'   => '',
			'cssclass'  => 'wpbc_bfb__section--timeline',
			'col_styles'=> '',
			'columns'   => array(
				array(
					'width' => '100.0000%',
					'items' => array( $item ),
				),
			),
		),
	);
}

/**
 * Wizard calendar + time fields section.
 *
 * Produces a section like:
 *   - Left column: calendar
 *   - Middle skinny column: vertical divider
 *   - Right column: rangetime / starttime / endtime stacked
 *
 * Matches the example structure used by Wizard (several steps) template.
 *
 * @param array $calendar_field   Simple Form "calendar" field.
 * @param array $time_fields_map  ['rangetime' => field, 'starttime' => field, 'endtime' => field] subset.
 * @param int   $field_seq        Field ID sequence (by reference).
 * @param int   $section_seq      Section ID sequence (by reference).
 *
 * @return array BFB section.
 */
function wpbc_simple_form__build_wizard_calendar_times_section( $calendar_field, $time_fields_map, &$field_seq, &$section_seq ) {

	$columns   = array();
	$time_items = array();
	$calendar_item = null;

	// 1) Calendar in left column.
	if ( ! empty( $calendar_field ) ) {
		++$field_seq;
		$calendar_item = wpbc_simple_form__map_field_to_bfb_item( $calendar_field, $field_seq );
	}

	// 2) Time fields stacked in the right column: rangetime -> starttime -> endtime (if exist).
	foreach ( array( 'rangetime', 'starttime', 'endtime' ) as $time_name ) {
		if ( isset( $time_fields_map[ $time_name ] ) && is_array( $time_fields_map[ $time_name ] ) ) {
			++$field_seq;
			$item = wpbc_simple_form__map_field_to_bfb_item( $time_fields_map[ $time_name ], $field_seq );
			if ( ! empty( $item ) ) {
				$time_items[] = $item;
			}
		}
	}

	// If we somehow have no calendar AND no time items, bail out.
	if ( empty( $calendar_item ) && empty( $time_items ) ) {
		return array();
	}

	// 3) Vertical divider in the middle column.
	++$field_seq;
	$divider_id = 'divider-vertical-' . $field_seq;

	$divider_item = array(
		'type' => 'field',
		'data' => array(
			'type'            => 'divider',
			'usage_key'       => 'divider_vertical',
			'orientation'     => 'vertical',
			'line_style'      => 'solid',
			'thickness_px'    => 1,
			'length'          => '90%',
			'align'           => 'middle',
			'color'           => '#ccc',
			'label'           => 'Divider_vertical',
			'margin_top_px'   => 0,
			'margin_bottom_px'=> 0,
			'margin_left_px'  => 0,
			'margin_right_px' => 0,
			'cssclass_extra'  => '',
			'id'              => $divider_id,
			'name'            => $divider_id,
			'html_id'         => '',
		),
	);

	// 4) Assemble columns:
	//    Left  column  ~ 41.1303%
	//    Middle column ~ 0.1%    (divider)
	//    Right column  ~ 52.7697%
	if ( $calendar_item ) {
		$columns[] = array(
			'width' => '41.1303%',
			'items' => array( $calendar_item ),
		);
	}

	$columns[] = array(
		'width' => '0.1%',
		'items' => array( $divider_item ),
	);

	if ( ! empty( $time_items ) ) {
		$columns[] = array(
			'width' => '52.7697%',
			'items' => $time_items,
		);
	}

	// If somehow there are less than 2 real columns, fallback to equal widths.
	if ( count( $columns ) < 2 ) {
		$cols_count = count( $columns );
		$col_width  = ( $cols_count > 0 ) ? round( 100 / $cols_count, 4 ) . '%' : '100.0000%';
		foreach ( $columns as &$c ) {
			$c['width'] = $col_width;
		}
		unset( $c );
	}

	// 5) Section wrapper.
	++$section_seq;
	$section_id = 'section-simple-wizard-' . $section_seq;

	return array(
		'type' => 'section',
		'data' => array(
			'id'        => $section_id,
			'label'     => __( 'Section', 'booking' ),
			'html_id'   => '',
			'cssclass'  => '',
			// Matches your example col_styles for calendar/time layout.
			'col_styles'=> '[{"aself":"stretch"},{"dir":"row","wrap":"wrap","jc":"center","ai":"center","aself":"stretch"},{"aself":"stretch"}]',
			'columns'   => $columns,
		),
	);
}

/**
 * Wizard navigation section: horizontal divider + Back/Next/Submit buttons.
 *
 * Layout per step:
 *   - First step (no duration OR duration step): Divider + Next
 *   - Middle step (only in 3-step wizard):       Divider + Back + Next
 *   - Last step:                                 Divider + Back + Submit
 *
 * Matches the example like:
 *   [divider-horizontal] [wizard_nav_back] [submit]
 *
 * @param int        $step_index      Current wizard step (1-based).
 * @param int        $steps_count     Total number of steps (2 or 3).
 * @param bool       $has_back        Whether to include a Back button.
 * @param bool       $include_next    Whether to include a Next button.
 * @param array|null $submit_field    Simple Form "submit" field (only for last step), or null.
 * @param int        $field_seq       Field ID sequence (by reference).
 * @param int        $section_seq     Section ID sequence (by reference).
 *
 * @return array BFB section.
 */
function wpbc_simple_form__build_wizard_nav_section( $step_index, $steps_count, $has_back, $include_next, $submit_field, &$field_seq, &$section_seq ) {

	$items = array();

	// 1) Horizontal divider (always present).
	++$field_seq;
	$divider_id = 'divider-vertical-he' . $field_seq;

	$items[] = array(
		'type' => 'field',
		'data' => array(
			'type'            => 'divider',
			'usage_key'       => 'divider_vertical',
			'orientation'     => 'horizontal',
			'line_style'      => 'solid',
			'thickness_px'    => 1,
			'length'          => '100%',
			'align'           => 'center',
			'color'           => '#ccc',
			'label'           => 'Divider_vertical',
			'margin_top_px'   => 0,
			'margin_bottom_px'=> 0,
			'margin_left_px'  => 0,
			'margin_right_px' => 0,
			'cssclass_extra'  => '',
			'id'              => $divider_id,
			'name'            => $divider_id,
			'html_id'         => '',
		),
	);

	// 2) Back button (if required).
	if ( $has_back ) {
		++$field_seq;
		$back_id = 'wizard_nav_back-' . $step_index;

		$items[] = array(
			'type' => 'field',
			'data' => array(
				'id'           => $back_id,
				'type'         => 'wizard_nav',
				'usage_key'    => 'wizard_nav_back',
				'direction'    => 'back',
				'target_step'  => max( 1, $step_index - 1 ),
				'label'        => __( 'Back', 'booking' ),
				'name'         => $back_id,
				'cssclass_extra' => '',
				'html_id'      => '',
			),
		);
	}

	// 3) Next button (for all non-final steps).
	if ( $include_next && $step_index < $steps_count ) {
		++$field_seq;
		$next_id = ( 1 === $step_index ) ? 'wizard_nav_next' : 'wizard_nav_next-' . $step_index;

		$items[] = array(
			'type' => 'field',
			'data' => array(
				'id'           => $next_id,
				'type'         => 'wizard_nav',
				'usage_key'    => 'wizard_nav_next',
				'direction'    => 'next',
				'target_step'  => $step_index + 1,
				'label'        => __( 'Next', 'booking' ),
				'name'         => $next_id,
				'cssclass_extra' => '',
				'html_id'      => '',
			),
		);
	}

	// 4) Submit button only on final step (if submit field exists).
	if ( ( $step_index === $steps_count ) && ! empty( $submit_field ) && is_array( $submit_field ) ) {
		++$field_seq;
		$submit_item = wpbc_simple_form__map_field_to_bfb_item( $submit_field, $field_seq );
		if ( ! empty( $submit_item ) ) {
			$items[] = $submit_item;
		}
	}

	// 5) Section wrapper – 1 column with all nav items, aligned to bottom-right.
	++$section_seq;
	$section_id = 'section-wizard-nav-' . $section_seq;

	return array(
		'type' => 'section',
		'data' => array(
			'id'        => $section_id,
			'label'     => __( 'Wizard navigation', 'booking' ),
			'html_id'   => '',
			'cssclass'  => '',
			// Same col_styles pattern as your example.
			'col_styles'=> '[{"dir":"row","wrap":"wrap","jc":"flex-end","ai":"flex-end","gap":"9px","aself":"flex-end"}]',
			'columns'   => array(
				array(
					'width' => '100%',
					'items' => $items,
				),
			),
		),
	);
}


// =====================================================================================================================
// == Map Fields ==
// =====================================================================================================================

/**
 * Map one Simple Form field to a BFB "field" item.
 *
 * BFB item shape:
 *   [
 *     'type' => 'field',
 *     'data' => [
 *       'id'        => 'calendar',
 *       'type'      => 'calendar',
 *       'usage_key' => 'calendar',
 *       'label'     => 'Select dates',
 *       'name'      => 'calendar',
 *       ... pack-specific keys ...
 *     ]
 *   ]
 *
 * @param array $form_field Normalized simple form field.
 * @param int   $seq        Sequence number for unique IDs.
 *
 * @return array BFB item (type=field) or empty array on skip.
 */
function wpbc_simple_form__map_field_to_bfb_item( $form_field, $seq ) {

	$defaults   = array(
		'type'              => 'text',
		'name'              => 'unique_name',
		'label'             => '',
		'value'             => '',
		'placeholder'       => '',
		'cssclass'          => '',
		'html_id'           => '',
		'obligatory'        => 'Off',
		'if_exist_required' => 'Off',
		'required'          => 'Off',
		'active'            => 'On',
	);
	$form_field = wp_parse_args( $form_field, $defaults );

	$type   = $form_field['type'];
	$name   = $form_field['name'];
	$label  = $form_field['label'];

	if ( '' === $label ) {
		$label = ucfirst( str_replace( array( '_', '-' ), ' ', $name ) );
	}

	$pack = wpbc_simple_form__guess_bfb_pack( $form_field );

	$required_flag = ( 'On' === $form_field['required'] ) ||
					 ( 'On' === $form_field['if_exist_required'] ) ||
					 ( 'On' === $form_field['obligatory'] );
	$required      = $required_flag ? 1 : 0;

	// Choose a stable field ID for the Builder.
	$field_id = $form_field['html_id'];
	if ( '' === $field_id ) {
		$field_id = $name;
	}
	if ( '' === $field_id ) {
		$field_id = $pack . '-' . $seq;
	}
	$field_id = sanitize_key( $field_id );

	$data = array(
		'id'        => $field_id,
		'type'      => $pack,
		'usage_key' => $pack,
		'label'     => $label,
		'name'      => $name,
		'html_id'   => (string) $form_field['html_id'],
		'cssclass'  => (string) $form_field['cssclass'],
	);

	switch ( $pack ) {

		case 'calendar':
			// Minimal calendar config – BFB pack will handle advanced options.
			$data['min_width']   = '250px';
			$data['resource_id'] = 1; // If you know resource here, adjust.
			$data['months']      = 1;
			break;

		case 'text':
		case 'email':
			$data['min_width']   = '8em';
			$data['placeholder'] = isset( $form_field['placeholder'] ) ? (string) $form_field['placeholder'] : '';
			$data['required']    = $required;
			break;

		case 'textarea':
			$data['min_width']   = '240px';
			$data['placeholder'] = isset( $form_field['placeholder'] ) ? (string) $form_field['placeholder'] : '';
			$data['required']    = $required;
			break;

		case 'select':
			$data['min_width']   = '100px';
			$data['placeholder'] = '--- Select ---';

			$data['options'] = array();
			$opts            = wpbc_simple_form__parse_select_options( $form_field );

			foreach ( $opts as $opt ) {
				$data['options'][] = array(
					'label'    => $opt['label'],
					'value'    => $opt['value'],
					'selected' => false,
				);
			}

			$data['required'] = $required;
			break;

		case 'checkbox':
			/**
			 * SIMPLE FORM -> BFB: single checkbox
			 *
			 * Simple booking form has only one checkbox per field (not a group).
			 * We still provide ONE option with Title/Value, so the exporter can
			 * use it if needed, but we also set use_label_element=1 so it can
			 * generate:
			 *
			 *   [checkbox name use_label_element "Label"]
			 *
			 * instead of group markup.
			 */
			$data['min_width']         = '240px';
			$data['use_label_element'] = 1;
			$data['required']          = $required;
			// Reset Label.
			$data['label'] = '';

			$data['options'] = array();

			// Try to parse a Title/Value pair from "value" (same helper as select/radio).
			$opts = wpbc_simple_form__parse_select_options( $form_field );

			if ( ! empty( $opts ) ) {
				// Use ONLY the first option, because Simple Form represents a single checkbox.
				$first = $opts[0];

				$data['options'][] = array(
					'label'    => $first['label'],
					'value'    => $first['value'],
					'selected' => false,
				);
			} else {
				// Fallback: use label as title and name as value.
				$data['options'][] = array(
					'label'    => $label,
					'value'    => $name,
					'selected' => false,
				);
			}

			// IMPORTANT: do NOT set "multiple" or "layout_inline" here – this is a single checkbox.
			// IMPORTANT: do NOT set placeholder "--- Select ---" for a single checkbox.
			break;

		case 'radio':
			$data['min_width'] = '240px';
			$data['options']   = array();

			$opts = wpbc_simple_form__parse_select_options( $form_field );
			foreach ( $opts as $opt ) {
				$data['options'][] = array(
					'label'    => $opt['label'],
					'value'    => $opt['value'],
					'selected' => false,
				);
			}

			$data['layout_inline'] = 1;
			$data['required']      = $required;
			break;

		case 'submit':
			$data['label']    = $label ? $label : __( 'Send', 'booking' );
			$data['cssclass'] = 'wpbc_bfb__btn wpbc_bfb__btn--primary';
			break;

		case 'rangetime':
		case 'starttime':
		case 'endtime':
		case 'durationtime':
			// Treat as time-based select for now; BFB time packs can enhance later.
			$data['min_width']   = '100px';
			$data['placeholder'] = '--- Select ---';
			$data['options']     = array();

			$opts = wpbc_simple_form__parse_select_options( $form_field );
			foreach ( $opts as $opt ) {
				$data['options'][] = array(
					'label'    => $opt['label'],
					'value'    => $opt['value'],
					'selected' => false,
				);
			}

			$data['required'] = $required;
			break;

		case 'captcha':
			// Minimal – BFB "captcha" pack will use its own defaults.
			break;

		default:
			// Fallback: just add required if meaningful.
			if ( $required ) {
				$data['required'] = $required;
			}
			break;
	}

	if ( ! isset( $data['html_id'] ) ) {
		$data['html_id'] = '';
	}
	if ( ! isset( $data['cssclass'] ) ) {
		$data['cssclass'] = '';
	}

	return array(
		'type' => 'field',
		'data' => $data,
	);
}

// ---------------------------------------------------------------------------------------------------------------------
// == Helpers ==
// ---------------------------------------------------------------------------------------------------------------------

	/**
	 * Guess BFB pack name based on Simple Form type/name.
	 *
	 * @param array $form_field
	 *
	 * @return string
	 */
	function wpbc_simple_form__guess_bfb_pack( $form_field ) {

		$type = isset( $form_field['type'] ) ? $form_field['type'] : '';
		$name = isset( $form_field['name'] ) ? $form_field['name'] : '';

		// Time-related packs.
		if ( in_array( $name, array( 'rangetime', 'durationtime', 'starttime', 'endtime' ), true ) ) {
			return $name; // 'rangetime', 'durationtime', 'starttime', 'endtime' packs.
		}

		// Special system fields.
		if ( 'calendar' === $type || 'calendar' === $name ) {
			return 'calendar';
		}
		if ( 'captcha' === $type || 'captcha' === $name ) {
			return 'captcha';
		}
		if ( 'submit' === $type || 'submit' === $name ) {
			return 'submit';
		}

		// Common fields.
		if ( 'text' === $type ) {
			return 'text';
		}
		if ( 'email' === $type ) {
			return 'email';
		}
		if ( 'textarea' === $type ) {
			return 'textarea';
		}
		if ( in_array( $type, array( 'select', 'selectbox' ), true ) ) {
			return 'select';
		}
		if ( 'checkbox' === $type ) {
			return 'checkbox';
		}
		if ( 'radio' === $type ) {
			return 'radio';
		}

		// Fallback.
		return $type ? $type : 'text';
	}

	/**
	 * Parse selectbox/radio/checkbox values from Simple Form into structured options.
	 *
	 * Supports simple lines:
	 *   Label
	 * …and paired format:
	 *   Label@@value
	 *
	 * @param array $form_field
	 *
	 * @return array
	 */
	function wpbc_simple_form__parse_select_options( $form_field ) {

		$options   = array();
		$raw_value = isset( $form_field['value'] ) ? (string) $form_field['value'] : '';
		$lines     = preg_split( '/\r\n|\r|\n/', $raw_value );

		if ( empty( $lines ) ) {
			return $options;
		}

		foreach ( $lines as $line ) {
			$line = trim( $line );
			if ( '' === $line ) {
				continue;
			}

			$label = $line;
			$value = $line;

			if ( false !== strpos( $line, '@@' ) ) {
				$parts = explode( '@@', $line, 2 );
				$label = trim( $parts[0] );
				$value = trim( $parts[1] );
			}

			// Strip quotes for safety.
			$label = str_replace( array( '"', "'" ), '', $label );
			$value = str_replace( array( '"', "'" ), '', $value );

			$options[] = array(
				'label' => $label,
				'value' => $value,
			);
		}

		return $options;
	}


// =====================================================================================================================
// == Ajax Handler of "Import from Simple Form" button ==
// =====================================================================================================================

/**
 * AJAX: import Simple Form -> BFB structure.
 */
function wpbc_bfb_ajax_import_simple_form() {
	check_ajax_referer( 'wpbc_bfb_import_simple_form', 'nonce' );

	$structure = wpbc_simple_form__export_to_bfb_structure();

	if ( empty( $structure ) ) {
		wp_send_json_error( array( 'message' => 'Empty structure.' ) );
	}

	wp_send_json_success(
		array(
			'structure' => $structure,
		)
	);
}
add_action( 'wp_ajax_wpbc_bfb_import_simple_form', 'wpbc_bfb_ajax_import_simple_form' );

```
