= the configured threshold, default 4) get a thank-you * panel with a prominent button to the public review link (the site's * `review_url` site variable, falling back to `google_business_url`). * - Low ratings get a private feedback form (name / phone / email / * comment) that lands in the CRM "Customer Experience" tab. * * The public-review link appears ONLY on high ratings (4-5). Low ratings (1-3) * see the private feedback form only - no public-review link. NOTE: routing only * happy customers to public reviews is "review gating", which can conflict with * Google's review policy and the FTC's 2024 review rule; this is an intentional * business decision (changed in 2.6.2 at the owner's request). * * Every star click is logged immediately (action=submit) for a CSAT signal; * the private form attaches to that row (action=feedback). Submission goes * through the WordPress REST proxy `a1-tools/v1/submit-review`, which signs * the payload with the form HMAC secret and forwards to * `tools.a-1chimney.com/api/customer_experience.php`. * * Mirrors the styling/control depth of class-a1-tools-web-form-widget.php. * * @package A1Tools * @since 2.6.0 */ if ( ! defined( 'ABSPATH' ) ) { exit; } class A1Tools_Review_Funnel_Elementor_Widget extends \Elementor\Widget_Base { public function get_name() { return 'a1tools_review_funnel'; } public function get_title() { return __( 'A1 Review Funnel', 'a1-tools' ); } public function get_icon() { return 'eicon-rating'; } public function get_categories() { return array( 'general' ); } public function get_keywords() { return array( 'a1', 'review', 'rating', 'stars', 'feedback', 'reputation', 'google', 'nps', 'csat' ); } public function get_style_depends() { return array( 'a1tools-review' ); } public function get_script_depends() { return array( 'a1tools-review' ); } protected function register_controls() { $this->register_content_prompt(); $this->register_content_positive(); $this->register_content_negative(); $this->register_content_messages(); $this->register_style_container(); $this->register_style_prompt(); $this->register_style_stars(); $this->register_style_panels(); $this->register_style_inputs(); $this->register_style_buttons(); $this->register_style_secondary(); } // ── Content : Prompt + threshold ──────────────────────────────── private function register_content_prompt(): void { $this->start_controls_section( 'section_prompt', array( 'label' => __( 'Prompt', 'a1-tools' ) ) ); $this->add_control( 'prompt_title', array( 'label' => __( 'Prompt title', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::TEXT, 'default' => __( 'How was your experience?', 'a1-tools' ), 'label_block' => true, ) ); $this->add_control( 'prompt_subtitle', array( 'label' => __( 'Subtitle', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::TEXTAREA, 'default' => __( 'Tap a star to rate us.', 'a1-tools' ), 'label_block' => true, 'rows' => 2, ) ); $this->add_control( 'positive_threshold', array( 'label' => __( 'Positive threshold', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::SELECT, 'default' => '4', 'options' => array( '4' => __( '4-5 stars = positive', 'a1-tools' ), '5' => __( 'Only 5 stars = positive', 'a1-tools' ), ), 'description' => __( 'Ratings at or above this go to the public-review thank-you; below it go to the private feedback form.', 'a1-tools' ), ) ); $this->end_controls_section(); } // ── Content : Positive (high rating) ──────────────────────────── private function register_content_positive(): void { $this->start_controls_section( 'section_positive', array( 'label' => __( 'Positive Path (high rating)', 'a1-tools' ) ) ); $this->add_control( 'positive_intro_title', array( 'label' => __( 'Form title (before submit)', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::TEXT, 'default' => __( 'So glad we hit the mark! 🎉', 'a1-tools' ), 'label_block' => true, ) ); $this->add_control( 'positive_intro_message', array( 'label' => __( 'Form message (before submit)', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::TEXTAREA, 'default' => __( 'Tell us what we did well.', 'a1-tools' ), 'rows' => 2, ) ); $this->add_control( 'positive_title', array( 'label' => __( 'Google-step title (after submit)', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::TEXT, 'default' => __( 'Thank you! 🎉', 'a1-tools' ), 'label_block' => true, ) ); $this->add_control( 'positive_message', array( 'label' => __( 'Google-step message (after submit)', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::TEXTAREA, 'default' => __( "We'd love it if you shared your experience on Google. It only takes a moment.", 'a1-tools' ), 'rows' => 3, ) ); $this->add_control( 'positive_feedback_label', array( 'label' => __( 'Comment field label', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::TEXT, 'default' => __( 'What did we do well?', 'a1-tools' ) ) ); $this->add_control( 'positive_submit_label', array( 'label' => __( 'Submit button label', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::TEXT, 'default' => __( 'Send', 'a1-tools' ) ) ); $this->add_control( 'review_url_override', array( 'label' => __( 'Review URL override', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::URL, 'default' => array( 'url' => '' ), 'placeholder' => __( 'Leave empty to use the site\'s Review Link variable', 'a1-tools' ), 'show_external' => false, 'description' => __( 'Defaults to the site\'s Review Link (Site Variables → Social Media), then the Google Business URL. Set here only to override for this placement.', 'a1-tools' ), ) ); $this->add_control( 'google_prompt', array( 'label' => __( 'Google-step message', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::TEXTAREA, 'default' => __( "Thanks for sharing your experience! If you'd like, leave us a quick review on Google too. We've copied your note so you can paste it right in.", 'a1-tools' ), 'rows' => 3, ) ); $this->add_control( 'positive_button_label', array( 'label' => __( 'Google button label', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::TEXT, 'default' => __( 'Rate us on Google', 'a1-tools' ), ) ); $this->end_controls_section(); } // ── Content : Negative (low rating) ───────────────────────────── private function register_content_negative(): void { $this->start_controls_section( 'section_negative', array( 'label' => __( 'Feedback Path (low rating)', 'a1-tools' ) ) ); $this->add_control( 'negative_title', array( 'label' => __( 'Title', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::TEXT, 'default' => __( "We're sorry we missed the mark", 'a1-tools' ), 'label_block' => true, ) ); $this->add_control( 'negative_message', array( 'label' => __( 'Message', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::TEXTAREA, 'default' => __( "Tell us what went wrong and how we can make it right. A manager will follow up personally.", 'a1-tools' ), 'rows' => 3, ) ); $this->add_control( 'name_label', array( 'label' => __( 'Name field label', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::TEXT, 'default' => __( 'Your name', 'a1-tools' ) ) ); $this->add_control( 'phone_label', array( 'label' => __( 'Phone field label', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::TEXT, 'default' => __( 'Phone', 'a1-tools' ) ) ); $this->add_control( 'email_label', array( 'label' => __( 'Email field label', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::TEXT, 'default' => __( 'Email', 'a1-tools' ) ) ); $this->add_control( 'feedback_label', array( 'label' => __( 'Comment field label', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::TEXT, 'default' => __( 'What happened?', 'a1-tools' ) ) ); $this->add_control( 'negative_submit_label', array( 'label' => __( 'Submit button label', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::TEXT, 'default' => __( 'Send feedback', 'a1-tools' ) ) ); $this->end_controls_section(); } // ── Content : Messages ────────────────────────────────────────── private function register_content_messages(): void { $this->start_controls_section( 'section_messages', array( 'label' => __( 'Messages', 'a1-tools' ) ) ); $this->add_control( 'success_message', array( 'label' => __( 'Feedback success message', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::TEXTAREA, 'default' => __( 'Thank you — we\'ve received your feedback 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, something went wrong. 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, 'selectors' => array( '{{WRAPPER}} .a1tools-review-wrap' => 'background-color: {{VALUE}};' ), ) ); $this->add_group_control( \Elementor\Group_Control_Border::get_type(), array( 'name' => 'container_border', 'selector' => '{{WRAPPER}} .a1tools-review-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-review-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-review-wrap' => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};' ), ) ); $this->add_responsive_control( 'container_align', array( 'label' => __( 'Text 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' ), ), 'default' => 'center', 'selectors' => array( '{{WRAPPER}} .a1tools-review-wrap' => 'text-align: {{VALUE}};' ), ) ); $this->add_group_control( \Elementor\Group_Control_Box_Shadow::get_type(), array( 'name' => 'container_shadow', 'selector' => '{{WRAPPER}} .a1tools-review-wrap' ) ); $this->end_controls_section(); } // ── Style : Prompt / panel headings ───────────────────────────── private function register_style_prompt(): void { $this->start_controls_section( 'style_prompt', array( 'label' => __( 'Titles & Text', '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-review__title' => 'color: {{VALUE}};' ), ) ); $this->add_group_control( \Elementor\Group_Control_Typography::get_type(), array( 'name' => 'title_typography', 'selector' => '{{WRAPPER}} .a1tools-review__title' ) ); $this->add_control( 'text_color', array( 'label' => __( 'Body text color', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::COLOR, 'separator' => 'before', 'selectors' => array( '{{WRAPPER}} .a1tools-review__text' => 'color: {{VALUE}};' ), ) ); $this->add_group_control( \Elementor\Group_Control_Typography::get_type(), array( 'name' => 'text_typography', 'selector' => '{{WRAPPER}} .a1tools-review__text' ) ); $this->end_controls_section(); } // ── Style : Stars ─────────────────────────────────────────────── private function register_style_stars(): void { $this->start_controls_section( 'style_stars', array( 'label' => __( 'Stars', 'a1-tools' ), 'tab' => \Elementor\Controls_Manager::TAB_STYLE ) ); $this->add_responsive_control( 'star_size', array( 'label' => __( 'Star size', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::SLIDER, 'size_units' => array( 'px' ), 'range' => array( 'px' => array( 'min' => 16, 'max' => 96 ) ), 'default' => array( 'size' => 40, 'unit' => 'px' ), 'selectors' => array( '{{WRAPPER}} .a1tools-review__star' => 'font-size: {{SIZE}}{{UNIT}};' ), ) ); $this->add_responsive_control( 'star_gap', array( 'label' => __( 'Gap between stars', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::SLIDER, 'size_units' => array( 'px' ), 'range' => array( 'px' => array( 'min' => 0, 'max' => 24 ) ), 'default' => array( 'size' => 6, 'unit' => 'px' ), 'selectors' => array( '{{WRAPPER}} .a1tools-review__stars' => 'gap: {{SIZE}}{{UNIT}};' ), ) ); $this->add_control( 'star_empty_color', array( 'label' => __( 'Empty color', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::COLOR, 'default' => '#d1d5db', 'selectors' => array( '{{WRAPPER}} .a1tools-review__star' => 'color: {{VALUE}};' ), ) ); $this->add_control( 'star_active_color', array( 'label' => __( 'Selected / hover color', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::COLOR, 'default' => '#f5a623', 'selectors' => array( '{{WRAPPER}} .a1tools-review__star.is-active' => 'color: {{VALUE}};', '{{WRAPPER}} .a1tools-review__stars:hover .a1tools-review__star.is-hover' => 'color: {{VALUE}};', ), ) ); $this->end_controls_section(); } // ── Style : Panels ────────────────────────────────────────────── private function register_style_panels(): void { $this->start_controls_section( 'style_panels', array( 'label' => __( 'Result Panels', 'a1-tools' ), 'tab' => \Elementor\Controls_Manager::TAB_STYLE ) ); $this->add_responsive_control( 'panel_spacing', array( 'label' => __( 'Top spacing', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::SLIDER, 'size_units' => array( 'px' ), 'range' => array( 'px' => array( 'min' => 0, 'max' => 60 ) ), 'default' => array( 'size' => 16, 'unit' => 'px' ), 'selectors' => array( '{{WRAPPER}} .a1tools-review__panel' => 'margin-top: {{SIZE}}{{UNIT}};' ), ) ); $this->end_controls_section(); } // ── Style : Inputs (feedback form) ────────────────────────────── private function register_style_inputs(): void { $this->start_controls_section( 'style_inputs', array( 'label' => __( 'Feedback Inputs', 'a1-tools' ), 'tab' => \Elementor\Controls_Manager::TAB_STYLE ) ); $inputs = '{{WRAPPER}} .a1tools-review__input, {{WRAPPER}} .a1tools-review__textarea'; $this->add_control( 'input_text_color', array( 'label' => __( 'Text color', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::COLOR, 'selectors' => array( $inputs => 'color: {{VALUE}};' ) ) ); $this->add_control( 'input_placeholder_color', array( 'label' => __( 'Placeholder color', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::COLOR, 'selectors' => array( '{{WRAPPER}} .a1tools-review__input::placeholder, {{WRAPPER}} .a1tools-review__textarea::placeholder' => 'color: {{VALUE}}; opacity: 1;' ) ) ); $this->add_control( 'input_bg', array( 'label' => __( 'Background color', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::COLOR, 'selectors' => array( $inputs => 'background-color: {{VALUE}};' ) ) ); $this->add_group_control( \Elementor\Group_Control_Typography::get_type(), array( 'name' => 'input_typography', 'selector' => $inputs ) ); $this->add_group_control( \Elementor\Group_Control_Border::get_type(), array( 'name' => 'input_border', 'selector' => $inputs ) ); $this->add_control( 'input_radius', array( 'label' => __( 'Border radius', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::DIMENSIONS, 'size_units' => array( 'px', '%', 'em' ), 'selectors' => array( $inputs => '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-review__input:focus, {{WRAPPER}} .a1tools-review__textarea:focus' => 'border-color: {{VALUE}};' ), ) ); $this->add_control( 'label_color', array( 'label' => __( 'Field label color', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::COLOR, 'separator' => 'before', 'selectors' => array( '{{WRAPPER}} .a1tools-review__label' => 'color: {{VALUE}};' ), ) ); $this->end_controls_section(); } // ── Style : Buttons (primary review + submit) ─────────────────── private function register_style_buttons(): void { $this->start_controls_section( 'style_buttons', array( 'label' => __( 'Buttons', 'a1-tools' ), 'tab' => \Elementor\Controls_Manager::TAB_STYLE ) ); $btn = '{{WRAPPER}} .a1tools-review__btn'; $this->start_controls_tabs( 'btn_tabs' ); $this->start_controls_tab( 'btn_normal', array( 'label' => __( 'Normal', 'a1-tools' ) ) ); $this->add_control( 'btn_bg', array( 'label' => __( 'Background', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::COLOR, 'default' => '#f5a623', 'selectors' => array( $btn => 'background-color: {{VALUE}};' ) ) ); $this->add_control( 'btn_color', array( 'label' => __( 'Text color', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::COLOR, 'default' => '#ffffff', 'selectors' => array( $btn => 'color: {{VALUE}};' ) ) ); $this->end_controls_tab(); $this->start_controls_tab( 'btn_hover', array( 'label' => __( 'Hover', 'a1-tools' ) ) ); $this->add_control( 'btn_bg_hover', array( 'label' => __( 'Background', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::COLOR, 'selectors' => array( $btn . ':hover' => 'background-color: {{VALUE}};' ) ) ); $this->add_control( 'btn_color_hover', array( 'label' => __( 'Text color', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::COLOR, 'selectors' => array( $btn . ':hover' => 'color: {{VALUE}};' ) ) ); $this->end_controls_tab(); $this->end_controls_tabs(); $this->add_group_control( \Elementor\Group_Control_Typography::get_type(), array( 'name' => 'btn_typography', 'selector' => $btn ) ); $this->add_control( 'btn_radius', array( 'label' => __( 'Border radius', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::DIMENSIONS, 'size_units' => array( 'px', '%', 'em' ), 'selectors' => array( $btn => 'border-radius: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};' ), ) ); $this->add_responsive_control( 'btn_padding', array( 'label' => __( 'Padding', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::DIMENSIONS, 'size_units' => array( 'px', 'em' ), 'selectors' => array( $btn => 'padding: {{TOP}}{{UNIT}} {{RIGHT}}{{UNIT}} {{BOTTOM}}{{UNIT}} {{LEFT}}{{UNIT}};' ), ) ); $this->end_controls_section(); } // ── Style : Messages ─────────────────────────── private function register_style_secondary(): void { $this->start_controls_section( 'style_secondary', array( 'label' => __( 'Messages', 'a1-tools' ), 'tab' => \Elementor\Controls_Manager::TAB_STYLE ) ); $this->add_control( 'success_color', array( 'label' => __( 'Success message color', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::COLOR, 'default' => '#15803d', 'selectors' => array( '{{WRAPPER}} .a1tools-review__feedback--success' => 'color: {{VALUE}};' ), ) ); $this->add_control( 'error_color', array( 'label' => __( 'Error message color', 'a1-tools' ), 'type' => \Elementor\Controls_Manager::COLOR, 'default' => '#dc2626', 'selectors' => array( '{{WRAPPER}} .a1tools-review__feedback--error' => 'color: {{VALUE}};' ), ) ); $this->end_controls_section(); } // ──────────────────────────────────────────────────────────────── // Render // ──────────────────────────────────────────────────────────────── protected function render(): void { $settings = $this->get_settings_for_display(); echo a1tools_render_review_funnel( $settings ); // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- builder escapes internally. } }