From c5a5a250068789d0761f8729373e053cebb5e989 Mon Sep 17 00:00:00 2001 From: Satyajit Talukder Date: Tue, 2 Apr 2024 16:16:43 +0600 Subject: [PATCH 1/9] feat(SSB-77): basic setup of the code is done --- .../FrequentlyBoughtModule.php | 111 ++++++ .../FrequentlyBought/Includes/Ajax.php | 216 ++++++++++++ .../Includes/EnqueueScript.php | 296 ++++++++++++++++ .../Includes/FrequentlyBought.php | 124 +++++++ .../css/frequently-bought-custom-admin.css | 147 ++++++++ .../assets/css/frequently-bought-front.css | 154 ++++++++ .../assets/css/frequently-bought-template.css | 157 +++++++++ .../assets/images/bump-preview.svg | 10 + .../images/upsell-order-bump-module-img.webp | Bin 0 -> 34968 bytes .../assets/images/upsell-order-bump.svg | 11 + .../js/frequently-bought-admin-custom.js | 4 + .../assets/js/frequently-bought-custom.js | 17 + .../FrequentlyBought/assets/package.json | 21 ++ .../assets/src/components/BasicInfo.js | 232 +++++++++++++ .../assets/src/components/CreateBump.js | 328 ++++++++++++++++++ .../assets/src/components/CreateBumpButton.js | 33 ++ .../assets/src/components/DesignSection.js | 25 ++ .../assets/src/components/OfferField.js | 47 +++ .../src/components/OfferProductContent.js | 33 ++ .../assets/src/components/OrderBump.js | 23 ++ .../src/components/OrderBumpGlobalSettings.js | 172 +++++++++ .../assets/src/components/OrderBumpList.js | 272 +++++++++++++++ .../assets/src/components/Preview.js | 161 +++++++++ .../components/appearance/ContentSection.js | 56 +++ .../components/appearance/TemplateSection.js | 124 +++++++ .../design-area/AcceptOfferSection.js | 91 +++++ .../template/design-area/DiscountSection.js | 93 +++++ .../design-area/OfferDescriptionSection.js | 90 +++++ .../template/design-area/OverviewBox.js | 115 ++++++ .../template/design-area/ProductSelection.js | 76 ++++ .../template/overview-area/OverViewArea.js | 92 +++++ .../FrequentlyBought/assets/src/helper.js | 94 +++++ .../FrequentlyBought/assets/src/settings.js | 48 +++ .../FrequentlyBought/assets/src/store.js | 84 +++++ .../templates/bump-product-front-view.php | 97 ++++++ assets/src/admin.scss | 28 +- .../Modules/FrequentlyBought/index.js | 12 + .../PromptNotice/PromptNotice.jsx | 11 + assets/src/components/pro-previews/index.js | 19 +- assets/src/components/settings/Layout.js | 18 +- package.json | 2 + 41 files changed, 3711 insertions(+), 33 deletions(-) create mode 100644 Includes/Modules/FrequentlyBought/FrequentlyBoughtModule.php create mode 100644 Includes/Modules/FrequentlyBought/Includes/Ajax.php create mode 100644 Includes/Modules/FrequentlyBought/Includes/EnqueueScript.php create mode 100644 Includes/Modules/FrequentlyBought/Includes/FrequentlyBought.php create mode 100644 Includes/Modules/FrequentlyBought/assets/css/frequently-bought-custom-admin.css create mode 100644 Includes/Modules/FrequentlyBought/assets/css/frequently-bought-front.css create mode 100644 Includes/Modules/FrequentlyBought/assets/css/frequently-bought-template.css create mode 100644 Includes/Modules/FrequentlyBought/assets/images/bump-preview.svg create mode 100644 Includes/Modules/FrequentlyBought/assets/images/upsell-order-bump-module-img.webp create mode 100644 Includes/Modules/FrequentlyBought/assets/images/upsell-order-bump.svg create mode 100644 Includes/Modules/FrequentlyBought/assets/js/frequently-bought-admin-custom.js create mode 100644 Includes/Modules/FrequentlyBought/assets/js/frequently-bought-custom.js create mode 100644 Includes/Modules/FrequentlyBought/assets/package.json create mode 100644 Includes/Modules/FrequentlyBought/assets/src/components/BasicInfo.js create mode 100644 Includes/Modules/FrequentlyBought/assets/src/components/CreateBump.js create mode 100644 Includes/Modules/FrequentlyBought/assets/src/components/CreateBumpButton.js create mode 100644 Includes/Modules/FrequentlyBought/assets/src/components/DesignSection.js create mode 100644 Includes/Modules/FrequentlyBought/assets/src/components/OfferField.js create mode 100644 Includes/Modules/FrequentlyBought/assets/src/components/OfferProductContent.js create mode 100644 Includes/Modules/FrequentlyBought/assets/src/components/OrderBump.js create mode 100644 Includes/Modules/FrequentlyBought/assets/src/components/OrderBumpGlobalSettings.js create mode 100644 Includes/Modules/FrequentlyBought/assets/src/components/OrderBumpList.js create mode 100644 Includes/Modules/FrequentlyBought/assets/src/components/Preview.js create mode 100644 Includes/Modules/FrequentlyBought/assets/src/components/appearance/ContentSection.js create mode 100644 Includes/Modules/FrequentlyBought/assets/src/components/appearance/TemplateSection.js create mode 100644 Includes/Modules/FrequentlyBought/assets/src/components/appearance/template/design-area/AcceptOfferSection.js create mode 100644 Includes/Modules/FrequentlyBought/assets/src/components/appearance/template/design-area/DiscountSection.js create mode 100644 Includes/Modules/FrequentlyBought/assets/src/components/appearance/template/design-area/OfferDescriptionSection.js create mode 100644 Includes/Modules/FrequentlyBought/assets/src/components/appearance/template/design-area/OverviewBox.js create mode 100644 Includes/Modules/FrequentlyBought/assets/src/components/appearance/template/design-area/ProductSelection.js create mode 100644 Includes/Modules/FrequentlyBought/assets/src/components/appearance/template/overview-area/OverViewArea.js create mode 100644 Includes/Modules/FrequentlyBought/assets/src/helper.js create mode 100644 Includes/Modules/FrequentlyBought/assets/src/settings.js create mode 100644 Includes/Modules/FrequentlyBought/assets/src/store.js create mode 100644 Includes/Modules/FrequentlyBought/templates/bump-product-front-view.php create mode 100644 assets/src/components/pro-previews/Modules/FrequentlyBought/index.js create mode 100644 assets/src/components/pro-previews/PromptNotice/PromptNotice.jsx diff --git a/Includes/Modules/FrequentlyBought/FrequentlyBoughtModule.php b/Includes/Modules/FrequentlyBought/FrequentlyBoughtModule.php new file mode 100644 index 00000000..33673e13 --- /dev/null +++ b/Includes/Modules/FrequentlyBought/FrequentlyBoughtModule.php @@ -0,0 +1,111 @@ +get_sanitized_create_bump_data(); + + $my_post = array( + 'post_title' => $bump_detail['name_of_order_bump'], + 'post_status' => 'publish', + 'post_type' => 'sgsb_order_bump', + 'post_excerpt' => maybe_serialize( $bump_detail ), + 'post_content' => 'Not defined', + + ); + if ( 0 === $bump_detail['offer_product_id'] ) { + $args_bump = array( + 'post_type' => 'sgsb_order_bump', + 'posts_per_page' => - 1, + ); + $bump_list = get_posts( $args_bump ); + if ( is_array( $bump_list ) && count( $bump_list ) >= 2 && ! SGSB_PRO_ACTIVE ) { + // don't allow creating more than 2 bumps. + return; + } + echo esc_attr( wp_insert_post( $my_post ) ); + } elseif ( ! empty( $bump_detail['offer_product_id'] ) ) { + $my_post['ID'] = $bump_detail['offer_product_id']; + wp_update_post( $my_post ); + } + + die(); + } + + /** + * Order bump list. + */ + public function bump_list() { + check_ajax_referer( 'ajd_protected' ); + $bump_id = isset( $_POST['data'] ) ? intval( wp_unslash( $_POST['data'] ) ) : null; + + if ( $bump_id ) { + $bump = get_post( $bump_id ); + wp_send_json_success( maybe_unserialize( $bump->post_excerpt ) ); + } else { + $args_bump = array( + 'post_type' => 'sgsb_order_bump', + 'posts_per_page' => - 1, + ); + $bump_list = get_posts( $args_bump ); + $bumps = array(); + foreach ( $bump_list as $bump ) { + $post_excerpt = maybe_unserialize( $bump->post_excerpt ); + $post_excerpt['id'] = $bump->ID; + + $bumps[] = $post_excerpt; + } + wp_send_json_success( $bumps ); + } + } + + /** + * Bump product delete. + */ + public function bump_delete() { + check_ajax_referer( 'ajd_protected' ); + $bump_id = isset( $_POST['data'] ) ? intval( wp_unslash( $_POST['data'] ) ) : null; + wp_delete_post( $bump_id, true ); + wp_send_json_success( 'yes' ); + } + + /** + * Bump product add to cart. + */ + public function offer_product_add_to_cart() { + check_ajax_referer( 'ajd_protected' ); + + global $woocommerce; + $all_cart_products = $woocommerce->cart->get_cart(); + + foreach ( $all_cart_products as $value ) { + $cat_ids = $value['data']->get_category_ids(); + foreach ( $cat_ids as $cat_id ) { + $all_cart_category_ids[] = $cat_id; + } + $all_cart_product_ids[] = $value['product_id']; + } + + $bump_price = isset( $_POST['data']['bump_price'] ) ? floatval( wp_unslash( $_POST['data']['bump_price'] ) ) : null; + $checked = isset( $_POST['data']['checked'] ) ? boolval( wp_unslash( $_POST['data']['checked'] ) ) : null; + $offer_product_id = isset( $_POST['data']['offer_product_id'] ) ? intval( wp_unslash( $_POST['data']['offer_product_id'] ) ) : null; + + if ( $checked ) { + $product_id = $offer_product_id; + $product_cart_id = WC()->cart->generate_cart_id( $product_id ); + $cart_item_key = WC()->cart->find_product_in_cart( $product_cart_id ); + foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) { + if ( $cart_item['product_id'] === $offer_product_id ) { + WC()->cart->remove_cart_item( $cart_item_key ); + } + } + } else { + $custom_price = $bump_price; + // Cart item data to send & save in order. + $cart_item_data = array( 'custom_price' => $custom_price ); + // Woocommerce function to add product into cart check its documentation also. + $woocommerce->cart->add_to_cart( $offer_product_id, 1, $variation_id = 0, $variation = array(), $cart_item_data ); + // Calculate totals. + $woocommerce->cart->calculate_totals(); + // Save cart to session. + $woocommerce->cart->set_session(); + // Maybe set cart cookies. + $woocommerce->cart->maybe_set_cart_cookies(); + + } + + die(); + } + + /** + * Sanitize create order bump data. + * + * @return array + */ + private function get_sanitized_create_bump_data() { + if ( ! isset( $_POST['data'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Missing + return array(); + } + + $data = $_POST['data']; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing + + if ( empty( $data['target_products'] ) ) { + $data['target_products'] = array(); + } + + if ( empty( $data['target_categories'] ) ) { + $data['target_categories'] = array(); + } + + $data['name_of_order_bump'] = sanitize_text_field( $data['name_of_order_bump'] ); + $data['target_products'] = array_map( 'intval', $data['target_products'] ); + $data['target_categories'] = array_map( 'intval', $data['target_categories'] ); + $data['bump_schedule'] = array_map( 'sanitize_text_field', $data['bump_schedule'] ); + $data['smart_offer'] = sanitize_text_field( $data['smart_offer'] ); + $data['offer_product'] = intval( $data['offer_product'] ); + $data['offer_type'] = sanitize_text_field( $data['offer_type'] ); + $data['offer_amount'] = sanitize_text_field( $data['offer_amount'] ); + $data['box_border_style'] = sanitize_text_field( $data['box_border_style'] ); + $data['box_border_color'] = sanitize_text_field( $data['box_border_color'] ); + $data['box_top_margin'] = sanitize_text_field( $data['box_top_margin'] ); + $data['box_bottom_margin'] = sanitize_text_field( $data['box_bottom_margin'] ); + $data['discount_background_color'] = sanitize_text_field( $data['discount_background_color'] ); + $data['discount_text_color'] = sanitize_text_field( $data['discount_text_color'] ); + $data['discount_font_size'] = sanitize_text_field( $data['discount_font_size'] ); + $data['product_description_text_color'] = sanitize_text_field( $data['product_description_text_color'] ); + $data['product_description_font_size'] = sanitize_text_field( $data['product_description_font_size'] ); + $data['accept_offer_background_color'] = sanitize_text_field( $data['accept_offer_background_color'] ); + $data['accept_offer_text_color'] = sanitize_text_field( $data['accept_offer_text_color'] ); + $data['accept_offer_font_size'] = sanitize_text_field( $data['accept_offer_font_size'] ); + $data['offer_description_background_color'] = sanitize_text_field( $data['offer_description_background_color'] ); + $data['offer_description_text_color'] = sanitize_text_field( $data['offer_description_text_color'] ); + $data['offer_description_font_size'] = sanitize_text_field( $data['offer_description_font_size'] ); + $data['offer_image_url'] = esc_url_raw( $data['offer_image_url'] ); + $data['offer_product_title'] = sanitize_text_field( $data['offer_product_title'] ); + $data['offer_product_id'] = intval( $data['offer_product_id'] ); + $data['offer_discount_title'] = sanitize_text_field( $data['offer_discount_title'] ); + $data['offer_fixed_price_title'] = sanitize_text_field( $data['offer_fixed_price_title'] ); + $data['product_description'] = sanitize_text_field( $data['product_description'] ); + $data['selection_title'] = sanitize_text_field( $data['selection_title'] ); + $data['offer_description'] = sanitize_text_field( $data['offer_description'] ); + $data['offer_product_regular_price'] = sanitize_text_field( $data['offer_product_regular_price'] ); + + return $data; + } +} diff --git a/Includes/Modules/FrequentlyBought/Includes/EnqueueScript.php b/Includes/Modules/FrequentlyBought/Includes/EnqueueScript.php new file mode 100644 index 00000000..e2bb612c --- /dev/null +++ b/Includes/Modules/FrequentlyBought/Includes/EnqueueScript.php @@ -0,0 +1,296 @@ + $this->prodcut_list(), + 'product_list_for_view' => $this->prodcut_list_for_view(), + 'category_list' => $this->category_list(), + 'order_bump_list' => $this->order_bump_list(), + ) + ); + + wp_localize_script( + 'sgsb-frequently-bought-settings', + 'bump_save_url', + array( + 'ajax_url' => admin_url( 'admin-ajax.php' ), + 'ajd_nonce' => $ajd_nonce, + 'image_folder' => sgsb_modules_url( 'FrequentlyBought/assets/images' ), + ) + ); + } + } + + /** + * Add CSS scripts to admin. + */ + public function admin_enqueue_styles() { + $ftime = filemtime( sgsb_modules_path( 'FrequentlyBought/assets/css/frequently-bought-custom-admin.css' ) ); + $ftime_template = filemtime( sgsb_modules_path( 'FrequentlyBought/assets/css/frequently-bought-template.css' ) ); + + wp_enqueue_style( + 'sgsb-frequently-bought-custom-admin-css', + sgsb_modules_url( 'FrequentlyBought/assets/css/frequently-bought-custom-admin.css' ), + null, + $ftime + ); + + wp_enqueue_style( + 'sgsb-frequently-bought-template-css', + sgsb_modules_url( 'FrequentlyBought/assets/css/frequently-bought-template.css' ), + null, + $ftime_template + ); + } + + /** + * Style for frontend. + */ + public function front_styles() { + if ( ! is_checkout() ) { + return; + } + + $ftime = filemtime( sgsb_modules_path( 'FrequentlyBought/assets/css/frequently-bought-front.css' ) ); + + wp_enqueue_style( + 'sgsb-frequently-bought-front-css', + sgsb_modules_url( 'FrequentlyBought/assets/css/frequently-bought-front.css' ), + null, + $ftime + ); + } + + /** + * Script for frontend. + */ + public function front_scripts() { + $ftime = filemtime( sgsb_modules_path( 'FrequentlyBought/assets/js/frequently-bought-custom.js' ) ); + + wp_enqueue_script( + 'sgsb-frequently-bought-front-js', + sgsb_modules_url( 'FrequentlyBought/assets/js/frequently-bought-custom.js' ), + 'jquery', + $ftime, + true + ); + + $action = 'ajd_protected'; + $ajd_nonce = wp_create_nonce( $action ); + wp_localize_script( + 'sgsb-frequently-bought-front-js', + 'bump_save_url', + array( + 'ajax_url_for_front' => admin_url( 'admin-ajax.php' ), + 'ajd_nonce' => $ajd_nonce, + ) + ); + } + + /** + * Product list. + */ + public function prodcut_list() { + $args = array( + 'post_type' => 'product', + 'posts_per_page' => -1, + 'tax_query' => array( + array( + 'taxonomy' => 'product_type', + 'field' => 'slug', + 'terms' => 'external', + 'operator' => 'NOT IN', + ), + ), + ); + + $products = get_posts( $args ); + + $product_list_for_select = array(); + $product_title_by_id = array(); + $simple_product_for_offer = array(); + + foreach ( $products as $product ) { + // Get the product category IDs. + $category_ids = wp_get_post_terms( $product->ID, 'product_cat', array( 'fields' => 'ids' ) ); + $product_list_for_select[] = array( + 'value' => $product->ID, + 'label' => $product->post_title, + 'catIds' => $category_ids, + ); + + $_product = wc_get_product( $product->ID ); + $sale_price = $_product->get_sale_price(); + $regular_price = $_product->get_regular_price(); + + // Prepare woocommerce price data. + $price = ! empty( $sale_price ) ? esc_html( $sale_price ) : esc_html( $regular_price ); + $price = wp_strip_all_tags( html_entity_decode( wc_price( $price ) ) ); + + // Render woocommerce price with currency symbol. + $_product_price = ' (' . $price . ')'; + $currency_symbol = wp_strip_all_tags( html_entity_decode( get_woocommerce_currency_symbol() ) ); + + // Offer product categories. + // Collect offer product categories. + $product_categories = wp_get_post_terms( $product->ID, 'product_cat' ); + + $category_names = array(); + foreach ( $product_categories as $category ) { + $category_names[] = $category->name; + } + + // Get categories csv. + $category_names = implode( ', ', $category_names ); + + if ( $regular_price ) { + $simple_product_for_offer[] = array( + 'price' => $price, + 'value' => $product->ID, + 'currency' => $currency_symbol, + 'offer_categories' => $category_names, + 'label' => $product->post_title . $_product_price, + ); + } + + $product_title_by_id[ $product->ID ] = $product->post_title; + } + + $product_info['productListForSelect'] = $product_list_for_select; + $product_info['simpleProductForOffer'] = $simple_product_for_offer; + $product_info['productTitleById'] = $product_title_by_id; + + return $product_info; + } + + /** + * Product list for view. + */ + public function prodcut_list_for_view() { + $args = array( + 'post_type' => 'product', + 'posts_per_page' => -1, + ); + $products = get_posts( $args ); + $product_list_for_view = array(); + + foreach ( $products as $product ) { + $_product = wc_get_product( $product->ID ); + $product->regular_price = $_product->get_regular_price(); + $product->image_url = wp_get_attachment_url( get_post_thumbnail_id( $product->ID ), 'thumbnail' ); + $product_list_for_view[ $product->ID ] = $product; + } + + return $product_list_for_view; + } + + /** + * Category list. + */ + public function category_list() { + $orderby = 'name'; + $order = 'asc'; + $hide_empty = false; + $cat_args = array( + 'orderby' => $orderby, + 'order' => $order, + 'hide_empty' => $hide_empty, + ); + + $product_categories = get_terms( 'product_cat', $cat_args ); + $category_list = array(); + $cat_name_by_bd = array(); + + foreach ( $product_categories as $key => $category ) { + $category_list[] = array( + 'value' => $category->term_id, + 'label' => $category->name, + ); + + $cat_name_by_id[ $category->term_id ] = $category->name; + } + + $catergory_info['catForSelect'] = $category_list; + $catergory_info['catNameById'] = $cat_name_by_id; + + return $catergory_info; + } + + /** + * Order bump list. + */ + public function order_bump_list() { + $args_bump = array( + 'post_type' => 'sgsb_order_bump', + 'posts_per_page' => -1, + ); + $bump_list = get_posts( $args_bump ); + $bumps = array(); + + foreach ( $bump_list as $bump ) { + if ( 'object' === gettype( json_decode( $bump->post_excerpt ) ) ) { + $bumps[ $bump->ID ] = json_decode( $bump->post_excerpt ); + } + } + + return $bumps; + } +} diff --git a/Includes/Modules/FrequentlyBought/Includes/FrequentlyBought.php b/Includes/Modules/FrequentlyBought/Includes/FrequentlyBought.php new file mode 100644 index 00000000..0cc382db --- /dev/null +++ b/Includes/Modules/FrequentlyBought/Includes/FrequentlyBought.php @@ -0,0 +1,124 @@ +cart->get_cart(); + $all_cart_product_ids = array(); + $all_cart_category_ids = array(); + $args_bump = array( + 'post_type' => 'sgsb_order_bump', + 'posts_per_page' => -1, + ); + $bump_list = get_posts( $args_bump ); + + foreach ( $all_cart_products as $value ) { + $cat_ids = $value['data']->get_category_ids(); + foreach ( $cat_ids as $cat_id ) { + $all_cart_category_ids[] = $cat_id; + } + $all_cart_product_ids[] = $value['product_id']; + } + + foreach ( $bump_list as $bump ) { + $bump_info = maybe_unserialize( $bump->post_excerpt ); + $bump_info = (object) $bump_info; + $offer_product_id = $bump_info->offer_product; + $offer_type = $bump_info->offer_type; + $offer_amount = $bump_info->offer_amount; + + $checked = ''; + if ( in_array( (int) $offer_product_id, $all_cart_product_ids, true ) ) { + $checked = 'checked'; + } + + $_product = wc_get_product( $offer_product_id ); + $regular_price = $_product->get_regular_price(); + if ( 'discount' === $offer_type ) { + $offer_price = ( $regular_price - ( $regular_price * $offer_amount / 100 ) ); + } else { + $offer_price = $offer_amount; + } + + $cart = WC()->cart; + $product_already_added_from_shop = false; + foreach ( $cart->get_cart() as $cart_item_key => $cart_item ) { + $product = $cart_item['data']; + $product_id = $product->get_id(); + if ( absint( $product_id ) !== absint( $offer_product_id ) ) { + continue; + } + $price = $product->get_price(); + if ( floatval( $price ) !== floatval( $offer_price ) ) { + $product_already_added_from_shop = true; + } + break; + } + if ( $product_already_added_from_shop ) { + // don't show the offer if the 'offer product' is already added in the cart from the shop page with regular price. + continue; + } + + $bump_type = ! empty( $bump_info->bump_type ) ? esc_html( $bump_info->bump_type ) : 'products'; + if ( $bump_type === 'products' ) { + $target_products = ! empty( $bump_info->target_products ) ? wc_clean( $bump_info->target_products ) : array(); + if ( $target_products && ( count( $all_cart_product_ids ) !== count( array_diff( $all_cart_product_ids, $target_products ) ) ) ) { + include __DIR__ . '/../templates/bump-product-front-view.php'; + } + } else { + $target_categories = ! empty( $bump_info->target_categories ) ? wc_clean( $bump_info->target_categories ) : array(); + if ( $target_categories && ( count( $all_cart_category_ids ) !== count( array_diff( $all_cart_category_ids, $target_categories ) ) ) ) { + include __DIR__ . '/../templates/bump-product-front-view.php'; + } + } + } + } + + + /** + * Product custom price. + * + * @param object $cart_object is all product of cart. + */ + public function woocommerce_custom_price_to_cart_item( $cart_object ) { + if ( ! WC()->session->__isset( 'reload_checkout' ) ) { + foreach ( $cart_object->cart_contents as $key => $value ) { + if ( isset( $value['custom_price'] ) ) { + $value['data']->set_price( $value['custom_price'] ); + } + } + } + } +} diff --git a/Includes/Modules/FrequentlyBought/assets/css/frequently-bought-custom-admin.css b/Includes/Modules/FrequentlyBought/assets/css/frequently-bought-custom-admin.css new file mode 100644 index 00000000..4201ac47 --- /dev/null +++ b/Includes/Modules/FrequentlyBought/assets/css/frequently-bought-custom-admin.css @@ -0,0 +1,147 @@ + +.tab-pan-wrapper .ant-tabs-nav{ + background: #d9ede1; + padding: 0 15px; + color: black; +} + + +.order-bump-offer{ + font-size: 16px; + background: #7979F5; + height: 50px; + font-size: 20px; +} + +.order-bump-appearance{ + font-size: 16px; + background: #7979F5; + height: 50px; + font-size: 20px; + margin-bottom: 10px; +} + +.order-bump-offer-text, .order-bump-appearance-text{ + font-size: 27px; +} + +.order-bump-offer-section{ + background: #EEEEEE; + height: 35px; + margin-top: 10px; + margin-bottom: 10px; + padding-left: 5px; + padding-top: 5px; +} + +.order-bump-offer-section-text{ + font-size: 16px; + color: #9b9bf5; +} +.order-bump-save-change-button{ + background: #7979F5; + border: 1px solid #7979F5; + margin-top: 20px; +} + + +/* Start Order Bump List Table Styles. */ +.create-upsell-order-bump-button { + border: 0; + width: 126px; + height: auto; + font-size: 15px; + font-weight: 500; + padding: 8px 24px; + line-height: 20px; + background-color: #CCE3FF; + border-radius: 5px !important; +} + +.create-upsell-order-bump-button, +.create-upsell-order-bump-button:hover { + color: #073B4C !important; +} + +.upsell-order-list-table .ant-table-container, +.upsell-order-list-table .ant-table-placeholder .ant-table-cell { + /*border-radius: 5px;*/ + border: 0 !important; +} + +.upsell-order-list-table .ant-table.ant-table-bordered { + overflow: hidden; + border-radius: 10px; + border: 1px solid #DDE5F9; +} + +.upsell-order-list-table .ant-table .ant-table-thead .ant-table-cell:last-child { + border-inline-end: 0 !important; +} + +/*.upsell-order-list-table .ant-table .ant-table-body .ant-table-cell {*/ +/* border: 0;*/ +/*}*/ + +.upsell-order-list-table .ant-table-thead .ant-table-cell { + color: #073B4C; + font-size: 16px; + background: #FFF; + font-weight: 500; + padding: 13px 20px; +} + +.upsell-order-list-table .ant-table-tbody .ant-table-row, +.upsell-order-list-table .ant-table-tbody .ant-table-placeholder { + background: #f7f9ff !important; +} + +.upsell-order-list-table .ant-table-tbody .ant-table-row .ant-table-cell { + color: #073B4C; + font-size: 14px; + font-weight: 500; + padding: 18px 16px; + vertical-align: top; +} + +.upsell-order-list-table .ant-table-tbody .ant-table-row .ant-table-cell .table-products, +.upsell-order-list-table .ant-table-tbody .ant-table-row .ant-table-cell .table-categories { + gap: 8px; + display: flex; + flex-wrap: wrap; + margin-top: 8px; +} + +.upsell-order-list-table .ant-table-tbody .ant-table-row .ant-table-cell .category-pills, +.upsell-order-list-table .ant-table-tbody .ant-table-row .ant-table-cell .product-pills { + border-radius: 4px; + padding: 2px 12px; + font-weight: 400; +} + +.upsell-order-list-table .ant-table-tbody .ant-table-row .ant-table-cell .product-pills { + color: #008DFF; + background: #EBF6FF; +} + +.upsell-order-list-table .ant-table-tbody .ant-table-row .ant-table-cell .category-pills { + color: #32DBBE; + background: #EEFCF9; +} + +.upsell-order-list-table .ant-table-tbody .ant-table-row .ant-table-cell:last-child { + border-right: 0 !important; +} + +.upsell-order-list-table .ant-table-tbody .ant-table-row:last-child td.ant-table-cell { + border: 0; +} + +.upsell-order-list-table .ant-table-container, +.upsell-order-list-table .ant-table-content .ant-table-cell { + border-color: #DDE6F9 !important; +} + +/* End Order Bump List Table Styles. */ + + diff --git a/Includes/Modules/FrequentlyBought/assets/css/frequently-bought-front.css b/Includes/Modules/FrequentlyBought/assets/css/frequently-bought-front.css new file mode 100644 index 00000000..252ae41e --- /dev/null +++ b/Includes/Modules/FrequentlyBought/assets/css/frequently-bought-front.css @@ -0,0 +1,154 @@ +.extra_product_design{ + background: rgb(185, 178, 241); + width: 50%; + padding: 5px; + border-radius: 3px; + color:black; +} + +.order-bump-product{ + width: 80%; + margin: auto; + margin-bottom: 5px; +} +.order-bump-product-checkbox{ + background: rgb(235, 182, 85); + padding: 6px; + color:black; + clear: both; + margin-bottom: 10px; +} + +.bump-product-image{ + width: 30%; + clear: left; + float: left; +} + +.bump-product-description{ + margin-left: -5%; + width: 65%; + float: left; + padding: 0%; + margin-top: -18px; +} + +.bump-content{ + font-size: 14px; + margin-top: -20px; +} +.bump-product-discount-offer{ + margin-top: 12px; + font-size: 14px; + color: red; + font-weight: bold; +} + +/* overview from admin */ + +.template-overview-area{ + width: 100%; + float: left; +} + +.offer-overview-top-text{ + width: 100%; + height: 45px; + background: rgb(146, 218, 146); + padding: 10px; + font-size: 16px; + font-weight: bold; + margin-bottom: 20px; +} + +.offer-main-wrap{ + width: 100%; + padding: 15px; + background: #fff; + border-radius: 15px; + margin-bottom: 48px; +} + +.offer-main-wrap .dynamic-offer-text{ + gap: 4px; + max-width: 100%; + display: flex; + min-height: 33px; + font-weight: bold; + text-align: center; + padding: 15px 75px; + border-radius: 10px; + align-items: center; + margin-bottom: 15px; + justify-content: center; +} + +.offer-main-wrap .product-image-and-title{ + width: 100%; + display: flex; + flex-wrap: wrap; + align-items: center; + line-height: normal; + justify-content: space-between; +} + +.offer-main-wrap .product-image-and-title .offer-product-image-title{ + display: flex; + align-items: center; + gap: 15px; + flex: 3; +} + +.offer-main-wrap .product-image-and-title .product-checkbox-and-excitement-message{ + flex: .8; + gap: 10px; + display: flex; + cursor: pointer; + font-size: 15px; + align-items: center; +} + +.offer-main-wrap .product-image-and-title .offer-price{ + flex: 1.5; + font-weight: 800; +} + +.offer-main-wrap .product-image-and-title .offer-product-image img{ + max-width: 100%; + border-radius: 10px; +} + +.offer-main-wrap .product-image-and-title .offer-product-title{ + gap: 15px; + padding: 5px; + display: grid; +} + +.offer-main-wrap .product-image-and-title .offer-product-title h3{ + margin: 0; + font-weight: bold; +} + +.offer-main-wrap .product-image-and-title .offer-product-title p { + margin: 0; + font-size: 12px; + font-weight: 500; +} + +.offer-main-wrap .product-checkbox-and-excitement-message .custom-checkbox{ + height: 15px; + width: 15px; + cursor: pointer; +} + +.offer-main-wrap .product-checkbox-and-excitement-message label { + cursor: pointer; +} + +.offer-main-wrap .product-description{ + width: 98%; + height: 200px; + background: rgba(48, 33, 182, 0.678); + margin-bottom: 20px; + padding: 5px; +} diff --git a/Includes/Modules/FrequentlyBought/assets/css/frequently-bought-template.css b/Includes/Modules/FrequentlyBought/assets/css/frequently-bought-template.css new file mode 100644 index 00000000..6893b16b --- /dev/null +++ b/Includes/Modules/FrequentlyBought/assets/css/frequently-bought-template.css @@ -0,0 +1,157 @@ +.bump-template-main-wrapper{ + width: 100%; + /* height: 900px; */ + /* background: rgb(185, 179, 190); */ + margin-bottom: 30px; + /* padding: 0 20px ; */ +} + +.bump-template-main-wrapper .demo-template-design-change-area{ + width: 55%; + float: left; +} + +.bump-template-main-wrapper .template-overview-area{ + width: 45%; + min-height: 800px; + float: left; + padding: 10 20px ; +} + +.offer-overview-top-text{ + width: 100%; + height: 45px; + background: rgb(146, 218, 146); + padding: 10px; + font-size: 16px; + font-weight: bold; + margin-bottom: 20px; +} + +.offer-main-wrap{ + width: 100%; + height: 600px; + padding: 10px; +} + +.offer-main-wrap .dynamic-offer-text{ + gap: 4px; + width: 100%; + display: flex; + min-height: 30px; + font-weight: bold; + align-items: center; + margin-bottom: 15px; + justify-content: center; +} + +.offer-main-wrap .product-image-and-title{ + width: 100%; + min-height: 160px; + margin-bottom: 20px; + padding: 5px; +} + +.offer-main-wrap .product-image-and-title .offer-product-image{ + background: rgb(247, 242, 242); + min-height: 120px; + width: 30%; + padding: 5px; + float: left; + +} + +.offer-main-wrap .product-image-and-title .offer-product-title{ + min-height: 150px; + width: 70%; + padding: 5px; + margin-bottom: 10px; + float: left; +} + +.offer-main-wrap .product-checkbox-and-excitement-message{ + width: 100%; + min-height: 35px; + margin-bottom: 20px; + padding: 5px; + font-size: 15px; + margin-top: 20px; + clear: both; + display: flex; + align-items: center; + gap: 5px; +} + +.offer-main-wrap .product-checkbox-and-excitement-message .custom-checkbox{ + height: 25px; + width: 25px; +} + +.offer-main-wrap .product-description{ + width: 100%; + height: 200px; + background: rgba(48, 33, 182, 0.678); + margin-bottom: 20px; + padding: 5px; +} + +/* Teamplate design change area */ +.demo-template-design-change-area .offer-offer-box-text{ + width: 98%; + height: 25px; + background: rgb(236, 233, 233); + padding-left: 5px; + font-size: 15px; + font-weight: bold; + margin-bottom: 10px; + border-radius: 5px; +} + +.form-input-distance{ + margin-top: -20px; +} + +.offer-discount-title::before{ + content: 'For Discount %dfgdfgdfg'; +} + +.App { + font-family: sans-serif; + padding: 20px; + } + + h2 { + margin-top: 40px; + } + + .rc-color-picker-panel { + border: 1px solid #ccc; + } + .rc-color-picker-panel-inner { + border: none; + box-shadow: none; + } + .rc-color-picker-panel-board-hsv { + border-radius: 12px; + outline: none; + } + .rc-color-picker-panel-board-value { + border: none; + border-radius: 12px; + } + .rc-color-picker-panel-board-saturation { + border: none; + border-radius: 12px; + } + .rc-color-picker-panel-ribbon { + border-radius: 12px; + } + .rc-color-picker-panel-wrap-preview { + border-radius: 12px; + } + .rc-color-picker-panel-preview span { + border-radius: 12px; + } + .rc-color-picker-panel-preview input { + border-radius: 12px; + } diff --git a/Includes/Modules/FrequentlyBought/assets/images/bump-preview.svg b/Includes/Modules/FrequentlyBought/assets/images/bump-preview.svg new file mode 100644 index 00000000..7d65a651 --- /dev/null +++ b/Includes/Modules/FrequentlyBought/assets/images/bump-preview.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/Includes/Modules/FrequentlyBought/assets/images/upsell-order-bump-module-img.webp b/Includes/Modules/FrequentlyBought/assets/images/upsell-order-bump-module-img.webp new file mode 100644 index 0000000000000000000000000000000000000000..e9704306eff40d122a93ae81809934abdd640082 GIT binary patch literal 34968 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 literal 0 HcmV?d00001 diff --git a/Includes/Modules/FrequentlyBought/assets/images/upsell-order-bump.svg b/Includes/Modules/FrequentlyBought/assets/images/upsell-order-bump.svg new file mode 100644 index 00000000..abb27fdf --- /dev/null +++ b/Includes/Modules/FrequentlyBought/assets/images/upsell-order-bump.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/Includes/Modules/FrequentlyBought/assets/js/frequently-bought-admin-custom.js b/Includes/Modules/FrequentlyBought/assets/js/frequently-bought-admin-custom.js new file mode 100644 index 00000000..9bb15ac1 --- /dev/null +++ b/Includes/Modules/FrequentlyBought/assets/js/frequently-bought-admin-custom.js @@ -0,0 +1,4 @@ +;(function($){ + $('#_custom_product_text_field').select2(); + $('#_discount_rate_for_bump_product').select2(); +})(jQuery); diff --git a/Includes/Modules/FrequentlyBought/assets/js/frequently-bought-custom.js b/Includes/Modules/FrequentlyBought/assets/js/frequently-bought-custom.js new file mode 100644 index 00000000..c1c76057 --- /dev/null +++ b/Includes/Modules/FrequentlyBought/assets/js/frequently-bought-custom.js @@ -0,0 +1,17 @@ +function extraProducts(product_id, check_status, offer_price) { + + var $ = jQuery; + var passData = { + offer_product_id : product_id, + checked : check_status, + bump_price : offer_price + + }; + $.post(bump_save_url.ajax_url_for_front, { + 'action' : 'offer_product_add_to_cart', + 'data' : passData, + '_ajax_nonce' : bump_save_url.ajd_nonce + }, function (data) { + location.reload(); + }); +} \ No newline at end of file diff --git a/Includes/Modules/FrequentlyBought/assets/package.json b/Includes/Modules/FrequentlyBought/assets/package.json new file mode 100644 index 00000000..ca0ce02c --- /dev/null +++ b/Includes/Modules/FrequentlyBought/assets/package.json @@ -0,0 +1,21 @@ +{ + "name": "sales-booster-frequently-bought", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "start": "wp-scripts start src/settings.js", + "build": "wp-scripts build src/settings.js" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "@wordpress/scripts": "^23.0.0" + }, + "dependencies": { + "antd": "^5.5.0", + "nanoid": "^3.3.4", + "@wordpress/i18n": "^4.24.0", + "react-input-color": "^4.0.0" + } +} diff --git a/Includes/Modules/FrequentlyBought/assets/src/components/BasicInfo.js b/Includes/Modules/FrequentlyBought/assets/src/components/BasicInfo.js new file mode 100644 index 00000000..46fc6fde --- /dev/null +++ b/Includes/Modules/FrequentlyBought/assets/src/components/BasicInfo.js @@ -0,0 +1,232 @@ +import { __ } from "@wordpress/i18n"; +import { + Col, + notification, +} from "antd"; +import { useDispatch, useSelect } from "@wordpress/data"; +import { Fragment } from "react"; +import TextInput from "sales-booster/src/components/settings/Panels/PanelSettings/Fields/TextInput"; +import SettingsSection from "sales-booster/src/components/settings/Panels/PanelSettings/SettingsSection"; +import MultiSelectBox from "sales-booster/src/components/settings/Panels/PanelSettings/Fields/MultiSelectBox"; +import SectionHeader from "sales-booster/src/components/settings/Panels/SectionHeader"; +import SelectBox from "sales-booster/src/components/settings/Panels/PanelSettings/Fields/SelectBox"; +import OfferField from "./OfferField"; +import TextRadioBox from "sales-booster/src/components/settings/Panels/PanelSettings/Fields/TextRadioBox"; + + +const BasicInfo = ({ clearErrors }) => { + const { setCreateFromData } = useDispatch("sgsb_order_bump"); + const { createBumpData } = useSelect((select) => ({ + createBumpData: select("sgsb_order_bump").getCreateFromData(), + })); + + const offerProductId = parseInt(createBumpData?.offer_product); + const originalProductListForSelect = + products_and_categories.product_list.productListForSelect; + const productListForSelect = offerProductId + ? originalProductListForSelect.filter( + (item) => item.value !== offerProductId + ) + : originalProductListForSelect; + + const targetProducts = createBumpData.target_products; + const originalSimpleProductForOffer = + products_and_categories.product_list.simpleProductForOffer; + const simpleProductForOffer = + Array.isArray(targetProducts) && targetProducts.length !== 0 + ? originalSimpleProductForOffer.filter( + (item) => !targetProducts.includes(item.value) + ) + : originalSimpleProductForOffer; + const bumpSchedules = [ + { value: "daily", label: __("Daily", "storegrowth-sales-booster") }, + { value: "saturday", label: __("Saturday", "storegrowth-sales-booster") }, + { value: "sunday", label: __("Sunday", "storegrowth-sales-booster") }, + { value: "monday", label: __("Monday", "storegrowth-sales-booster") }, + { value: "tuesday", label: __("Tuesday", "storegrowth-sales-booster") }, + { value: "wednesday", label: __("Wednesday", "storegrowth-sales-booster") }, + { value: "thursday", label: __("Thursday", "storegrowth-sales-booster") }, + { value: "friday", label: __("Friday", "storegrowth-sales-booster") }, + ]; + + const offerOptions = [ + { value: "discount", label: __("Discount%", "storegrowth-sales-booster") }, + { value: "price", label: __("Price", "storegrowth-sales-booster") }, + ]; + + const filterByValue = (data, key) => { + const item = data.find((item) => item.value === key); + return item ? item.label : "Value not found"; + }; + + const onFieldChange = (key, value) => { + clearErrors(); + // Handle offer amount validation with actual price. + if (key === "offer_amount") { + const product = simpleProductForOffer.find( + (item) => item?.value === offerProductId + ); + const currencySymbol = product?.currency; + const productPrice = product?.price?.replace(new RegExp('[' + currencySymbol + ',]', 'g'), ''); + if ( + createBumpData.offer_type === "price" && + parseFloat(productPrice) < value + ) { + return notification["error"]({ + message: __( + "Offer price can't be greater than product price!", + "storegrowth-sales-booster" + ), + }); + } + + if (createBumpData.offer_type === "discount" && value > 100) { + return notification["error"]({ + message: __( + "Discount offer can't be greater than 100 percent!", + "storegrowth-sales-booster" + ), + }); + } + } + + if (key === "offer_product") { + setCreateFromData({ + ...createBumpData, + [key]: value, + offer_image_url: + products_and_categories.product_list_for_view[value].image_url, + offer_product_title: + products_and_categories.product_list_for_view[value].post_title, + offer_product_regular_price: + products_and_categories.product_list_for_view[value].regular_price, + }); + } else { + setCreateFromData({ + ...createBumpData, + [key]: value, + }); + } + }; + + const orderBumpDeal = [ + { key: 'products', value: __('Products', 'storegrowth-sales-booster') }, + { key: 'categories', value: __('Categories', 'storegrowth-sales-booster') }, + ]; + + return ( + + + + + {createBumpData?.bump_type !== "categories" ? ( + + ) : ( + + )} + + + + + + option?.children?.[0] + ?.toString() + ?.toLowerCase() + ?.includes(inputValue.toLowerCase()) + } + /> + + + + + + ); +}; + +export default BasicInfo; diff --git a/Includes/Modules/FrequentlyBought/assets/src/components/CreateBump.js b/Includes/Modules/FrequentlyBought/assets/src/components/CreateBump.js new file mode 100644 index 00000000..6daac8ac --- /dev/null +++ b/Includes/Modules/FrequentlyBought/assets/src/components/CreateBump.js @@ -0,0 +1,328 @@ +import { __ } from '@wordpress/i18n'; +import { Form, notification } from 'antd'; +import { useDispatch, useSelect } from '@wordpress/data'; +import { useEffect,useState } from '@wordpress/element'; +import { convertBumpItemHtmlEntitiesToTexts, convertBumpItemTextDatasToHtmlEntities } from '../helper'; +import BasicInfo from './BasicInfo'; +import PanelPreview from "sales-booster/src/components/settings/Panels/PanelPreview"; +import PanelRow from "sales-booster/src/components/settings/Panels/PanelRow"; +import PanelSettings from "sales-booster/src/components/settings/Panels/PanelSettings"; +import DesignSection from "./DesignSection"; +import Preview from "./Preview"; +import { createBumpForm } from "../helper"; +import ActionsHandler from "sales-booster/src/components/settings/Panels/PanelSettings/ActionsHandler"; +import OverViewArea from "./appearance/template/overview-area/OverViewArea"; +import TouchPreview from "sales-booster/src/components/settings/Panels/TouchPreview"; + +function CreateBump({navigate, useParams, useSearchParams}) { + const [allBumpsData, setallBumpsData] = useState([]); + const [duplicateDataError, setDuplicateDataError] = useState({}); + const [isModalVisible, setIsModalVisible] = useState(false); + const { setPageLoading } = useDispatch( 'sgsb' ); + const [buttonLoading, setButtonLoading] = useState(false); + const { setCreateFromData, resetCreateFromData } = useDispatch( 'sgsb_order_bump' ); + let {bump_id,action_name} = useParams(); + + const { bumpData, createBumpData } = useSelect( ( select ) => ({ + createBumpData: select('sgsb_order_bump').getCreateFromData(), + bumpData: wp.data.select('sgsb_order_bump').getBumpData() + })); + useEffect(() => { + if(!bumpData?.length > 0){ + setPageLoading( true ); + jQuery.post( bump_save_url.ajax_url, { + 'action': 'bump_list', + 'data': [], + '_ajax_nonce': bump_save_url.ajd_nonce + }, function ( bumpDataFromAjax ) { + setPageLoading( false ); + const bumpDataParsed = bumpDataFromAjax.data.map(bumpItem => convertBumpItemHtmlEntitiesToTexts(bumpItem)); + setallBumpsData( bumpDataParsed ); + } ); + }else{ + setallBumpsData( bumpData ); + } + }, []) + + + const showModal = () => { + setIsModalVisible(true); + }; + + const handleOk = () => { + setIsModalVisible(false); + }; + + const handleCancel = () => { + setIsModalVisible(false); + }; + + const changeTab = ( key ) => { + navigate( "/upsell-order-bump/create-bump?tab_name=" + key ); + }; + + if( action_name == 'delete' ) { + setPageLoading(true); + let $ = jQuery; + $.post( bump_save_url.ajax_url, { 'action': 'bump_delete', 'data': bump_id, '_ajax_nonce' : bump_save_url.ajd_nonce }, function ( data ) { + setPageLoading(false); + notification['error'] ( { + message: 'Order Bump deleted', + } ); + navigate("/upsell-order-bump"); + }); + } + + if( bump_id ) { + + useEffect( () => { + setPageLoading( true ); + let $ = jQuery; + $.post( bump_save_url.ajax_url, { 'action': 'bump_list', 'data': bump_id, '_ajax_nonce' : bump_save_url.ajd_nonce }, function ( data ) { + setPageLoading(false); + + const parsedBumpItem = convertBumpItemHtmlEntitiesToTexts(data.data) + setCreateFromData({ + ...createBumpData, + ...parsedBumpItem, + offer_product_id:bump_id + }); + + } ); + + + }, []); + + } else { + useEffect( () => { + resetCreateFromData(); + }, []); + } + + const layout = { + labelCol: { + span: 8, + }, + wrapperCol: { + span: 15, + }, + }; + + + + const onFormSave = () => { + + if( !createBumpData.name_of_order_bump ) { + notification['error'] ( { + message: 'Please enter name of order bump', + } ); + return null; + } + + + if ( createBumpData.bump_type === 'products' && createBumpData.target_products.length == 0 ){ + notification['error'] ( { + message: 'You have to select target products for bump products type', + } ); + + return null; + } + + if ( createBumpData.bump_type === 'categories' && createBumpData.target_categories.length == 0 ){ + notification['error'] ( { + message: 'You have to select target categories for bump categories type', + } ); + + return null; + } + + if ( createBumpData.bump_schedule.length == 0 ){ + notification['error'] ( { + message: 'Please select bump schedule', + } ); + + return null; + + } + + if ( !createBumpData.offer_product ) { + notification['error'] ( { + message: 'Please select offer product', + } ); + + return null; + + } + + if( createBumpData.offer_type.length ==0 ) { + notification['error'] ( { + message: 'Please select offer type', + } ); + + return null; + + } + + if(!createBumpData.offer_amount ){ + notification['error'] ( { + message: 'Please select offer amount', + } ); + + return null; + + } + + const isEditingExistingBumpItem = typeof bump_id == "string" && + !isNaN(bump_id) && // use type coercion to parse the _entirety_ of the string (`parseFloat` alone does not do this) + !isNaN(parseFloat(bump_id)) // if bump_id is just whitespaces then fail + const intBumpId = isEditingExistingBumpItem && parseInt(bump_id); + const filteredBumpsData = isEditingExistingBumpItem ? allBumpsData.filter(item => item.id !== intBumpId) : allBumpsData; + + const duplicateErrs = { + duplicateTargetCats: [], + duplicateTargetProducts: [], + } + + const newOfferProduct = createBumpData.offer_product; + const newTargetCats = createBumpData.target_categories; + const newTargetProducts = createBumpData.target_products; + const newTargetSchedules = createBumpData.bump_schedule; + + for (const bumpItem of filteredBumpsData) { + if( parseInt( bumpItem.offer_product ) !== parseInt( newOfferProduct ) ){ + continue; + } + let isSameScheduleExist = false; + for (const newScheduleItem of newTargetSchedules) { + if(bumpItem.bump_schedule.includes(newScheduleItem)){ + isSameScheduleExist = true; + break; + } + } + if(!isSameScheduleExist){ + continue; + } + for (const newCatItem of newTargetCats) { + if(bumpItem.target_categories.includes(newCatItem)){ + duplicateErrs.duplicateTargetCats.push(newCatItem); + break; + } + } + for (const newProductItem of newTargetProducts) { + if(bumpItem.target_products.includes(newProductItem)){ + duplicateErrs.duplicateTargetProducts.push(newProductItem); + break; + } + } + if( ( duplicateErrs.duplicateTargetCats.length > 0 || duplicateErrs.duplicateTargetProducts.length > 0 ) ){ + if ( window.location.hash === '#/upsell-order-bump/create-bump' ) { + setDuplicateDataError(duplicateErrs); + return false; + } + } + } + + // Check if bump order not duplicate then saved. + if ( ! ( isDuplicateCatsFound || isDuplicateProductsFound ) ) { + setButtonLoading( true ); + const bumpDataParsedToEntities = convertBumpItemTextDatasToHtmlEntities(createBumpData); + let $ = jQuery; + $.post( bump_save_url.ajax_url, { + 'action' : 'bump_create', + 'data' : bumpDataParsedToEntities, + '_ajax_nonce' : bump_save_url.ajd_nonce + }, function ( data ) { + setCreateFromData( { + ...bumpDataParsedToEntities, + offer_product_id: data + } ); + setButtonLoading( false ); + + notification['success']({ + message : 'Order Bump Creation', + description : 'Data for order bump creation saved successfully', + }); + + navigate( "/upsell-order-bump" ); + }); + } + + } + + const onFormReset = () => { + setCreateFromData( { ...createBumpForm } ); + } + + const clearErrors = () => setDuplicateDataError({}); + const isDuplicateCatsFound = duplicateDataError?.duplicateTargetCats?.length > 0; + const isDuplicateProductsFound = duplicateDataError?.duplicateTargetProducts?.length > 0; + + + const [ searchParams, setSearchParams ] = useSearchParams(); + const tabName = searchParams.get( 'tab_name' ); + + const tabPanels = [ + { + key: 'basic', + title: __( 'Basic Information', 'storegrowth-sales-booster' ), + panel: , + }, + { + key: 'design', + title: __( 'Design', 'storegrowth-sales-booster' ), + panel: , + }, + ]; + + const excludeTabs = [ 'basic' ]; + const showPreview = ! excludeTabs?.includes( tabName ); + + return ( + <> +
+ + + { showPreview && tabName && ( + + + + ) } + + + {/* Render preview panel for responsive preview. */} + + + + + { ( isDuplicateCatsFound || isDuplicateProductsFound ) && + notification['error'] ( { + message: __( + `Error!!! another bump with the given offer product for the specified schedule already exists for the selected ${ ( isDuplicateCatsFound && isDuplicateProductsFound ) ? 'categories & products' : isDuplicateProductsFound ? 'products' : 'categories' }. Please change your inputs and then try again.`, + 'storegrowth-sales-booster' + ), + } ) + } + + + + {/**/} + {/**/} + {/* */} + {/**/} + + + ); +} + +export default CreateBump; diff --git a/Includes/Modules/FrequentlyBought/assets/src/components/CreateBumpButton.js b/Includes/Modules/FrequentlyBought/assets/src/components/CreateBumpButton.js new file mode 100644 index 00000000..2499ad4b --- /dev/null +++ b/Includes/Modules/FrequentlyBought/assets/src/components/CreateBumpButton.js @@ -0,0 +1,33 @@ +import React from "react"; +import { Button } from "antd"; +import { __ } from "@wordpress/i18n"; +import { useSelect } from "@wordpress/data"; + +const CreateBumpButton = ({ navigate }) => { + const { bumpListData } = useSelect((select) => ({ + bumpListData: select("sgsb_order_bump").getBumpData(), + })); + const hash = window.location.hash.replace(/^#/, ''); // Remove the leading '#' + const isBumpListPage = hash === 'upsell-order-bump' || hash === '/upsell-order-bump'; + const isDisableBumpCreation = bumpListData?.length >= 2 && !sgsbAdmin.isPro; + const buttonProps = { + shape: "round", + disabled: isBumpListPage ? isDisableBumpCreation : false, + className: "create-upsell-order-bump-button", + }; + + const renderButton = (label, onClick) => ( + + ); + + return ( +
+ {!isBumpListPage && renderButton(__("Bump List", "storegrowth-sales-booster"), () => navigate("upsell-order-bump"))} + {isBumpListPage && renderButton(__("Create New", "storegrowth-sales-booster"), () => navigate("upsell-order-bump/create-bump"))} +
+ ); +}; + +export default CreateBumpButton; diff --git a/Includes/Modules/FrequentlyBought/assets/src/components/DesignSection.js b/Includes/Modules/FrequentlyBought/assets/src/components/DesignSection.js new file mode 100644 index 00000000..d055cf31 --- /dev/null +++ b/Includes/Modules/FrequentlyBought/assets/src/components/DesignSection.js @@ -0,0 +1,25 @@ +import { __ } from "@wordpress/i18n"; +import ContentSection from "./appearance/ContentSection"; +import TemplateSection from "./appearance/TemplateSection"; +import ExpandPanels from "sales-booster/src/components/settings/Panels/PanelSettings/ExpandPanels"; + +const DesignSection = () => { + const panels = [ + { + key: 1, + label: __( 'Template Section', 'storegrowth-sales-booster' ), + children: , + }, + { + key: 2, + label: __( 'Content Section', 'storegrowth-sales-booster' ), + children: , + } + ]; + + return ( + + ); +} + +export default DesignSection; diff --git a/Includes/Modules/FrequentlyBought/assets/src/components/OfferField.js b/Includes/Modules/FrequentlyBought/assets/src/components/OfferField.js new file mode 100644 index 00000000..9830e25c --- /dev/null +++ b/Includes/Modules/FrequentlyBought/assets/src/components/OfferField.js @@ -0,0 +1,47 @@ +import React from "react"; +import { __ } from "@wordpress/i18n"; +import { Row, Col, Select, Card, InputNumber, Typography } from "antd"; + +import SettingsTooltip from "sales-booster/src/components/settings/Panels/PanelSettings/SettingsTooltip"; +const { Title } = Typography; + +const OfferField = ({ createBumpData, offerOptions, onFieldChange }) => { + return ( + <> + + + +
+ + {__("Offer Price/Discount", "storegrowth-sales-booster")} + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +