'yes', 'field_name' => 'first_name', 'field_label' => __( 'First name', 'a1-tools' ), 'field_placeholder' => '', 'field_required' => 'yes' ), array( 'enabled' => 'yes', 'field_name' => 'last_name', 'field_label' => __( 'Last name', 'a1-tools' ), 'field_placeholder' => '', 'field_required' => 'yes' ), array( 'enabled' => 'yes', 'field_name' => 'phone', 'field_label' => __( 'Phone', 'a1-tools' ), 'field_placeholder' => '', 'field_required' => 'yes' ), array( 'enabled' => 'yes', 'field_name' => 'email', 'field_label' => __( 'Email', 'a1-tools' ), 'field_placeholder' => '', 'field_required' => 'yes' ), array( 'enabled' => 'yes', 'field_name' => 'address', 'field_label' => __( 'Address', 'a1-tools' ), 'field_placeholder' => '', 'field_required' => 'yes' ), array( 'enabled' => 'yes', 'field_name' => 'city', 'field_label' => __( 'City', 'a1-tools' ), 'field_placeholder' => '', 'field_required' => 'yes' ), array( 'enabled' => 'yes', 'field_name' => 'state', 'field_label' => __( 'State', 'a1-tools' ), 'field_placeholder' => '', 'field_required' => 'yes' ), array( 'enabled' => 'yes', 'field_name' => 'zip', 'field_label' => __( 'Zip code', 'a1-tools' ), 'field_placeholder' => '', 'field_required' => 'yes' ), array( 'enabled' => 'yes', 'field_name' => 'commentary', 'field_label' => __( 'Tell us about your project', 'a1-tools' ),'field_placeholder' => '', 'field_required' => 'yes' ), array( 'enabled' => 'yes', 'field_name' => 'preferred_contact', 'field_label' => __( 'Preferred contact method', 'a1-tools' ), 'field_placeholder' => '', 'field_required' => 'yes' ), ); } /** * Allowed Custom Field types — mirrors the server-side validation * whitelist in `api/web_forms.php`. */ public static function custom_field_types(): array { return array( 'text' => __( 'Text', 'a1-tools' ), 'textarea' => __( 'Textarea', 'a1-tools' ), 'email' => __( 'Email', 'a1-tools' ), 'tel' => __( 'Phone', 'a1-tools' ), 'url' => __( 'URL', 'a1-tools' ), 'number' => __( 'Number', 'a1-tools' ), 'date' => __( 'Date', 'a1-tools' ), 'select' => __( 'Dropdown (Select)', 'a1-tools' ), 'checkbox' => __( 'Checkbox', 'a1-tools' ), 'radio' => __( 'Radio group', 'a1-tools' ), ); } protected function register_controls() { $this->register_content_controls(); $this->register_standard_fields_section(); $this->register_custom_fields_section(); $this->register_consent_section(); $this->register_antibot_section(); $this->register_submit_section(); $this->register_style_container(); $this->register_style_title(); $this->register_style_labels(); $this->register_style_inputs(); $this->register_style_asterisk(); $this->register_style_submit(); $this->register_style_consent(); } // ── Content : Header ──────────────────────────────────────────── private function register_content_controls(): void { $this->start_controls_section( 'section_header', array( 'label' => __( 'Header', 'a1-tools' ) ) ); $this->add_control( 'form_title', array( 'label' => __( 'Form title', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::TEXT, 'default' => __( 'Get a free estimate', 'a1-tools' ), 'label_block' => true, ) ); $this->add_control( 'form_subtitle', array( 'label' => __( 'Subtitle', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::TEXTAREA, 'default' => __( 'Tell us what you need and we\'ll be in touch shortly.', 'a1-tools' ), 'label_block' => true, 'rows' => 2, ) ); $this->end_controls_section(); } // ── Content : Standard Fields repeater ────────────────────────── private function register_standard_fields_section(): void { $this->start_controls_section( 'section_standard_fields', array( 'label' => __( 'Standard Fields', 'a1-tools' ) ) ); $this->add_control( 'standard_fields_help', array( 'type' => \Elementor\Controls_Manager::RAW_HTML, 'raw' => __( 'Toggle, relabel, and reorder the ten CRM lead-intake fields. These map directly to columns in crm_web_form_submissions so the CRM\'s standard filters (state, preferred contact, etc.) keep working. To add fields beyond these, use the Custom Fields section below.', 'a1-tools' ), 'content_classes' => 'elementor-descriptor', ) ); $std_repeater = new \Elementor\Repeater(); $std_repeater->add_control( 'enabled', array( 'label' => __( 'Show this field', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::SWITCHER, 'label_on' => __( 'Show', 'a1-tools' ), 'label_off' => __( 'Hide', 'a1-tools' ), 'return_value' => 'yes', 'default' => 'yes', ) ); $std_repeater->add_control( 'field_name', array( 'label' => __( 'Field key', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::TEXT, 'default' => '', 'description' => __( 'Read-only. Don\'t change — this is the CRM column name.', 'a1-tools' ), ) ); $std_repeater->add_control( 'field_label', array( 'label' => __( 'Label', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::TEXT, 'default' => '', 'label_block' => true, ) ); $std_repeater->add_control( 'field_placeholder', array( 'label' => __( 'Placeholder', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::TEXT, 'default' => '', 'label_block' => true, ) ); $std_repeater->add_control( 'field_required', array( 'label' => __( 'Required', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::SWITCHER, 'return_value' => 'yes', 'default' => 'yes', ) ); $this->add_control( 'standard_fields', array( 'label' => __( 'Fields', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::REPEATER, 'fields' => $std_repeater->get_controls(), 'default' => self::default_standard_fields(), 'title_field' => '{{{ field_label }}}', 'prevent_empty' => false, ) ); $this->end_controls_section(); } // ── Content : Custom Fields repeater ──────────────────────────── private function register_custom_fields_section(): void { $this->start_controls_section( 'section_custom_fields', array( 'label' => __( 'Custom Fields', 'a1-tools' ) ) ); $this->add_control( 'custom_fields_help', array( 'type' => \Elementor\Controls_Manager::RAW_HTML, 'raw' => __( 'Add arbitrary fields beyond the 10 standard ones. They\'re stored as a self-describing JSON array on the submission row and rendered in the CRM\'s submission detail view alongside the standard fields.', 'a1-tools' ), 'content_classes' => 'elementor-descriptor', ) ); $cf_repeater = new \Elementor\Repeater(); $cf_repeater->add_control( 'field_id', array( 'label' => __( 'Field ID (slug)', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::TEXT, 'default' => '', 'description' => __( 'Internal identifier. Letters, numbers, underscores only. Stable across edits.', 'a1-tools' ), ) ); $cf_repeater->add_control( 'field_label', array( 'label' => __( 'Label', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::TEXT, 'default' => '', 'label_block' => true, ) ); $cf_repeater->add_control( 'field_type', array( 'label' => __( 'Type', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::SELECT, 'options' => self::custom_field_types(), 'default' => 'text', ) ); $cf_repeater->add_control( 'field_placeholder', array( 'label' => __( 'Placeholder', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::TEXT, 'default' => '', 'condition' => array( 'field_type!' => array( 'checkbox', 'radio', 'select' ) ), ) ); $cf_repeater->add_control( 'field_options', array( 'label' => __( 'Options (one per line)', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::TEXTAREA, 'default' => '', 'description' => __( 'For Dropdown, Checkbox, and Radio types. Use value|Label per line — or just Label to use the label as the value.', 'a1-tools' ), 'rows' => 4, 'condition' => array( 'field_type' => array( 'select', 'radio' ) ), ) ); $cf_repeater->add_control( 'field_help', array( 'label' => __( 'Help text (below field)', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::TEXT, 'default' => '', 'label_block' => true, ) ); $cf_repeater->add_control( 'field_required', array( 'label' => __( 'Required', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::SWITCHER, 'return_value' => 'yes', 'default' => '', ) ); $this->add_control( 'custom_fields', array( 'label' => __( 'Custom fields', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::REPEATER, 'fields' => $cf_repeater->get_controls(), 'default' => array(), 'title_field' => '{{{ field_label || field_id || "Custom field" }}}', 'prevent_empty' => false, ) ); $this->end_controls_section(); } // ── Content : SMS Consent ─────────────────────────────────────── private function register_consent_section(): void { $this->start_controls_section( 'section_consent', array( 'label' => __( 'SMS Consent', 'a1-tools' ) ) ); $this->add_control( 'consent_enabled', array( 'label' => __( 'Enable SMS consent checkbox', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::SWITCHER, 'return_value' => 'yes', 'default' => '', 'label_on' => __( 'On', 'a1-tools' ), 'label_off' => __( 'Off', 'a1-tools' ), ) ); $this->add_control( 'consent_required', array( 'label' => __( 'Require check to submit', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::SWITCHER, 'return_value' => 'yes', 'default' => 'yes', 'condition' => array( 'consent_enabled' => 'yes' ), ) ); $this->add_control( 'consent_text', array( 'label' => __( 'Consent text', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::WYSIWYG, 'default' => __( 'By checking this box, I consent to receive text messages related to orders, service appointments, or other relevant information. You can reply STOP at any time to opt out. Message and data rates may apply. Message frequency may vary. For more information, please refer to our Privacy Policy and SMS Terms and Conditions.', 'a1-tools' ), 'description' => __( 'Placeholders: {{privacy_url}}, {{sms_terms_url}}, {{help_phone}} are replaced at render time. Site-relative defaults are used for URLs when the fields below are left empty.', 'a1-tools' ), 'condition' => array( 'consent_enabled' => 'yes' ), ) ); $this->add_control( 'consent_privacy_url', array( 'label' => __( 'Privacy Policy URL', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::URL, 'default' => array( 'url' => '' ), 'placeholder' => __( 'Leave empty to use [site]/privacy-policy/', 'a1-tools' ), 'show_external' => false, 'condition' => array( 'consent_enabled' => 'yes' ), ) ); $this->add_control( 'consent_sms_terms_url', array( 'label' => __( 'SMS Terms URL', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::URL, 'default' => array( 'url' => '' ), 'placeholder' => __( 'Leave empty to use [site]/terms-and-conditions/', 'a1-tools' ), 'show_external' => false, 'condition' => array( 'consent_enabled' => 'yes' ), ) ); $this->add_control( 'consent_help_phone', array( 'label' => __( 'Help phone number (optional)', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::TEXT, 'default' => '', 'placeholder' => '(555) 123-4567', 'description' => __( 'Replaces {{help_phone}} in the consent text. Leave empty to omit.', 'a1-tools' ), 'condition' => array( 'consent_enabled' => 'yes' ), ) ); $this->end_controls_section(); } // ── Content : Anti-bot ────────────────────────────────────────── private function register_antibot_section(): void { $this->start_controls_section( 'section_antibot', array( 'label' => __( 'Anti-bot', 'a1-tools' ) ) ); $this->add_control( 'antibot_enabled', array( 'label' => __( 'Show "I\'m not a robot" checkbox', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::SWITCHER, 'return_value' => 'yes', 'default' => '', 'description' => __( 'Simple in-form checkbox the visitor ticks before submit. Blocks naive scripts without the friction of an external CAPTCHA service. The form already has an invisible honeypot + minimum-fill-time gate, so this is purely additive.', 'a1-tools' ), ) ); $this->add_control( 'antibot_label', array( 'label' => __( 'Checkbox label', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::TEXT, 'default' => __( 'I\'m not a robot', 'a1-tools' ), 'condition' => array( 'antibot_enabled' => 'yes' ), ) ); $this->end_controls_section(); } // ── Content : Submit + Messages ───────────────────────────────── private function register_submit_section(): void { $this->start_controls_section( 'section_submit', array( 'label' => __( 'Submit & Messages', 'a1-tools' ) ) ); $this->add_control( 'submit_label', array( 'label' => __( 'Submit button label', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::TEXT, 'default' => __( 'Send', 'a1-tools' ), ) ); $this->add_control( 'success_message', array( 'label' => __( 'Success message', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::TEXTAREA, 'default' => __( 'Thanks! We received your message and will be in touch shortly.', 'a1-tools' ), ) ); $this->add_control( 'error_message', array( 'label' => __( 'Error message', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::TEXTAREA, 'default' => __( 'Sorry, we couldn\'t submit your message right now. Please try again or call us.', 'a1-tools' ), ) ); $this->end_controls_section(); } // ── Style : Container ─────────────────────────────────────────── private function register_style_container(): void { $this->start_controls_section( 'style_container', array( 'label' => __( 'Container', 'a1-tools' ), 'tab' => \Elementor\Controls_Manager::TAB_STYLE, ) ); $this->add_control( 'container_bg', array( 'label' => __( 'Background color', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::COLOR, 'default' => '', 'selectors' => array( '{{WRAPPER}} .a1tools-web-form-wrap' => 'background-color: {{VALUE}};', ), ) ); $this->add_group_control( \Elementor\Group_Control_Border::get_type(), array( 'name' => 'container_border', 'selector' => '{{WRAPPER}} .a1tools-web-form-wrap', ) ); $this->add_control( 'container_radius', array( 'label' => __( 'Border radius', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::DIMENSIONS, 'size_units' => array( 'px', '%', 'em' ), 'selectors' => array( '{{WRAPPER}} .a1tools-web-form-wrap' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}}; overflow: hidden;', ), ) ); $this->add_responsive_control( 'container_padding', array( 'label' => __( 'Padding', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::DIMENSIONS, 'size_units' => array( 'px', 'em', '%' ), 'selectors' => array( '{{WRAPPER}} .a1tools-web-form-wrap' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', ), ) ); $this->add_group_control( \Elementor\Group_Control_Box_Shadow::get_type(), array( 'name' => 'container_shadow', 'selector' => '{{WRAPPER}} .a1tools-web-form-wrap', ) ); $this->end_controls_section(); } // ── Style : Title & Subtitle ──────────────────────────────────── private function register_style_title(): void { $this->start_controls_section( 'style_title', array( 'label' => __( 'Title & Subtitle', 'a1-tools' ), 'tab' => \Elementor\Controls_Manager::TAB_STYLE, ) ); $this->add_control( 'title_color', array( 'label' => __( 'Title color', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::COLOR, 'selectors' => array( '{{WRAPPER}} .a1tools-web-form__title' => 'color: {{VALUE}};', ), ) ); $this->add_group_control( \Elementor\Group_Control_Typography::get_type(), array( 'name' => 'title_typography', 'selector' => '{{WRAPPER}} .a1tools-web-form__title', ) ); $this->add_control( 'subtitle_color', array( 'label' => __( 'Subtitle color', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::COLOR, 'selectors' => array( '{{WRAPPER}} .a1tools-web-form__subtitle' => 'color: {{VALUE}};', ), ) ); $this->add_group_control( \Elementor\Group_Control_Typography::get_type(), array( 'name' => 'subtitle_typography', 'selector' => '{{WRAPPER}} .a1tools-web-form__subtitle', ) ); $this->end_controls_section(); } // ── Style : Labels ────────────────────────────────────────────── private function register_style_labels(): void { $this->start_controls_section( 'style_labels', array( 'label' => __( 'Field Labels', 'a1-tools' ), 'tab' => \Elementor\Controls_Manager::TAB_STYLE, ) ); $this->add_control( 'label_color', array( 'label' => __( 'Color', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::COLOR, 'selectors' => array( '{{WRAPPER}} .a1tools-web-form__label' => 'color: {{VALUE}};', ), ) ); $this->add_group_control( \Elementor\Group_Control_Typography::get_type(), array( 'name' => 'label_typography', 'selector' => '{{WRAPPER}} .a1tools-web-form__label', ) ); $this->end_controls_section(); } // ── Style : Inputs ────────────────────────────────────────────── private function register_style_inputs(): void { $this->start_controls_section( 'style_inputs', array( 'label' => __( 'Inputs', 'a1-tools' ), 'tab' => \Elementor\Controls_Manager::TAB_STYLE, ) ); $this->add_control( 'input_text_color', array( 'label' => __( 'Text color', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::COLOR, 'selectors' => array( '{{WRAPPER}} .a1tools-web-form__input, {{WRAPPER}} .a1tools-web-form__select, {{WRAPPER}} .a1tools-web-form__textarea' => 'color: {{VALUE}};', ), ) ); $this->add_control( 'input_placeholder_color', array( 'label' => __( 'Placeholder color', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::COLOR, 'selectors' => array( '{{WRAPPER}} .a1tools-web-form__input::placeholder, {{WRAPPER}} .a1tools-web-form__textarea::placeholder' => 'color: {{VALUE}}; opacity: 1;', ), ) ); $this->add_control( 'input_bg', array( 'label' => __( 'Background color', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::COLOR, 'selectors' => array( '{{WRAPPER}} .a1tools-web-form__input, {{WRAPPER}} .a1tools-web-form__select, {{WRAPPER}} .a1tools-web-form__textarea' => 'background-color: {{VALUE}};', ), ) ); $this->add_group_control( \Elementor\Group_Control_Typography::get_type(), array( 'name' => 'input_typography', 'selector' => '{{WRAPPER}} .a1tools-web-form__input, {{WRAPPER}} .a1tools-web-form__select, {{WRAPPER}} .a1tools-web-form__textarea', ) ); $this->add_group_control( \Elementor\Group_Control_Border::get_type(), array( 'name' => 'input_border', 'selector' => '{{WRAPPER}} .a1tools-web-form__input, {{WRAPPER}} .a1tools-web-form__select, {{WRAPPER}} .a1tools-web-form__textarea', ) ); $this->add_control( 'input_radius', array( 'label' => __( 'Border radius', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::DIMENSIONS, 'size_units' => array( 'px', '%', 'em' ), 'selectors' => array( '{{WRAPPER}} .a1tools-web-form__input, {{WRAPPER}} .a1tools-web-form__select, {{WRAPPER}} .a1tools-web-form__textarea' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', ), ) ); $this->add_control( 'input_focus_border', array( 'label' => __( 'Focus border color', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::COLOR, 'selectors' => array( '{{WRAPPER}} .a1tools-web-form__input:focus, {{WRAPPER}} .a1tools-web-form__select:focus, {{WRAPPER}} .a1tools-web-form__textarea:focus' => 'border-color: {{VALUE}};', ), ) ); $this->end_controls_section(); } // ── Style : Required Asterisk ─────────────────────────────────── private function register_style_asterisk(): void { $this->start_controls_section( 'style_asterisk', array( 'label' => __( 'Required asterisk', 'a1-tools' ), 'tab' => \Elementor\Controls_Manager::TAB_STYLE, ) ); $this->add_control( 'asterisk_color', array( 'label' => __( 'Color', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::COLOR, 'default' => '#dc2626', 'selectors' => array( '{{WRAPPER}} .a1tools-web-form__required' => 'color: {{VALUE}};', ), ) ); $this->add_control( 'asterisk_weight', array( 'label' => __( 'Weight', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::SELECT, 'options' => array( '400' => __( 'Normal', 'a1-tools' ), '600' => __( 'Semi-bold', 'a1-tools' ), '700' => __( 'Bold', 'a1-tools' ), ), 'default' => '700', 'selectors' => array( '{{WRAPPER}} .a1tools-web-form__required' => 'font-weight: {{VALUE}};', ), ) ); $this->end_controls_section(); } // ── Style : Submit Button ─────────────────────────────────────── private function register_style_submit(): void { $this->start_controls_section( 'style_submit', array( 'label' => __( 'Submit Button', 'a1-tools' ), 'tab' => \Elementor\Controls_Manager::TAB_STYLE, ) ); $this->add_control( 'submit_alignment', array( 'label' => __( 'Alignment', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::CHOOSE, 'options' => array( 'left' => array( 'title' => __( 'Left', 'a1-tools' ), 'icon' => 'eicon-text-align-left' ), 'center' => array( 'title' => __( 'Center', 'a1-tools' ), 'icon' => 'eicon-text-align-center' ), 'right' => array( 'title' => __( 'Right', 'a1-tools' ), 'icon' => 'eicon-text-align-right' ), 'full' => array( 'title' => __( 'Full width', 'a1-tools' ), 'icon' => 'eicon-text-align-justify' ), ), 'default' => 'left', 'selectors_dictionary' => array( 'left' => 'justify-content: flex-start;', 'center' => 'justify-content: center;', 'right' => 'justify-content: flex-end;', 'full' => 'justify-content: stretch;', ), 'selectors' => array( '{{WRAPPER}} .a1tools-web-form__actions' => '{{VALUE}}', '{{WRAPPER}} .a1tools-web-form__actions[data-align="full"] .a1tools-web-form__submit' => 'width: 100%;', ), ) ); $this->start_controls_tabs( 'submit_tabs' ); $this->start_controls_tab( 'submit_normal_tab', array( 'label' => __( 'Normal', 'a1-tools' ) ) ); $this->add_control( 'submit_bg', array( 'label' => __( 'Background', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::COLOR, 'selectors' => array( '{{WRAPPER}} .a1tools-web-form__submit' => 'background-color: {{VALUE}};' ), ) ); $this->add_control( 'submit_color', array( 'label' => __( 'Text color', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::COLOR, 'selectors' => array( '{{WRAPPER}} .a1tools-web-form__submit' => 'color: {{VALUE}};' ), ) ); $this->end_controls_tab(); $this->start_controls_tab( 'submit_hover_tab', array( 'label' => __( 'Hover', 'a1-tools' ) ) ); $this->add_control( 'submit_bg_hover', array( 'label' => __( 'Background', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::COLOR, 'selectors' => array( '{{WRAPPER}} .a1tools-web-form__submit:hover' => 'background-color: {{VALUE}};' ), ) ); $this->add_control( 'submit_color_hover', array( 'label' => __( 'Text color', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::COLOR, 'selectors' => array( '{{WRAPPER}} .a1tools-web-form__submit:hover' => 'color: {{VALUE}};' ), ) ); $this->end_controls_tab(); $this->end_controls_tabs(); $this->add_group_control( \Elementor\Group_Control_Typography::get_type(), array( 'name' => 'submit_typography', 'selector' => '{{WRAPPER}} .a1tools-web-form__submit' ) ); $this->add_control( 'submit_radius', array( 'label' => __( 'Border radius', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::DIMENSIONS, 'size_units' => array( 'px', '%', 'em' ), 'selectors' => array( '{{WRAPPER}} .a1tools-web-form__submit' => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', ), ) ); $this->add_responsive_control( 'submit_padding', array( 'label' => __( 'Padding', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::DIMENSIONS, 'size_units' => array( 'px', 'em' ), 'selectors' => array( '{{WRAPPER}} .a1tools-web-form__submit' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};', ), ) ); $this->end_controls_section(); } // ── Style : Consent Checkbox ──────────────────────────────────── private function register_style_consent(): void { $this->start_controls_section( 'style_consent', array( 'label' => __( 'Consent & Anti-bot blocks', 'a1-tools' ), 'tab' => \Elementor\Controls_Manager::TAB_STYLE, ) ); $this->add_control( 'consent_text_color', array( 'label' => __( 'Text color', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::COLOR, 'selectors' => array( '{{WRAPPER}} .a1tools-web-form__consent, {{WRAPPER}} .a1tools-web-form__antibot' => 'color: {{VALUE}};', ), ) ); $this->add_control( 'consent_link_color', array( 'label' => __( 'Link color', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::COLOR, 'selectors' => array( '{{WRAPPER}} .a1tools-web-form__consent a' => 'color: {{VALUE}};', ), ) ); $this->add_group_control( \Elementor\Group_Control_Typography::get_type(), array( 'name' => 'consent_typography', 'selector' => '{{WRAPPER}} .a1tools-web-form__consent, {{WRAPPER}} .a1tools-web-form__antibot', ) ); $this->add_control( 'consent_check_color', array( 'label' => __( 'Checkbox accent color', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::COLOR, 'selectors' => array( '{{WRAPPER}} .a1tools-web-form__consent input[type="checkbox"], {{WRAPPER}} .a1tools-web-form__antibot input[type="checkbox"]' => 'accent-color: {{VALUE}};', ), ) ); $this->end_controls_section(); } // ──────────────────────────────────────────────────────────────── // Render // ──────────────────────────────────────────────────────────────── protected function render(): void { $settings = $this->get_settings_for_display(); // (2.5.6) Auto-resolve company_id from the A1 Tools API when // the WP-Admin option is still at the default 0 — see // class-a1-tools-web-chat-widget.php for the rationale. $company_id = (int) get_option( 'a1tools_form_crm_company_id', 0 ); if ( $company_id === 0 && function_exists( 'a1tools_resolve_crm_company_id' ) ) { $company_id = a1tools_resolve_crm_company_id(); } $rest_url = esc_url_raw( rest_url( 'a1-tools/v1/submit-web-form' ) ); $page_url = esc_url( is_singular() ? get_permalink() : home_url( add_query_arg( null, null ) ) ); $form_id = 'a1tools-web-form-' . wp_unique_id(); // Build merged Standard Fields keyed by field_name, defaulting // to the prebuilt defaults so an empty repeater (i.e. first // install) still renders all 10 fields. $std_rows = is_array( $settings['standard_fields'] ?? null ) && ! empty( $settings['standard_fields'] ) ? $settings['standard_fields'] : self::default_standard_fields(); $custom_rows = is_array( $settings['custom_fields'] ?? null ) ? $settings['custom_fields'] : array(); $submit_align = $settings['submit_alignment'] ?? 'left'; // Consent $consent_enabled = ( $settings['consent_enabled'] ?? '' ) === 'yes'; $consent_required = ( $settings['consent_required'] ?? 'yes' ) === 'yes'; $consent_text = ''; if ( $consent_enabled ) { $privacy_url = ! empty( $settings['consent_privacy_url']['url'] ) ? $settings['consent_privacy_url']['url'] : home_url( '/privacy-policy/' ); $sms_terms_url = ! empty( $settings['consent_sms_terms_url']['url'] ) ? $settings['consent_sms_terms_url']['url'] : home_url( '/terms-and-conditions/' ); $help_phone = $settings['consent_help_phone'] ?? ''; $raw_consent = wp_kses_post( $settings['consent_text'] ?? '' ); $consent_text = strtr( $raw_consent, array( '{{privacy_url}}' => esc_url( $privacy_url ), '{{sms_terms_url}}' => esc_url( $sms_terms_url ), '{{help_phone}}' => esc_html( $help_phone ), ) ); } // Anti-bot $antibot_enabled = ( $settings['antibot_enabled'] ?? '' ) === 'yes'; $antibot_label = $settings['antibot_label'] ?? __( 'I\'m not a robot', 'a1-tools' ); ?>

render_standard_field( $form_id, $row ); } // Render Custom Fields below the standard ones. foreach ( $custom_rows as $row ) { $this->render_custom_field( $form_id, $row ); } ?>
*' : ''; $input_id = $form_id . '-' . preg_replace( '/[^a-z0-9_]/i', '_', $name ); if ( $name === '' || $label === '' ) return; switch ( $name ) { case 'first_name': case 'last_name': $autocomplete = ( $name === 'first_name' ) ? 'given-name' : 'family-name'; echo '
'; echo ''; echo ''; echo '
'; return; case 'phone': echo '
'; echo ''; echo ''; echo '
'; return; case 'email': echo '
'; echo ''; echo ''; echo '
'; return; case 'address': echo '
'; echo ''; echo ''; echo '
'; return; case 'city': echo '
'; echo ''; echo ''; echo '
'; return; case 'state': echo '
'; echo ''; echo ''; echo '
'; return; case 'zip': echo '
'; echo ''; echo ''; echo '
'; return; case 'commentary': echo '
'; echo ''; echo ''; echo '
'; return; case 'preferred_contact': echo '
'; echo '' . esc_html( $label ) . $req_mark . ''; echo '
'; $opts = array( 'call' => __( 'Call me', 'a1-tools' ), 'email' => __( 'Email me', 'a1-tools' ), 'sms' => __( 'Text me', 'a1-tools' ) ); foreach ( $opts as $val => $optLabel ) { echo ''; } echo '
'; echo '
'; return; } } /** * Render a custom field. Sends as part of the `custom_fields` JSON * array — name attribute uses the `cf__` convention so the JS * can collect them generically. */ private function render_custom_field( string $form_id, array $row ): void { $id = preg_replace( '/[^A-Za-z0-9_]/', '', $row['field_id'] ?? '' ); $label = $row['field_label'] ?? ''; $type = strtolower( $row['field_type'] ?? 'text' ); $placeholder = $row['field_placeholder'] ?? ''; $help = $row['field_help'] ?? ''; $required = ( $row['field_required'] ?? '' ) === 'yes'; $req_attr = $required ? 'required' : ''; $req_mark = $required ? '' : ''; $opts_raw = $row['field_options'] ?? ''; $input_id = $form_id . '-cf-' . $id; $name = 'cf__' . $id; if ( $id === '' || $label === '' ) return; $allowed = array_keys( self::custom_field_types() ); if ( ! in_array( $type, $allowed, true ) ) $type = 'text'; // Parse options for select / radio. $options = array(); if ( in_array( $type, array( 'select', 'radio' ), true ) ) { foreach ( preg_split( '/\r?\n/', $opts_raw ) as $line ) { $line = trim( $line ); if ( $line === '' ) continue; if ( strpos( $line, '|' ) !== false ) { list( $val, $optLabel ) = array_map( 'trim', explode( '|', $line, 2 ) ); } else { $val = $line; $optLabel = $line; } $options[ $val ] = $optLabel; } } echo '
'; if ( $type === 'checkbox' ) { echo ''; } else { echo ''; switch ( $type ) { case 'textarea': echo ''; break; case 'select': echo ''; break; case 'radio': echo '
'; foreach ( $options as $val => $optLabel ) { echo ''; } echo '
'; break; default: $inputType = in_array( $type, array( 'email', 'tel', 'url', 'number', 'date' ), true ) ? $type : 'text'; echo ''; } } if ( $help !== '' ) { echo '' . esc_html( $help ) . ''; } echo '
'; } } /** * US states (abbr => name). Used by the state dropdown. */ function a1tools_web_form_us_states(): array { return array( 'AL' => 'Alabama', 'AK' => 'Alaska', 'AZ' => 'Arizona', 'AR' => 'Arkansas', 'CA' => 'California', 'CO' => 'Colorado', 'CT' => 'Connecticut', 'DE' => 'Delaware', 'DC' => 'District of Columbia', 'FL' => 'Florida', 'GA' => 'Georgia', 'HI' => 'Hawaii', 'ID' => 'Idaho', 'IL' => 'Illinois', 'IN' => 'Indiana', 'IA' => 'Iowa', 'KS' => 'Kansas', 'KY' => 'Kentucky', 'LA' => 'Louisiana', 'ME' => 'Maine', 'MD' => 'Maryland', 'MA' => 'Massachusetts', 'MI' => 'Michigan', 'MN' => 'Minnesota', 'MS' => 'Mississippi', 'MO' => 'Missouri', 'MT' => 'Montana', 'NE' => 'Nebraska', 'NV' => 'Nevada', 'NH' => 'New Hampshire', 'NJ' => 'New Jersey', 'NM' => 'New Mexico', 'NY' => 'New York', 'NC' => 'North Carolina', 'ND' => 'North Dakota', 'OH' => 'Ohio', 'OK' => 'Oklahoma', 'OR' => 'Oregon', 'PA' => 'Pennsylvania', 'RI' => 'Rhode Island', 'SC' => 'South Carolina', 'SD' => 'South Dakota', 'TN' => 'Tennessee', 'TX' => 'Texas', 'UT' => 'Utah', 'VT' => 'Vermont', 'VA' => 'Virginia', 'WA' => 'Washington', 'WV' => 'West Virginia', 'WI' => 'Wisconsin', 'WY' => 'Wyoming', ); }