From 9d90ea6b3e9a17480ea34f0e6e236b5f4d666d8a Mon Sep 17 00:00:00 2001
From: Nurul Umbhiya <zikubd@gmail.com>
Date: Fri, 1 Dec 2023 11:56:41 +0600
Subject: [PATCH 1/5] new: added skeleton for product fields feature

---
 includes/Product/Manager.php       | 409 ++++++++++++++---------
 includes/ProductForm/Component.php | 328 ++++++++++++++++++
 includes/ProductForm/Elements.php  |  78 +++++
 includes/ProductForm/Factory.php   | 238 ++++++++++++++
 includes/ProductForm/Field.php     | 264 +++++++++++++++
 includes/ProductForm/Init.php      | 512 +++++++++++++++++++++++++++++
 includes/ProductForm/Section.php   |  99 ++++++
 7 files changed, 1769 insertions(+), 159 deletions(-)
 create mode 100644 includes/ProductForm/Component.php
 create mode 100644 includes/ProductForm/Elements.php
 create mode 100644 includes/ProductForm/Factory.php
 create mode 100644 includes/ProductForm/Field.php
 create mode 100644 includes/ProductForm/Init.php
 create mode 100644 includes/ProductForm/Section.php

diff --git a/includes/Product/Manager.php b/includes/Product/Manager.php
index 6fdeaf0362..cdade3e327 100644
--- a/includes/Product/Manager.php
+++ b/includes/Product/Manager.php
@@ -3,10 +3,12 @@
 namespace WeDevs\Dokan\Product;
 
 use WC_Product;
+use WC_Product_Attribute;
 use WC_Product_Download;
 use WeDevs\Dokan\Cache;
 use WP_Query;
 use WP_Error;
+use WeDevs\Dokan\ProductForm\Elements as FormElements;
 
 /**
  * Product manager Class
@@ -28,7 +30,7 @@ public function all( $args = [] ) {
         $defaults = [
             'post_type'      => 'product',
             'post_status'    => $post_statuses,
-            'posts_per_page' => - 1,
+            'posts_per_page' => -1,
             'orderby'        => 'post_date ID',
             'order'          => 'DESC',
             'paged'          => 1,
@@ -71,100 +73,99 @@ public function get( $product_id ) {
      * @return WC_Product|null|false
      */
     public function create( $args = [] ) {
-        $id = isset( $args['id'] ) ? absint( $args['id'] ) : 0;
+        $product_id = isset( $args[ FormElements::ID ] ) ? absint( $args[ FormElements::ID ] ) : 0;
+        $is_update  = ! empty( $product_id );
 
         // Type is the most important part here because we need to be using the correct class and methods.
-        if ( isset( $args['type'] ) ) {
-            $classname = \WC_Product_Factory::get_classname_from_product_type( $args['type'] );
+        if ( isset( $args[ FormElements::TYPE ] ) ) {
+            $classname = \WC_Product_Factory::get_classname_from_product_type( $args[ FormElements::TYPE ] );
 
             if ( ! class_exists( $classname ) ) {
                 $classname = 'WC_Product_Simple';
             }
 
-            $product = new $classname( $id );
-        } elseif ( isset( $args['id'] ) ) {
-            $product = wc_get_product( $id );
+            $product = new $classname( $product_id );
+        } elseif ( isset( $args[ FormElements::ID ] ) ) {
+            $product = wc_get_product( $product_id );
         } else {
             $product = new \WC_Product_Simple();
         }
 
         // Post title.
-        if ( isset( $args['name'] ) ) {
-            $product->set_name( wp_filter_post_kses( $args['name'] ) );
+        if ( isset( $args[ FormElements::NAME ] ) ) {
+            $product->set_name( wp_filter_post_kses( $args[ FormElements::NAME ] ) );
         }
 
         // Post content.
-        if ( isset( $args['description'] ) ) {
-            $product->set_description( wp_filter_post_kses( $args['description'] ) );
+        if ( isset( $args[ FormElements::DESCRIPTION ] ) ) {
+            $product->set_description( wp_filter_post_kses( $args[ FormElements::DESCRIPTION ] ) );
         }
 
         // Post excerpt.
-        if ( isset( $args['short_description'] ) ) {
-            $product->set_short_description( wp_filter_post_kses( $args['short_description'] ) );
+        if ( isset( $args[ FormElements::SHORT_DESCRIPTION ] ) ) {
+            $product->set_short_description( wp_filter_post_kses( $args[ FormElements::SHORT_DESCRIPTION ] ) );
         }
 
         // Post status.
-        if ( isset( $args['status'] ) ) {
-            $product->set_status( get_post_status_object( $args['status'] ) ? $args['status'] : 'draft' );
+        if ( isset( $args[ FormElements::STATUS ] ) ) {
+            $product->set_status( get_post_status_object( $args[ FormElements::STATUS ] ) ? $args[ FormElements::STATUS ] : 'draft' );
         }
 
         // Post slug.
-        if ( isset( $args['slug'] ) ) {
-            $product->set_slug( $args['slug'] );
+        if ( isset( $args[ FormElements::SLUG ] ) ) {
+            $product->set_slug( $args[ FormElements::SLUG ] );
         }
 
         // Menu order.
-        if ( isset( $args['menu_order'] ) ) {
-            $product->set_menu_order( $args['menu_order'] );
+        if ( isset( $args[ FormElements::MENU_ORDER ] ) ) {
+            $product->set_menu_order( $args[ FormElements::MENU_ORDER ] );
         }
 
         // Comment status.
-        if ( isset( $args['reviews_allowed'] ) ) {
-            $product->set_reviews_allowed( $args['reviews_allowed'] );
+        if ( isset( $args[ FormElements::REVIEWS_ALLOWED ] ) ) {
+            $product->set_reviews_allowed( $args[ FormElements::REVIEWS_ALLOWED ] );
         }
 
         // Virtual.
-        if ( isset( $args['virtual'] ) ) {
-            $product->set_virtual( $args['virtual'] );
+        if ( isset( $args[ FormElements::VIRTUAL ] ) ) {
+            $product->set_virtual( $args[ FormElements::VIRTUAL ] );
         }
 
         // Tax status.
-        if ( isset( $args['tax_status'] ) ) {
-            $product->set_tax_status( $args['tax_status'] );
+        if ( isset( $args[ FormElements::TAX_STATUS ] ) ) {
+            $product->set_tax_status( $args[ FormElements::TAX_STATUS ] );
         }
 
         // Tax Class.
-        if ( isset( $args['tax_class'] ) ) {
-            $product->set_tax_class( $args['tax_class'] );
+        if ( isset( $args[ FormElements::TAX_CLASS ] ) ) {
+            $product->set_tax_class( $args[ FormElements::TAX_CLASS ] );
         }
 
         // Catalog Visibility.
-        if ( isset( $args['catalog_visibility'] ) ) {
-            $product->set_catalog_visibility( $args['catalog_visibility'] );
+        if ( isset( $args[ FormElements::CATALOG_VISIBILITY ] ) ) {
+            $product->set_catalog_visibility( $args[ FormElements::CATALOG_VISIBILITY ] );
         }
 
         // Purchase Note.
-        if ( isset( $args['purchase_note'] ) ) {
-            $product->set_purchase_note( wp_kses_post( wp_unslash( $args['purchase_note'] ) ) );
+        if ( isset( $args[ FormElements::PURCHASE_NOTE ] ) ) {
+            $product->set_purchase_note( wp_kses_post( wp_unslash( $args[ FormElements::PURCHASE_NOTE ] ) ) );
         }
 
         // Featured Product.
-        if ( isset( $args['featured'] ) ) {
-            $product->set_featured( $args['featured'] );
+        if ( isset( $args[ FormElements::FEATURED ] ) ) {
+            $product->set_featured( $args[ FormElements::FEATURED ] );
         }
 
         // Shipping data.
         $product = $this->save_product_shipping_data( $product, $args );
 
         // SKU.
-        if ( isset( $args['sku'] ) ) {
-            $product->set_sku( wc_clean( $args['sku'] ) );
+        if ( isset( $args[ FormElements::SKU ] ) ) {
+            $product->set_sku( wc_clean( $args[ FormElements::SKU ] ) );
         }
 
         // Attributes.
-        if ( isset( $args['attributes'] ) ) {
-            $product->set_attributes( $args['attributes'] );
-        }
+        $product = $this->save_product_attribute_data( $product, $args );
 
         // Sales and prices.
         if ( in_array( $product->get_type(), [ 'variable', 'grouped' ], true ) ) {
@@ -175,45 +176,45 @@ public function create( $args = [] ) {
             $product->set_price( '' );
         } else {
             // Regular Price.
-            if ( isset( $args['regular_price'] ) ) {
-                $product->set_regular_price( $args['regular_price'] );
+            if ( isset( $args[ FormElements::REGULAR_PRICE ] ) ) {
+                $product->set_regular_price( $args[ FormElements::REGULAR_PRICE ] );
             }
 
             // Sale Price.
-            if ( isset( $args['sale_price'] ) ) {
-                $product->set_sale_price( $args['sale_price'] );
+            if ( isset( $args[ FormElements::SALE_PRICE ] ) ) {
+                $product->set_sale_price( $args[ FormElements::SALE_PRICE ] );
             }
 
-            if ( isset( $args['date_on_sale_from'] ) ) {
-                $product->set_date_on_sale_from( $args['date_on_sale_from'] );
+            if ( isset( $args[ FormElements::DATE_ON_SALE_FROM ] ) ) {
+                $product->set_date_on_sale_from( $args[ FormElements::DATE_ON_SALE_FROM ] );
             }
 
-            if ( isset( $args['date_on_sale_from_gmt'] ) ) {
-                $product->set_date_on_sale_from( $args['date_on_sale_from_gmt'] ? strtotime( $args['date_on_sale_from_gmt'] ) : null );
+            if ( isset( $args[ FormElements::DATE_ON_SALE_FROM_GMT ] ) ) {
+                $product->set_date_on_sale_from( $args[ FormElements::DATE_ON_SALE_FROM_GMT ] ? strtotime( $args[ FormElements::DATE_ON_SALE_FROM_GMT ] ) : null );
             }
 
-            if ( isset( $args['date_on_sale_to'] ) ) {
-                $product->set_date_on_sale_to( $args['date_on_sale_to'] );
+            if ( isset( $args[ FormElements::DATE_ON_SALE_TO ] ) ) {
+                $product->set_date_on_sale_to( $args[ FormElements::DATE_ON_SALE_TO ] );
             }
 
-            if ( isset( $args['date_on_sale_to_gmt'] ) ) {
-                $product->set_date_on_sale_to( $args['date_on_sale_to_gmt'] ? strtotime( $args['date_on_sale_to_gmt'] ) : null );
+            if ( isset( $args[ FormElements::DATE_ON_SALE_TO_GMT ] ) ) {
+                $product->set_date_on_sale_to( $args[ FormElements::DATE_ON_SALE_TO_GMT ] ? strtotime( $args[ FormElements::DATE_ON_SALE_TO_GMT ] ) : null );
             }
         }
 
         // Product parent ID.
-        if ( isset( $args['parent_id'] ) ) {
-            $product->set_parent_id( $args['parent_id'] );
+        if ( isset( $args[ FormElements::PARENT_ID ] ) ) {
+            $product->set_parent_id( $args[ FormElements::PARENT_ID ] );
         }
 
         // Sold individually.
-        if ( isset( $args['sold_individually'] ) ) {
-            $product->set_sold_individually( $args['sold_individually'] );
+        if ( isset( $args[ FormElements::SOLD_INDIVIDUALLY ] ) ) {
+            $product->set_sold_individually( $args[ FormElements::SOLD_INDIVIDUALLY ] );
         }
 
         // Stock status; stock_status has priority over in_stock.
-        if ( isset( $args['stock_status'] ) ) {
-            $stock_status = $args['stock_status'];
+        if ( isset( $args[ FormElements::STOCK_STATUS ] ) ) {
+            $stock_status = $args[ FormElements::STOCK_STATUS ];
         } else {
             $stock_status = $product->get_stock_status();
         }
@@ -221,13 +222,17 @@ public function create( $args = [] ) {
         // Stock data.
         if ( 'yes' === get_option( 'woocommerce_manage_stock' ) ) {
             // Manage stock.
-            if ( isset( $args['manage_stock'] ) ) {
-                $product->set_manage_stock( $args['manage_stock'] );
+            if ( isset( $args[ FormElements::MANAGE_STOCK ] ) ) {
+                $product->set_manage_stock( $args[ FormElements::MANAGE_STOCK ] );
+            }
+
+            if ( isset( $args[ FormElements::LOW_STOCK_AMOUNT ] ) ) {
+                $product->set_low_stock_amount( $args[ FormElements::LOW_STOCK_AMOUNT ] );
             }
 
             // Backorders.
-            if ( isset( $args['backorders'] ) ) {
-                $product->set_backorders( $args['backorders'] );
+            if ( isset( $args[ FormElements::BACKORDERS ] ) ) {
+                $product->set_backorders( $args[ FormElements::BACKORDERS ] );
             }
 
             if ( $product->is_type( 'grouped' ) ) {
@@ -247,11 +252,11 @@ public function create( $args = [] ) {
                 }
 
                 // Stock quantity.
-                if ( isset( $args['stock_quantity'] ) ) {
-                    $product->set_stock_quantity( wc_stock_amount( $args['stock_quantity'] ) );
-                } elseif ( isset( $args['inventory_delta'] ) ) {
+                if ( isset( $args[ FormElements::STOCK_QUANTITY ] ) ) {
+                    $product->set_stock_quantity( wc_stock_amount( $args[ FormElements::STOCK_QUANTITY ] ) );
+                } elseif ( isset( $args[ FormElements::INVENTORY_DELTA ] ) ) {
                     $stock_quantity = wc_stock_amount( $product->get_stock_quantity() );
-                    $stock_quantity += wc_stock_amount( $args['inventory_delta'] );
+                    $stock_quantity += wc_stock_amount( $args[ FormElements::INVENTORY_DELTA ] );
                     $product->set_stock_quantity( wc_stock_amount( $stock_quantity ) );
                 }
             } else {
@@ -268,9 +273,9 @@ public function create( $args = [] ) {
         $product = $this->maybe_update_stock_status( $product, $stock_status );
 
         // Upsells.
-        if ( isset( $args['upsell_ids'] ) ) {
+        if ( isset( $args[ FormElements::UPSELL_IDS ] ) ) {
             $upsells = [];
-            $ids     = $args['upsell_ids'];
+            $ids     = $args[ FormElements::UPSELL_IDS ];
 
             if ( ! empty( $ids ) ) {
                 foreach ( $ids as $id ) {
@@ -284,9 +289,9 @@ public function create( $args = [] ) {
         }
 
         // Cross sells.
-        if ( isset( $args['cross_sell_ids'] ) ) {
+        if ( isset( $args[ FormElements::CROSS_SELL_IDS ] ) ) {
             $crosssells = [];
-            $ids        = $args['cross_sell_ids'];
+            $ids        = $args[ FormElements::CROSS_SELL_IDS ];
 
             if ( ! empty( $ids ) ) {
                 foreach ( $ids as $id ) {
@@ -300,47 +305,47 @@ public function create( $args = [] ) {
         }
 
         // Product categories.
-        if ( isset( $args['categories'] ) && is_array( $args['categories'] ) ) {
-            $product = $this->save_taxonomy_terms( $product, $args['categories'] );
+        if ( isset( $args[ FormElements::CATEGORIES ] ) && is_array( $args[ FormElements::CATEGORIES ] ) ) {
+            $product = $this->save_taxonomy_terms( $product, $args[ FormElements::CATEGORIES ] );
         }
 
         // Product tags.
-        if ( isset( $args['tags'] ) && is_array( $args['tags'] ) ) {
-            $product = $this->save_taxonomy_terms( $product, $args['tags'], 'tag' );
+        if ( isset( $args[ FormElements::TAGS ] ) && is_array( $args[ FormElements::TAGS ] ) ) {
+            $product = $this->save_taxonomy_terms( $product, $args[ FormElements::TAGS ], 'tag' );
         }
 
         // Downloadable.
-        if ( isset( $args['downloadable'] ) ) {
-            $product->set_downloadable( $args['downloadable'] );
+        if ( isset( $args[ FormElements::DOWNLOADABLE ] ) ) {
+            $product->set_downloadable( $args[ FormElements::DOWNLOADABLE ] );
         }
 
         // Downloadable options.
         if ( $product->get_downloadable() ) {
 
             // Downloadable files.
-            if ( isset( $args['downloads'] ) && is_array( $args['downloads'] ) ) {
-                $product = $this->save_downloadable_files( $product, $args['downloads'] );
+            if ( isset( $args[ FormElements::DOWNLOADS ] ) && is_array( $args[ FormElements::DOWNLOADS ] ) ) {
+                $product = $this->save_downloadable_files( $product, $args[ FormElements::DOWNLOADS ] );
             }
 
             // Download limit.
-            if ( isset( $args['download_limit'] ) ) {
-                $product->set_download_limit( $args['download_limit'] );
+            if ( isset( $args[ FormElements::DOWNLOAD_LIMIT ] ) ) {
+                $product->set_download_limit( $args[ FormElements::DOWNLOAD_LIMIT ] );
             }
 
             // Download expiry.
-            if ( isset( $args['download_expiry'] ) ) {
-                $product->set_download_expiry( $args['download_expiry'] );
+            if ( isset( $args[ FormElements::DOWNLOAD_EXPIRY ] ) ) {
+                $product->set_download_expiry( $args[ FormElements::DOWNLOAD_EXPIRY ] );
             }
         }
 
         // Product url and button text for external products.
         if ( $product->is_type( 'external' ) ) {
-            if ( isset( $args['external_url'] ) ) {
-                $product->set_product_url( $args['external_url'] );
+            if ( isset( $args[ FormElements::EXTERNAL_URL ] ) ) {
+                $product->set_product_url( $args[ FormElements::EXTERNAL_URL ] );
             }
 
-            if ( isset( $args['button_text'] ) ) {
-                $product->set_button_text( $args['button_text'] );
+            if ( isset( $args[ FormElements::BUTTON_TEXT ] ) ) {
+                $product->set_button_text( $args[ FormElements::BUTTON_TEXT ] );
             }
         }
 
@@ -350,51 +355,94 @@ public function create( $args = [] ) {
         }
 
         // Set children for a grouped product.
-        if ( $product->is_type( 'grouped' ) && isset( $args['grouped_products'] ) ) {
-            $product->set_children( $args['grouped_products'] );
+        if ( $product->is_type( 'grouped' ) && isset( $args[ FormElements::GROUP_PRODUCTS ] ) ) {
+            $product->set_children( $args[ FormElements::GROUP_PRODUCTS ] );
         }
 
         // Set featured image id
-        if ( ! empty( $args['featured_image_id'] ) ) {
-            $product->set_image_id( $args['featured_image_id'] );
+        if ( ! empty( $args[ FormElements::FEATURED_IMAGE_ID ] ) ) {
+            $product->set_image_id( $args[ FormElements::FEATURED_IMAGE_ID ] );
         }
 
         // Set gallery image ids
-        if ( ! empty( $args['gallery_image_ids'] ) ) {
-            $product->set_gallery_image_ids( $args['gallery_image_ids'] );
+        if ( ! empty( $args[ FormElements::GALLERY_IMAGE_IDS ] ) ) {
+            $product->set_gallery_image_ids( $args[ FormElements::GALLERY_IMAGE_IDS ] );
         }
 
         // Allow set meta_data.
-        if ( ! empty( $args['meta_data'] ) && is_array( $args['meta_data'] ) ) {
-            foreach ( $args['meta_data'] as $meta ) {
+        if ( ! empty( $args[ FormElements::META_DATA ] ) && is_array( $args[ FormElements::META_DATA ] ) ) {
+            foreach ( $args[ FormElements::META_DATA ] as $meta ) {
                 $product->update_meta_data( $meta['key'], $meta['value'], isset( $meta['id'] ) ? $meta['id'] : '' );
             }
         }
 
-        if ( ! empty( $args['date_created'] ) ) {
-            $date = rest_parse_date( $args['date_created'] );
+        // Set total sales for newly created product
+        if ( ! $is_update ) {
+            // Set date created.
+            if ( ! empty( $args[ FormElements::DATE_CREATED ] ) ) {
+                $date = rest_parse_date( $args[ FormElements::DATE_CREATED ] );
 
-            if ( $date ) {
-                $product->set_date_created( $date );
+                if ( $date ) {
+                    $product->set_date_created( $date );
+                }
             }
-        }
 
-        if ( ! empty( $args['date_created_gmt'] ) ) {
-            $date = rest_parse_date( $args['date_created_gmt'], true );
+            // set date created gmt
+            if ( ! empty( $args[ FormElements::DATE_CREATED_GMT ] ) ) {
+                $date = rest_parse_date( $args[ FormElements::DATE_CREATED_GMT ], true );
 
-            if ( $date ) {
-                $product->set_date_created( $date );
+                if ( $date ) {
+                    $product->set_date_created( $date );
+                }
             }
-        }
 
-        // Set total sales for newly created product
-        if ( ! empty( $id ) ) {
+            // Set total sales to zero
             $product->set_total_sales( 0 );
         }
 
         $product_id = $product->save();
 
-        return wc_get_product( $product_id );
+        //call dokan hooks
+        if ( ! $is_update ) {
+            do_action( 'dokan_new_product_added', $product_id, $product );
+        } else {
+            do_action( 'dokan_product_updated', $product_id, $product );
+        }
+
+        return $this->get( $product_id );
+    }
+
+    /**
+     * Update product data
+     *
+     * @since 3.0.0
+     *
+     * @return WC_Product|WP_Error|false
+     */
+    public function update( $args = [] ) {
+        $id = isset( $args['id'] ) ? absint( $args['id'] ) : 0;
+
+        if ( empty( $id ) ) {
+            return new WP_Error( 'no-id-found', __( 'No product ID found for updating', 'dokan-lite' ), [ 'status' => 401 ] );
+        }
+
+        return $this->create( $args );
+    }
+
+    /**
+     * Delete product data
+     *
+     * @since 3.0.0
+     *
+     * @return WC_Product|null|false
+     */
+    public function delete( $product_id, $force = false ) {
+        $product = $this->get( $product_id );
+        if ( $product ) {
+            $product->delete( [ 'force_delete' => $force ] );
+        }
+
+        return $product;
     }
 
     /**
@@ -402,7 +450,7 @@ public function create( $args = [] ) {
      *
      * @since 3.0.0
      *
-     * @param WC_Product      $product Product instance.
+     * @param WC_Product       $product Product instance.
      * @param \WP_REST_Request $request Request data.
      *
      * @return WC_Product
@@ -458,39 +506,6 @@ public function save_default_attributes( $product, $request ) {
         return $product;
     }
 
-    /**
-     * Update product data
-     *
-     * @since 3.0.0
-     *
-     * @return WC_Product|WP_Error|false
-     */
-    public function update( $args = [] ) {
-        $id = isset( $args['id'] ) ? absint( $args['id'] ) : 0;
-
-        if ( empty( $id ) ) {
-            return new WP_Error( 'no-id-found', __( 'No product ID found for updating', 'dokan-lite' ), [ 'status' => 401 ] );
-        }
-
-        return $this->create( $args );
-    }
-
-    /**
-     * Delete product data
-     *
-     * @since 3.0.0
-     *
-     * @return WC_Product|null|false
-     */
-    public function delete( $product_id, $force = false ) {
-        $product = $this->get( $product_id );
-        if ( $product ) {
-            $product->delete( [ 'force_delete' => $force ] );
-        }
-
-        return $product;
-    }
-
     /**
      * Save product shipping data.
      *
@@ -501,42 +516,119 @@ public function delete( $product_id, $force = false ) {
      */
     protected function save_product_shipping_data( $product, $data ) {
         // Virtual.
-        if ( isset( $data['virtual'] ) && true === $data['virtual'] ) {
+        if ( isset( $data[ FormElements::VIRTUAL ] ) && true === $data[ FormElements::VIRTUAL ] ) {
             $product->set_weight( '' );
             $product->set_height( '' );
             $product->set_length( '' );
             $product->set_width( '' );
         } else {
-            if ( isset( $data['weight'] ) ) {
-                $product->set_weight( $data['weight'] );
+            if ( isset( $data[ FormElements::WEIGHT ] ) ) {
+                $product->set_weight( $data[ FormElements::WEIGHT ] );
             }
 
             // Height.
-            if ( isset( $data['dimensions']['height'] ) ) {
-                $product->set_height( $data['dimensions']['height'] );
+            if ( isset( $data[ FormElements::DIMENSIONS ][ FormElements::DIMENSIONS_HEIGHT ] ) ) {
+                $product->set_height( $data[ FormElements::DIMENSIONS ][ FormElements::DIMENSIONS_HEIGHT ] );
             }
 
             // Width.
-            if ( isset( $data['dimensions']['width'] ) ) {
-                $product->set_width( $data['dimensions']['width'] );
+            if ( isset( $data[ FormElements::DIMENSIONS ][ FormElements::DIMENSIONS_WIDTH ] ) ) {
+                $product->set_width( $data[ FormElements::DIMENSIONS ][ FormElements::DIMENSIONS_WIDTH ] );
             }
 
             // Length.
-            if ( isset( $data['dimensions']['length'] ) ) {
-                $product->set_length( $data['dimensions']['length'] );
+            if ( isset( $data[ FormElements::DIMENSIONS ][ FormElements::DIMENSIONS_LENGTH ] ) ) {
+                $product->set_length( $data[ FormElements::DIMENSIONS ][ FormElements::DIMENSIONS_LENGTH ] );
             }
         }
 
         // Shipping class.
-        if ( isset( $data['shipping_class'] ) ) {
+        if ( isset( $data[ FormElements::SHIPPING_CLASS ] ) ) {
             $data_store        = $product->get_data_store();
-            $shipping_class_id = $data_store->get_shipping_class_id_by_slug( wc_clean( $data['shipping_class'] ) );
+            $shipping_class_id = $data_store->get_shipping_class_id_by_slug( wc_clean( $data[ FormElements::SHIPPING_CLASS ] ) );
             $product->set_shipping_class_id( $shipping_class_id );
         }
 
         return $product;
     }
 
+    /**
+     * Save product attribute data.
+     *
+     * @param WC_Product $product Product instance.
+     * @param array      $args    Product data's.
+     *
+     * @return WC_Product
+     */
+    protected function save_product_attribute_data( $product, $args ) {
+        if ( isset( $args[ FormElements::ATTRIBUTES ] ) ) {
+            $attributes = [];
+
+            foreach ( $args[ FormElements::ATTRIBUTES ] as $attribute ) {
+                $attribute_id   = 0;
+                $attribute_name = '';
+
+                // Check ID for global attributes or name for product attributes.
+                if ( ! empty( $attribute[ FormElements::ATTRIBUTES_ID ] ) ) {
+                    $attribute_id   = absint( $attribute[ FormElements::ATTRIBUTES_ID ] );
+                    $attribute_name = wc_attribute_taxonomy_name_by_id( $attribute_id );
+                } elseif ( ! empty( $attribute[ FormElements::ATTRIBUTES_NAME ] ) ) {
+                    $attribute_name = wc_clean( $attribute[ FormElements::ATTRIBUTES_NAME ] );
+                }
+
+                if ( ! $attribute_id && ! $attribute_name ) {
+                    continue;
+                }
+
+                if ( $attribute_id ) {
+
+                    if ( isset( $attribute[ FormElements::ATTRIBUTES_OPTIONS ] ) ) {
+                        $options = $attribute[ FormElements::ATTRIBUTES_OPTIONS ];
+
+                        if ( ! is_array( $attribute[ FormElements::ATTRIBUTES_OPTIONS ] ) ) {
+                            // Text based attributes - Posted values are term names.
+                            $options = explode( WC_DELIMITER, $options );
+                        }
+
+                        $values = array_map( 'wc_sanitize_term_text_based', $options );
+                        $values = array_filter( $values, 'strlen' );
+                    } else {
+                        $values = [];
+                    }
+
+                    if ( ! empty( $values ) ) {
+                        // Add attribute to array, but don't set values.
+                        $attribute_object = new WC_Product_Attribute();
+                        $attribute_object->set_id( $attribute_id );
+                        $attribute_object->set_name( $attribute_name );
+                        $attribute_object->set_options( $values );
+                        $attribute_object->set_position( isset( $attribute[ FormElements::ATTRIBUTES_POSITION ] ) ? (string) absint( $attribute[ FormElements::ATTRIBUTES_POSITION ] ) : '0' );
+                        $attribute_object->set_visible( ( isset( $attribute[ FormElements::ATTRIBUTES_VISIBLE ] ) && $attribute[ FormElements::ATTRIBUTES_VISIBLE ] ) ? 1 : 0 );
+                        $attribute_object->set_variation( ( isset( $attribute[ FormElements::ATTRIBUTES_VARIATION ] ) && $attribute[ FormElements::ATTRIBUTES_VARIATION ] ) ? 1 : 0 );
+                        $attributes[] = $attribute_object;
+                    }
+                } elseif ( isset( $attribute[ FormElements::ATTRIBUTES_OPTIONS ] ) ) {
+                    // Custom attribute - Add attribute to array and set the values.
+                    if ( is_array( $attribute[ FormElements::ATTRIBUTES_OPTIONS ] ) ) {
+                        $values = $attribute[ FormElements::ATTRIBUTES_OPTIONS ];
+                    } else {
+                        $values = explode( WC_DELIMITER, $attribute[ FormElements::ATTRIBUTES_OPTIONS ] );
+                    }
+                    $attribute_object = new WC_Product_Attribute();
+                    $attribute_object->set_name( $attribute_name );
+                    $attribute_object->set_options( $values );
+                    $attribute_object->set_position( isset( $attribute[ FormElements::ATTRIBUTES_POSITION ] ) ? (string) absint( $attribute[ FormElements::ATTRIBUTES_POSITION ] ) : '0' );
+                    $attribute_object->set_visible( ( isset( $attribute[ FormElements::ATTRIBUTES_VISIBLE ] ) && $attribute[ FormElements::ATTRIBUTES_VISIBLE ] ) ? 1 : 0 );
+                    $attribute_object->set_variation( ( isset( $attribute[ FormElements::ATTRIBUTES_VARIATION ] ) && $attribute[ FormElements::ATTRIBUTES_VARIATION ] ) ? 1 : 0 );
+                    $attributes[] = $attribute_object;
+                }
+            }
+            $product->set_attributes( $attributes );
+        }
+
+        return $product;
+    }
+
     /**
      * Save taxonomy terms.
      *
@@ -547,10 +639,12 @@ protected function save_product_shipping_data( $product, $data ) {
      * @return WC_Product
      */
     protected function save_taxonomy_terms( $product, $terms, $taxonomy = 'cat' ) {
+        $term_ids = wp_list_pluck( $terms, 'id' );
+
         if ( 'cat' === $taxonomy ) {
-            $product->set_category_ids( $terms );
+            $product->set_category_ids( $term_ids );
         } elseif ( 'tag' === $taxonomy ) {
-            $product->set_tag_ids( $terms );
+            $product->set_tag_ids( $term_ids );
         }
 
         return $product;
@@ -559,8 +653,8 @@ protected function save_taxonomy_terms( $product, $terms, $taxonomy = 'cat' ) {
     /**
      * Save downloadable files.
      *
-     * @param WC_Product $product    Product instance.
-     * @param array      $downloads  Downloads data.
+     * @param WC_Product $product   Product instance.
+     * @param array      $downloads Downloads data.
      *
      * @return WC_Product
      */
@@ -593,10 +687,7 @@ protected function save_downloadable_files( $product, $downloads ) {
      * @return mixed
      */
     protected function maybe_update_stock_status( $product, $stock_status ) {
-        if ( $product->is_type( 'external' ) ) {
-            // External products are always in stock.
-            $product->set_stock_status( 'instock' );
-        } elseif ( isset( $stock_status ) ) {
+        if ( isset( $stock_status ) ) {
             if ( $product->is_type( 'variable' ) && ! $product->get_manage_stock() ) {
                 // Stock status is determined by children.
                 foreach ( $product->get_children() as $child_id ) {
@@ -679,7 +770,7 @@ public function best_selling( $args = [] ) {
         $args['orderby']  = 'meta_value_num';
 
         $product_visibility_term_ids = wc_get_product_visibility_term_ids();
-        $args['tax_query'][]           = [ // phpcs:ignore
+        $args['tax_query'][]         = [ // phpcs:ignore
             'taxonomy' => 'product_visibility',
             'field'    => 'term_taxonomy_id',
             'terms'    => is_search() ? $product_visibility_term_ids['exclude-from-search'] : $product_visibility_term_ids['exclude-from-catalog'],
diff --git a/includes/ProductForm/Component.php b/includes/ProductForm/Component.php
new file mode 100644
index 0000000000..1ad7f6d1ae
--- /dev/null
+++ b/includes/ProductForm/Component.php
@@ -0,0 +1,328 @@
+<?php
+
+namespace WeDevs\Dokan\ProductForm;
+
+defined( 'ABSPATH' ) || exit;
+
+abstract class Component {
+
+    /**
+     * Required field attributes
+     *
+     * @since DOKAN_SINCE
+     *
+     * @var array $required_attributes
+     */
+    protected $required_fields = [
+        'id',
+        'title',
+    ];
+
+    /**
+     * Field Data
+     *
+     * @since DOKAN_SINCE
+     *
+     * @var array $data
+     */
+    protected $data = [
+        'id'            => '', // html id attribute of the field, required
+        'title'         => '', // label for the field
+        'description'   => '', // description of the field
+        'help_content'  => '', // help content for the field
+        'visibility'    => true, // field visibility, if the field is visible under frontend
+        'required'      => false, // by default, all fields are not required
+        'order'         => 30,
+        'error_message' => '',
+    ];
+
+    public function __construct( string $id, array $args = [] ) {
+        foreach ( $this->data as $key => $value ) {
+            if ( method_exists( $this, "set_{$key}" ) ) {
+                $this->{"set_{$key}"}( $args[ $key ] );
+            }
+        }
+    }
+
+    /**
+     * Get field id
+     *
+     * @since DOKAN_SINCE
+     *
+     * @return string
+     */
+    public function get_id(): string {
+        return $this->data['id'];
+    }
+
+    /**
+     * Set Field ID
+     *
+     * @since DOKAN_SINCE
+     *
+     * @param string $id
+     *
+     * @see   sanitize_key()
+     *
+     * @return $this
+     */
+    public function set_id( string $id ): self {
+        $this->data['id'] = sanitize_key( $id );
+
+        return $this;
+    }
+
+    /**
+     * Get field label
+     *
+     * @since DOKAN_SINCE
+     *
+     * @return string
+     */
+    public function get_title(): string {
+        return $this->data['title'];
+    }
+
+    /**
+     * Set field label validated with
+     *
+     * @since DOKAN_SINCE
+     *
+     * @param string $title
+     *
+     * @see   sanitize_text_field()
+     *
+     * @return $this
+     */
+    public function set_title( string $title ): self {
+        $this->data['title'] = sanitize_text_field( $title );
+
+        return $this;
+    }
+
+    /**
+     * Get field description
+     *
+     * @since DOKAN_SINCE
+     *
+     * @return string
+     */
+    public function get_description(): string {
+        return $this->data['description'];
+    }
+
+    /**
+     * Set field description, validated with
+     *
+     * @since DOKAN_SINCE
+     *
+     * @param string $description
+     *
+     * @see   wp_kses_post()
+     *
+     * @return $this
+     */
+    public function set_description( string $description ): self {
+        $this->data['description'] = wp_kses_post( $description );
+
+        return $this;
+    }
+
+    /**
+     * Get field help content
+     *
+     * @since DOKAN_SINCE
+     *
+     * @return string
+     */
+    public function get_help_content(): string {
+        return $this->data['help_content'];
+    }
+
+    /**
+     * Set field help content, validated with
+     *
+     * @since DOKAN_SINCE
+     *
+     * @param string $help_content
+     *
+     * @see   wp_kses_post()
+     *
+     * @return $this
+     */
+    public function set_help_content( string $help_content ): self {
+        $this->data['help_content'] = wp_kses_post( $help_content );
+
+        return $this;
+    }
+
+    /**
+     * Get field visibility
+     *
+     * @since DOKAN_SINCE
+     *
+     * @return bool
+     */
+    public function get_visibility(): bool {
+        return wc_string_to_bool( $this->data['visibility'] );
+    }
+
+    /**
+     * Check the field is visible or not
+     *
+     * @since DOKAN_SINCE
+     *
+     * @return bool
+     */
+    public function is_visible(): bool {
+        return $this->get_visibility();
+    }
+
+    /**
+     * Set field visibility
+     *
+     * @since DOKAN_SINCE
+     *
+     * @param bool $visibility
+     *
+     * @return $this
+     */
+    public function set_visibility( bool $visibility ): self {
+        $this->data['visibility'] = $visibility;
+
+        return $this;
+    }
+
+    /**
+     * Get field required status
+     *
+     * @since DOKAN_SINCE
+     *
+     * @return bool
+     */
+    public function get_required(): bool {
+        return wc_string_to_bool( $this->data['required'] );
+    }
+
+    /**
+     * Check the field is required or not
+     *
+     * @since DOKAN_SINCE
+     *
+     * @return bool
+     */
+    public function is_required(): bool {
+        return $this->get_required();
+    }
+
+    /**
+     * Set field required status
+     *
+     * @since DOKAN_SINCE
+     *
+     * @param bool $required
+     *
+     * @return $this
+     */
+    public function set_required( bool $required ): self {
+        $this->data['required'] = $required;
+
+        return $this;
+    }
+
+    /**
+     * Get field options
+     *
+     * @since DOKAN_SINCE
+     *
+     * @return int
+     */
+    public function get_order(): int {
+        return $this->data['order'];
+    }
+
+    /**
+     * Set field order
+     *
+     * @since DOKAN_SINCE
+     *
+     * @param int $order
+     *
+     * @return $this
+     */
+    public function set_order( int $order ): self {
+        $this->data['order'] = $order;
+
+        return $this;
+    }
+
+    /**
+     * Get a field error message
+     *
+     * @since DOKAN_SINCE
+     *
+     * @return string
+     */
+    public function get_error_message() {
+        return $this->data['error_message'];
+    }
+
+    /**
+     * Set field error message
+     *
+     * @since DOKAN_SINCE
+     *
+     * @param string $error_message
+     *
+     * @see   wp_kses_post()
+     *
+     * @return $this
+     */
+    public function set_error_message( string $error_message ): self {
+        $this->data['error_message'] = wp_kses_post( $error_message );
+
+        return $this;
+    }
+
+    /**
+     * Sorting function for product fields form Component class.
+     *
+     * @param Component $a       Component a.
+     * @param Component $b       Component b.
+     * @param string    $sort_by sort order.
+     *
+     * @return int
+     */
+    public static function sort( Component $a, Component $b, string $sort_by = 'asc' ) {
+        $a_val = $a->get_order();
+        $b_val = $b->get_order();
+        if ( 'asc' === $sort_by ) {
+            return $a_val <=> $b_val;
+        } else {
+            return $b_val <=> $a_val;
+        }
+    }
+
+    /**
+     * Get missing arguments of args array.
+     *
+     * @param array $args field arguments.
+     *
+     * @return array
+     */
+    public function get_missing_arguments( $args ) {
+        return array_values(
+            array_filter(
+                $this->required_fields,
+                function ( $arg_key ) use ( $args ) {
+                    // return false if not exists or empty
+                    if ( ! array_key_exists( $arg_key, $args ) || empty( $args[ $arg_key ] ) ) {
+                        return false;
+                    }
+
+                    return true;
+                }
+            )
+        );
+    }
+}
diff --git a/includes/ProductForm/Elements.php b/includes/ProductForm/Elements.php
new file mode 100644
index 0000000000..5c6f73c8f1
--- /dev/null
+++ b/includes/ProductForm/Elements.php
@@ -0,0 +1,78 @@
+<?php
+
+namespace WeDevs\Dokan\ProductForm;
+
+defined( 'ABSPATH' ) || exit;
+
+/**
+ * Product Form Elements
+ *
+ * @since DOKAN_SINCE
+ */
+class Elements {
+    const ID = 'id';
+    const TYPE = 'type';
+    const NAME = 'name';
+    const DESCRIPTION = 'description';
+    const SHORT_DESCRIPTION = 'short_description';
+    const STATUS = 'status';
+    const SLUG = 'slug';
+    const MENU_ORDER = 'menu_order';
+    const REVIEWS_ALLOWED = 'reviews_allowed';
+    const VIRTUAL = 'virtual';
+    const TAX_STATUS = 'tax_status';
+    const TAX_CLASS = 'tax_class';
+    const CATALOG_VISIBILITY = 'catalog_visibility';
+    const PURCHASE_NOTE = 'purchase_note';
+    const FEATURED = 'featured';
+    const SKU = 'sku';
+    const WEIGHT = 'weight';
+    const DIMENSIONS = 'dimensions';
+    const DIMENSIONS_HEIGHT = 'height';
+    const DIMENSIONS_WIDTH = 'width';
+    const DIMENSIONS_LENGTH = 'length';
+    const SHIPPING_CLASS = 'shipping_class_id';
+    const ATTRIBUTES = 'attributes';
+    const ATTRIBUTES_ID = 'id';
+    const ATTRIBUTES_NAME = 'name';
+    const ATTRIBUTES_OPTIONS = 'options';
+    const ATTRIBUTES_POSITION = 'position';
+    const ATTRIBUTES_VISIBLE = 'visible';
+    const ATTRIBUTES_VARIATION = 'variation';
+    const DEFAULT_ATTRIBUTES = 'default_attributes';
+    const REGULAR_PRICE = 'regular_price';
+    const SALE_PRICE = 'sale_price';
+    const DATE_CREATED = 'date_created';
+    const DATE_CREATED_GMT = 'date_created_gmt';
+    const DATE_ON_SALE_FROM = 'date_on_sale_from';
+    const DATE_ON_SALE_FROM_GMT = 'date_on_sale_from_gmt';
+    const DATE_ON_SALE_TO = 'date_on_sale_to';
+    const DATE_ON_SALE_TO_GMT = 'date_on_sale_to_gmt';
+    const PARENT_ID = 'parent_id';
+    const SOLD_INDIVIDUALLY = 'sold_individually';
+    const LOW_STOCK_AMOUNT = 'low_stock_amount';
+    const STOCK_STATUS = 'stock_status';
+    const MANAGE_STOCK = 'manage_stock';
+    const BACKORDERS = 'backorders';
+    const STOCK_QUANTITY = 'stock_quantity';
+    const INVENTORY_DELTA = 'inventory_delta';
+    const UPSELL_IDS = 'upsell_ids';
+    const CROSS_SELL_IDS = 'cross_sell_ids';
+    const CATEGORIES = 'categories';
+    const TAGS = 'tags';
+    const DOWNLOADABLE = 'downloadable';
+    const DOWNLOADS = 'downloads';
+    const DOWNLOAD_LIMIT = 'download_limit';
+    const DOWNLOAD_EXPIRY = 'download_expiry';
+    const EXTERNAL_URL = 'product_url';
+    const BUTTON_TEXT = 'button_text';
+    const GROUP_PRODUCTS = 'children';
+    const FEATURED_IMAGE_ID = 'featured_image_id';
+    const GALLERY_IMAGE_IDS = 'gallery_image_ids';
+    const META_DATA = 'meta_data';
+    const DISABLE_SHIPPING_META = '_disable_shipping';
+    const OVERWRITE_SHIPPING_META = '_overwrite_shipping';
+    const ADDITIONAL_SHIPPING_COST_META = '_additional_price';
+    const ADDITIONAL_SHIPPING_QUANTITY_META = '_additional_qty';
+    const ADDITIONAL_SHIPPING_PROCESSING_TIME_META = '_dps_processing_time';
+}
diff --git a/includes/ProductForm/Factory.php b/includes/ProductForm/Factory.php
new file mode 100644
index 0000000000..336c6c2495
--- /dev/null
+++ b/includes/ProductForm/Factory.php
@@ -0,0 +1,238 @@
+<?php
+
+namespace WeDevs\Dokan\ProductForm;
+
+use WeDevs\Dokan\Traits\Singleton;
+use WP_Error;
+
+defined( 'ABSPATH' ) || exit;
+
+/**
+ * Product Form Factory
+ *
+ * @since DOKAN_SINCE
+ */
+class Factory {
+    use Singleton;
+
+    /**
+     * Store form sections.
+     *
+     * @var array
+     */
+    protected static $form_sections = [];
+
+    /**
+     * Store form fields.
+     *
+     * @var array
+     */
+    protected static $form_fields = [];
+
+    /**
+     * Adds a field to the product form.
+     *
+     * @param string $id   Field id.
+     * @param array  $args Array containing the necessary arguments.
+     *                     $args = array(
+     *                     'id'              => '', // (string - required) html id attribute of the field
+     *                     'title'           => '', // (string - required) label for the field
+     *                     'section'         => '', // (string - required) section id
+     *                     'name'            => '', // (string) html name attribute of the field, if not exists id value will be used as name
+     *                     'property'        => '', // (string) if exists, this will be the name of the field
+     *                     'field_type'      => '', // (string) html field type
+     *                     'placeholder'     => '', // (string) html placeholder attribute value for the field
+     *                     'options'         => '', // (array) if the field is select, radio, checkbox, etc
+     *                     'additional_args' => [], // (array) additional arguments for the field
+     *                     'description'      => '', // (string) description of the field
+     *                     'help_content'     => '', // (string) help content for the field
+     *                     'visibility'       => true, // (bool) field visibility, if the field is visible under frontend
+     *                     'required'         => false, // (bool) by default, all fields are not required
+     *                     'order'            => 30, // (int) field order
+     *                     ).
+     *
+     * @return Field|WP_Error New field or WP_Error.
+     */
+    public static function add_field( $id, $args ) {
+        $new_field = self::create_item( 'field', 'Field', $id, $args );
+        if ( is_wp_error( $new_field ) ) {
+            return $new_field;
+        }
+
+        // store newly created field
+        self::$form_fields[ $id ] = $new_field;
+
+        // sort fields after adding a new item
+        self::$form_fields = self::get_items( 'field', 'Field' );
+
+        return $new_field;
+    }
+
+    /**
+     * Adds a section to the product form.
+     *
+     * @since DOKAN_SINCE
+     *
+     * @param string $id   Card id.
+     * @param array  $args Array containing the necessary arguments.
+     *
+     * @return Section|WP_Error New section or WP_Error.
+     */
+    public static function add_section( $id, $args ) {
+        $new_section = self::create_item( 'section', 'Section', $id, $args );
+        if ( is_wp_error( $new_section ) ) {
+            return $new_section;
+        }
+
+        // store newly created section
+        self::$form_sections[ $id ] = $new_section;
+
+        //sort sections after adding a new item
+        self::$form_sections = self::get_items( 'section', 'Section' );
+
+        return $new_section;
+    }
+
+    /**
+     * Returns list of registered fields.
+     *
+     * @return array list of registered fields.
+     */
+    public static function get_fields() {
+        return self::$form_fields;
+    }
+
+    /**
+     * Returns registered field based on given field id.
+     *
+     * @param string $id Field id.
+     *
+     * @return Field|WP_Error New field or WP_Error.
+     */
+    public static function get_field( string $id ) {
+        if ( array_key_exists( $id, self::$form_fields ) ) {
+            return self::$form_fields[ $id ];
+        }
+
+        return new WP_Error( 'dokan_product_form_missing_field', esc_html__( 'No field found with given id', 'dokan-lite' ) );
+    }
+
+    /**
+     * Returns list of registered sections.
+     *
+     * @param string $sort_by key and order to sort by.
+     *
+     * @return array list of registered sections.
+     */
+    public static function get_sections( string $sort_by = 'asc' ): array {
+        return self::get_items( 'section', 'Section', $sort_by );
+    }
+
+    /**
+     * Returns registerd section with given section id.
+     *
+     * @param string $id Section id.
+     *
+     * @return Section|WP_Error New section or WP_Error.
+     */
+    public static function get_section( string $id ) {
+        if ( array_key_exists( $id, self::$form_sections ) ) {
+            return self::$form_sections[ $id ];
+        }
+
+        return new WP_Error( 'dokan_product_form_missing_section', esc_html__( 'No section found with given id', 'dokan-lite' ) );
+    }
+
+    /**
+     * Returns list of registered items.
+     *
+     * @param string       $type       Form component type.
+     * @param class-string $class_name Class name of \WeDevs\Dokan\ProductForm\Component type.
+     * @param string       $sort_by    key and order to sort by.
+     *
+     * @return array       list of registered items.
+     */
+    private static function get_items( string $type, string $class_name, string $sort_by = 'asc' ) {
+        $item_list = self::get_item_list( $type );
+        $items     = array_values( $item_list );
+
+        if ( class_exists( $class_name ) && method_exists( $class_name, 'sort' ) ) {
+            /**
+             * @var \WeDevs\Dokan\ProductForm\Component $class_name
+             */
+            usort(
+                $items,
+                function ( $a, $b ) use ( $sort_by, $class_name ) {
+                    return $class_name::sort( $a, $b, $sort_by );
+                }
+            );
+        }
+
+        return $items;
+    }
+
+    /**
+     * @param string $type
+     *
+     * @return array
+     */
+    private static function get_item_list( string $type ): array {
+        $mapping = [
+            'field'   => self::$form_fields,
+            'section' => self::$form_sections,
+        ];
+
+        if ( array_key_exists( $type, $mapping ) ) {
+            return $mapping[ $type ];
+        }
+
+        return [];
+    }
+
+    /**
+     * Creates a new item.
+     *
+     * @since DOKAN_SINCE
+     *
+     * @param string       $type       Form component type.
+     * @param class-string $class_name Class name of \WeDevs\Dokan\ProductForm\Component type.
+     * @param string       $id         Item id.
+     * @param array        $args       additional arguments for item.
+     *
+     * @return Field|Section|WP_Error New product form item or WP_Error.
+     */
+    private static function create_item( string $type, string $class_name, string $id, array $args ) {
+        if ( ! class_exists( $class_name ) ) {
+            return new WP_Error(
+                'dokan_product_form_' . $type . '_missing_form_class',
+                sprintf(
+                /* translators: 1: missing class name. */
+                    esc_html__( '%1$s class does not exist.', 'dokan-lite' ),
+                    $class_name
+                )
+            );
+        }
+
+        $item_list = self::get_item_list( $type );
+        if ( isset( $item_list[ $id ] ) ) {
+            return new WP_Error(
+                'dokan_product_form_' . $type . '_duplicate_field_id',
+                sprintf(
+                /* translators: 1: Item type 2: Duplicate registered item id. */
+                    esc_html__( 'You have attempted to register a duplicate form %1$s with WooCommerce Form: %2$s', 'dokan-lite' ),
+                    $type,
+                    '`' . $id . '`'
+                )
+            );
+        }
+
+        try {
+            return new $class_name( $id, $args );
+        } catch ( \Exception $e ) {
+            return new WP_Error(
+                'dokan_product_form_' . $type . '_class_creation',
+                $e->getMessage()
+            );
+        }
+    }
+}
diff --git a/includes/ProductForm/Field.php b/includes/ProductForm/Field.php
new file mode 100644
index 0000000000..f1230e0ba5
--- /dev/null
+++ b/includes/ProductForm/Field.php
@@ -0,0 +1,264 @@
+<?php
+
+namespace WeDevs\Dokan\ProductForm;
+
+defined( 'ABSPATH' ) || exit;
+
+/**
+ * Product Form Field
+ *
+ * @since DOKAN_SINCE
+ */
+class Field extends Component {
+
+    /**
+     * Required field attributes
+     *
+     * @since DOKAN_SINCE
+     *
+     * @var array $required_attributes
+     */
+    protected $required_fields = [
+        'id',
+        'title',
+        'section',
+    ];
+
+    /**
+     * Class constructor
+     *
+     * @since DOKAN_SINCE
+     *
+     * @param array $args
+     *
+     * @throws \Exception
+     */
+    public function __construct( string $id, array $args = [] ) {
+        $data = [
+            'name'            => '', // html name attribute of the field, if not exists id value will be used as name
+            'property'        => '', // if exists, this will be the name of the field
+            'section'         => '', // section id, required
+            'field_type'      => '', // html field type
+            'placeholder'     => '', // html placeholder attribute value for the field
+            'options'         => '', // if the field is select, radio, checkbox, etc
+            'additional_properties' => [], // additional arguments for the field
+        ];
+        $this->data = array_merge( $this->data, $data );
+
+        // set id from the args
+        $this->set_id( $id );
+
+        // call parent constructor
+        parent::__construct( $id, $args );
+
+        // check if the required attributes are exists
+        $missing_arguments = self::get_missing_arguments( $this->data );
+        if ( count( $missing_arguments ) > 0 ) {
+            throw new \Exception(
+                sprintf(
+                    /* translators: 1: Missing arguments list. */
+                    esc_html__( 'You are missing required arguments of Dokan ProductForm Field: %1$s', 'dokan-lite' ),
+                    esc_attr( join( ', ', $missing_arguments ) )
+                )
+            );
+        }
+    }
+
+    /**
+     * Get field name
+     *
+     * @since DOKAN_SINCE
+     *
+     * @return string
+     */
+    public function get_name(): string {
+        return $this->data['name'];
+    }
+
+    /**
+     * Set field name, validated by @since DOKAN_SINCE
+     *
+     * @param string $name
+     *
+     * @see sanitize_title()
+     *
+     * @return $this
+     */
+    public function set_name( string $name ): Field {
+        $this->data['name'] = sanitize_title( $name );
+
+        return $this;
+    }
+
+    /**
+     * Get field property
+     *
+     * @since DOKAN_SINCE
+     *
+     * @return string
+     */
+    public function get_property(): string {
+        return $this->data['property'];
+    }
+
+    /**
+     * Set field property, validated by
+     *
+     * @since DOKAN_SINCE
+     *
+     * @param string $property
+     *
+     * @see   sanitize_key()
+     *
+     * @return $this
+     */
+    public function set_property( string $property ): Field {
+        $this->data['property'] = sanitize_key( $property );
+
+        return $this;
+    }
+
+    /**
+     * Get the field section
+     *
+     * @since DOKAN_SINCE
+     *
+     * @return string
+     */
+    public function get_section(): string {
+        return $this->data['section'];
+    }
+
+    /**
+     * Set field section
+     *
+     * @since DOKAN_SINCE
+     *
+     * @param string $section
+     *
+     * @return $this
+     */
+    public function set_section( string $section ): Field {
+        $this->data['section'] = sanitize_key( $section );
+
+        return $this;
+    }
+
+    /**
+     * Get the field type
+     *
+     * @since DOKAN_SINCE
+     *
+     * @return string
+     */
+    public function get_field_type(): string {
+        return $this->data['field_type'];
+    }
+
+    /**
+     * Set field type
+     *
+     * @since DOKAN_SINCE
+     *
+     * @param string $field_type
+     *
+     * @return $this
+     */
+    public function set_field_type( string $field_type ): Field {
+        // todo: add field type validation
+        $this->data['field_type'] = sanitize_key( $field_type );
+
+        return $this;
+    }
+
+    /**
+     * Get field placeholder
+     *
+     * @since DOKAN_SINCE
+     *
+     * @return string
+     */
+    public function get_placeholder(): string {
+        return $this->data['placeholder'];
+    }
+
+    /**
+     * Set field placeholder, validated with
+     *
+     * @since DOKAN_SINCE
+     *
+     * @param string $placeholder
+     *
+     * @see   wp_kses_post()
+     *
+     * @return $this
+     */
+    public function set_placeholder( string $placeholder ): Field {
+        $this->data['placeholder'] = wp_kses_post( $placeholder );
+
+        return $this;
+    }
+
+    /**
+     * Get field options
+     *
+     * @since DOKAN_SINCE
+     *
+     * @return array
+     */
+    public function get_options(): array {
+        return $this->data['options'];
+    }
+
+    /**
+     * Set field options
+     *
+     * @since DOKAN_SINCE
+     *
+     * @param array $options
+     *
+     * @return $this
+     */
+    public function set_options( array $options ): Field {
+        $this->data['options'] = $options;
+
+        return $this;
+    }
+
+    /**
+     * Get field property
+     *
+     * @since DOKAN_SINCE
+     *
+     * @param string $property
+     *
+     * @return string|array
+     */
+    public function get_additional_properties( string $property = '' ) {
+        if ( ! empty( $property ) ) {
+            return array_key_exists( $property, $this->data['additional_properties'] ) ? $this->data['additional_properties'][ $property ] : '';
+        }
+
+        return $this->data['additional_properties'];
+    }
+
+    /**
+     * Set additional properties
+     *
+     * @since DOKAN_SINCE
+     *
+     * @param mixed $property
+     * @param mixed $value
+     *
+     * @return $this
+     */
+    public function set_additional_properties( $property, $value = null ): Field {
+        if ( ! empty( $property ) && null !== $value ) {
+            $this->data['additional_properties'][ $property ] = $value;
+        } elseif ( is_array( $property ) ) {
+            $this->data['additional_properties'] = $property;
+        }
+
+        return $this;
+    }
+}
diff --git a/includes/ProductForm/Init.php b/includes/ProductForm/Init.php
new file mode 100644
index 0000000000..4df60fb6c3
--- /dev/null
+++ b/includes/ProductForm/Init.php
@@ -0,0 +1,512 @@
+<?php
+
+namespace WeDevs\Dokan\ProductForm;
+
+defined( 'ABSPATH' ) || exit;
+
+/**
+ * Init Product Form Fields
+ *
+ * @since DOKAN_SINCE
+ */
+class Init {
+
+    /**
+     * Class constructor
+     *
+     * @since DOKAN_SINCE
+     *
+     * @return void
+     */
+    public function __construct() {
+        add_action( 'init', [ $this, 'init_form_fields' ], 1 );
+    }
+
+    /**
+     * Init form fields
+     *
+     * @since DOKAN_SINCE
+     *
+     * @return void
+     */
+    public function init_form_fields() {
+        $this->init_general_fields();
+        $this->init_inventory_fields();
+        $this->init_downloadable_fields();
+        $this->init_other_fields();
+    }
+
+    /**
+     * Init general fields
+     *
+     * @since DOKAN_SINCE
+     *
+     * @return void
+     */
+    public function init_general_fields() {
+        $section = Factory::add_section(
+            'general',
+            [
+                'title' => __( 'General', 'dokan-lite' ),
+                'order' => 10,
+            ]
+        );
+
+        $section->add_field(
+            Elements::ID,
+            [
+                'title'       => __( 'Title', 'dokan-lite' ),
+                'type'        => 'text',
+                'name'        => 'post_title',
+                'placeholder' => __( 'Enter product title...', 'dokan-lite' ),
+                'error_msg'   => __( 'Please enter product title!', 'dokan-lite' ),
+            ]
+        );
+
+        $section->add_field(
+            Elements::TYPE, [
+                'title'        => __( 'Product Type', 'dokan-lite' ),
+                'type'         => 'select',
+                'name'         => 'product_type',
+                'options'      => apply_filters(
+                    'dokan_product_types',
+                    [
+                        'simple' => __( 'Simple', 'dokan-lite' ), // dokan lite only supports simple product
+                    ]
+                ),
+                'help_content' => __( 'Choose Variable if your product has multiple attributes - like sizes, colors, quality etc', 'dokan-lite' ),
+            ]
+        );
+
+        $section->add_field(
+            Elements::REGULAR_PRICE, [
+                'title'       => __( 'Price', 'dokan-lite' ),
+                'type'        => 'text',
+                'name'        => '_regular_price',
+                'placeholder' => '0.00',
+            ]
+        );
+
+        $section->add_field(
+            Elements::SALE_PRICE, [
+                'title'       => __( 'Discounted Price', 'dokan-lite' ),
+                'type'        => 'text',
+                'name'        => '_sale_price',
+                'placeholder' => '0.00',
+            ]
+        );
+
+        $section->add_field(
+            Elements::DATE_ON_SALE_FROM, [
+                'title'       => __( 'From', 'dokan-lite' ),
+                'type'        => 'text',
+                'name'        => '_sale_price_dates_from',
+                'placeholder' => wc_date_format(),
+            ]
+        );
+
+        $section->add_field(
+            Elements::DATE_ON_SALE_TO, [
+                'title'       => __( 'To', 'dokan-lite' ),
+                'type'        => 'text',
+                'name'        => '_sale_price_dates_to',
+                'placeholder' => wc_date_format(),
+            ]
+        );
+
+        $section->add_field(
+            Elements::CATEGORIES, [
+                'title'       => __( 'Categories', 'dokan-lite' ),
+                'type'        => 'select',
+                'name'        => 'product_cat[]',
+                'placeholder' => __( 'Select product categories', 'dokan-lite' ),
+                'options'     => [],
+            ]
+        );
+
+        $can_create_tags  = dokan_get_option( 'product_vendors_can_create_tags', 'dokan_selling' );
+        $tags_placeholder = 'on' === $can_create_tags ? __( 'Select tags/Add tags', 'dokan-lite' ) : __( 'Select product tags', 'dokan-lite' );
+        $section->add_field(
+            Elements::TAGS, [
+                'title'       => __( 'Tags', 'dokan-lite' ),
+                'type'        => 'select',
+                'name'        => 'product_tag[]',
+                'placeholder' => $tags_placeholder,
+            ]
+        );
+
+        $section->add_field(
+            Elements::FEATURED_IMAGE_ID, [
+                'title'       => __( 'Product Image', 'dokan-lite' ),
+                'type'        => 'image',
+                'name'        => 'feat_image_id',
+                'placeholder' => __( 'Select product image', 'dokan-lite' ),
+            ]
+        );
+
+        $section->add_field(
+            Elements::GALLERY_IMAGE_IDS, [
+                'title'       => __( 'Product Gallery', 'dokan-lite' ),
+                'type'        => 'gallery',
+                'name'        => 'product_image_gallery',
+                'placeholder' => __( 'Select product gallery images', 'dokan-lite' ),
+            ]
+        );
+
+        $section->add_field(
+            Elements::SHORT_DESCRIPTION, [
+                'title'       => __( 'Short Description', 'dokan-lite' ),
+                'type'        => 'textarea',
+                'name'        => 'post_excerpt',
+                'placeholder' => __( 'Enter product short description', 'dokan-lite' ),
+                'visibility'  => false,
+            ]
+        );
+
+        $section->add_field(
+            Elements::DESCRIPTION, [
+                'title'       => __( 'Description', 'dokan-lite' ),
+                'type'        => 'textarea',
+                'name'        => 'post_content',
+                'placeholder' => __( 'Enter product description', 'dokan-lite' ),
+                'required'    => true,
+                'visibility'  => false,
+            ]
+        );
+    }
+
+    /**
+     * Init inventory fields
+     *
+     * @since DOKAN_SINCE
+     *
+     * @return void
+     */
+    public function init_inventory_fields() {
+        $section = Factory::add_section(
+            'inventory',
+            [
+                'title'       => __( 'Inventory', 'dokan-lite' ),
+                'description' => __( 'Manage your product stock', 'dokan-lite' ),
+                'order'       => 20,
+            ]
+        );
+
+        $section->add_field(
+            Elements::SKU, [
+                'title'       => __( 'SKU', 'dokan-lite' ),
+                'type'        => 'text',
+                'name'        => '_sku',
+                'placeholder' => __( 'Enter product SKU', 'dokan-lite' ),
+            ]
+        );
+
+        $section->add_field(
+            Elements::STOCK_STATUS, [
+                'title'   => __( 'Stock Status', 'dokan-lite' ),
+                'type'    => 'select',
+                'name'    => '_stock_status',
+                'options' => wc_get_product_stock_status_options(),
+            ]
+        );
+
+        $section->add_field(
+            Elements::MANAGE_STOCK, [
+                'title' => __( 'Enable product stock management', 'dokan-lite' ),
+                'type'  => 'checkbox',
+                'name'  => '_manage_stock',
+                'value' => 'yes',
+            ]
+        );
+
+        $section->add_field(
+            Elements::STOCK_QUANTITY, [
+                'title'       => __( 'Stock quantity', 'dokan-lite' ),
+                'type'        => 'number',
+                'name'        => '_stock',
+                'placeholder' => '1',
+                'min'         => 0,
+                'step'        => 1,
+            ]
+        );
+
+        $section->add_field(
+            Elements::LOW_STOCK_AMOUNT, [
+                'title'       => __( 'Low stock threshold', 'dokan-lite' ),
+                'type'        => 'number',
+                'name'        => '_low_stock_amount',
+                'placeholder' => 1,
+                'min'         => 0,
+                'step'        => 1,
+            ]
+        );
+
+        $section->add_field(
+            Elements::BACKORDERS, [
+                'title'   => __( 'Allow Backorders', 'dokan-lite' ),
+                'type'    => 'select',
+                'name'    => '_backorders',
+                'options' => wc_get_product_backorder_options(),
+            ]
+        );
+
+        $section->add_field(
+            Elements::SOLD_INDIVIDUALLY, [
+                'title' => __( 'Sold Individually', 'dokan-lite' ),
+                'type'  => 'checkbox',
+                'name'  => '_sold_individually',
+                'value' => 'yes',
+            ]
+        );
+    }
+
+    /**
+     * Init downloadable fields
+     *
+     * @since DOKAN_SINCE
+     *
+     * @return void
+     */
+    public function init_downloadable_fields() {
+        $section = Factory::add_section(
+            'downloadable',
+            [
+                'title'       => __( 'Downloadable Options', 'dokan-lite' ),
+                'description' => __( 'Configure your downloadable product settings', 'dokan-lite' ),
+                'order'       => 30,
+            ]
+        );
+
+        $section->add_field(
+            Elements::DOWNLOAD_LIMIT, [
+                'title'       => __( 'Download Limit', 'dokan-lite' ),
+                'type'        => 'number',
+                'name'        => '_download_limit',
+                'placeholder' => __( 'Unlimited', 'dokan-lite' ),
+                'min'         => 0,
+                'step'        => 1,
+            ]
+        );
+
+        $section->add_field(
+            Elements::DOWNLOAD_EXPIRY, [
+                'title'       => __( 'Download Expiry', 'dokan-lite' ),
+                'type'        => 'number',
+                'name'        => '_download_expiry',
+                'placeholder' => __( 'Never', 'dokan-lite' ),
+                'min'         => 0,
+                'step'        => 1,
+            ]
+        );
+    }
+
+    /**
+     * Init other fields
+     *
+     * @since DOKAN_SINCE
+     *
+     * @return void
+     */
+    public function init_other_fields() {
+        $section = Factory::add_section(
+            'others',
+            [
+                'title'       => __( 'Other Options', 'dokan-lite' ),
+                'description' => __( 'Set your extra product options', 'dokan-lite' ),
+                'order'       => 30,
+            ]
+        );
+
+        $section->add_field(
+            Elements::STATUS, [
+                'title'   => __( 'Product Status', 'dokan-lite' ),
+                'type'    => 'select',
+                'name'    => 'post_status',
+                'default' => dokan_get_default_product_status( dokan_get_current_user_id() ),
+                'options' => dokan_get_available_post_status(), // get it with product_id param
+            ]
+        );
+
+        $section->add_field(
+            Elements::CATALOG_VISIBILITY, [
+                'title'   => __( 'Visibility', 'dokan-lite' ),
+                'type'    => 'select',
+                'name'    => '_visibility',
+                'options' => dokan_get_product_visibility_options(),
+            ]
+        );
+
+        $section->add_field(
+            Elements::PURCHASE_NOTE, [
+                'title'       => __( 'Purchase Note', 'dokan-lite' ),
+                'type'        => 'textarea',
+                'name'        => '_purchase_note',
+                'placeholder' => __( 'Customer will get this info in their order email', 'dokan-lite' ),
+            ]
+        );
+
+        $section->add_field(
+            Elements::REVIEWS_ALLOWED, [
+                'title' => __( 'Enable product reviews', 'dokan-lite' ),
+                'type'  => 'checkbox',
+                'name'  => '_enable_reviews',
+                'value' => 'yes',
+            ]
+        );
+    }
+
+    /**
+     * Init shipping fields
+     *
+     * @since DOKAN_SINCE
+     *
+     * @return void
+     */
+    public function init_shipping_fields() {
+        if ( ! dokan()->is_pro_exists() ) {
+            return;
+        }
+
+        $is_shipping_disabled = 'sell_digital' === dokan_pro()->digital_product->get_selling_product_type();
+        $tab_title            = $is_shipping_disabled ? __( 'Tax', 'dokan-lite' ) : __( 'Shipping and Tax', 'dokan-lite' );
+        $tab_desc             = $is_shipping_disabled ? __( 'Manage tax for this product', 'dokan-lite' ) : __( 'Manage shipping and tax for this product', 'dokan-lite' );
+
+        $section = Factory::add_section(
+            'shipping',
+            [
+                'title'       => $tab_title,
+                'description' => $tab_desc,
+                'order'       => 30,
+            ]
+        );
+
+        $section->add_field(
+            Elements::DISABLE_SHIPPING_META, [
+                'title' => __( 'Disable shipping', 'dokan-lite' ),
+                'type'  => 'checkbox',
+                'name'  => '_disable_shipping',
+                'value' => 'no',
+            ]
+        );
+
+        $section->add_field(
+            Elements::WEIGHT, [
+                'title'       => __( 'Weight', 'dokan-lite' ),
+                'type'        => 'number',
+                'name'        => '_weight',
+                'placeholder' => '0.00',
+                'min'         => 0,
+                'step'        => 0.01,
+            ]
+        );
+
+        $section->add_field(
+            Elements::DIMENSIONS_LENGTH, [
+                'title'       => __( 'Length', 'dokan-lite' ),
+                'type'        => 'number',
+                'name'        => '_length',
+                'placeholder' => '0.00',
+                'min'         => 0,
+                'step'        => 0.01,
+            ]
+        );
+
+        $section->add_field(
+            Elements::DIMENSIONS_WIDTH, [
+                'title'       => __( 'Width', 'dokan-lite' ),
+                'type'        => 'number',
+                'name'        => '_width',
+                'placeholder' => '0.00',
+                'min'         => 0,
+                'step'        => 0.01,
+            ]
+        );
+
+        $section->add_field(
+            Elements::DIMENSIONS_HEIGHT, [
+                'title'       => __( 'Height', 'dokan-lite' ),
+                'type'        => 'number',
+                'name'        => '_height',
+                'placeholder' => '0.00',
+                'min'         => 0,
+                'step'        => 0.01,
+            ]
+        );
+
+        $shipping_settings_link = sprintf( '<a href="%1$s">', dokan_get_navigation_url( 'settings/shipping' ) );
+        /* translators: %1$s is replaced with "HTML open entities," %2$s is replaced with "HTML close entities"*/
+        $product_shipping_help_block = sprintf( esc_html__( 'Shipping classes are used by certain shipping methods to group similar products. Before adding a product, please configure the %1$s shipping settings %2$s', 'dokan-lite' ), $shipping_settings_link, '</a>' );
+
+        $section->add_field(
+            Elements::SHIPPING_CLASS, [
+                'title'       => __( 'Shipping Class', 'dokan-lite' ),
+                'type'        => 'select',
+                'name'        => '_shipping_class',
+                'placeholder' => __( 'Select shipping class', 'dokan-lite' ),
+                'options'     => [],
+            ]
+        );
+
+        $section->add_field(
+            Elements::OVERWRITE_SHIPPING_META, [
+                'title' => __( 'Override your store\'s default shipping cost for this product', 'dokan-lite' ),
+                'type'  => 'checkbox',
+                'name'  => '_overwrite_shipping',
+                'value' => 'no',
+            ]
+        );
+
+        $section->add_field(
+            Elements::ADDITIONAL_SHIPPING_COST_META, [
+                'title'       => __( 'Additional cost', 'dokan-lite' ),
+                'type'        => 'number',
+                'name'        => '_additional_price',
+                'placeholder' => '0.00',
+                'min'         => 0,
+                'step'        => 0.01,
+            ]
+        );
+
+        $section->add_field(
+            Elements::ADDITIONAL_SHIPPING_QUANTITY_META, [
+                'title'       => __( 'Per Qty Additional Price', 'dokan-lite' ),
+                'type'        => 'number',
+                'name'        => '_additional_qty',
+                'placeholder' => '0',
+                'min'         => 0,
+                'step'        => 0.01,
+            ]
+        );
+
+        $section->add_field(
+            Elements::ADDITIONAL_SHIPPING_PROCESSING_TIME_META, [
+                'title'   => __( 'Processing Time', 'dokan-lite' ),
+                'type'    => 'select',
+                'name'    => '_dps_processing_time',
+                'options' => [],
+            ]
+        );
+
+        $section->add_field(
+            Elements::TAX_STATUS, [
+                'title'   => __( 'Tax Status', 'dokan-lite' ),
+                'type'    => 'select',
+                'name'    => '_tax_status',
+                'options' => [
+                    'taxable'  => __( 'Taxable', 'dokan-lite' ),
+                    'shipping' => __( 'Shipping only', 'dokan-lite' ),
+                    'none'     => _x( 'None', 'Tax status', 'dokan-lite' ),
+                ],
+            ]
+        );
+
+        $section->add_field(
+            Elements::TAX_CLASS, [
+                'title'   => __( 'Tax Class', 'dokan-lite' ),
+                'type'    => 'select',
+                'name'    => '_tax_class',
+                'options' => wc_get_product_tax_class_options(),
+            ]
+        );
+    }
+}
diff --git a/includes/ProductForm/Section.php b/includes/ProductForm/Section.php
new file mode 100644
index 0000000000..ee306074f6
--- /dev/null
+++ b/includes/ProductForm/Section.php
@@ -0,0 +1,99 @@
+<?php
+
+namespace WeDevs\Dokan\ProductForm;
+
+defined( 'ABSPATH' ) || exit;
+
+/**
+ * Product Form Section
+ *
+ * @since DOKAN_SINCE
+ */
+class Section extends Component {
+
+    /**
+     * Section constructor.
+     *
+     * @since DOKAN_SINCE
+     *
+     * @param string $id
+     * @param array  $args
+     *
+     * @throws \Exception
+     *
+     * @return void
+     */
+    public function __construct( string $id, array $args = [] ) {
+        $this->data['fields'] = [];
+        $this->data = wp_parse_args( $args, $this->data );
+
+        // set id from the args
+        $this->set_id( $id );
+
+        // call parent constructor
+        parent::__construct( $id, $args );
+
+        // check if the required attributes are exists
+        $missing_arguments = self::get_missing_arguments( $this->data );
+        if ( count( $missing_arguments ) > 0 ) {
+            throw new \Exception(
+                sprintf(
+                /* translators: 1: Missing arguments list. */
+                    esc_html__( 'You are missing required arguments of Dokan ProductForm Field: %1$s', 'dokan-lite' ),
+                    esc_attr( join( ', ', $missing_arguments ) )
+                )
+            );
+        }
+    }
+
+    /**
+     * Get fields of the section
+     *
+     * @since DOKAN_SINCE
+     *
+     * @return \WeDevs\Dokan\ProductForm\Field[]
+     */
+    public function get_fields(): array {
+        // initialize fields
+        $this->init_fields();
+
+        return $this->data['fields'];
+    }
+
+    /**
+     * Add field to the section
+     *
+     * @since DOKAN_SINCE
+     *
+     * @param string $id
+     * @param array  $args
+     *
+     * @return void
+     */
+    public function add_field( string $id, array $args ) {
+        // set section id manually
+        $args['section'] = $this->get_id();
+
+        // todo: handle error
+        Factory::add_field( $id, $args );
+    }
+
+    /**
+     * Initialize fields
+     *
+     * @since DOKAN_SINCE
+     *
+     * @return $this
+     */
+    private function init_fields() {
+        $fields = Factory::instance()->get_fields();
+        $this->data['fields'] = array_filter(
+            $fields,
+            function ( Field $field ) {
+                return $field->get_section() === $this->get_id();
+            }
+        );
+
+        return $this;
+    }
+}

From af756c634e8ab2aee3bfddc21958ca9704ca77e7 Mon Sep 17 00:00:00 2001
From: Nurul Umbhiya <zikubd@gmail.com>
Date: Thu, 7 Dec 2023 19:06:11 +0600
Subject: [PATCH 2/5] refactor: removed `Add New Product` page and modal
 related codebase and made Single Step Product Edit page as default refactor:
 replaced product add/edit page code with OOP fix: added Dokan Pro checks for
 `product_vendors_can_create_tags` and `product_category_style` admin settings

---
 assets/src/js/product-editor.js               |  64 --
 dokan.php                                     |   3 +
 includes/Admin/Settings.php                   |  19 -
 includes/Ajax.php                             |  39 --
 includes/Assets.php                           |   2 +-
 includes/Blocks/ProductBlock.php              |   2 +-
 includes/CatalogMode/Dashboard/Products.php   |  15 +-
 includes/Dashboard/Manager.php                |   7 +
 includes/Dashboard/Templates/Manager.php      |  20 +-
 includes/Dashboard/Templates/Products.php     | 655 ++++++++----------
 includes/Emails/NewProduct.php                |   2 +-
 includes/Emails/NewProductPending.php         |   2 +-
 includes/Product/Hooks.php                    |  14 +-
 includes/Product/Manager.php                  |  35 +-
 includes/Product/functions.php                | 152 +---
 includes/ProductCategory/Helper.php           |  72 +-
 includes/ProductForm/Component.php            |  14 +-
 includes/ProductForm/Elements.php             |   6 +-
 includes/ProductForm/Factory.php              |  22 +-
 includes/ProductForm/Field.php                | 164 ++++-
 includes/ProductForm/Init.php                 | 429 ++++++++----
 includes/ProductForm/Section.php              |  25 +-
 includes/REST/Manager.php                     |   2 +-
 includes/Rewrites.php                         |   2 +-
 includes/functions.php                        |   9 +-
 includes/woo-views/html-product-download.php  |  39 +-
 templates/products/add-new-product-modal.php  |   3 -
 templates/products/download-virtual.php       |  13 -
 templates/products/downloadable.php           |  71 --
 templates/products/edit-product-single.php    | 538 --------------
 .../products/edit/edit-product-single.php     | 213 ++++++
 .../sections}/catalog-mode-content.php        |   2 +
 .../edit/sections/download-virtual.php        |  61 ++
 .../products/edit/sections/downloadable.php   | 150 ++++
 templates/products/edit/sections/general.php  | 383 ++++++++++
 .../products/edit/sections/inventory.php      | 229 ++++++
 templates/products/edit/sections/others.php   | 112 +++
 templates/products/inventory.php              |  96 ---
 templates/products/new-product.php            | 367 ----------
 templates/products/others.php                 |  59 --
 templates/products/tmpl-add-product-popup.php | 145 ----
 41 files changed, 2134 insertions(+), 2123 deletions(-)
 delete mode 100644 templates/products/add-new-product-modal.php
 delete mode 100644 templates/products/download-virtual.php
 delete mode 100644 templates/products/downloadable.php
 delete mode 100755 templates/products/edit-product-single.php
 create mode 100755 templates/products/edit/edit-product-single.php
 rename templates/products/{ => edit/sections}/catalog-mode-content.php (99%)
 create mode 100644 templates/products/edit/sections/download-virtual.php
 create mode 100644 templates/products/edit/sections/downloadable.php
 create mode 100644 templates/products/edit/sections/general.php
 create mode 100644 templates/products/edit/sections/inventory.php
 create mode 100644 templates/products/edit/sections/others.php
 delete mode 100644 templates/products/inventory.php
 delete mode 100755 templates/products/new-product.php
 delete mode 100644 templates/products/others.php
 delete mode 100644 templates/products/tmpl-add-product-popup.php

diff --git a/assets/src/js/product-editor.js b/assets/src/js/product-editor.js
index dce29dc460..ebdd90acbf 100755
--- a/assets/src/js/product-editor.js
+++ b/assets/src/js/product-editor.js
@@ -66,7 +66,6 @@
             $( '.dokan-product-attribute-wrapper ul.dokan-attribute-option-list' ).on( 'click', 'button.dokan-add-new-attribute', this.attribute.addNewExtraAttr );
             $( '.product-edit-container .dokan-product-attribute-wrapper' ).on( 'click', 'a.dokan-product-remove-attribute', this.attribute.removeAttribute );
             $( '.product-edit-container .dokan-product-attribute-wrapper' ).on( 'click', 'a.dokan-save-attribute', this.attribute.saveAttribute );
-            $( 'body' ).on( 'click', '.product-container-footer input[type="submit"]', this.createNewProduct );
 
             this.attribute.disbalePredefinedAttribute();
 
@@ -340,69 +339,6 @@
             $( 'body' ).trigger( 'dokan-product-editor-popup-opened', Dokan_Editor );
         },
 
-        createNewProduct: function (e) {
-            e.preventDefault();
-
-            var self = $(this),
-                form = self.closest('form#dokan-add-new-product-form'),
-                btn_id = self.attr('data-btn_id');
-
-            form.find( 'span.dokan-show-add-product-success' ).html('');
-            form.find( 'span.dokan-show-add-product-error' ).html('');
-            form.find( 'span.dokan-add-new-product-spinner' ).css( 'display', 'inline-block' );
-
-            self.attr( 'disabled', 'disabled' );
-
-            if ( form.find( 'input[name="post_title"]' ).val() == '' ) {
-                $( 'span.dokan-show-add-product-error' ).html( dokan.product_title_required );
-                self.removeAttr( 'disabled' );
-                form.find( 'span.dokan-add-new-product-spinner' ).css( 'display', 'none' );
-                return;
-            }
-
-            if ( form.find( 'select[name="product_cat"]' ).val() == '-1' ) {
-                $( 'span.dokan-show-add-product-error' ).html( dokan.product_category_required );
-                self.removeAttr( 'disabled' );
-                form.find( 'span.dokan-add-new-product-spinner' ).css( 'display', 'none' );
-                return;
-            }
-
-            var data = {
-                action:   'dokan_create_new_product',
-                postdata: form.serialize(),
-                _wpnonce : dokan.nonce
-            };
-
-            Dokan_Editor.modal.iziModal('startLoading');
-            $.post( dokan.ajaxurl, data, function( resp ) {
-                if ( resp.success ) {
-                    self.removeAttr( 'disabled' );
-                    if ( btn_id === 'create_new' ) {
-                        $( '#dokan-add-product-popup' ).iziModal('close');
-                        window.location.href = resp.data;
-                    } else {
-                        product_featured_frame = undefined;
-                        $('.dokan-dashboard-product-listing-wrapper').load( window.location.href + ' table.product-listing-table' );
-                        Dokan_Editor.modal.iziModal('resetContent');
-                        Dokan_Editor.openProductPopup();
-                        Dokan_Editor.reRenderPopupElements();
-                        $( 'span.dokan-show-add-product-success' ).html( dokan.product_created_response );
-
-                        setTimeout(function() {
-                            $( 'span.dokan-show-add-product-success' ).html( '' );
-                        }, 3000);
-                    }
-                } else {
-                    self.removeAttr( 'disabled' );
-                    $( 'span.dokan-show-add-product-error' ).html( resp.data );
-                }
-                form.find( 'span.dokan-add-new-product-spinner' ).css( 'display', 'none' );
-            })
-            .always( function () {
-                Dokan_Editor.modal.iziModal('stopLoading');
-            });
-        },
-
         attribute: {
 
             toggleAttribute: function(e) {
diff --git a/dokan.php b/dokan.php
index 503d542a6a..c47fe6f109 100755
--- a/dokan.php
+++ b/dokan.php
@@ -55,6 +55,7 @@
  * @property WeDevs\Dokan\Vendor\Manager $vendor Instance of Vendor Manager Class
  * @property WeDevs\Dokan\BackgroundProcess\Manager $bg_process Instance of WeDevs\Dokan\BackgroundProcess\Manager class
  * @property WeDevs\Dokan\Frontend\Frontend $frontend_manager Instance of \WeDevs\Dokan\Frontend\Frontend class
+ * @property WeDevs\Dokan\Dashboard\Manager $dashboard Instance of \WeDevs\Dokan\Dashboard\Manager class
  */
 final class WeDevs_Dokan {
 
@@ -256,6 +257,7 @@ public function define_constants() {
         $this->define( 'DOKAN_FILE', __FILE__ );
         $this->define( 'DOKAN_DIR', __DIR__ );
         $this->define( 'DOKAN_INC_DIR', __DIR__ . '/includes' );
+        $this->define( 'DOKAN_TEMPLATE_DIR', __DIR__ . '/templates' );
         $this->define( 'DOKAN_LIB_DIR', __DIR__ . '/lib' );
         $this->define( 'DOKAN_PLUGIN_ASSEST', plugins_url( 'assets', __FILE__ ) );
 
@@ -373,6 +375,7 @@ public function init_classes() {
         new \WeDevs\Dokan\Vendor\UserSwitch();
         new \WeDevs\Dokan\CacheInvalidate();
         new \WeDevs\Dokan\Shipping\Hooks();
+        new \WeDevs\Dokan\ProductForm\Init();
 
         if ( is_admin() ) {
             new \WeDevs\Dokan\Admin\Hooks();
diff --git a/includes/Admin/Settings.php b/includes/Admin/Settings.php
index bf19079aea..bc6e37afee 100644
--- a/includes/Admin/Settings.php
+++ b/includes/Admin/Settings.php
@@ -557,25 +557,6 @@ public function get_settings_fields() {
                     'default' => 'on',
                     'tooltip' => __( 'If checked, vendors will have permission to sell immediately after registration. If unchecked, newly registered vendors cannot add products until selling capability is activated manually from admin dashboard.', 'dokan-lite' ),
                 ],
-                'one_step_product_create'     => [
-                    'name'    => 'one_step_product_create',
-                    'label'   => __( 'One Page Product Creation', 'dokan-lite' ),
-                    'desc'    => __( 'Add new product in single page view', 'dokan-lite' ),
-                    'type'    => 'switcher',
-                    'default' => 'on',
-                    'tooltip' => __( 'If disabled, instead of a single add product page it will open a pop up window or vendor will redirect to product page when adding new product.', 'dokan-lite' ),
-                ],
-                'disable_product_popup'     => [
-                    'name'    => 'disable_product_popup',
-                    'label'   => __( 'Disable Product Popup', 'dokan-lite' ),
-                    'desc'    => __( 'Disable add new product in popup view', 'dokan-lite' ),
-                    'type'    => 'switcher',
-                    'default' => 'off',
-                    'show_if' => [
-                        'dokan_selling.one_step_product_create' => [ 'equal' => 'off' ],
-                    ],
-                    'tooltip' => __( 'If disabled, instead of a pop up window vendor will redirect to product page when adding new product.', 'dokan-lite' ),
-                ],
                 'order_status_change'       => [
                     'name'    => 'order_status_change',
                     'label'   => __( 'Order Status Change', 'dokan-lite' ),
diff --git a/includes/Ajax.php b/includes/Ajax.php
index 7b983a18f7..ed3a17607a 100755
--- a/includes/Ajax.php
+++ b/includes/Ajax.php
@@ -41,8 +41,6 @@ public function __construct() {
         add_action( 'wp_ajax_dokan_seller_listing_search', [ $this, 'seller_listing_search' ] );
         add_action( 'wp_ajax_nopriv_dokan_seller_listing_search', [ $this, 'seller_listing_search' ] );
 
-        add_action( 'wp_ajax_dokan_create_new_product', [ $this, 'create_product' ] );
-
         add_action( 'wp_ajax_custom-header-crop', [ $this, 'crop_store_banner' ] );
 
         add_action( 'wp_ajax_dokan_json_search_products_tags', [ $this, 'dokan_json_search_products_tags' ] );
@@ -57,43 +55,6 @@ public function __construct() {
         add_action( 'wp_ajax_dokan-upgrade-dissmiss', [ $this, 'dismiss_pro_notice' ] );
     }
 
-    /**
-     * Create product from popup submission
-     *
-     * @since  2.5.0
-     *
-     * @return void
-     */
-    public function create_product() {
-        check_ajax_referer( 'dokan_reviews' );
-
-        if ( ! current_user_can( 'dokan_add_product' ) ) {
-            wp_send_json_error( __( 'You have no permission to do this action', 'dokan-lite' ) );
-        }
-
-        $submited_data = isset( $_POST['postdata'] ) ? wp_unslash( $_POST['postdata'] ) : ''; //phpcs:ignore
-
-        parse_str( $submited_data, $postdata );
-
-        $response = dokan_save_product( $postdata );
-
-        if ( is_wp_error( $response ) ) {
-            wp_send_json_error( $response->get_error_message() );
-        }
-
-        if ( is_int( $response ) ) {
-            if ( current_user_can( 'dokan_edit_product' ) ) {
-                $redirect = dokan_edit_product_url( $response );
-            } else {
-                $redirect = dokan_get_navigation_url( 'products' );
-            }
-
-            wp_send_json_success( $redirect );
-        } else {
-            wp_send_json_error( __( 'Something wrong, please try again later', 'dokan-lite' ) );
-        }
-    }
-
     /**
      * Check the availability of shop name.
      *
diff --git a/includes/Assets.php b/includes/Assets.php
index db566c4305..afaa082cdd 100644
--- a/includes/Assets.php
+++ b/includes/Assets.php
@@ -514,7 +514,7 @@ public function get_scripts() {
             ],
             'dokan-util-helper'         => [
                 'src'       => $asset_url . '/js/helper.js',
-                'deps'      => [ 'jquery', 'dokan-sweetalert2', 'moment' ],
+                'deps'      => [ 'jquery', 'dokan-sweetalert2', 'moment', 'jquery-tiptip' ],
                 'version'   => filemtime( $asset_path . 'js/helper.js' ),
                 'in_footer' => false,
             ],
diff --git a/includes/Blocks/ProductBlock.php b/includes/Blocks/ProductBlock.php
index 000624ce54..826703f139 100644
--- a/includes/Blocks/ProductBlock.php
+++ b/includes/Blocks/ProductBlock.php
@@ -17,7 +17,7 @@ class ProductBlock {
      * @return array
      */
     public function get_configurations() {
-        $can_create_tags = dokan_get_option( 'product_vendors_can_create_tags', 'dokan_selling' );
+        $can_create_tags = dokan()->is_pro_exists() ? dokan_get_option( 'product_vendors_can_create_tags', 'dokan_selling', 'off' ) : 'off';
 
         return apply_filters(
             'dokan_get_product_block_configurations',
diff --git a/includes/CatalogMode/Dashboard/Products.php b/includes/CatalogMode/Dashboard/Products.php
index d5f4afaffe..fd6aec805d 100644
--- a/includes/CatalogMode/Dashboard/Products.php
+++ b/includes/CatalogMode/Dashboard/Products.php
@@ -27,7 +27,7 @@ public function __construct() {
             return;
         }
         // render catalog mode section under single product edit page
-        add_action( 'dokan_product_edit_after_options', [ $this, 'render_product_section' ], 99, 1 );
+        add_action( 'dokan_product_edit_after_options', [ $this, 'render_product_section' ], 99, 2 );
         // save catalog mode section data
         add_action( 'dokan_product_updated', [ $this, 'save_catalog_mode_data' ], 13 );
     }
@@ -38,29 +38,28 @@ public function __construct() {
      * @since 3.6.4
      *
      * @param $product_id int
+     * @param $product    \WC_Product
      *
      * @return void
      */
-    public function render_product_section( $product_id ) {
+    public function render_product_section( $product_id, $product ) {
         // check permission, don't let vendor staff view this section
         if ( ! current_user_can( 'dokandar' ) ) {
             return;
         }
 
-        // get product data
-        $product = wc_get_product( $product_id );
-        // return if product type is optional
-        if ( ! $product || 'auction' === $product->get_type() ) {
+        // return if product type is auction
+        if ( 'auction' === $product->get_type() ) {
             return;
         }
 
         $defaults = Helper::get_defaults();
         // check for saved values
-        $catalog_mode_data = get_post_meta( $product_id, '_dokan_catalog_mode', true );
+        $catalog_mode_data = $product->get_meta( '_dokan_catalog_mode', true );
 
         //load template
         dokan_get_template_part(
-            'products/catalog-mode-content', '', [
+            'products/edit/sections/catalog-mode-content', '', [
                 'product_id' => $product_id,
                 'saved_data' => $catalog_mode_data ? $catalog_mode_data : $defaults,
             ]
diff --git a/includes/Dashboard/Manager.php b/includes/Dashboard/Manager.php
index d15f96ce14..b99340dca1 100644
--- a/includes/Dashboard/Manager.php
+++ b/includes/Dashboard/Manager.php
@@ -5,6 +5,13 @@
 use WeDevs\Dokan\Dashboard\Templates\Manager as TemplateManager;
 use WeDevs\Dokan\Traits\ChainableContainer;
 
+/**
+ * Dashboard Manager
+ *
+ * @since 3.0.0
+ *
+ * @property TemplateManager $templates Instance of TemplateManager class
+ */
 class Manager {
 
     use ChainableContainer;
diff --git a/includes/Dashboard/Templates/Manager.php b/includes/Dashboard/Templates/Manager.php
index ca75447fa7..ae1e5c8cf3 100644
--- a/includes/Dashboard/Templates/Manager.php
+++ b/includes/Dashboard/Templates/Manager.php
@@ -2,15 +2,21 @@
 
 namespace WeDevs\Dokan\Dashboard\Templates;
 
-use WeDevs\Dokan\Dashboard\Templates\Dashboard;
-use WeDevs\Dokan\Dashboard\Templates\Main;
-use WeDevs\Dokan\Dashboard\Templates\Orders;
-use WeDevs\Dokan\Dashboard\Templates\Products;
-use WeDevs\Dokan\Dashboard\Templates\Settings;
-use WeDevs\Dokan\Dashboard\Templates\Withdraw;
-use WeDevs\Dokan\Dashboard\Templates\MultiStepCategories;
 use WeDevs\Dokan\Traits\ChainableContainer;
 
+/**
+ * Dashboard Template Manager
+ *
+ * @since 3.0.0
+ *
+ * @property Dashboard           $dashboard          Instance of Dashboard class
+ * @property Orders              $orders             Instance of Orders class
+ * @property Products            $products           Instance of Dashboard class
+ * @property Settings            $settings           Instance of Settings class
+ * @property Withdraw            $withdraw           Instance of Withdraw class
+ * @property MultiStepCategories $product_category   Instance of MultiStepCategories class
+ * @property ReverseWithdrawal   $reverse_withdrawal Instance of ReverseWithdrawal class
+ */
 class Manager {
 
     use ChainableContainer;
diff --git a/includes/Dashboard/Templates/Products.php b/includes/Dashboard/Templates/Products.php
index 7128a1092f..437a7f23c2 100644
--- a/includes/Dashboard/Templates/Products.php
+++ b/includes/Dashboard/Templates/Products.php
@@ -2,7 +2,12 @@
 
 namespace WeDevs\Dokan\Dashboard\Templates;
 
-use WeDevs\Dokan\ProductCategory\Helper;
+use WC_Product;
+use WC_Product_Factory;
+use WC_Product_Simple;
+use WeDevs\Dokan\ProductForm\Elements as ProductFormElements;
+use WeDevs\Dokan\ProductForm\Factory as ProductFormFactory;
+use WP_Post;
 
 /**
  *  Product Functionality for Product Handler
@@ -13,8 +18,31 @@
  */
 class Products {
 
-    public static $errors;
+    /**
+     *  Errors
+     *
+     * @since 3.0.0
+     *
+     * @var array
+     */
+    public static $errors = [];
+
+    /**
+     *  Product Category
+     *
+     * @since 3.0.0
+     *
+     * @var array
+     */
     public static $product_cat;
+
+    /**
+     *  Post Content
+     *
+     * @since 3.0.0
+     *
+     * @var array
+     */
     public static $post_content;
 
     /**
@@ -26,19 +54,20 @@ class Products {
      * @uses  filters
      */
     public function __construct() {
+        // render product listing template
         add_action( 'dokan_render_product_listing_template', [ $this, 'render_product_listing_template' ], 11 );
-        add_action( 'template_redirect', [ $this, 'handle_product_add' ], 11 );
-        add_action( 'template_redirect', [ $this, 'handle_product_update' ], 11 );
-        add_action( 'template_redirect', [ $this, 'handle_delete_product' ] );
-        add_action( 'dokan_render_new_product_template', [ $this, 'render_new_product_template' ], 10 );
+
+        // render product edit page template sections
         add_action( 'dokan_render_product_edit_template', [ $this, 'load_product_edit_template' ], 11 );
-        add_action( 'template_redirect', [ $this, 'render_product_edit_page_for_email' ], 1 );
-        add_action( 'dokan_after_listing_product', [ $this, 'load_add_new_product_popup' ], 10 );
-        add_action( 'dokan_after_listing_product', [ $this, 'load_add_new_product_modal' ], 10 );
         add_action( 'dokan_product_edit_after_title', [ __CLASS__, 'load_download_virtual_template' ], 10, 2 );
         add_action( 'dokan_product_edit_after_main', [ __CLASS__, 'load_inventory_template' ], 5, 2 );
         add_action( 'dokan_product_edit_after_main', [ __CLASS__, 'load_downloadable_template' ], 10, 2 );
         add_action( 'dokan_product_edit_after_inventory_variants', [ __CLASS__, 'load_others_template' ], 85, 2 );
+
+        // handle product edit/delete operations
+        add_action( 'template_redirect', [ $this, 'render_product_edit_page_for_email' ], 1 );
+        add_action( 'template_redirect', [ $this, 'handle_delete_product' ] );
+        add_action( 'template_redirect', [ $this, 'handle_product_update' ], 11 );
     }
 
     /**
@@ -46,7 +75,7 @@ public function __construct() {
      *
      * @since 3.0.0
      *
-     * @param void $errors
+     * @param array $errors
      *
      * @return void
      */
@@ -76,56 +105,145 @@ public function get_errors() {
         return self::$errors;
     }
 
+    /**
+     * Load Product Edit Template
+     *
+     * @since 2.4
+     *
+     * @return void
+     */
+    public function load_product_edit_template() {
+        // check for permission
+        if ( ! current_user_can( 'dokan_edit_product' ) ) {
+            dokan_get_template_part(
+                'global/dokan-error', '', [
+                    'deleted' => false,
+                    'message' => __( 'You have no permission to view this page', 'dokan-lite' ),
+                ]
+            );
+
+            return;
+        }
+
+        // check if seller is enabled for selling
+        if ( ! dokan_is_seller_enabled( dokan_get_current_user_id() ) ) {
+            dokan_seller_not_enabled_notice();
+
+            return;
+        }
+
+        // check for valid nonce and product id
+        if ( ! isset( $_GET['_dokan_edit_product_nonce'], $_GET['product_id'] ) || ! wp_verify_nonce( sanitize_key( $_GET['_dokan_edit_product_nonce'] ), 'dokan_edit_product_nonce' ) ) {
+            dokan_get_template_part(
+                'global/dokan-error', '', [
+                    'deleted' => false,
+                    'message' => __( 'Are you cheating?', 'dokan-lite' ),
+                ]
+            );
+
+            return;
+        }
+
+        // check if this is a product add or edit page
+        $product_id  = intval( wp_unslash( $_GET['product_id'] ) );
+        $new_product = false;
+        if ( ! $product_id ) {
+            // this is a product add page request
+            // now create a new product with auto draft status
+            $product = new WC_Product_Simple();
+            $product->set_status( 'auto-draft' );
+            $product->save();
+            $new_product = true;
+        } else {
+            // this is a product edit page request
+            $product = wc_get_product( $product_id );
+        }
+
+        if ( ! dokan_is_product_author( $product->get_id() ) ) {
+            dokan_get_template_part(
+                'global/dokan-error', '', [
+                    'deleted' => false,
+                    'message' => __( 'Access Denied. Given product is not yours.', 'dokan-lite' ),
+                ]
+            );
+
+            return;
+        }
+
+        dokan_get_template_part(
+            'products/edit/edit-product-single', '',
+            [
+                'product'        => $product,
+                'new_product'    => $new_product,
+                'from_shortcode' => true,
+            ]
+        );
+    }
+
     /**
      * Load product
      *
      * @since 1.0.0
      *
+     * @param WP_Post    $post
+     * @param int        $post_id
+     *
      * @return void
      */
     public static function load_download_virtual_template( $post, $post_id ) {
-        $_downloadable   = get_post_meta( $post_id, '_downloadable', true );
-        $_virtual        = get_post_meta( $post_id, '_virtual', true );
-        $is_downloadable = 'yes' === $_downloadable;
-        $is_virtual      = 'yes' === $_virtual;
-        $digital_mode    = dokan_get_option( 'global_digital_mode', 'dokan_general', 'sell_both' );
+        $product = wc_get_product( $post_id );
+        if ( ! $product ) {
+            return;
+        }
 
+        $digital_mode = dokan_get_option( 'global_digital_mode', 'dokan_general', 'sell_both' );
         if ( 'sell_physical' === $digital_mode ) {
             return;
         }
 
+        // get section data
+        $section = ProductFormFactory::get_section( 'downloadable_virtual' );
+        if ( is_wp_error( $section ) || ! $section->is_visible() ) {
+            return;
+        }
+
         dokan_get_template_part(
-            'products/download-virtual', '', [
-                'post_id'         => $post_id,
-                'post'            => $post,
-                'is_downloadable' => $is_downloadable,
-                'is_virtual'      => $is_virtual,
-                'digital_mode'    => $digital_mode,
-                'class'           => 'show_if_subscription hide_if_variable-subscription show_if_simple',
+            'products/edit/sections/download-virtual', '', [
+                'product'      => $product,
+                'section'      => $section,
+                'digital_mode' => $digital_mode,
+                'class'        => 'show_if_subscription hide_if_variable-subscription show_if_simple',
             ]
         );
     }
 
     /**
-     * Load invendor template
+     * Load inventory template
      *
      * @since 2.9.2
      *
+     * @param WP_Post    $post
+     * @param int        $post_id
+     *
      * @return void
      */
     public static function load_inventory_template( $post, $post_id ) {
-        $_sold_individually = get_post_meta( $post_id, '_sold_individually', true );
-        $_stock             = get_post_meta( $post_id, '_stock', true );
-        $_low_stock_amount  = get_post_meta( $post_id, '_low_stock_amount', true );
+        $product = wc_get_product( $post_id );
+        if ( ! $product ) {
+            return;
+        }
+
+        // get section data
+        $section = ProductFormFactory::get_section( 'inventory' );
+        if ( is_wp_error( $section ) || ! $section->is_visible() ) {
+            return;
+        }
 
         dokan_get_template_part(
-            'products/inventory', '', [
-                'post_id'            => $post_id,
-                'post'               => $post,
-                '_sold_individually' => $_sold_individually,
-                '_stock'             => $_stock,
-                '_low_stock_amount'  => $_low_stock_amount,
-                'class'              => '',
+            'products/edit/sections/inventory', '', [
+                'section' => $section,
+                'product' => $product,
+                'class'   => '',
             ]
         );
     }
@@ -135,19 +253,32 @@ public static function load_inventory_template( $post, $post_id ) {
      *
      * @since 2.9.2
      *
+     * @param WP_Post    $post
+     * @param int        $post_id
+     *
      * @return void
      */
     public static function load_downloadable_template( $post, $post_id ) {
-        $digital_mode = dokan_get_option( 'global_digital_mode', 'dokan_general', 'sell_both' );
+        $product = wc_get_product( $post_id );
+        if ( ! $product ) {
+            return;
+        }
 
+        $digital_mode = dokan_get_option( 'global_digital_mode', 'dokan_general', 'sell_both' );
         if ( 'sell_physical' === $digital_mode ) {
             return;
         }
 
+        // get section data
+        $section = ProductFormFactory::get_section( 'downloadable' );
+        if ( is_wp_error( $section ) || ! $section->is_visible() ) {
+            return;
+        }
+
         dokan_get_template_part(
-            'products/downloadable', '', [
-                'post_id' => $post_id,
-                'post'    => $post,
+            'products/edit/sections/downloadable', '', [
+                'section' => $section,
+                'product' => $product,
                 'class'   => 'show_if_downloadable',
             ]
         );
@@ -158,64 +289,36 @@ public static function load_downloadable_template( $post, $post_id ) {
      *
      * @since 2.9.2
      *
+     * @param WP_Post    $post
+     * @param int        $post_id
+     *
      * @return void
      */
     public static function load_others_template( $post, $post_id ) {
-        $product            = wc_get_product( $post_id );
-        $_visibility        = $product->get_catalog_visibility();
-        $visibility_options = dokan_get_product_visibility_options();
+        $product = wc_get_product( $post_id );
+        if ( ! $product ) {
+            return;
+        }
+
+        $section = ProductFormFactory::get_section( 'others' );
+        if ( is_wp_error( $section ) || ! $section->is_visible() ) {
+            return;
+        }
 
         // set new post status
         $post_status = dokan_get_default_product_status( dokan_get_current_user_id() );
         $post_status = $product->get_status() === 'auto-draft' ? $post_status : $product->get_status();
 
         dokan_get_template_part(
-            'products/others', '', [
-                'post_id'            => $post_id,
-                'post'               => $post,
-                'post_status'        => apply_filters( 'dokan_post_edit_default_status', $post_status, $product ),
-                '_visibility'        => $_visibility,
-                'visibility_options' => $visibility_options,
-                'class'              => '',
+            'products/edit/sections/others', '', [
+                'section'     => $section,
+                'product'     => $product,
+                'post_status' => apply_filters( 'dokan_post_edit_default_status', $post_status, $product ),
+                'class'       => '',
             ]
         );
     }
 
-    /**
-     * Render New Product Template for only free version
-     *
-     * @since 2.4
-     *
-     * @param array $query_vars
-     *
-     * @return void
-     */
-    public function render_new_product_template( $query_vars ) {
-        if ( isset( $query_vars['new-product'] ) && ! dokan()->is_pro_exists() ) {
-            dokan_get_template_part( 'products/new-product' );
-        }
-    }
-
-    /**
-     * Load Product Edit Template
-     *
-     * @since 2.4
-     *
-     * @return void
-     */
-    public function load_product_edit_template() {
-        if ( current_user_can( 'dokan_edit_product' ) ) {
-            dokan_get_template_part( 'products/edit-product-single' );
-        } else {
-            dokan_get_template_part(
-                'global/dokan-error', '', [
-                    'deleted' => false,
-                    'message' => __( 'You have no permission to view this page', 'dokan-lite' ),
-                ]
-            );
-        }
-    }
-
     /**
      * Render Product Edit Page for Email.
      *
@@ -243,11 +346,9 @@ public function render_product_edit_page_for_email() {
      *
      * @since 2.4
      *
-     * @param string $action
-     *
      * @return void
      */
-    public function render_product_listing_template( $action ) {
+    public function render_product_listing_template() {
         $bulk_statuses = apply_filters(
             'dokan_bulk_product_statuses', [
                 '-1'     => __( 'Bulk Actions', 'dokan-lite' ),
@@ -259,11 +360,13 @@ public function render_product_listing_template( $action ) {
     }
 
     /**
-     * Handle product add
+     * Handle product update
+     *
+     * @var $product WC_Product
      *
      * @return void
      */
-    public function handle_product_add() {
+    public function handle_product_update() {
         if ( ! is_user_logged_in() ) {
             return;
         }
@@ -272,320 +375,157 @@ public function handle_product_add() {
             return;
         }
 
-        if ( ! isset( $_POST['dokan_add_new_product_nonce'] ) || ! wp_verify_nonce( sanitize_key( $_POST['dokan_add_new_product_nonce'] ), 'dokan_add_new_product' ) ) {
+        if ( ! isset( $_POST['dokan_edit_product_nonce'] ) || ! wp_verify_nonce( sanitize_key( $_POST['dokan_edit_product_nonce'] ), 'dokan_edit_product' ) ) {
             return;
         }
 
-        $postdata = wp_unslash( $_POST ); // phpcs:ignore
-
-        $errors             = [];
-        self::$product_cat  = - 1;
-        self::$post_content = __( 'Details of your product ...', 'dokan-lite' );
-
-        if ( isset( $postdata['add_product'] ) ) {
-            $post_title     = sanitize_text_field( $postdata['post_title'] );
-            $post_content   = wp_kses_post( $postdata['post_content'] );
-            $post_excerpt   = wp_kses_post( $postdata['post_excerpt'] );
-            $featured_image = absint( sanitize_text_field( $postdata['feat_image_id'] ) );
-
-            if ( empty( $post_title ) ) {
-                $errors[] = __( 'Please enter product title', 'dokan-lite' );
-            }
-
-            if ( ! isset( $postdata['chosen_product_cat'] ) ) {
-                if ( Helper::product_category_selection_is_single() ) {
-                    if ( absint( $postdata['product_cat'] ) < 0 ) {
-                        $errors[] = __( 'Please select a category', 'dokan-lite' );
-                    }
-                } else {
-                    if ( ! isset( $postdata['product_cat'] ) || empty( $postdata['product_cat'] ) ) {
-                        $errors[] = __( 'Please select at least one category', 'dokan-lite' );
-                    }
-                }
-            } elseif ( empty( $postdata['chosen_product_cat'] ) ) {
-                $errors[] = __( 'Please select a category', 'dokan-lite' );
-            }
-
-            self::$errors = apply_filters( 'dokan_can_add_product', $errors );
-
-            if ( ! self::$errors ) {
-                $timenow        = dokan_current_datetime()->setTimezone( new \DateTimeZone( 'UTC' ) );
-                $product_status = dokan_get_default_product_status( dokan_get_current_user_id() );
-                $post_data      = apply_filters(
-                    'dokan_insert_product_post_data', [
-                        'post_type'         => 'product',
-                        'post_status'       => $product_status,
-                        'post_title'        => $post_title,
-                        'post_content'      => $post_content,
-                        'post_excerpt'      => $post_excerpt,
-                        'post_date_gmt'     => $timenow->format( 'Y-m-d H:i:s' ),
-                        'post_modified_gmt' => $timenow->format( 'Y-m-d H:i:s' ),
-                    ]
-                );
-
-                $product_id = wp_insert_post( $post_data );
-
-                if ( $product_id ) {
-                    // set images
-                    if ( $featured_image ) {
-                        set_post_thumbnail( $product_id, $featured_image );
-                    }
-
-                    if ( isset( $postdata['product_tag'] ) && ! empty( $postdata['product_tag'] ) ) {
-                        $tags_ids = array_map( 'absint', (array) $postdata['product_tag'] );
-                        wp_set_object_terms( $product_id, $tags_ids, 'product_tag' );
-                    }
-
-                    /* set product category */
-                    if ( ! isset( $postdata['chosen_product_cat'] ) ) {
-                        if ( Helper::product_category_selection_is_single() ) {
-                            wp_set_object_terms( $product_id, (int) $postdata['product_cat'], 'product_cat' );
-                        } else {
-                            if ( isset( $postdata['product_cat'] ) && ! empty( $postdata['product_cat'] ) ) {
-                                $cat_ids = array_map( 'absint', (array) $postdata['product_cat'] );
-                                wp_set_object_terms( $product_id, $cat_ids, 'product_cat' );
-                            }
-                        }
-                    } else {
-                        $chosen_cat = Helper::product_category_selection_is_single() ? [ reset( $postdata['chosen_product_cat'] ) ] : $postdata['chosen_product_cat'];
-                        Helper::set_object_terms_from_chosen_categories( $product_id, $chosen_cat );
-                    }
-
-                    /** Set Product type, default is simple */
-                    $product_type = empty( $postdata['product_type'] ) ? 'simple' : $postdata['product_type'];
-                    wp_set_object_terms( $product_id, $product_type, 'product_type' );
-
-                    // Gallery Images
-                    if ( ! empty( $postdata['product_image_gallery'] ) ) {
-                        $attachment_ids = array_filter( explode( ',', wc_clean( $postdata['product_image_gallery'] ) ) );
-                        update_post_meta( $product_id, '_product_image_gallery', implode( ',', $attachment_ids ) );
-                    }
-
-                    if ( isset( $postdata['_regular_price'] ) ) {
-                        update_post_meta( $product_id, '_regular_price', ( $postdata['_regular_price'] === '' ) ? '' : wc_format_decimal( $postdata['_regular_price'] ) );
-                    }
-
-                    if ( isset( $postdata['_sale_price'] ) ) {
-                        update_post_meta( $product_id, '_sale_price', ( $postdata['_sale_price'] === '' ? '' : wc_format_decimal( $postdata['_sale_price'] ) ) );
-                        $date_from = isset( $postdata['_sale_price_dates_from'] ) ? wc_clean( $postdata['_sale_price_dates_from'] ) : '';
-                        $date_to   = isset( $postdata['_sale_price_dates_to'] ) ? wc_clean( $postdata['_sale_price_dates_to'] ) : '';
-                        $now       = dokan_current_datetime();
-
-                        // Dates
-                        if ( $date_from ) {
-                            update_post_meta( $product_id, '_sale_price_dates_from', $now->modify( $date_from )->setTime( 0, 0, 0 )->getTimestamp() );
-                        } else {
-                            update_post_meta( $product_id, '_sale_price_dates_from', '' );
-                        }
-
-                        if ( $date_to ) {
-                            update_post_meta( $product_id, '_sale_price_dates_to', $now->modify( $date_to )->setTime( 23, 59, 59 )->getTimestamp() );
-                        } else {
-                            update_post_meta( $product_id, '_sale_price_dates_to', '' );
-                        }
-
-                        if ( $date_to && ! $date_from ) {
-                            update_post_meta( $product_id, '_sale_price_dates_from', $now->setTime( 0, 0, 0 )->getTimestamp() );
-                        }
-
-                        if ( '' !== $postdata['_sale_price'] && '' === $date_to && '' === $date_from ) {
-                            update_post_meta( $product_id, '_price', wc_format_decimal( $postdata['_sale_price'] ) );
-                        } else {
-                            update_post_meta( $product_id, '_price', ( $postdata['_regular_price'] === '' ) ? '' : wc_format_decimal( $postdata['_regular_price'] ) );
-                        }
-                        // Update price if on sale
-                        if ( '' !== $postdata['_sale_price'] && $date_from && $now->modify( $date_from )->getTimestamp() < $now->getTimestamp() ) {
-                            update_post_meta( $product_id, '_price', wc_format_decimal( $postdata['_sale_price'] ) );
-                        }
-                    }
-
-                    update_post_meta( $product_id, '_visibility', 'visible' );
-                    update_post_meta( $product_id, '_stock_status', 'instock' );
-
-                    do_action( 'dokan_new_product_added', $product_id, $postdata );
-
-                    if ( current_user_can( 'dokan_edit_product' ) ) {
-                        $redirect = dokan_edit_product_url( $product_id );
-                    } else {
-                        $redirect = dokan_get_navigation_url( 'products' );
-                    }
-
-                    if ( 'create_and_add_new' === $postdata['add_product'] ) {
-                        $redirect = add_query_arg(
-                            [
-                                'created_product'          => $product_id,
-                                '_dokan_add_product_nonce' => wp_create_nonce( 'dokan_add_product_nonce' ),
-                            ],
-                            dokan_get_navigation_url( 'new-product' )
-                        );
-                    }
-
-                    wp_safe_redirect( apply_filters( 'dokan_add_new_product_redirect', $redirect, $product_id ) );
-                    exit;
-                }
-            }
-        }
-    }
-
-    /**
-     * Handle product update
-     *
-     * @return void
-     */
-    public function handle_product_update() {
-        if ( ! is_user_logged_in() ) {
-            return;
-        }
+        $product_id = isset( $_POST['dokan_product_id'] ) ? absint( $_POST['dokan_product_id'] ) : 0;
+        if ( ! $product_id ) {
+            self::$errors[] = __( 'No product id is set!', 'dokan-lite' );
 
-        if ( ! dokan_is_user_seller( get_current_user_id() ) ) {
             return;
         }
 
-        if ( ! isset( $_POST['dokan_update_product'] ) ) {
-            return;
-        }
+        if ( ! dokan_is_product_author( $product_id ) ) {
+            self::$errors[] = __( 'I swear this is not your product!', 'dokan-lite' );
 
-        if ( ! isset( $_POST['dokan_edit_product_nonce'] ) || ! wp_verify_nonce( sanitize_key( $_POST['dokan_edit_product_nonce'] ), 'dokan_edit_product' ) ) {
             return;
         }
 
-        $errors       = [];
-        $post_title   = isset( $_POST['post_title'] ) ? sanitize_text_field( wp_unslash( $_POST['post_title'] ) ) : '';
-        $post_slug    = ! empty( $_POST['editable-post-name'] ) ? sanitize_title( wp_unslash( $_POST['editable-post-name'] ) ) : '';
-        $post_status  = isset( $_POST['post_status'] ) ? sanitize_text_field( wp_unslash( $_POST['post_status'] ) ) : '';
-        $post_content = isset( $_POST['post_content'] ) ? wp_kses_post( wp_unslash( $_POST['post_content'] ) ) : '';
-        $post_excerpt = isset( $_POST['post_excerpt'] ) ? wp_kses_post( wp_unslash( $_POST['post_excerpt'] ) ) : '';
+        // Process the product type first so that we have the correct class to run setters.
+        $product_type_field = ProductFormFactory::get_field( ProductFormElements::TYPE );
+        $product_type       = empty( $_POST[ $product_type_field->get_name() ] ) ? WC_Product_Factory::get_product_type( $product_id ) : sanitize_title( wp_unslash( $_POST[ $product_type_field->get_name() ] ) );
+        $classname          = WC_Product_Factory::get_product_classname( $product_id, $product_type ? $product_type : 'simple' );
+        $product            = new $classname( $product_id );
 
-        if ( empty( $post_title ) ) {
-            $errors[] = __( 'Please enter product title', 'dokan-lite' );
-        }
+        /**
+         * @var WC_Product $product
+         */
+        if ( ! $product ) {
+            self::$errors[] = __( 'No product found with given product id!', 'dokan-lite' );
 
-        $post_id = isset( $_POST['dokan_product_id'] ) ? absint( $_POST['dokan_product_id'] ) : 0;
-        if ( ! $post_id ) {
-            $errors[] = __( 'No product found!', 'dokan-lite' );
+            return;
         }
 
-        $current_post_status = get_post_status( $post_id );
+        $props               = [];
+        $meta_data           = [];
+        $current_post_status = $product->get_status();
         $is_new_product      = 'auto-draft' === $current_post_status;
 
-        if ( empty( $post_status ) ) {
-            $post_status = $current_post_status;
-        }
-
-        if ( ! dokan_is_product_author( $post_id ) ) {
-            $errors[] = __( 'I swear this is not your product!', 'dokan-lite' );
-        }
+        foreach ( ProductFormFactory::get_fields() as $field_id => $field ) {
+            if ( $field->is_other_type() ) {
+                // we will only process props and meta in this block
+                continue;
+            }
+            // check if the field is visible
+            if ( ! $field->is_visible() ) {
+                continue;
+            }
 
-        if ( empty( $_POST['chosen_product_cat'] ) ) {
-            $errors[] = __( 'Please select a category', 'dokan-lite' );
-        }
+            // check if field is required
+            if ( $field->is_required() && empty( $_POST[ $field->get_name() ] ) ) {
+                self::$errors[ $field->get_id() ][] = ! empty( $field->get_error_message() )
+                    ? $field->get_error_message()
+                    // translators: 1) field title
+                    : sprintf( __( '%1$s is a required field.', 'dokan-lite' ), $field->get_title() );
+                continue;
+            }
 
-        if ( isset( $_POST['product_tag'] ) ) {
-            /**
-             * Filter of maximun a vendor can add tags.
-             *
-             * @since 3.3.7
-             *
-             * @param integer default -1
-             */
-            $maximum_tags_select_length = apply_filters( 'dokan_product_tags_select_max_length', - 1 );
+            // check if the field is present
+            if ( ! isset( $_POST[ $field->get_name() ] ) ) {
+                continue;
+            }
 
-            // Setting limitation for how many product tags that vendor can input.
-            $tag_count = count( (array) $_POST['product_tag'] );
-            if ( $maximum_tags_select_length !== - 1 && $tag_count > $maximum_tags_select_length ) {
-                // translators: %s: maximum tag length
-                $errors[] = sprintf( __( 'You can only select %s tags', 'dokan-lite' ), number_format_i18n( $maximum_tags_select_length ) );
+            // validate field data
+            switch ( $field->get_id() ) {
+                case ProductFormElements::CATEGORIES:
+                    $field_value = $field->sanitize( (array) wp_unslash( $_POST['chosen_product_cat'] ), $product ); // phpcs:ignore
+                    break;
+
+                case ProductFormElements::DOWNLOADS:
+                    $file_names  = isset( $_POST['_wc_file_names'] ) ? wp_unslash( $_POST['_wc_file_names'] ) : [];  // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
+                    $file_urls   = isset( $_POST['_wc_file_urls'] ) ? wp_unslash( $_POST['_wc_file_urls'] ) : [];  // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
+                    $file_hashes = isset( $_POST['_wc_file_hashes'] ) ? wp_unslash( $_POST['_wc_file_hashes'] ) : []; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
+                    $field_value = $field->sanitize( $file_names, $file_urls, $file_hashes ); //phpcs:ignore
+                    break;
+
+                case ProductFormElements::STOCK_QUANTITY:
+                    $original_stock = isset( $_POST['_original_stock'] ) ? wc_stock_amount( wp_unslash( $_POST['_original_stock'] ) ) : false; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
+                    $field_value    = $field->sanitize( wp_unslash( $_POST[ $field->get_name() ] ), $original_stock, $product ); //phpcs:ignore
+                    break;
+
+                default:
+                    // give a chance to other plugins to sanitize their data
+                    $field_value = apply_filters( 'dokan_product_update_field_value', null, $field, wp_unslash( $_POST[ $field->get_name() ] ), $product ); // phpcs:ignore
+                    if ( null === $field_value ) {
+                        $field_value = $field->sanitize( wp_unslash( $_POST[ $field->get_name() ] ) ); //phpcs:ignore
+                    }
+                    break;
             }
-        }
 
-        self::$errors = apply_filters( 'dokan_can_edit_product', $errors );
+            if ( is_wp_error( $field_value ) ) {
+                self::$errors[ $field->get_id() ] = $field_value->get_error_message();
+            } else {
+                //store field value
+                $field->set_value( $field_value );
+            }
 
-        if ( self::$errors ) {
-            return;
+            // store props and meta data
+            if ( $field->is_prop() ) {
+                $props[ $field->get_id() ] = $field_value;
+            } elseif ( $field->is_meta() ) {
+                $meta_data[ $field->get_id() ] = $field_value;
+            }
         }
 
-        $product_info = apply_filters(
-            'dokan_update_product_post_data', [
-                'ID'             => $post_id,
-                'post_title'     => $post_title,
-                'post_content'   => $post_content,
-                'post_excerpt'   => $post_excerpt,
-                'post_status'    => $post_status,
-                'comment_status' => isset( $_POST['_enable_reviews'] ) ? 'open' : 'closed',
-            ]
-        );
-
-        if ( $post_slug ) {
-            $product_info['post_name'] = wp_unique_post_slug( $post_slug, $post_id, $post_status, 'product', 0 );
+        // check for valid product tag
+        if ( ! empty( $_POST['editable-post-name'] ) ) {
+            $post_slug                       = ProductFormFactory::get_field( ProductFormElements::SLUG );
+            $post_status                     = $props[ ProductFormElements::STATUS ] ?? dokan_get_default_product_status( dokan_get_current_user_id() );
+            $props[ $post_slug->get_name() ] = $post_slug->sanitize( wp_unslash( $_POST['editable-post-name'] ), $product_id, $post_status ); //phpcs:ignore
         }
 
-        wp_update_post( $product_info );
+        // apply hooks before saving product
+        $props     = apply_filters( 'dokan_product_edit_props', $props, $product );
+        $meta_data = apply_filters( 'dokan_product_edit_meta_data', $meta_data, $product );
 
-        /** Set Product tags */
-        if ( isset( $_POST['product_tag'] ) ) {
-            $tags_ids = array_map( 'absint', (array) wp_unslash( $_POST['product_tag'] ) );
-        } else {
-            $tags_ids = [];
+        $errors = $product->set_props( $props );
+        if ( is_wp_error( $errors ) ) {
+            self::$errors = array_merge( self::$errors, $errors->get_error_messages() );
+            exit;
         }
 
-        wp_set_object_terms( $post_id, $tags_ids, 'product_tag' );
-
-        /* set product category */
-        $chosen_product_categories = array_map( 'absint', (array) wp_unslash( $_POST['chosen_product_cat'] ) );
-        $chosen_cat                = Helper::product_category_selection_is_single() ? [ reset( $chosen_product_categories ) ] : $chosen_product_categories;
-        Helper::set_object_terms_from_chosen_categories( $post_id, $chosen_cat );
-
-        //set prodcuct type default is simple
-        $product_type = empty( $_POST['product_type'] ) ? 'simple' : sanitize_text_field( wp_unslash( $_POST['product_type'] ) );
-        wp_set_object_terms( $post_id, $product_type, 'product_type' );
-
-        // set images
-        $featured_image = isset( $_POST['feat_image_id'] ) ? absint( wp_unslash( $_POST['feat_image_id'] ) ) : 0;
-        if ( $featured_image ) {
-            set_post_thumbnail( $post_id, $featured_image );
-        } else {
-            delete_post_thumbnail( $post_id );
+        // return early for validation errors
+        self::$errors = apply_filters( 'dokan_can_edit_product', self::$errors );
+        if ( ! empty( self::$errors ) ) {
+            return;
         }
 
-        $postdata = wp_unslash( $_POST ); // phpcs:ignore
-        /**  Process all variation products meta */
-        dokan_process_product_meta( $post_id, $postdata );
-
         if ( $is_new_product ) {
-            do_action( 'dokan_new_product_added', $post_id, $postdata );
+            do_action( 'dokan_before_new_product_added', $product, $props, $meta_data );
         } else {
-            do_action( 'dokan_product_updated', $post_id, $postdata );
+            do_action( 'dokan_before_product_updated', $product, $props, $meta_data );
         }
 
-        $redirect = apply_filters( 'dokan_add_new_product_redirect', dokan_edit_product_url( $post_id ), $post_id );
+        // add product meta data
+        foreach ( $meta_data as $meta_key => $meta_value ) {
+            $product->add_meta_data( $meta_key, $meta_value, true );
+        }
 
-        // if any error inside dokan_process_product_meta function
-        global $woocommerce_errors;
+        // save product
+        $product->save();
 
-        if ( $woocommerce_errors ) {
-            wp_safe_redirect( add_query_arg( [ 'errors' => array_map( 'urlencode', $woocommerce_errors ) ], $redirect ) );
-            exit;
+        if ( $is_new_product ) {
+            do_action( 'dokan_new_product_added', $product_id, [] );
+        } else {
+            do_action( 'dokan_product_updated', $product_id, [] );
         }
 
+        $redirect = apply_filters( 'dokan_add_new_product_redirect', dokan_edit_product_url( $product_id ), $product_id );
         wp_safe_redirect( add_query_arg( [ 'message' => 'success' ], $redirect ) );
         exit;
     }
 
-    public function load_add_new_product_popup() {
-        dokan_get_template_part( 'products/tmpl-add-product-popup' );
-    }
-
-    /**
-     * Add new product open modal html
-     *
-     * @since 3.7.0
-     *
-     * @return void
-     */
-    public function load_add_new_product_modal() {
-        dokan_get_template_part( 'products/add-new-product-modal' );
-    }
-
     /**
      * Handle delete product link
      *
@@ -630,5 +570,4 @@ public function handle_delete_product() {
         wp_safe_redirect( add_query_arg( [ 'message' => 'product_deleted' ], $redirect ) );
         exit;
     }
-
 }
diff --git a/includes/Emails/NewProduct.php b/includes/Emails/NewProduct.php
index 51b29c9b1d..6490f21a05 100644
--- a/includes/Emails/NewProduct.php
+++ b/includes/Emails/NewProduct.php
@@ -80,7 +80,7 @@ public function trigger( $product_id ) {
             return;
         }
 
-        $product->update_meta_data( '_dokan_new_product_email_sent', 'yes' );
+        $product->add_meta_data( '_dokan_new_product_email_sent', 'yes', true );
         $product->save();
 
         if ( ! $this->is_enabled() || ! $this->get_recipient() ) {
diff --git a/includes/Emails/NewProductPending.php b/includes/Emails/NewProductPending.php
index cb86194dc6..707b09807f 100644
--- a/includes/Emails/NewProductPending.php
+++ b/includes/Emails/NewProductPending.php
@@ -81,7 +81,7 @@ public function trigger( $product_id ) {
             return;
         }
 
-        $product->update_meta_data( '_dokan_new_product_email_sent', 'yes' );
+        $product->add_meta_data( '_dokan_new_product_email_sent', 'yes', true );
         $product->save();
 
         if ( ! $this->is_enabled() || ! $this->get_recipient() ) {
diff --git a/includes/Product/Hooks.php b/includes/Product/Hooks.php
index a2c9cb99a2..96e74e0275 100644
--- a/includes/Product/Hooks.php
+++ b/includes/Product/Hooks.php
@@ -362,16 +362,18 @@ public function set_product_status( $all_statuses, int $product_id ) {
      *
      * @since 3.8.2
      *
-     * @param int|WC_Product $product_id
+     * @param int $product_id
      *
      * @return void
      */
     public function set_new_product_email_status( $product_id ) {
-        if ( is_a( $product_id, 'WC_Product' ) ) {
-            $product_id->update_meta_data( '_dokan_new_product_email_sent', 'no' );
-        } else {
-            update_post_meta( $product_id, '_dokan_new_product_email_sent', 'no' );
+        $product = wc_get_product( $product_id );
+        if ( ! $product ) {
+            return;
         }
+
+        $product->add_meta_data( '_dokan_new_product_email_sent', 'no', true );
+        $product->save();
     }
 
     /**
@@ -409,7 +411,7 @@ public function own_product_not_purchasable_notice() {
 
         wc_print_notice( __( 'As this is your own product, the "Add to Cart" button has been removed. Please visit as a guest to view it.', 'dokan-lite' ), 'notice' );
     }
-  
+
     /**
      * Filter the recipients of the product review notification.
      *
diff --git a/includes/Product/Manager.php b/includes/Product/Manager.php
index cdade3e327..623acf4344 100644
--- a/includes/Product/Manager.php
+++ b/includes/Product/Manager.php
@@ -404,9 +404,9 @@ public function create( $args = [] ) {
 
         //call dokan hooks
         if ( ! $is_update ) {
-            do_action( 'dokan_new_product_added', $product_id, $product );
+            do_action( 'dokan_new_product_added', $product_id, [] );
         } else {
-            do_action( 'dokan_product_updated', $product_id, $product );
+            do_action( 'dokan_product_updated', $product_id, [] );
         }
 
         return $this->get( $product_id );
@@ -706,6 +706,37 @@ protected function maybe_update_stock_status( $product, $stock_status ) {
         return $product;
     }
 
+    /**
+     * Prepare downloads for save.
+     *
+     * @since DOKAN_SINCE
+     *
+     * @param array $file_names  File names.
+     * @param array $file_urls   File urls.
+     * @param array $file_hashes File hashes.
+     *
+     * @return array
+     */
+    public function prepare_downloads( $file_names, $file_urls, $file_hashes ) {
+        $downloads = [];
+
+        if ( ! empty( $file_urls ) ) {
+            $file_url_size = count( $file_urls );
+
+            for ( $i = 0; $i < $file_url_size; $i++ ) {
+                if ( ! empty( $file_urls[ $i ] ) ) {
+                    $downloads[] = [
+                        'name'        => wc_clean( $file_names[ $i ] ),
+                        'file'        => wp_unslash( trim( $file_urls[ $i ] ) ),
+                        'download_id' => wc_clean( $file_hashes[ $i ] ),
+                    ];
+                }
+            }
+        }
+
+        return $downloads;
+    }
+
     /**
      * Get featured products
      *
diff --git a/includes/Product/functions.php b/includes/Product/functions.php
index 8b2f44bdba..d45533dda8 100644
--- a/includes/Product/functions.php
+++ b/includes/Product/functions.php
@@ -14,154 +14,18 @@
  *
  * @param array $args
  *
- * @return int|bool|WP_Error
+ * @deprecated DOKAN_SINCE
+ *
+ * @return \WP_Error|\WC_Product
  */
 function dokan_save_product( $args ) {
-    $defaults = [
-        'post_title'       => '',
-        'post_content'     => '',
-        'post_excerpt'     => '',
-        'post_status'      => '',
-        'post_type'        => 'product',
-        'product_tag'      => [],
-        '_visibility'      => 'visible',
-    ];
-
-    $data = wp_parse_args( $args, $defaults );
-
-    if ( empty( $data['post_title'] ) ) {
-        return new WP_Error( 'no-title', __( 'Please enter product title', 'dokan-lite' ) );
-    }
-
-    if ( ! isset( $data['chosen_product_cat'] ) ) {
-        if ( Helper::product_category_selection_is_single() ) {
-            if ( absint( $data['product_cat'] ) < 0 ) {
-                return new WP_Error( 'no-category', __( 'Please select a category', 'dokan-lite' ) );
-            }
-        } else {
-            if ( ! isset( $data['product_cat'] ) && empty( $data['product_cat'] ) ) {
-                return new WP_Error( 'no-category', __( 'Please select at least one category', 'dokan-lite' ) );
-            }
-        }
-    } elseif ( empty( $data['chosen_product_cat'] ) ) {
-        return new WP_Error( 'no-category', __( 'Please select a category', 'dokan-lite' ) );
-    }
-
-    $error = apply_filters( 'dokan_new_product_popup_args', '', $data );
-
-    if ( is_wp_error( $error ) ) {
-        return $error;
-    }
-
-    $post_status = ! empty( $data['post_status'] ) ? sanitize_text_field( $data['post_status'] ) : dokan_get_default_product_status();
-
-    $post_arr = [
-        'post_type'    => 'product',
-        'post_status'  => $post_status,
-        'post_title'   => sanitize_text_field( $data['post_title'] ),
-        'post_content' => wp_kses_post( $data['post_content'] ),
-        'post_excerpt' => wp_kses_post( $data['post_excerpt'] ),
-    ];
-
-    if ( ! empty( $data['ID'] ) ) {
-        $post_arr['ID'] = absint( $data['ID'] );
-
-        if ( ! dokan_is_product_author( $post_arr['ID'] ) ) {
-            return new WP_Error( 'not-own', __( 'Sorry, You can not modify another vendor\'s product !', 'dokan-lite' ) );
-        }
-
-        $is_updating = true;
-    } else {
-        $is_updating = false;
-    }
-
-    $post_arr = apply_filters( 'dokan_insert_product_post_data', $post_arr, $data );
-
-    $post_data = [
-        'id'                 => $is_updating ? $post_arr['ID'] : '',
-        'name'               => $post_arr['post_title'],
-        'type'               => ! empty( $data['product_type'] ) ? $data['product_type'] : 'simple',
-        'description'        => $post_arr['post_content'],
-        'short_description'  => $post_arr['post_excerpt'],
-        'status'             => $post_status,
-    ];
-
-    if ( ! isset( $data['chosen_product_cat'] ) ) {
-        if ( Helper::product_category_selection_is_single() ) {
-            $cat_ids[] = $data['product_cat'];
-        } else {
-            if ( ! empty( $data['product_cat'] ) ) {
-                $cat_ids = array_map( 'absint', (array) $data['product_cat'] );
-            }
-        }
-        $post_data['categories'] = $cat_ids;
-    }
-
-    if ( isset( $data['feat_image_id'] ) ) {
-        $post_data['featured_image_id'] = ! empty( $data['feat_image_id'] ) ? absint( $data['feat_image_id'] ) : '';
-    }
+    wc_deprecated_function( __FUNCTION__, 'DOKAN_SINCE', 'dokan()->product->create()' );
 
-    if ( isset( $data['product_image_gallery'] ) ) {
-        $post_data['gallery_image_ids'] = ! empty( $data['product_image_gallery'] ) ? array_filter( explode( ',', wc_clean( $data['product_image_gallery'] ) ) ) : [];
+    try {
+        return dokan()->product->create( $args );
+    } catch ( Exception $e ) {
+        return new WP_Error( $e->getCode(), $e->getMessage() );
     }
-
-    if ( isset( $data['product_tag'] ) ) {
-        /**
-         * Filter of maximun a vendor can add tags.
-         *
-         * @since 3.3.7
-         *
-         * @param integer default -1
-         */
-        $maximum_tags_select_length = apply_filters( 'dokan_product_tags_select_max_length', -1 );
-
-        // Setting limitation for how many product tags that vendor can input.
-        if ( $maximum_tags_select_length !== -1 && count( $data['product_tag'] ) !== 0 && count( $data['product_tag'] ) > $maximum_tags_select_length ) {
-            /* translators: %s: maximum tag length */
-            return new WP_Error( 'tags-limit', sprintf( __( 'You can only select %s tags', 'dokan-lite' ), number_format_i18n( $maximum_tags_select_length ) ) );
-        }
-
-        $post_data['tags'] = array_map( 'absint', (array) $data['product_tag'] );
-    }
-
-    if ( isset( $data['_regular_price'] ) ) {
-        $post_data['regular_price'] = $data['_regular_price'] === '' ? '' : wc_format_decimal( $data['_regular_price'] );
-    }
-
-    if ( isset( $data['_sale_price'] ) ) {
-        $post_data['sale_price'] = wc_format_decimal( $data['_sale_price'] );
-    }
-
-    if ( isset( $data['_sale_price_dates_from'] ) ) {
-        $post_data['date_on_sale_from'] = wc_clean( $data['_sale_price_dates_from'] );
-    }
-
-    if ( isset( $data['_sale_price_dates_to'] ) ) {
-        $post_data['date_on_sale_to'] = wc_clean( $data['_sale_price_dates_to'] );
-    }
-
-    if ( isset( $data['_visibility'] ) && array_key_exists( $data['_visibility'], dokan_get_product_visibility_options() ) ) {
-        $post_data['catalog_visibility'] = sanitize_text_field( $data['_visibility'] );
-    }
-
-    $product = dokan()->product->create( $post_data );
-
-    if ( $product ) {
-        $chosen_cat = Helper::product_category_selection_is_single() ? [ reset( $data['chosen_product_cat'] ) ] : $data['chosen_product_cat'];
-        Helper::set_object_terms_from_chosen_categories( $product->get_id(), $chosen_cat );
-    }
-
-    if ( ! $is_updating ) {
-        do_action( 'dokan_new_product_added', $product->get_id(), $data );
-    } else {
-        do_action( 'dokan_product_updated', $product->get_id(), $data );
-    }
-
-    if ( $product ) {
-        return $product->get_id();
-    }
-
-    return false;
 }
 
 /**
diff --git a/includes/ProductCategory/Helper.php b/includes/ProductCategory/Helper.php
index b8bdfd671e..2d40c6d827 100644
--- a/includes/ProductCategory/Helper.php
+++ b/includes/ProductCategory/Helper.php
@@ -19,6 +19,10 @@ class Helper {
      * @return boolean
      */
     public static function product_category_selection_is_single() {
+		if ( ! dokan()->is_pro_exists() ) {
+			return 'single';
+		}
+
         return 'single' === dokan_get_option( 'product_category_style', 'dokan_selling', 'single' );
     }
 
@@ -150,6 +154,44 @@ public static function generate_chosen_categories( $terms ) {
         return $chosen_cats;
     }
 
+	/**
+	 * Get all ancestors of chosen categories.
+	 *
+	 * @since 3.6.2
+	 *
+	 * @param \WC_Product|int $product
+	 * @param array $chosen_categories
+	 *
+	 * @return array
+	 */
+	public static function get_object_terms_from_chosen_categories( $product, $chosen_categories = [] ) {
+		if ( is_numeric( $product ) ) {
+			$product = wc_get_product( $product );
+		}
+
+		if ( ! $product ) {
+			return [];
+		}
+
+		if ( empty( $chosen_categories ) || ! is_array( $chosen_categories ) ) {
+			// get chosen categories from product meta
+			$chosen_categories = self::get_product_chosen_category( $product->get_id() );
+		}
+
+		$all_ancestors = [];
+		// If category middle selection is true, then we will save only the chosen categories or we will save all the ancestors.
+		if ( self::is_any_category_selection_enabled() ) {
+			$all_ancestors = $chosen_categories;
+		} else {
+			// we need to assign all ancestor of chosen category to add to the given product
+			foreach ( $chosen_categories as $term_id ) {
+				$all_ancestors = array_merge( $all_ancestors, get_ancestors( $term_id, 'product_cat' ), [ (int) $term_id ] );
+			}
+		}
+
+		return array_map( 'absint', array_unique( $all_ancestors ) );
+	}
+
     /**
      * Set all ancestors to a product from chosen product categories
      *
@@ -165,22 +207,7 @@ public static function set_object_terms_from_chosen_categories( $post_id, $chose
             return;
         }
 
-        /**
-         * If enabled any one middle category in dokan product multi-step category selection.
-         */
-        $any_category_selection = self::is_any_category_selection_enabled();
-
-        $all_ancestors = [];
-
-        // If category middle selection is true, then we will save only the chosen categories or we will save all the ancestors.
-        if ( $any_category_selection ) {
-            $all_ancestors = $chosen_categories;
-        } else {
-            // we need to assign all ancestor of chosen category to add to the given product
-            foreach ( $chosen_categories as $term_id ) {
-                $all_ancestors = array_merge( $all_ancestors, get_ancestors( $term_id, 'product_cat' ), [ $term_id ] );
-            }
-        }
+        $all_ancestors = self::get_object_terms_from_chosen_categories( $post_id, $chosen_categories );
 
         // save chosen cat to database
         update_post_meta( $post_id, 'chosen_product_cat', $chosen_categories );
@@ -254,12 +281,19 @@ public static function enqueue_and_localize_dokan_multistep_category() {
      *
      * @since 3.7.0
      *
-     * @param int $product_id
+     * @param \WC_Product|int $product
      *
      * @return array
      */
-    public static function get_product_chosen_category( $product_id ) {
-        return get_post_meta( $product_id, 'chosen_product_cat', true );
+    public static function get_product_chosen_category( $product ) {
+		if ( is_numeric( $product ) ) {
+			$product = wc_get_product( $product );
+		}
+		if ( ! $product ) {
+			return [];
+		}
+
+		return (array) $product->get_meta( 'chosen_product_cat', true );
     }
 
     /**
diff --git a/includes/ProductForm/Component.php b/includes/ProductForm/Component.php
index 1ad7f6d1ae..fece579c2a 100644
--- a/includes/ProductForm/Component.php
+++ b/includes/ProductForm/Component.php
@@ -37,9 +37,9 @@ abstract class Component {
     ];
 
     public function __construct( string $id, array $args = [] ) {
-        foreach ( $this->data as $key => $value ) {
-            if ( method_exists( $this, "set_{$key}" ) ) {
-                $this->{"set_{$key}"}( $args[ $key ] );
+        foreach ( $args as $key => $value ) {
+            if ( method_exists( $this, "set_{$key}" ) && null !== $value ) {
+                $this->{"set_{$key}"}( $value );
             }
         }
     }
@@ -90,12 +90,12 @@ public function get_title(): string {
      *
      * @param string $title
      *
-     * @see   sanitize_text_field()
+     * @see   wp_kses_post()
      *
      * @return $this
      */
     public function set_title( string $title ): self {
-        $this->data['title'] = sanitize_text_field( $title );
+        $this->data['title'] = wp_kses_post( $title );
 
         return $this;
     }
@@ -317,10 +317,10 @@ public function get_missing_arguments( $args ) {
                 function ( $arg_key ) use ( $args ) {
                     // return false if not exists or empty
                     if ( ! array_key_exists( $arg_key, $args ) || empty( $args[ $arg_key ] ) ) {
-                        return false;
+                        return true;
                     }
 
-                    return true;
+                    return false;
                 }
             )
         );
diff --git a/includes/ProductForm/Elements.php b/includes/ProductForm/Elements.php
index 5c6f73c8f1..fc1b7189df 100644
--- a/includes/ProductForm/Elements.php
+++ b/includes/ProductForm/Elements.php
@@ -58,8 +58,8 @@ class Elements {
     const INVENTORY_DELTA = 'inventory_delta';
     const UPSELL_IDS = 'upsell_ids';
     const CROSS_SELL_IDS = 'cross_sell_ids';
-    const CATEGORIES = 'categories';
-    const TAGS = 'tags';
+    const CATEGORIES = 'category_ids';
+    const TAGS = 'tag_ids';
     const DOWNLOADABLE = 'downloadable';
     const DOWNLOADS = 'downloads';
     const DOWNLOAD_LIMIT = 'download_limit';
@@ -67,7 +67,7 @@ class Elements {
     const EXTERNAL_URL = 'product_url';
     const BUTTON_TEXT = 'button_text';
     const GROUP_PRODUCTS = 'children';
-    const FEATURED_IMAGE_ID = 'featured_image_id';
+    const FEATURED_IMAGE_ID = 'image_id';
     const GALLERY_IMAGE_IDS = 'gallery_image_ids';
     const META_DATA = 'meta_data';
     const DISABLE_SHIPPING_META = '_disable_shipping';
diff --git a/includes/ProductForm/Factory.php b/includes/ProductForm/Factory.php
index 336c6c2495..5f9fcf0f1b 100644
--- a/includes/ProductForm/Factory.php
+++ b/includes/ProductForm/Factory.php
@@ -54,6 +54,10 @@ class Factory {
      * @return Field|WP_Error New field or WP_Error.
      */
     public static function add_field( $id, $args ) {
+        if ( empty( $args['id'] ) ) {
+            $args['id'] = $id;
+        }
+
         $new_field = self::create_item( 'field', 'Field', $id, $args );
         if ( is_wp_error( $new_field ) ) {
             return $new_field;
@@ -79,6 +83,10 @@ public static function add_field( $id, $args ) {
      * @return Section|WP_Error New section or WP_Error.
      */
     public static function add_section( $id, $args ) {
+        if ( empty( $args['id'] ) ) {
+            $args['id'] = $id;
+        }
+
         $new_section = self::create_item( 'section', 'Section', $id, $args );
         if ( is_wp_error( $new_section ) ) {
             return $new_section;
@@ -96,9 +104,9 @@ public static function add_section( $id, $args ) {
     /**
      * Returns list of registered fields.
      *
-     * @return array list of registered fields.
+     * @return Field[] list of registered fields.
      */
-    public static function get_fields() {
+    public static function get_fields(): array {
         return self::$form_fields;
     }
 
@@ -154,21 +162,20 @@ public static function get_section( string $id ) {
      */
     private static function get_items( string $type, string $class_name, string $sort_by = 'asc' ) {
         $item_list = self::get_item_list( $type );
-        $items     = array_values( $item_list );
 
         if ( class_exists( $class_name ) && method_exists( $class_name, 'sort' ) ) {
             /**
-             * @var \WeDevs\Dokan\ProductForm\Component $class_name
+             * @var Component $class_name
              */
-            usort(
-                $items,
+            uasort(
+                $item_list,
                 function ( $a, $b ) use ( $sort_by, $class_name ) {
                     return $class_name::sort( $a, $b, $sort_by );
                 }
             );
         }
 
-        return $items;
+        return $item_list;
     }
 
     /**
@@ -202,6 +209,7 @@ private static function get_item_list( string $type ): array {
      * @return Field|Section|WP_Error New product form item or WP_Error.
      */
     private static function create_item( string $type, string $class_name, string $id, array $args ) {
+        $class_name = __NAMESPACE__ . '\\' . $class_name;
         if ( ! class_exists( $class_name ) ) {
             return new WP_Error(
                 'dokan_product_form_' . $type . '_missing_form_class',
diff --git a/includes/ProductForm/Field.php b/includes/ProductForm/Field.php
index f1230e0ba5..d3f65b2452 100644
--- a/includes/ProductForm/Field.php
+++ b/includes/ProductForm/Field.php
@@ -22,6 +22,7 @@ class Field extends Component {
         'id',
         'title',
         'section',
+        'type',
     ];
 
     /**
@@ -34,14 +35,17 @@ class Field extends Component {
      * @throws \Exception
      */
     public function __construct( string $id, array $args = [] ) {
-        $data = [
-            'name'            => '', // html name attribute of the field, if not exists id value will be used as name
-            'property'        => '', // if exists, this will be the name of the field
-            'section'         => '', // section id, required
-            'field_type'      => '', // html field type
-            'placeholder'     => '', // html placeholder attribute value for the field
-            'options'         => '', // if the field is select, radio, checkbox, etc
+        $data       = [
+            'name'                  => '', // html name attribute of the field, if not exists id value will be used as name
+            'value'                 => '', // html value attribute of the field
+            'property'              => '', // if exists, this will be the name of the field
+            'section'               => '', // section id, required
+            'type'                  => 'prop', // field type, accept value can be 'prop', 'meta' or 'other'
+            'field_type'            => '', // html field type
+            'placeholder'           => '', // html placeholder attribute value for the field
+            'options'               => [], // if the field is select, radio, checkbox, etc
             'additional_properties' => [], // additional arguments for the field
+            'sanitize_callback'     => '', // callback function for sanitizing the field value
         ];
         $this->data = array_merge( $this->data, $data );
 
@@ -56,7 +60,7 @@ public function __construct( string $id, array $args = [] ) {
         if ( count( $missing_arguments ) > 0 ) {
             throw new \Exception(
                 sprintf(
-                    /* translators: 1: Missing arguments list. */
+                /* translators: 1: Missing arguments list. */
                     esc_html__( 'You are missing required arguments of Dokan ProductForm Field: %1$s', 'dokan-lite' ),
                     esc_attr( join( ', ', $missing_arguments ) )
                 )
@@ -76,11 +80,13 @@ public function get_name(): string {
     }
 
     /**
-     * Set field name, validated by @since DOKAN_SINCE
+     * Set field name, validated by sanitize_title()
+     *
+     * @since DOKAN_SINCE
      *
      * @param string $name
      *
-     * @see sanitize_title()
+     * @see   sanitize_title()
      *
      * @return $this
      */
@@ -90,6 +96,32 @@ public function set_name( string $name ): Field {
         return $this;
     }
 
+    /**
+     * Get field value
+     *
+     * @since DOKAN_SINCE
+     *
+     * @return mixed
+     */
+    public function get_value() {
+        return $this->data['value'];
+    }
+
+    /**
+     * Set field value
+     *
+     * @since DOKAN_SINCE
+     *
+     * @param mixed $value
+     *
+     * @return $this
+     */
+    public function set_value( $value ): Field {
+        $this->data['value'] = $value;
+
+        return $this;
+    }
+
     /**
      * Get field property
      *
@@ -102,7 +134,7 @@ public function get_property(): string {
     }
 
     /**
-     * Set field property, validated by
+     * Set field property, validated by sanitize_key()
      *
      * @since DOKAN_SINCE
      *
@@ -144,6 +176,65 @@ public function set_section( string $section ): Field {
         return $this;
     }
 
+    /**
+     * Get type of the current field
+     *
+     * @since DOKAN_SINCE
+     *
+     * @return string
+     */
+    public function get_type(): string {
+        return $this->data['type'];
+    }
+
+    /**
+     * Return if the type is a prop
+     *
+     * @since DOKAN_SINCE
+     *
+     * @return bool
+     */
+    public function is_prop(): bool {
+        return 'prop' === $this->data['type'];
+    }
+
+    /**
+     * Return if the type is a meta_data
+     *
+     * @since DOKAN_SINCE
+     *
+     * @return bool
+     */
+    public function is_meta(): bool {
+        return 'meta' === $this->data['type'];
+    }
+
+    /**
+     * Return if the type is a date_prop
+     *
+     * @since DOKAN_SINCE
+     *
+     * @return bool
+     */
+    public function is_other_type(): bool {
+        return 'other' === $this->data['type'];
+    }
+
+    /**
+     * Set field type
+     *
+     * @since DOKAN_SINCE
+     *
+     * @param string $field_type
+     *
+     * @return $this
+     */
+    public function set_type( string $type ): Field {
+        $this->data['type'] = in_array( $type, [ 'prop', 'meta', 'other' ], true ) ? sanitize_key( $type ) : 'other';
+
+        return $this;
+    }
+
     /**
      * Get the field type
      *
@@ -183,7 +274,7 @@ public function get_placeholder(): string {
     }
 
     /**
-     * Set field placeholder, validated with
+     * Set field placeholder, validated with wp_kses_post()
      *
      * @since DOKAN_SINCE
      *
@@ -261,4 +352,53 @@ public function set_additional_properties( $property, $value = null ): Field {
 
         return $this;
     }
+
+    /**
+     * Set field sanitize callback
+     *
+     * @since DOKAN_SINCE
+     *
+     * @param callable|string $callback
+     *
+     * @return void
+     */
+    public function set_sanitize_callback( $callback ): Field {
+        $this->data['sanitize_callback'] = $callback;
+
+        return $this;
+    }
+
+    /**
+     * Get field sanitize callback
+     *
+     * @since DOKAN_SINCE
+     *
+     * @return callable|string
+     */
+    public function get_sanitize_callback() {
+        return $this->data['sanitize_callback'];
+    }
+
+    /**
+     * Get field sanitize callback
+     *
+     * @since DOKAN_SINCE
+     *
+     * @param mixed $value
+     *
+     * @return mixed|\WP_Error
+     */
+    public function sanitize( ...$value ) {
+        $callback = $this->get_sanitize_callback();
+        if ( ! empty( $callback ) && is_callable( $callback ) ) {
+            return call_user_func( $callback, ...$value );
+        }
+
+        $value = current( $value );
+        if ( is_string( $value ) ) {
+            return wc_clean( $value );
+        }
+
+        return $value;
+    }
 }
diff --git a/includes/ProductForm/Init.php b/includes/ProductForm/Init.php
index 4df60fb6c3..6c3c977bc9 100644
--- a/includes/ProductForm/Init.php
+++ b/includes/ProductForm/Init.php
@@ -2,6 +2,9 @@
 
 namespace WeDevs\Dokan\ProductForm;
 
+use WC_Product;
+use WeDevs\Dokan\ProductCategory\Helper as ProductCategoryHelper;
+
 defined( 'ABSPATH' ) || exit;
 
 /**
@@ -19,7 +22,7 @@ class Init {
      * @return void
      */
     public function __construct() {
-        add_action( 'init', [ $this, 'init_form_fields' ], 1 );
+        add_action( 'init', [ $this, 'init_form_fields' ], 5 );
     }
 
     /**
@@ -32,6 +35,7 @@ public function __construct() {
     public function init_form_fields() {
         $this->init_general_fields();
         $this->init_inventory_fields();
+        $this->init_downloadable_virtual_fields();
         $this->init_downloadable_fields();
         $this->init_other_fields();
     }
@@ -53,21 +57,39 @@ public function init_general_fields() {
         );
 
         $section->add_field(
-            Elements::ID,
+            Elements::NAME,
             [
                 'title'       => __( 'Title', 'dokan-lite' ),
-                'type'        => 'text',
+                'field_type'  => 'text',
                 'name'        => 'post_title',
                 'placeholder' => __( 'Enter product title...', 'dokan-lite' ),
+                'required'    => true,
                 'error_msg'   => __( 'Please enter product title!', 'dokan-lite' ),
             ]
         );
 
+        $section->add_field(
+            Elements::SLUG,
+            [
+                'title'       => __( 'Slug', 'dokan-lite' ),
+                'field_type'  => 'text',
+                'type'        => 'other',
+                'name'        => 'slug',
+                'placeholder' => __( 'Enter product slug...', 'dokan-lite' ),
+                'required'    => false,
+                'error_msg'   => __( 'Please enter product slug!', 'dokan-lite' ),
+                'sanitize_callback' => function ( $slug, $product_id, $status, $post_parent = 0 ) {
+                    return wp_unique_post_slug( $slug, $product_id, $status, 'product', $post_parent );
+                },
+            ]
+        );
+
         $section->add_field(
             Elements::TYPE, [
                 'title'        => __( 'Product Type', 'dokan-lite' ),
-                'type'         => 'select',
-                'name'         => 'product_type',
+                'field_type'   => 'select',
+                'type'         => 'other',
+                'name'         => 'product-type',
                 'options'      => apply_filters(
                     'dokan_product_types',
                     [
@@ -81,7 +103,7 @@ public function init_general_fields() {
         $section->add_field(
             Elements::REGULAR_PRICE, [
                 'title'       => __( 'Price', 'dokan-lite' ),
-                'type'        => 'text',
+                'field_type'  => 'text',
                 'name'        => '_regular_price',
                 'placeholder' => '0.00',
             ]
@@ -90,7 +112,7 @@ public function init_general_fields() {
         $section->add_field(
             Elements::SALE_PRICE, [
                 'title'       => __( 'Discounted Price', 'dokan-lite' ),
-                'type'        => 'text',
+                'field_type'  => 'text',
                 'name'        => '_sale_price',
                 'placeholder' => '0.00',
             ]
@@ -99,47 +121,88 @@ public function init_general_fields() {
         $section->add_field(
             Elements::DATE_ON_SALE_FROM, [
                 'title'       => __( 'From', 'dokan-lite' ),
-                'type'        => 'text',
+                'field_type'  => 'text',
                 'name'        => '_sale_price_dates_from',
-                'placeholder' => wc_date_format(),
+                'placeholder' => 'YYYY-MM-DD',
             ]
         );
 
         $section->add_field(
             Elements::DATE_ON_SALE_TO, [
                 'title'       => __( 'To', 'dokan-lite' ),
-                'type'        => 'text',
+                'field_type'  => 'text',
                 'name'        => '_sale_price_dates_to',
-                'placeholder' => wc_date_format(),
+                'placeholder' => 'YYYY-MM-DD',
             ]
         );
 
+        $category_error_message = ProductCategoryHelper::product_category_selection_is_single()
+            ? __( 'Please select at least one category!', 'dokan-lite' )
+            : __( 'Please select a category', 'dokan-lite' );
+
         $section->add_field(
             Elements::CATEGORIES, [
                 'title'       => __( 'Categories', 'dokan-lite' ),
-                'type'        => 'select',
-                'name'        => 'product_cat[]',
+                'field_type'  => 'select',
+                'name'        => 'chosen_product_cat[]',
                 'placeholder' => __( 'Select product categories', 'dokan-lite' ),
                 'options'     => [],
+                'required'    => true,
+                'error_msg'   => $category_error_message,
+                'sanitize_callback' => function ( array $categories, WC_Product $product ) {
+                    $chosen_product_categories = array_map( 'absint', $categories );
+                    $chosen_cat                = ProductCategoryHelper::product_category_selection_is_single() ? [ reset( $chosen_product_categories ) ] : $chosen_product_categories;
+
+                    // store product category in meta data
+                    $product->add_meta_data( 'chosen_product_cat', $chosen_cat, true );
+
+                    return array_map( 'absint', ProductCategoryHelper::get_object_terms_from_chosen_categories( $product, $chosen_cat ) );
+                },
             ]
         );
 
-        $can_create_tags  = dokan_get_option( 'product_vendors_can_create_tags', 'dokan_selling' );
+        $can_create_tags  = dokan()->is_pro_exists() ? dokan_get_option( 'product_vendors_can_create_tags', 'dokan_selling', 'off' ) : 'off';
         $tags_placeholder = 'on' === $can_create_tags ? __( 'Select tags/Add tags', 'dokan-lite' ) : __( 'Select product tags', 'dokan-lite' );
         $section->add_field(
             Elements::TAGS, [
-                'title'       => __( 'Tags', 'dokan-lite' ),
-                'type'        => 'select',
-                'name'        => 'product_tag[]',
-                'placeholder' => $tags_placeholder,
+                'title'             => __( 'Tags', 'dokan-lite' ),
+                'field_type'        => 'select',
+                'name'              => 'product_tag[]',
+                'placeholder'       => $tags_placeholder,
+                'sanitize_callback' => function ( $tags ) {
+                    /**
+                     * Maximum number of tags a vendor can add.
+                     *
+                     * @since 3.3.7
+                     *
+                     * @args  int -1 default to unlimited
+                     */
+                    $maximum_tags_select_length = apply_filters( 'dokan_product_tags_select_max_length', -1 );
+
+                    if ( $maximum_tags_select_length !== -1 && count( $tags ) > $maximum_tags_select_length ) {
+                        // translators: %s: maximum tag length
+                        return sprintf( __( 'You can only select %s tags', 'dokan-lite' ), number_format_i18n( $maximum_tags_select_length ) );
+                    }
+
+                    // todo: add support for creating tags
+                    return array_map(
+                        function ( $tag ) {
+                            if ( is_numeric( $tag ) ) {
+                                    return absint( $tag );
+                            } else {
+                                return sanitize_text_field( $tag );
+                            }
+                        }, (array) $tags
+                    );
+                },
             ]
         );
 
         $section->add_field(
             Elements::FEATURED_IMAGE_ID, [
                 'title'       => __( 'Product Image', 'dokan-lite' ),
-                'type'        => 'image',
-                'name'        => 'feat_image_id',
+                'field_type'  => 'image',
+                'name'        => 'image_id',
                 'placeholder' => __( 'Select product image', 'dokan-lite' ),
             ]
         );
@@ -147,8 +210,8 @@ public function init_general_fields() {
         $section->add_field(
             Elements::GALLERY_IMAGE_IDS, [
                 'title'       => __( 'Product Gallery', 'dokan-lite' ),
-                'type'        => 'gallery',
-                'name'        => 'product_image_gallery',
+                'field_type'  => 'gallery',
+                'name'        => 'gallery_image_ids',
                 'placeholder' => __( 'Select product gallery images', 'dokan-lite' ),
             ]
         );
@@ -156,21 +219,67 @@ public function init_general_fields() {
         $section->add_field(
             Elements::SHORT_DESCRIPTION, [
                 'title'       => __( 'Short Description', 'dokan-lite' ),
-                'type'        => 'textarea',
+                'field_type'  => 'textarea',
                 'name'        => 'post_excerpt',
                 'placeholder' => __( 'Enter product short description', 'dokan-lite' ),
-                'visibility'  => false,
             ]
         );
 
         $section->add_field(
             Elements::DESCRIPTION, [
                 'title'       => __( 'Description', 'dokan-lite' ),
-                'type'        => 'textarea',
+                'field_type'  => 'textarea',
                 'name'        => 'post_content',
                 'placeholder' => __( 'Enter product description', 'dokan-lite' ),
                 'required'    => true,
-                'visibility'  => false,
+            ]
+        );
+    }
+
+    /**
+     * Init downloadable and virtual fields
+     *
+     * @since DOKAN_SINCE
+     *
+     * @return void
+     */
+    public function init_downloadable_virtual_fields() {
+        $section = Factory::add_section(
+            'downloadable_virtual',
+            [
+                'title'       => __( 'Downloadable', 'dokan-lite' ),
+                'description' => __( 'Downloadable products give access to a file upon purchase.', 'dokan-lite' ),
+                'order'       => 20,
+            ]
+        );
+
+        $section->add_field(
+            Elements::DOWNLOADABLE, [
+                'title'                 => __( 'Downloadable', 'dokan-lite' ),
+                'description'           => __( 'Virtual products are intangible and are not shipped.', 'dokan-lite' ),
+                'field_type'            => 'checkbox',
+                'name'                  => '_downloadable',
+                'additional_properties' => [
+                    'value' => 'yes',
+                ],
+                'sanitize_callback'     => function ( $value ) {
+                    return ! empty( $value ) && 'yes' === $value;
+                },
+            ]
+        );
+
+        $section->add_field(
+            Elements::VIRTUAL, [
+                'title'                 => __( 'Virtual', 'dokan-lite' ),
+                'description'           => __( 'Downloadable products give access to a file upon purchase.', 'dokan-lite' ),
+                'field_type'            => 'checkbox',
+                'name'                  => '_virtual',
+                'additional_properties' => [
+                    'value' => 'yes',
+                ],
+                'sanitize_callback'     => function ( $value ) {
+                    return ! empty( $value ) && 'yes' === $value;
+                },
             ]
         );
     }
@@ -187,75 +296,123 @@ public function init_inventory_fields() {
             'inventory',
             [
                 'title'       => __( 'Inventory', 'dokan-lite' ),
-                'description' => __( 'Manage your product stock', 'dokan-lite' ),
+                'description' => __( 'Manage inventory for this product', 'dokan-lite' ),
                 'order'       => 20,
             ]
         );
 
         $section->add_field(
             Elements::SKU, [
-                'title'       => __( 'SKU', 'dokan-lite' ),
-                'type'        => 'text',
-                'name'        => '_sku',
+                'title'       => '<abbr title="' . esc_attr__( 'Stock Keeping Unit', 'dokan-lite' ) . '">' . esc_html__( 'SKU', 'dokan-lite' ) . '</abbr>',
+                'description' => __( 'SKU refers to a Stock-keeping unit, a unique identifier for each distinct product and service that can be purchased.', 'dokan-lite' ),
                 'placeholder' => __( 'Enter product SKU', 'dokan-lite' ),
+                'field_type'  => 'text',
+                'name'        => '_sku',
             ]
         );
 
         $section->add_field(
             Elements::STOCK_STATUS, [
-                'title'   => __( 'Stock Status', 'dokan-lite' ),
-                'type'    => 'select',
-                'name'    => '_stock_status',
-                'options' => wc_get_product_stock_status_options(),
+                'title'       => __( 'Stock Status', 'dokan-lite' ),
+                'description' => __( 'Controls whether or not the product is listed as "in stock" or "out of stock" on the frontend.', 'dokan-lite' ),
+                'field_type'  => 'select',
+                'name'        => '_stock_status',
+                'options'     => wc_get_product_stock_status_options(),
             ]
         );
 
         $section->add_field(
             Elements::MANAGE_STOCK, [
-                'title' => __( 'Enable product stock management', 'dokan-lite' ),
-                'type'  => 'checkbox',
-                'name'  => '_manage_stock',
-                'value' => 'yes',
+                'title'                 => __( 'Enable product stock management', 'dokan-lite' ),
+                'description'           => __( 'Manage stock level (quantity)', 'dokan-lite' ),
+                'field_type'            => 'checkbox',
+                'name'                  => '_manage_stock',
+                'additional_properties' => [
+                    'value' => 'yes',
+                ],
+                'sanitize_callback'     => function ( $value ) {
+                    return ! empty( $value ) && 'yes' === $value;
+                },
             ]
         );
 
         $section->add_field(
             Elements::STOCK_QUANTITY, [
-                'title'       => __( 'Stock quantity', 'dokan-lite' ),
-                'type'        => 'number',
-                'name'        => '_stock',
-                'placeholder' => '1',
-                'min'         => 0,
-                'step'        => 1,
+                'title'                 => __( 'Stock quantity', 'dokan-lite' ),
+                'description'           => __( 'Stock quantity. If this is a variable product this value will be used to control stock for all variations, unless you define stock at variation level.', 'dokan-lite' ),
+                'field_type'            => 'number',
+                'name'                  => '_stock',
+                'placeholder'           => '1',
+                'additional_properties' => [
+                    'min'  => 0,
+                    'step' => 'any',
+                ],
+                'sanitize_callback'     => function ( $value, $original_stock = false, $product = null ) {
+                    if (
+                        false !== $original_stock
+                        && $product instanceof WC_Product
+                        && wc_stock_amount( $product->get_stock_quantity( 'edit' ) ) !== wc_stock_amount( $original_stock )
+                    ) {
+                        return new \WP_Error(
+                            'invalid-stock-quantity',
+                            sprintf(
+                                /* translators: 1: product ID 2: quantity in stock */
+                                __(
+                                    'The stock has not been updated because the value has changed since editing. Product %1$d has %2$d units in stock.',
+                                    'dokan-lite'
+                                ),
+                                $product->get_id(),
+                                $product->get_stock_quantity( 'edit' )
+                            )
+                        );
+                    }
+
+                    return wc_stock_amount( $value );
+                },
             ]
         );
 
         $section->add_field(
             Elements::LOW_STOCK_AMOUNT, [
-                'title'       => __( 'Low stock threshold', 'dokan-lite' ),
-                'type'        => 'number',
-                'name'        => '_low_stock_amount',
-                'placeholder' => 1,
-                'min'         => 0,
-                'step'        => 1,
+                'title'                 => __( 'Low stock threshold', 'dokan-lite' ),
+                'description'           => __( 'When product stock reaches this amount you will be notified by email. It is possible to define different values for each variation individually.', 'dokan-lite' ),
+                'field_type'            => 'number',
+                'name'                  => '_low_stock_amount',
+                'placeholder'           => sprintf(
+                /* translators: %d: Amount of stock left */
+                    esc_attr__( 'Store-wide threshold (%d)', 'dokan-lite' ),
+                    esc_attr( get_option( 'woocommerce_notify_low_stock_amount' ) )
+                ),
+                'additional_properties' => [
+                    'min'  => 0,
+                    'step' => 'any',
+                ],
+                'sanitize_callback'     => 'wc_stock_amount',
             ]
         );
 
         $section->add_field(
             Elements::BACKORDERS, [
-                'title'   => __( 'Allow Backorders', 'dokan-lite' ),
-                'type'    => 'select',
-                'name'    => '_backorders',
-                'options' => wc_get_product_backorder_options(),
+                'title'       => __( 'Allow Backorders', 'dokan-lite' ),
+                'description' => __( 'If managing stock, this controls whether or not backorders are allowed. If enabled, stock quantity can go below 0.', 'dokan-lite' ),
+                'field_type'  => 'select',
+                'name'        => '_backorders',
+                'options'     => wc_get_product_backorder_options(),
             ]
         );
 
         $section->add_field(
             Elements::SOLD_INDIVIDUALLY, [
-                'title' => __( 'Sold Individually', 'dokan-lite' ),
-                'type'  => 'checkbox',
-                'name'  => '_sold_individually',
-                'value' => 'yes',
+                'title'                 => __( 'Sold Individually', 'dokan-lite' ),
+                'description'           => __( 'Check to let customers to purchase only 1 item in a single order. This is particularly useful for items that have limited quantity, for example art or handmade goods.', 'dokan-lite' ),
+                'field_type'            => 'checkbox',
+                'name'                  => '_sold_individually',
+                'additional_properties' => [
+                    'value' => 'yes',
+                ],
+                'sanitize_callback'     => function ( $value ) {
+                    return ! empty( $value ) && 'yes' === $value;
+                },
             ]
         );
     }
@@ -277,25 +434,45 @@ public function init_downloadable_fields() {
             ]
         );
 
+        $section->add_field(
+            Elements::DOWNLOADS, [
+                'title'       => __( 'Downloadable Files', 'dokan-lite' ),
+                'description' => __( 'Upload files that customers can download after purchase.', 'dokan-lite' ),
+                'field_type'  => 'downloadable',
+                'name'        => '_wc_file_urls[]',
+                'sanitize_callback' => function ( $file_names, $file_urls, $file_hashes ) {
+                    return dokan()->product->prepare_downloads( $file_names, $file_urls, $file_hashes );
+                },
+            ]
+        );
+
         $section->add_field(
             Elements::DOWNLOAD_LIMIT, [
-                'title'       => __( 'Download Limit', 'dokan-lite' ),
-                'type'        => 'number',
-                'name'        => '_download_limit',
-                'placeholder' => __( 'Unlimited', 'dokan-lite' ),
-                'min'         => 0,
-                'step'        => 1,
+                'title'                 => __( 'Download Limit', 'dokan-lite' ),
+                'placeholder'           => __( 'Unlimited', 'dokan-lite' ),
+                'description'           => __( 'Leave blank for unlimited re-downloads.', 'dokan-lite' ),
+                'field_type'            => 'number',
+                'name'                  => '_download_limit',
+                'additional_properties' => [
+                    'min'  => 0,
+                    'step' => 1,
+                ],
+                'sanitize_callback'     => 'absint',
             ]
         );
 
         $section->add_field(
             Elements::DOWNLOAD_EXPIRY, [
-                'title'       => __( 'Download Expiry', 'dokan-lite' ),
-                'type'        => 'number',
-                'name'        => '_download_expiry',
-                'placeholder' => __( 'Never', 'dokan-lite' ),
-                'min'         => 0,
-                'step'        => 1,
+                'title'                 => __( 'Download Expiry', 'dokan-lite' ),
+                'placeholder'           => __( 'Never', 'dokan-lite' ),
+                'description'           => __( 'Enter the number of days before a download link expires, or leave blank.', 'dokan-lite' ),
+                'field_type'            => 'number',
+                'name'                  => '_download_expiry',
+                'additional_properties' => [
+                    'min'  => 0,
+                    'step' => 1,
+                ],
+                'sanitize_callback'     => 'absint',
             ]
         );
     }
@@ -319,38 +496,43 @@ public function init_other_fields() {
 
         $section->add_field(
             Elements::STATUS, [
-                'title'   => __( 'Product Status', 'dokan-lite' ),
-                'type'    => 'select',
-                'name'    => 'post_status',
-                'default' => dokan_get_default_product_status( dokan_get_current_user_id() ),
-                'options' => dokan_get_available_post_status(), // get it with product_id param
+                'title'      => __( 'Product Status', 'dokan-lite' ),
+                'field_type' => 'select',
+                'name'       => 'status',
+                'options'    => dokan_get_available_post_status(), // get it with product_id param
             ]
         );
 
         $section->add_field(
             Elements::CATALOG_VISIBILITY, [
-                'title'   => __( 'Visibility', 'dokan-lite' ),
-                'type'    => 'select',
-                'name'    => '_visibility',
-                'options' => dokan_get_product_visibility_options(),
+                'title'      => __( 'Visibility', 'dokan-lite' ),
+                'field_type' => 'select',
+                'name'       => '_visibility',
+                'options'    => dokan_get_product_visibility_options(),
             ]
         );
 
         $section->add_field(
             Elements::PURCHASE_NOTE, [
-                'title'       => __( 'Purchase Note', 'dokan-lite' ),
-                'type'        => 'textarea',
-                'name'        => '_purchase_note',
-                'placeholder' => __( 'Customer will get this info in their order email', 'dokan-lite' ),
+                'title'             => __( 'Purchase Note', 'dokan-lite' ),
+                'field_type'        => 'textarea',
+                'name'              => '_purchase_note',
+                'placeholder'       => __( 'Customer will get this info in their order email', 'dokan-lite' ),
+                'sanitize_callback' => 'wp_kses_post',
             ]
         );
 
         $section->add_field(
             Elements::REVIEWS_ALLOWED, [
-                'title' => __( 'Enable product reviews', 'dokan-lite' ),
-                'type'  => 'checkbox',
-                'name'  => '_enable_reviews',
-                'value' => 'yes',
+                'title'                 => __( 'Enable product reviews', 'dokan-lite' ),
+                'field_type'            => 'checkbox',
+                'name'                  => 'comment_status',
+                'additional_properties' => [
+                    'value' => 'yes',
+                ],
+                'sanitize_callback'     => function ( $value ) {
+                    return ! empty( $value ) && 'yes' === $value;
+                },
             ]
         );
     }
@@ -382,17 +564,20 @@ public function init_shipping_fields() {
 
         $section->add_field(
             Elements::DISABLE_SHIPPING_META, [
-                'title' => __( 'Disable shipping', 'dokan-lite' ),
-                'type'  => 'checkbox',
-                'name'  => '_disable_shipping',
-                'value' => 'no',
+                'title'                 => __( 'Disable shipping', 'dokan-lite' ),
+                'type'                  => 'meta',
+                'field_type'            => 'checkbox',
+                'name'                  => '_disable_shipping',
+                'additional_properties' => [
+                    'value' => 'no',
+                ],
             ]
         );
 
         $section->add_field(
             Elements::WEIGHT, [
                 'title'       => __( 'Weight', 'dokan-lite' ),
-                'type'        => 'number',
+                'field_type'  => 'number',
                 'name'        => '_weight',
                 'placeholder' => '0.00',
                 'min'         => 0,
@@ -403,7 +588,7 @@ public function init_shipping_fields() {
         $section->add_field(
             Elements::DIMENSIONS_LENGTH, [
                 'title'       => __( 'Length', 'dokan-lite' ),
-                'type'        => 'number',
+                'field_type'  => 'number',
                 'name'        => '_length',
                 'placeholder' => '0.00',
                 'min'         => 0,
@@ -414,7 +599,7 @@ public function init_shipping_fields() {
         $section->add_field(
             Elements::DIMENSIONS_WIDTH, [
                 'title'       => __( 'Width', 'dokan-lite' ),
-                'type'        => 'number',
+                'field_type'  => 'number',
                 'name'        => '_width',
                 'placeholder' => '0.00',
                 'min'         => 0,
@@ -425,7 +610,7 @@ public function init_shipping_fields() {
         $section->add_field(
             Elements::DIMENSIONS_HEIGHT, [
                 'title'       => __( 'Height', 'dokan-lite' ),
-                'type'        => 'number',
+                'field_type'  => 'number',
                 'name'        => '_height',
                 'placeholder' => '0.00',
                 'min'         => 0,
@@ -439,27 +624,32 @@ public function init_shipping_fields() {
 
         $section->add_field(
             Elements::SHIPPING_CLASS, [
-                'title'       => __( 'Shipping Class', 'dokan-lite' ),
-                'type'        => 'select',
-                'name'        => '_shipping_class',
-                'placeholder' => __( 'Select shipping class', 'dokan-lite' ),
-                'options'     => [],
+                'title'                 => __( 'Shipping Class', 'dokan-lite' ),
+                'field_type'            => 'select',
+                'name'                  => 'product_shipping_class',
+                'placeholder'           => __( 'Select shipping class', 'dokan-lite' ),
+                'options'               => [],
+                'sanitization_callback' => 'absint',
             ]
         );
 
         $section->add_field(
             Elements::OVERWRITE_SHIPPING_META, [
-                'title' => __( 'Override your store\'s default shipping cost for this product', 'dokan-lite' ),
-                'type'  => 'checkbox',
-                'name'  => '_overwrite_shipping',
-                'value' => 'no',
+                'title'                 => __( 'Override your store\'s default shipping cost for this product', 'dokan-lite' ),
+                'type'                  => 'meta',
+                'field_type'            => 'checkbox',
+                'name'                  => '_overwrite_shipping',
+                'additional_properties' => [
+                    'value' => 'no',
+                ],
             ]
         );
 
         $section->add_field(
             Elements::ADDITIONAL_SHIPPING_COST_META, [
                 'title'       => __( 'Additional cost', 'dokan-lite' ),
-                'type'        => 'number',
+                'type'        => 'meta',
+                'field_type'  => 'number',
                 'name'        => '_additional_price',
                 'placeholder' => '0.00',
                 'min'         => 0,
@@ -470,7 +660,8 @@ public function init_shipping_fields() {
         $section->add_field(
             Elements::ADDITIONAL_SHIPPING_QUANTITY_META, [
                 'title'       => __( 'Per Qty Additional Price', 'dokan-lite' ),
-                'type'        => 'number',
+                'type'        => 'meta',
+                'field_type'  => 'number',
                 'name'        => '_additional_qty',
                 'placeholder' => '0',
                 'min'         => 0,
@@ -480,19 +671,20 @@ public function init_shipping_fields() {
 
         $section->add_field(
             Elements::ADDITIONAL_SHIPPING_PROCESSING_TIME_META, [
-                'title'   => __( 'Processing Time', 'dokan-lite' ),
-                'type'    => 'select',
-                'name'    => '_dps_processing_time',
-                'options' => [],
+                'title'      => __( 'Processing Time', 'dokan-lite' ),
+                'type'       => 'meta',
+                'field_type' => 'select',
+                'name'       => '_dps_processing_time',
+                'options'    => [],
             ]
         );
 
         $section->add_field(
             Elements::TAX_STATUS, [
-                'title'   => __( 'Tax Status', 'dokan-lite' ),
-                'type'    => 'select',
-                'name'    => '_tax_status',
-                'options' => [
+                'title'      => __( 'Tax Status', 'dokan-lite' ),
+                'field_type' => 'select',
+                'name'       => '_tax_status',
+                'options'    => [
                     'taxable'  => __( 'Taxable', 'dokan-lite' ),
                     'shipping' => __( 'Shipping only', 'dokan-lite' ),
                     'none'     => _x( 'None', 'Tax status', 'dokan-lite' ),
@@ -502,10 +694,11 @@ public function init_shipping_fields() {
 
         $section->add_field(
             Elements::TAX_CLASS, [
-                'title'   => __( 'Tax Class', 'dokan-lite' ),
-                'type'    => 'select',
-                'name'    => '_tax_class',
-                'options' => wc_get_product_tax_class_options(),
+                'title'             => __( 'Tax Class', 'dokan-lite' ),
+                'field_type'        => 'select',
+                'name'              => '_tax_class',
+                'options'           => wc_get_product_tax_class_options(),
+                'sanitize_callback' => 'sanitize_title',
             ]
         );
     }
diff --git a/includes/ProductForm/Section.php b/includes/ProductForm/Section.php
index ee306074f6..09e796ae2a 100644
--- a/includes/ProductForm/Section.php
+++ b/includes/ProductForm/Section.php
@@ -2,6 +2,8 @@
 
 namespace WeDevs\Dokan\ProductForm;
 
+use WP_Error;
+
 defined( 'ABSPATH' ) || exit;
 
 /**
@@ -25,7 +27,6 @@ class Section extends Component {
      */
     public function __construct( string $id, array $args = [] ) {
         $this->data['fields'] = [];
-        $this->data = wp_parse_args( $args, $this->data );
 
         // set id from the args
         $this->set_id( $id );
@@ -78,6 +79,17 @@ public function add_field( string $id, array $args ) {
         Factory::add_field( $id, $args );
     }
 
+    /**
+     * Returns registered field based on given field id.
+     *
+     * @param string $id Field id.
+     *
+     * @return Field|WP_Error New field or WP_Error.
+     */
+    public function get_field( string $id ) {
+        return Factory::get_field( $id );
+    }
+
     /**
      * Initialize fields
      *
@@ -86,13 +98,12 @@ public function add_field( string $id, array $args ) {
      * @return $this
      */
     private function init_fields() {
-        $fields = Factory::instance()->get_fields();
-        $this->data['fields'] = array_filter(
-            $fields,
-            function ( Field $field ) {
-                return $field->get_section() === $this->get_id();
+        $this->data['fields'] = [];
+        foreach ( Factory::instance()->get_fields() as $field ) {
+            if ( $field->get_section() === $this->get_id() ) {
+                $this->data['fields'][ $field->get_id() ] = $field;
             }
-        );
+        }
 
         return $this;
     }
diff --git a/includes/REST/Manager.php b/includes/REST/Manager.php
index ce0d1f936b..1adaed2ac4 100644
--- a/includes/REST/Manager.php
+++ b/includes/REST/Manager.php
@@ -176,7 +176,7 @@ public function on_dokan_rest_insert_product( $object, $request, $creating ) {
             return;
         }
 
-        do_action( 'dokan_new_product_added', $object->get_id(), $request );
+        do_action( 'dokan_new_product_added', $object->get_id(), [] );
     }
 
     /**
diff --git a/includes/Rewrites.php b/includes/Rewrites.php
index 542473ab45..134df16c16 100755
--- a/includes/Rewrites.php
+++ b/includes/Rewrites.php
@@ -267,7 +267,7 @@ public function product_edit_template( $template ) {
             return $template;
         }
 
-        $edit_product_url = dokan_locate_template( 'products/edit-product-single.php' );
+        $edit_product_url = dokan_locate_template( 'products/edit/edit-product-single.php' );
 
         return apply_filters( 'dokan_get_product_edit_template', $edit_product_url );
     }
diff --git a/includes/functions.php b/includes/functions.php
index d39da0a525..0e96817e07 100755
--- a/includes/functions.php
+++ b/includes/functions.php
@@ -705,6 +705,7 @@ class="<?php echo esc_attr( $class ); ?>"
 
         case 'checkbox':
             $label = isset( $attr['label'] ) ? $attr['label'] : '';
+            $desc  = isset( $attr['desc'] ) ? $attr['desc'] : '';
             $class = ( $class === 'dokan-form-control' ) ? '' : $class;
             ?>
 
@@ -712,8 +713,14 @@ class="<?php echo esc_attr( $class ); ?>"
                 <input type="hidden" name="<?php echo esc_attr( $name ); ?>" value="no">
                 <input <?php echo esc_attr( $required ); ?> name="<?php echo esc_attr( $name ); ?>" id="<?php echo esc_attr( $name ); ?>" value="yes" type="checkbox"<?php checked( $value, 'yes' ); ?>>
                 <?php echo esc_html( $label ); ?>
+                <?php if ( $desc ) { ?>
+                    <i
+                        class="fas fa-question-circle tips"
+                        aria-hidden="true"
+                        data-title="<?php echo esc_attr( $desc ); ?>">
+                    </i>
+                <?php } ?>
             </label>
-
             <?php
             break;
 
diff --git a/includes/woo-views/html-product-download.php b/includes/woo-views/html-product-download.php
index a7c75ed107..db2eed492b 100755
--- a/includes/woo-views/html-product-download.php
+++ b/includes/woo-views/html-product-download.php
@@ -1,17 +1,48 @@
+<?php
+/**
+ * Template used to form individual rows within the downloadable files table.
+ *
+ * @var array  $file Product download data.
+ * @var string $key  Product download key.
+ */
+
+if ( ! defined( 'ABSPATH' ) ) {
+    exit;
+}
+?>
 <tr>
     <td>
-        <input type="text" class="dokan-form-control input_text" placeholder="<?php esc_attr_e( 'File Name', 'dokan-lite' ); ?>" name="_wc_file_names[]" value="<?php echo esc_attr( $file['name'] ); ?>" />
+        <input
+            type="text"
+            class="dokan-form-control input_text"
+            placeholder="<?php esc_attr_e( 'File Name', 'dokan-lite' ); ?>"
+            name="_wc_file_names[]"
+            value="<?php echo esc_attr( $file['name'] ); ?>" />
+        <input type="hidden" name="_wc_file_hashes[]" value="<?php echo esc_attr( $key ); ?>" />
     </td>
     <td>
         <p>
-            <input type="text" class="dokan-form-control dokan-w8 input_text wc_file_url" placeholder="https://" name="_wc_file_urls[]" value="<?php echo esc_attr( $file['file'] ); ?>" style="margin-right: 8px;" />
-            <a href="#" class="dokan-btn dokan-btn-sm dokan-btn-default upload_file_button" data-choose="<?php esc_attr_e( 'Choose file', 'dokan-lite' ); ?>" data-update="<?php esc_attr_e( 'Insert file URL', 'dokan-lite' ); ?>"><?php echo esc_html( str_replace( ' ', '&nbsp;', __( 'Choose file', 'dokan-lite' ) ) ); ?></a>
+            <input
+                type="text"
+                class="dokan-form-control dokan-w8 input_text wc_file_url"
+                placeholder="https://" name="_wc_file_urls[]"
+                value="<?php echo esc_attr( $file['file'] ); ?>"
+                style="margin-right: 8px;"/>
+            <a
+                href="#"
+                class="dokan-btn dokan-btn-sm dokan-btn-default upload_file_button"
+                data-choose="<?php esc_attr_e( 'Choose file', 'dokan-lite' ); ?>"
+                data-update="<?php esc_attr_e( 'Insert file URL', 'dokan-lite' ); ?>">
+                <?php echo esc_html( str_replace( ' ', '&nbsp;', __( 'Choose file', 'dokan-lite' ) ) ); ?>
+            </a>
         </p>
     </td>
 
     <td>
         <p>
-            <a href="#" class="dokan-btn dokan-btn-sm dokan-btn-danger dokan-product-delete"><span><?php esc_html_e( 'Delete', 'dokan-lite' ); ?></span></a>
+            <a href="#" class="dokan-btn dokan-btn-sm dokan-btn-danger dokan-product-delete">
+                <span><?php esc_html_e( 'Delete', 'dokan-lite' ); ?></span>
+            </a>
         </p>
     </td>
 </tr>
diff --git a/templates/products/add-new-product-modal.php b/templates/products/add-new-product-modal.php
deleted file mode 100644
index ed5811ed49..0000000000
--- a/templates/products/add-new-product-modal.php
+++ /dev/null
@@ -1,3 +0,0 @@
-<div id="dokan-add-product-popup"
-     data-izimodal-title="<i class='fas fa-briefcase'>&nbsp;</i>&nbsp;<?php esc_html_e( 'Add New Product', 'dokan-lite' ); ?>"
-></div>
diff --git a/templates/products/download-virtual.php b/templates/products/download-virtual.php
deleted file mode 100644
index 8d62fe39f6..0000000000
--- a/templates/products/download-virtual.php
+++ /dev/null
@@ -1,13 +0,0 @@
-<div class="dokan-form-group dokan-product-type-container <?php echo esc_attr( $class ); ?>">
-        <div class="content-half-part downloadable-checkbox">
-            <label>
-                <input type="checkbox" <?php checked( $is_downloadable, true ); ?> class="_is_downloadable" name="_downloadable" id="_downloadable"> <?php esc_html_e( 'Downloadable', 'dokan-lite' ); ?> <i class="fas fa-question-circle tips" aria-hidden="true" data-title="<?php esc_attr_e( 'Downloadable products give access to a file upon purchase.', 'dokan-lite' ); ?>"></i>
-            </label>
-        </div>
-    <div class="content-half-part virtual-checkbox">
-        <label>
-            <input type="checkbox" <?php checked( $is_virtual, true ); ?> class="_is_virtual" name="_virtual" id="_virtual"> <?php esc_html_e( 'Virtual', 'dokan-lite' ); ?> <i class="fas fa-question-circle tips" aria-hidden="true" data-title="<?php esc_attr_e( 'Virtual products are intangible and aren\'t shipped.', 'dokan-lite' ); ?>"></i>
-        </label>
-    </div>
-    <div class="dokan-clearfix"></div>
-</div>
diff --git a/templates/products/downloadable.php b/templates/products/downloadable.php
deleted file mode 100644
index 50b0deaf89..0000000000
--- a/templates/products/downloadable.php
+++ /dev/null
@@ -1,71 +0,0 @@
-<div class="dokan-download-options dokan-edit-row dokan-clearfix <?php echo esc_attr( $class ); ?>">
-    <div class="dokan-section-heading" data-togglehandler="dokan_download_options">
-        <h2><i class="fas fa-download" aria-hidden="true"></i> <?php esc_html_e( 'Downloadable Options', 'dokan-lite' ); ?></h2>
-        <p><?php esc_html_e( 'Configure your downloadable product settings', 'dokan-lite' ); ?></p>
-        <a href="#" class="dokan-section-toggle">
-            <i class="fas fa-sort-down fa-flip-vertical" aria-hidden="true"></i>
-        </a>
-        <div class="dokan-clearfix"></div>
-    </div>
-
-    <div class="dokan-section-content">
-        <div class="dokan-divider-top dokan-clearfix">
-
-            <?php do_action( 'dokan_product_edit_before_sidebar' ); ?>
-
-            <div class="dokan-side-body dokan-download-wrapper">
-                <table class="dokan-table">
-                    <tfoot>
-                        <tr>
-                            <th colspan="3">
-                                <?php
-                                $file = [
-                                    'file' => '',
-                                    'name' => '',
-                                ];
-                                ob_start();
-                                require DOKAN_INC_DIR . '/woo-views/html-product-download.php';
-                                $row_html = ob_get_clean();
-                                ?>
-                                <a href="#" class="insert-file-row dokan-btn dokan-btn-sm dokan-btn-success" data-row="<?php echo esc_attr( $row_html ); ?>">
-                                    <?php esc_html_e( 'Add File', 'dokan-lite' ); ?>
-                                </a>
-                            </th>
-                        </tr>
-                    </tfoot>
-                    <thead>
-                        <tr>
-                            <th><?php esc_html_e( 'Name', 'dokan-lite' ); ?> <span class="tips" title="<?php esc_attr_e( 'This is the name of the download shown to the customer.', 'dokan-lite' ); ?>">[?]</span></th>
-                            <th><?php esc_html_e( 'File URL', 'dokan-lite' ); ?> <span class="tips" title="<?php esc_attr_e( 'This is the URL or absolute path to the file which customers will get access to.', 'dokan-lite' ); ?>">[?]</span></th>
-                            <th><?php esc_html_e( 'Action', 'dokan-lite' ); ?></th>
-                        </tr>
-                    </thead>
-                    <tbody>
-                        <?php
-                        $downloadable_files = get_post_meta( $post_id, '_downloadable_files', true );
-
-                        if ( $downloadable_files ) {
-                            foreach ( $downloadable_files as $key => $file ) {
-                                include DOKAN_INC_DIR . '/woo-views/html-product-download.php';
-                            }
-                        }
-                        ?>
-                    </tbody>
-                </table>
-
-                <div class="dokan-clearfix">
-                    <div class="content-half-part">
-                        <label for="_download_limit" class="form-label"><?php esc_html_e( 'Download Limit', 'dokan-lite' ); ?></label>
-                        <?php dokan_post_input_box( $post_id, '_download_limit', array( 'placeholder' => __( 'Unlimited', 'dokan-lite' ) ) ); ?>
-                    </div><!-- .content-half-part -->
-
-                    <div class="content-half-part">
-                        <label for="_download_expiry" class="form-label"><?php esc_html_e( 'Download Expiry', 'dokan-lite' ); ?></label>
-                        <?php dokan_post_input_box( $post_id, '_download_expiry', array( 'placeholder' => __( 'Never', 'dokan-lite' ) ) ); ?>
-                    </div><!-- .content-half-part -->
-                </div>
-
-            </div> <!-- .dokan-side-body -->
-        </div> <!-- .downloadable -->
-    </div>
-</div>
diff --git a/templates/products/edit-product-single.php b/templates/products/edit-product-single.php
deleted file mode 100755
index 049cd14de0..0000000000
--- a/templates/products/edit-product-single.php
+++ /dev/null
@@ -1,538 +0,0 @@
-<?php
-
-use WeDevs\Dokan\ProductCategory\Helper;
-
-// phpcs:disable WordPress.WP.GlobalVariablesOverride.Prohibited
-global $post;
-
-$from_shortcode = false;
-$new_product    = false;
-
-if (
-    ! isset( $post->ID )
-    && (
-        ! isset( $_GET['_dokan_edit_product_nonce'] ) ||
-        ! wp_verify_nonce( sanitize_key( $_GET['_dokan_edit_product_nonce'] ), 'dokan_edit_product_nonce' ) ||
-        ! isset( $_GET['product_id'] )
-    )
-) {
-    wp_die( esc_html__( 'Access Denied, No product found', 'dokan-lite' ) );
-}
-
-if ( isset( $post->ID ) && $post->ID && 'product' === $post->post_type ) {
-    $post_id      = $post->ID;
-    $post_title   = $post->post_title;
-    $post_content = $post->post_content;
-    $post_excerpt = $post->post_excerpt;
-    $post_status  = $post->post_status;
-    $product      = wc_get_product( $post_id );
-}
-
-if ( isset( $_GET['product_id'] ) ) {
-    $post_id        = intval( $_GET['product_id'] );
-    $post           = get_post( $post_id );
-    $post_status    = $post->post_status;
-    $auto_draft     = 'auto-draft' === $post_status;
-    $post_title     = $auto_draft ? '' : $post->post_title;
-    $post_content   = $auto_draft ? '' : $post->post_content;
-    $post_excerpt   = $post->post_excerpt;
-    $product        = wc_get_product( $post_id );
-    $from_shortcode = true;
-}
-
-if ( isset( $_GET['product_id'] ) && 0 === absint( $_GET['product_id'] ) ) {
-    $post_id      = intval( $_GET['product_id'] );
-    $post_title   = '';
-    $post_content = '';
-    $post_excerpt = '';
-    $post_status  = 'auto-draft';
-    $product      = new WC_Product( $post_id );
-
-    $product->set_name( $post_title );
-    $product->set_status( $post_status );
-    $post_id        = $product->save();
-    $post           = get_post( $post_id );
-    $from_shortcode = true;
-    $new_product    = true;
-}
-
-if ( ! dokan_is_product_author( $post_id ) ) {
-    wp_die( esc_html__( 'Access Denied', 'dokan-lite' ) );
-}
-
-$_regular_price         = get_post_meta( $post_id, '_regular_price', true );
-$_sale_price            = get_post_meta( $post_id, '_sale_price', true );
-$is_discount            = ! empty( $_sale_price );
-$_sale_price_dates_from = get_post_meta( $post_id, '_sale_price_dates_from', true );
-$_sale_price_dates_to   = get_post_meta( $post_id, '_sale_price_dates_to', true );
-
-$_sale_price_dates_from = ! empty( $_sale_price_dates_from ) ? date_i18n( 'Y-m-d', $_sale_price_dates_from ) : '';
-$_sale_price_dates_to   = ! empty( $_sale_price_dates_to ) ? date_i18n( 'Y-m-d', $_sale_price_dates_to ) : '';
-$show_schedule          = false;
-
-if ( ! empty( $_sale_price_dates_from ) && ! empty( $_sale_price_dates_to ) ) {
-    $show_schedule = true;
-}
-
-$_featured        = get_post_meta( $post_id, '_featured', true );
-$terms            = wp_get_object_terms( $post_id, 'product_type' );
-$product_type     = ( ! empty( $terms ) ) ? sanitize_title( current( $terms )->name ) : 'simple';
-$variations_class = ( $product_type === 'simple' ) ? 'dokan-hide' : '';
-$_visibility      = ( version_compare( WC_VERSION, '2.7', '>' ) ) ? $product->get_catalog_visibility() : get_post_meta( $post_id, '_visibility', true );
-
-if ( ! $from_shortcode ) {
-    get_header();
-}
-
-if ( ! empty( $_GET['errors'] ) ) {
-    dokan()->dashboard->templates->products->set_errors( array_map( 'sanitize_text_field', wp_unslash( $_GET['errors'] ) ) );
-}
-
-/**
- * Action hook to fire before dokan dashboard wrap
- *
- *  @since 2.4
- */
-do_action( 'dokan_dashboard_wrap_before', $post, $post_id );
-?>
-
-<?php do_action( 'dokan_dashboard_wrap_start' ); ?>
-
-    <div class="dokan-dashboard-wrap">
-
-        <?php
-        /**
-         * Action took to fire before dashboard content.
-         *
-         *  @hooked get_dashboard_side_navigation
-         *
-         *  @since 2.4
-         */
-        do_action( 'dokan_dashboard_content_before' );
-
-        /**
-         * Action hook to fire before product content area.
-         *
-         * @since 2.4
-         */
-        do_action( 'dokan_before_product_content_area' );
-        ?>
-
-        <div class="dokan-dashboard-content dokan-product-edit">
-
-            <?php
-            /**
-             * Action hook to fire inside product content area before
-             *
-             *  @since 2.4
-             */
-            do_action( 'dokan_product_content_inside_area_before' );
-
-            if ( $new_product ) {
-                do_action( 'dokan_new_product_before_product_area' );
-            }
-            ?>
-
-            <header class="dokan-dashboard-header dokan-clearfix">
-                <h1 class="entry-title">
-                    <?php
-                    if ( $new_product || 'auto-draft' === $post->post_status ) {
-                        esc_html_e( 'Add New Product', 'dokan-lite' );
-                    } else {
-                        esc_html_e( 'Edit Product', 'dokan-lite' );
-                    }
-                    ?>
-                    <span class="dokan-label <?php echo esc_attr( dokan_get_post_status_label_class( $post->post_status ) ); ?> dokan-product-status-label">
-                        <?php echo esc_html( dokan_get_post_status( $post->post_status ) ); ?>
-                    </span>
-
-                    <?php if ( $post->post_status === 'publish' ) : ?>
-                        <span class="dokan-right">
-                            <a class="dokan-btn dokan-btn-theme dokan-btn-sm" href="<?php echo esc_url( get_permalink( $post->ID ) ); ?>" target="_blank"><?php esc_html_e( 'View Product', 'dokan-lite' ); ?></a>
-                        </span>
-                    <?php endif; ?>
-
-                    <?php if ( $_visibility === 'hidden' ) : ?>
-                        <span class="dokan-right dokan-label dokan-label-default dokan-product-hidden-label"><i class="far fa-eye-slash"></i> <?php esc_html_e( 'Hidden', 'dokan-lite' ); ?></span>
-                    <?php endif; ?>
-                </h1>
-            </header><!-- .entry-header -->
-
-            <div class="product-edit-new-container product-edit-container">
-                <?php if ( dokan()->dashboard->templates->products->has_errors() ) : ?>
-                    <div class="dokan-alert dokan-alert-danger">
-                        <a class="dokan-close" data-dismiss="alert">&times;</a>
-
-                        <?php foreach ( dokan()->dashboard->templates->products->get_errors() as $error ) : ?>
-                            <strong><?php esc_html_e( 'Error!', 'dokan-lite' ); ?></strong> <?php echo esc_html( $error ); ?>.<br>
-                        <?php endforeach; ?>
-                    </div>
-                <?php endif; ?>
-
-                <?php if ( isset( $_GET['message'] ) && $_GET['message'] === 'success' ) : ?>
-                    <div class="dokan-message">
-                        <button type="button" class="dokan-close" data-dismiss="alert">&times;</button>
-                        <strong><?php esc_html_e( 'Success!', 'dokan-lite' ); ?></strong> <?php esc_html_e( 'The product has been saved successfully.', 'dokan-lite' ); ?>
-
-                        <?php if ( $post->post_status === 'publish' ) : ?>
-                            <a href="<?php echo esc_url( get_permalink( $post_id ) ); ?>" target="_blank"><?php esc_html_e( 'View Product &rarr;', 'dokan-lite' ); ?></a>
-                        <?php endif; ?>
-                    </div>
-                <?php endif; ?>
-
-                <?php if ( apply_filters( 'dokan_can_post', true ) ) : ?>
-                    <?php if ( dokan_is_seller_enabled( get_current_user_id() ) ) : ?>
-                        <form class="dokan-product-edit-form" role="form" method="post" id="post">
-
-                            <?php do_action( 'dokan_product_data_panel_tabs' ); ?>
-                            <?php do_action( 'dokan_product_edit_before_main' ); ?>
-
-                            <div class="dokan-form-top-area">
-
-                                <div class="content-half-part dokan-product-meta">
-
-                                    <div id="dokan-product-title-area" class="dokan-form-group">
-                                        <input type="hidden" name="dokan_product_id" id="dokan-edit-product-id" value="<?php echo esc_attr( $post_id ); ?>"/>
-
-                                        <label for="post_title" class="form-label"><?php esc_html_e( 'Title', 'dokan-lite' ); ?></label>
-                                        <?php
-                                        dokan_post_input_box(
-                                            $post_id,
-                                            'post_title',
-                                            [
-                                                'placeholder' => __( 'Product name..', 'dokan-lite' ),
-                                                'value'       => $post_title,
-                                            ]
-                                        );
-                                        ?>
-                                        <div class="dokan-product-title-alert dokan-hide">
-                                            <?php esc_html_e( 'Please enter product title!', 'dokan-lite' ); ?>
-                                        </div>
-
-                                        <div id="edit-slug-box" class="hide-if-no-js"></div>
-                                        <?php wp_nonce_field( 'samplepermalink', 'samplepermalinknonce', false ); ?>
-                                        <input type="hidden" name="editable-post-name" class="dokan-hide" id="editable-post-name-full-dokan">
-                                        <input type="hidden" value="<?php echo esc_attr( $post->post_name ); ?>" name="edited-post-name" class="dokan-hide" id="edited-post-name-dokan">
-                                    </div>
-
-                                    <?php $product_types = apply_filters( 'dokan_product_types', [ 'simple' => __( 'Simple', 'dokan-lite' ) ] ); ?>
-
-                                    <?php if ( is_array( $product_types ) ) : ?>
-                                        <?php if ( count( $product_types ) === 1 && array_key_exists( 'simple', $product_types ) ) : ?>
-                                            <input type="hidden" id="product_type" name="product_type" value="simple">
-                                        <?php else : ?>
-                                            <div class="dokan-form-group">
-                                                <label for="product_type" class="form-label"><?php esc_html_e( 'Product Type', 'dokan-lite' ); ?> <i class="fas fa-question-circle tips" aria-hidden="true" data-title="<?php esc_html_e( 'Choose Variable if your product has multiple attributes - like sizes, colors, quality etc', 'dokan-lite' ); ?>"></i></label>
-                                                <select name="product_type" class="dokan-form-control" id="product_type">
-                                                    <?php foreach ( $product_types as $key => $value ) : ?>
-                                                        <option value="<?php echo esc_attr( $key ); ?>" <?php selected( $product_type, $key ); ?>><?php echo esc_html( $value ); ?></option>
-                                                    <?php endforeach; ?>
-                                                </select>
-                                            </div>
-                                        <?php endif; ?>
-                                    <?php endif; ?>
-
-                                    <?php do_action( 'dokan_product_edit_after_title', $post, $post_id ); ?>
-
-                                    <div class="show_if_simple dokan-clearfix show_if_external">
-
-                                        <div class="dokan-form-group dokan-clearfix dokan-price-container">
-
-                                            <div class="content-half-part regular-price">
-                                                <label for="_regular_price" class="form-label"><?php esc_html_e( 'Price', 'dokan-lite' ); ?></label>
-                                                <div class="dokan-input-group">
-                                                    <span class="dokan-input-group-addon"><?php echo esc_html( get_woocommerce_currency_symbol() ); ?></span>
-                                                    <?php
-                                                    dokan_post_input_box(
-                                                        $post_id,
-                                                        '_regular_price',
-                                                        [
-                                                            'class'       => 'dokan-product-regular-price',
-                                                            'placeholder' => __( '0.00', 'dokan-lite' ),
-                                                        ],
-                                                        'price'
-                                                    );
-                                                    ?>
-                                                </div>
-                                            </div>
-
-                                            <div class="content-half-part sale-price">
-                                                <label for="_sale_price" class="form-label">
-                                                    <?php esc_html_e( 'Discounted Price', 'dokan-lite' ); ?>
-                                                    <a href="#" class="sale_schedule <?php echo $show_schedule ? 'dokan-hide' : ''; ?>"><?php esc_html_e( 'Schedule', 'dokan-lite' ); ?></a>
-                                                    <a href="#" class="cancel_sale_schedule <?php echo ( ! $show_schedule ) ? 'dokan-hide' : ''; ?>"><?php esc_html_e( 'Cancel', 'dokan-lite' ); ?></a>
-                                                </label>
-
-                                                <div class="dokan-input-group">
-                                                    <span class="dokan-input-group-addon"><?php echo esc_html( get_woocommerce_currency_symbol() ); ?></span>
-                                                    <?php
-                                                    dokan_post_input_box(
-                                                        $post_id,
-                                                        '_sale_price',
-                                                        [
-                                                            'class'       => 'dokan-product-sales-price',
-                                                            'placeholder' => __( '0.00', 'dokan-lite' ),
-                                                        ],
-                                                        'price'
-                                                    );
-                                                    ?>
-                                                </div>
-                                            </div>
-                                        </div>
-
-                                        <div class="dokan-form-group dokan-clearfix dokan-price-container">
-                                            <div class="dokan-product-less-price-alert dokan-hide">
-                                                <?php esc_html_e( 'Product price can\'t be less than the vendor fee!', 'dokan-lite' ); ?>
-                                            </div>
-                                        </div>
-
-                                        <div class="sale_price_dates_fields dokan-clearfix dokan-form-group <?php echo ( ! $show_schedule ) ? 'dokan-hide' : ''; ?>">
-                                            <div class="content-half-part from">
-                                                <div class="dokan-input-group">
-                                                    <span class="dokan-input-group-addon"><?php esc_html_e( 'From', 'dokan-lite' ); ?></span>
-                                                    <input type="text" name="_sale_price_dates_from" class="dokan-form-control dokan-start-date" value="<?php echo esc_attr( $_sale_price_dates_from ); ?>" maxlength="10" pattern="[0-9]{4}-(0[1-9]|1[012])-(0[1-9]|1[0-9]|2[0-9]|3[01])" placeholder="<?php esc_html_e( 'YYYY-MM-DD', 'dokan-lite' ); ?>">
-                                                </div>
-                                            </div>
-
-                                            <div class="content-half-part to">
-                                                <div class="dokan-input-group">
-                                                    <span class="dokan-input-group-addon"><?php esc_html_e( 'To', 'dokan-lite' ); ?></span>
-                                                    <input type="text" name="_sale_price_dates_to" class="dokan-form-control dokan-end-date" value="<?php echo esc_attr( $_sale_price_dates_to ); ?>" maxlength="10" pattern="[0-9]{4}-(0[1-9]|1[012])-(0[1-9]|1[0-9]|2[0-9]|3[01])" placeholder="<?php esc_html_e( 'YYYY-MM-DD', 'dokan-lite' ); ?>">
-                                                </div>
-                                            </div>
-                                        </div><!-- .sale-schedule-container -->
-                                    </div>
-
-                                    <div class="dokan-form-group">
-                                    <?php
-                                        do_action( 'dokan_product_edit_after_pricing', $post, $post_id );
-
-                                        $data = Helper::get_saved_products_category( $post_id );
-                                        $data['from'] = 'edit_product';
-
-                                        dokan_get_template_part( 'products/dokan-category-header-ui', '', $data );
-                                    ?>
-                                    </div>
-
-                                    <div class="dokan-form-group">
-                                        <label for="product_tag_edit" class="form-label"><?php esc_html_e( 'Tags', 'dokan-lite' ); ?></label>
-                                        <?php
-                                        $terms            = wp_get_post_terms( $post_id, 'product_tag', array( 'fields' => 'all' ) );
-                                        $can_create_tags  = dokan_get_option( 'product_vendors_can_create_tags', 'dokan_selling' );
-                                        $tags_placeholder = 'on' === $can_create_tags ? __( 'Select tags/Add tags', 'dokan-lite' ) : __( 'Select product tags', 'dokan-lite' );
-
-                                        $drop_down_tags = array(
-                                            'hide_empty' => 0,
-                                        );
-                                        ?>
-                                        <select multiple="multiple" id="product_tag_edit" name="product_tag[]" class="product_tag_search dokan-form-control" data-placeholder="<?php echo esc_attr( $tags_placeholder ); ?>">
-                                            <?php if ( ! empty( $terms ) ) : ?>
-                                                <?php foreach ( $terms as $tax_term ) : ?>
-                                                    <option value="<?php echo esc_attr( $tax_term->term_id ); ?>" selected="selected" ><?php echo esc_html( $tax_term->name ); ?></option>
-                                                <?php endforeach ?>
-                                            <?php endif ?>
-                                        </select>
-                                    </div>
-
-                                    <?php do_action( 'dokan_product_edit_after_product_tags', $post, $post_id ); ?>
-                                </div><!-- .content-half-part -->
-
-                                <div class="content-half-part featured-image">
-
-                                    <div class="dokan-feat-image-upload dokan-new-product-featured-img">
-                                        <?php
-                                        $wrap_class        = ' dokan-hide';
-                                        $instruction_class = '';
-                                        $feat_image_id     = 0;
-
-                                        if ( has_post_thumbnail( $post_id ) ) {
-                                            $wrap_class        = '';
-                                            $instruction_class = ' dokan-hide';
-                                            $feat_image_id     = get_post_thumbnail_id( $post_id );
-                                        }
-                                        ?>
-
-                                        <div class="instruction-inside<?php echo esc_attr( $instruction_class ); ?>">
-                                            <input type="hidden" name="feat_image_id" class="dokan-feat-image-id" value="<?php echo esc_attr( $feat_image_id ); ?>">
-
-                                            <i class="fas fa-cloud-upload-alt"></i>
-                                            <a href="#" class="dokan-feat-image-btn btn btn-sm"><?php esc_html_e( 'Upload a product cover image', 'dokan-lite' ); ?></a>
-                                        </div>
-
-                                        <div class="image-wrap<?php echo esc_attr( $wrap_class ); ?>">
-                                            <a class="close dokan-remove-feat-image">&times;</a>
-                                            <?php if ( $feat_image_id ) : ?>
-                                                <?php
-                                                echo get_the_post_thumbnail(
-                                                    $post_id,
-                                                    apply_filters( 'single_product_large_thumbnail_size', 'shop_single' ),
-                                                    [
-                                                        'height' => '',
-                                                        'width'  => '',
-                                                    ]
-                                                );
-                                                ?>
-                                            <?php else : ?>
-                                                <img height="" width="" src="" alt="">
-                                            <?php endif; ?>
-                                        </div>
-                                    </div><!-- .dokan-feat-image-upload -->
-
-                                        <div class="dokan-product-gallery">
-                                            <div class="dokan-side-body" id="dokan-product-images">
-                                                <div id="product_images_container">
-                                                    <ul class="product_images dokan-clearfix">
-                                                        <?php
-                                                        $product_images = get_post_meta( $post_id, '_product_image_gallery', true );
-                                                        $gallery = explode( ',', $product_images );
-
-                                                        if ( $gallery ) :
-                                                            foreach ( $gallery as $image_id ) :
-                                                                if ( empty( $image_id ) ) :
-                                                                    continue;
-                                                                endif;
-
-                                                                $attachment_image = wp_get_attachment_image_src( $image_id, 'thumbnail' );
-                                                                ?>
-                                                                <li class="image" data-attachment_id="<?php echo esc_attr( $image_id ); ?>">
-                                                                    <img src="<?php echo esc_url( $attachment_image[0] ); ?>" alt="">
-                                                                    <a href="#" class="action-delete" title="<?php esc_attr_e( 'Delete image', 'dokan-lite' ); ?>">&times;</a>
-                                                                </li>
-                                                                <?php
-                                                            endforeach;
-                                                        endif;
-                                                        ?>
-                                                        <li class="add-image add-product-images tips" data-title="<?php esc_html_e( 'Add gallery image', 'dokan-lite' ); ?>">
-                                                            <a href="#" class="add-product-images"><i class="fas fa-plus" aria-hidden="true"></i></a>
-                                                        </li>
-                                                    </ul>
-
-                                                    <input type="hidden" id="product_image_gallery" name="product_image_gallery" value="<?php echo esc_attr( $product_images ); ?>">
-                                                </div>
-                                            </div>
-                                        </div> <!-- .product-gallery -->
-
-                                    <?php do_action( 'dokan_product_gallery_image_count' ); ?>
-
-                                </div><!-- .content-half-part -->
-                            </div><!-- .dokan-form-top-area -->
-
-                            <div class="dokan-product-short-description">
-                                <label for="post_excerpt" class="form-label"><?php esc_html_e( 'Short Description', 'dokan-lite' ); ?></label>
-                                <?php
-                                wp_editor(
-                                    $post_excerpt,
-                                    'post_excerpt',
-                                    apply_filters(
-                                        'dokan_product_short_description',
-                                        [
-                                            'editor_height' => 50,
-                                            'quicktags'     => true,
-                                            'media_buttons' => false,
-                                            'teeny'         => false,
-                                            'editor_class'  => 'post_excerpt',
-                                        ]
-                                    )
-                                );
-                                ?>
-                            </div>
-
-                            <div class="dokan-product-description">
-                                <label for="post_content" class="form-label"><?php esc_html_e( 'Description', 'dokan-lite' ); ?></label>
-                                <?php
-                                wp_editor(
-                                    $post_content,
-                                    'post_content',
-                                    apply_filters(
-                                        'dokan_product_description',
-                                        [
-                                            'editor_height' => 50,
-                                            'quicktags'     => true,
-                                            'media_buttons' => false,
-                                            'teeny'         => false,
-                                            'editor_class'  => 'post_content',
-                                        ]
-                                    )
-                                );
-                                ?>
-                            </div>
-
-                            <?php do_action( 'dokan_new_product_form', $post, $post_id ); ?>
-                            <?php do_action( 'dokan_product_edit_after_main', $post, $post_id ); ?>
-
-                            <?php do_action( 'dokan_product_edit_after_inventory_variants', $post, $post_id ); ?>
-
-                            <?php if ( $post_id ) : ?>
-                                <?php do_action( 'dokan_product_edit_after_options', $post_id ); ?>
-                            <?php endif; ?>
-
-                            <?php wp_nonce_field( 'dokan_edit_product', 'dokan_edit_product_nonce' ); ?>
-
-                            <input type="hidden" name="dokan_product_id" id="dokan_product_id" value="<?php echo esc_attr( $post_id ); ?>" />
-                            <!--hidden input for Firefox issue-->
-                            <input type="hidden" name="dokan_update_product" value="<?php esc_attr_e( 'Save Product', 'dokan-lite' ); ?>"/>
-                            <input type="submit" name="dokan_update_product" id="publish" class="dokan-btn dokan-btn-theme dokan-btn-lg dokan-right" value="<?php esc_attr_e( 'Save Product', 'dokan-lite' ); ?>"/>
-                            <div class="dokan-clearfix"></div>
-                        </form>
-                    <?php else : ?>
-                        <div class="dokan-alert dokan-alert">
-                            <?php echo esc_html( dokan_seller_not_enabled_notice() ); ?>
-                        </div>
-                    <?php endif; ?>
-
-                <?php else : ?>
-
-                    <?php do_action( 'dokan_can_post_notice' ); ?>
-
-                <?php endif; ?>
-            </div> <!-- #primary .content-area -->
-
-            <?php
-            /**
-             * Action took to fire inside product content after.
-             *
-             *  @since 2.4
-             */
-            do_action( 'dokan_product_content_inside_area_after' );
-            ?>
-        </div>
-
-        <?php
-        /**
-         * Action took to fire after dashboard content.
-         *
-         *  @since 2.4
-         */
-        do_action( 'dokan_dashboard_content_after' );
-
-        /**
-         * Action took to fire after product content area.
-         *
-         *  @since 2.4
-         */
-        do_action( 'dokan_after_product_content_area' );
-        ?>
-
-    </div><!-- .dokan-dashboard-wrap -->
-
-<?php do_action( 'dokan_dashboard_wrap_end' ); ?>
-
-<div class="dokan-clearfix"></div>
-
-<?php
-
-/**
- * Action hook to fire after dokan dashboard wrap
- *
- *  @since 2.4
- */
-do_action( 'dokan_dashboard_wrap_after', $post, $post_id );
-
-wp_reset_postdata();
-
-if ( ! $from_shortcode ) {
-    get_footer();
-}
-
-// phpcs:enable WordPress.WP.GlobalVariablesOverride.Prohibited
-?>
diff --git a/templates/products/edit/edit-product-single.php b/templates/products/edit/edit-product-single.php
new file mode 100755
index 0000000000..bea7d138c8
--- /dev/null
+++ b/templates/products/edit/edit-product-single.php
@@ -0,0 +1,213 @@
+<?php
+/**
+ * Dokan Dashboard Product Edit Template
+ *
+ * @since 2.4
+ *
+ * @var $product WC_Product instance of WC_Product object
+ * @var $from_shortcode bool if the template loaded from shortcode
+ * @var $new_product bool if the product is new
+ * @package dokan
+ */
+
+defined( 'ABSPATH' ) || exit;
+
+global $post; // phpcs:disable WordPress.WP.GlobalVariablesOverride.Prohibited
+
+if ( ! isset( $from_shortcode ) || ! wc_string_to_bool( $from_shortcode ) ) {
+    // this file is loaded from theme
+    // apply security check for theme
+    if ( ! current_user_can( 'dokan_edit_product' ) ) {
+        dokan_get_template_part(
+            'global/dokan-error', '', [
+                'deleted' => false,
+                'message' => __( 'You have no permission to view this page', 'dokan-lite' ),
+            ]
+        );
+        return;
+    }
+
+    // check if seller is enabled for selling
+    if ( ! dokan_is_seller_enabled( dokan_get_current_user_id() ) ) {
+        dokan_seller_not_enabled_notice();
+        return;
+    }
+
+    // while calling from theme, we need to check if the product id is passed or not
+    $post_id = isset( $_GET['product_id'] ) ? intval( wp_unslash( $_GET['product_id'] ) ) : $post->ID; //phpcs:ignore
+    if ( ! $post_id ) {
+        // this is `add new` product page
+        $product = new WC_Product_Simple();
+        $product->set_status( 'auto-draft' );
+        $product->save();
+        $new_product = true;
+    } else {
+        $product = wc_get_product( $post_id );
+        $new_product = false;
+    }
+}
+
+$post = get_post( $product->get_id() );
+
+if ( ! dokan_is_product_author( $product->get_id() ) ) {
+    wp_die( esc_html__( 'Access Denied', 'dokan-lite' ) );
+}
+
+if ( ! $from_shortcode ) {
+    get_header();
+}
+
+if ( isset( $_GET['errors'] ) ) { // phpcs:ignore
+    dokan()->dashboard->templates->products->set_errors( array_map( 'sanitize_text_field', wp_unslash( $_GET['errors'] ) ) ); //phpcs:ignore
+}
+
+/**
+ * Action hook to fire before dokan dashboard wrap
+ *
+ *  @since 2.4
+ */
+do_action( 'dokan_dashboard_wrap_before', $post, $product->get_id() );
+?>
+
+<?php do_action( 'dokan_dashboard_wrap_start' ); ?>
+
+    <div class="dokan-dashboard-wrap">
+
+        <?php
+        /**
+         * Action took to fire before dashboard content.
+         *
+         *  @hooked get_dashboard_side_navigation
+         *
+         *  @since 2.4
+         */
+        do_action( 'dokan_dashboard_content_before' );
+
+        /**
+         * Action hook to fire before product content area.
+         *
+         * @since 2.4
+         */
+        do_action( 'dokan_before_product_content_area' );
+        ?>
+
+        <div class="dokan-dashboard-content dokan-product-edit">
+
+            <?php
+            /**
+             * Action hook to fire inside product content area before
+             *
+             *  @since 2.4
+             */
+            do_action( 'dokan_product_content_inside_area_before' );
+
+            if ( $new_product ) {
+                do_action( 'dokan_new_product_before_product_area' );
+            }
+            ?>
+
+            <header class="dokan-dashboard-header dokan-clearfix">
+                <h1 class="entry-title">
+                    <?php
+                    if ( $new_product || 'auto-draft' === $product->get_status() ) {
+                        esc_html_e( 'Add New Product', 'dokan-lite' );
+                    } else {
+                        esc_html_e( 'Edit Product', 'dokan-lite' );
+                    }
+                    ?>
+                    <span class="dokan-label <?php echo esc_attr( dokan_get_post_status_label_class( $product->get_status() ) ); ?> dokan-product-status-label">
+                        <?php echo esc_html( dokan_get_post_status( $product->get_status() ) ); ?>
+                    </span>
+
+                    <?php if ( $product->get_status() === 'publish' ) : ?>
+                        <span class="dokan-right">
+                            <a class="dokan-btn dokan-btn-theme dokan-btn-sm" href="<?php echo esc_url( $product->get_permalink() ); ?>" target="_blank"><?php esc_html_e( 'View Product', 'dokan-lite' ); ?></a>
+                        </span>
+                    <?php endif; ?>
+
+                    <?php if ( $product->get_catalog_visibility() === 'hidden' ) : ?>
+                        <span class="dokan-right dokan-label dokan-label-default dokan-product-hidden-label"><i class="far fa-eye-slash"></i> <?php esc_html_e( 'Hidden', 'dokan-lite' ); ?></span>
+                    <?php endif; ?>
+                </h1>
+            </header><!-- .entry-header -->
+
+            <div class="product-edit-new-container product-edit-container">
+                <?php if ( dokan()->dashboard->templates->products->has_errors() ) : ?>
+                    <div class="dokan-alert dokan-alert-danger">
+                        <a class="dokan-close" data-dismiss="alert">&times;</a>
+
+                        <?php foreach ( dokan()->dashboard->templates->products->get_errors() as $error ) : ?>
+                            <strong><?php esc_html_e( 'Error!', 'dokan-lite' ); ?></strong> <?php echo esc_html( $error ); ?>.<br>
+                        <?php endforeach; ?>
+                    </div>
+                <?php endif; ?>
+
+                <?php if ( isset( $_GET['message'] ) && sanitize_text_field( wp_unslash( $_GET['message'] ) ) === 'success' ) : //phpcs:ignore ?>
+                    <div class="dokan-message">
+                        <button type="button" class="dokan-close" data-dismiss="alert">&times;</button>
+                        <strong><?php esc_html_e( 'Success!', 'dokan-lite' ); ?></strong> <?php esc_html_e( 'The product has been saved successfully.', 'dokan-lite' ); ?>
+
+                        <?php if ( $product->get_status() === 'publish' ) : ?>
+                            <a href="<?php echo esc_url( $product->get_permalink() ); ?>" target="_blank"><?php esc_html_e( 'View Product &rarr;', 'dokan-lite' ); ?></a>
+                        <?php endif; ?>
+                    </div>
+                <?php endif; ?>
+
+                <?php
+                if ( apply_filters( 'dokan_can_post', true ) ) {
+                    // we are using require_once intentionally
+                    include DOKAN_TEMPLATE_DIR . '/products/edit/sections/general.php';
+                } else {
+                    do_action( 'dokan_can_post_notice' );
+                }
+                ?>
+            </div> <!-- #primary .content-area -->
+
+            <?php
+            /**
+             * Action took to fire inside product content after.
+             *
+             *  @since 2.4
+             */
+            do_action( 'dokan_product_content_inside_area_after' );
+            ?>
+        </div>
+
+        <?php
+        /**
+         * Action took to fire after dashboard content.
+         *
+         *  @since 2.4
+         */
+        do_action( 'dokan_dashboard_content_after' );
+
+        /**
+         * Action took to fire after product content area.
+         *
+         *  @since 2.4
+         */
+        do_action( 'dokan_after_product_content_area' );
+        ?>
+
+    </div><!-- .dokan-dashboard-wrap -->
+
+<?php do_action( 'dokan_dashboard_wrap_end' ); ?>
+
+<div class="dokan-clearfix"></div>
+
+<?php
+
+/**
+ * Action hook to fire after dokan dashboard wrap
+ *
+ *  @since 2.4
+ */
+do_action( 'dokan_dashboard_wrap_after', $post, $product->get_id() );
+
+wp_reset_postdata();
+
+if ( ! $from_shortcode ) {
+    get_footer();
+}
+
+// phpcs:enable WordPress.WP.GlobalVariablesOverride.Prohibited
diff --git a/templates/products/catalog-mode-content.php b/templates/products/edit/sections/catalog-mode-content.php
similarity index 99%
rename from templates/products/catalog-mode-content.php
rename to templates/products/edit/sections/catalog-mode-content.php
index d6c259d93f..28ad13b949 100644
--- a/templates/products/catalog-mode-content.php
+++ b/templates/products/edit/sections/catalog-mode-content.php
@@ -8,6 +8,8 @@
 
 use WeDevs\Dokan\CatalogMode\Helper;
 
+defined( 'ABSPATH' ) || exit;
+
 ?>
 <?php do_action( 'dokan_product_edit_before_catalog_mode', $product_id, $saved_data ); ?>
 
diff --git a/templates/products/edit/sections/download-virtual.php b/templates/products/edit/sections/download-virtual.php
new file mode 100644
index 0000000000..f966427ba4
--- /dev/null
+++ b/templates/products/edit/sections/download-virtual.php
@@ -0,0 +1,61 @@
+<?php
+/**
+ * Downloadable and Virtual product type data.
+ *
+ * @since 2.4
+ *
+ * @var WC_Product $product
+ * @var Section    $section
+ * @var string     $class
+ * @var string     $digital_mode
+ */
+
+use WeDevs\Dokan\ProductForm\Elements;
+use WeDevs\Dokan\ProductForm\Section;
+
+defined( 'ABSPATH' ) || exit;
+
+
+?>
+<div class="dokan-form-group dokan-product-type-container <?php echo esc_attr( $class ); ?>">
+    <?php
+    $downloadable = $section->get_field( Elements::DOWNLOADABLE );
+    if ( ! is_wp_error( $downloadable ) && $downloadable->is_visible() ) :
+        ?>
+        <div class="content-half-part downloadable-checkbox">
+            <?php
+            dokan_post_input_box(
+                $product->get_id(),
+                $downloadable->get_name(),
+                [
+                    'value' => $product->is_downloadable() ? 'yes' : 'no',
+                    'label' => $downloadable->get_title(),
+                    'desc'  => $downloadable->get_description(),
+                ],
+                'checkbox'
+            );
+            ?>
+        </div>
+    <?php endif; ?>
+
+    <?php
+    $virtual = $section->get_field( Elements::VIRTUAL );
+    if ( ! is_wp_error( $virtual ) && $virtual->is_visible() ) :
+        ?>
+        <div class="content-half-part virtual-checkbox">
+            <?php
+            dokan_post_input_box(
+                $product->get_id(),
+                $virtual->get_name(),
+                [
+                    'value' => $product->is_virtual() ? 'yes' : 'no',
+                    'label' => $virtual->get_title(),
+                    'desc'  => $virtual->get_description(),
+                ],
+                'checkbox'
+            );
+            ?>
+        </div>
+    <?php endif; ?>
+    <div class="dokan-clearfix"></div>
+</div>
diff --git a/templates/products/edit/sections/downloadable.php b/templates/products/edit/sections/downloadable.php
new file mode 100644
index 0000000000..705980d618
--- /dev/null
+++ b/templates/products/edit/sections/downloadable.php
@@ -0,0 +1,150 @@
+<?php
+/**
+ * Product general data panel.
+ *
+ * @var WC_Product $product
+ * @var Section    $section
+ * @var string     $class
+ *
+ * @package WooCommerce\Admin
+ */
+
+use WeDevs\Dokan\ProductForm\Elements;
+use WeDevs\Dokan\ProductForm\Section;
+
+defined( 'ABSPATH' ) || exit;
+
+$post = get_post( $product->get_id() ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
+?>
+
+<div class="dokan-download-options dokan-edit-row dokan-clearfix <?php echo esc_attr( $class ); ?>">
+    <div class="dokan-section-heading" data-togglehandler="dokan_download_options">
+        <h2><i class="fas fa-download" aria-hidden="true"></i> <?php esc_html_e( 'Downloadable Options', 'dokan-lite' ); ?></h2>
+        <p><?php esc_html_e( 'Configure your downloadable product settings', 'dokan-lite' ); ?></p>
+        <a href="#" class="dokan-section-toggle">
+            <i class="fas fa-sort-down fa-flip-vertical" aria-hidden="true"></i>
+        </a>
+        <div class="dokan-clearfix"></div>
+    </div>
+
+    <div class="dokan-section-content">
+        <div class="dokan-divider-top dokan-clearfix">
+
+            <?php do_action( 'dokan_product_edit_before_sidebar' ); ?>
+
+            <div class="dokan-side-body dokan-download-wrapper">
+                <table class="dokan-table">
+                    <tfoot>
+                    <tr>
+                        <th colspan="3">
+                            <?php
+                            $key = '';
+                            $file = [
+                                'file' => '',
+                                'name' => '',
+                            ];
+                            ob_start();
+                            require DOKAN_INC_DIR . '/woo-views/html-product-download.php';
+                            $row_html = ob_get_clean();
+                            ?>
+                            <a href="#" class="insert-file-row dokan-btn dokan-btn-sm dokan-btn-success" data-row="<?php echo esc_attr( $row_html ); ?>">
+                                <?php esc_html_e( 'Add File', 'dokan-lite' ); ?>
+                            </a>
+                        </th>
+                    </tr>
+                    </tfoot>
+                    <thead>
+                    <tr>
+                        <th>
+                            <?php esc_html_e( 'Name', 'dokan-lite' ); ?>
+                            <span class="tips" title="<?php esc_attr_e( 'This is the name of the download shown to the customer.', 'dokan-lite' ); ?>">[?]</span>
+                        </th>
+                        <th>
+                            <?php esc_html_e( 'File URL', 'dokan-lite' ); ?>
+                            <span class="tips" title="<?php esc_attr_e( 'This is the URL or absolute path to the file which customers will get access to.', 'dokan-lite' ); ?>">[?]</span>
+                        </th>
+                        <th><?php esc_html_e( 'Action', 'dokan-lite' ); ?></th>
+                    </tr>
+                    </thead>
+                    <tbody>
+                    <?php
+
+                    $downloadable_files       = $product->get_downloads( 'edit' );
+                    $disabled_downloads_count = 0;
+
+                    if ( $downloadable_files ) {
+                        foreach ( $downloadable_files as $key => $file ) {
+                            include DOKAN_INC_DIR . '/woo-views/html-product-download.php';
+                        }
+                    }
+                    ?>
+                    </tbody>
+                </table>
+
+                <div class="dokan-clearfix">
+                    <?php
+                    $download_limit = $section->get_field( Elements::DOWNLOAD_LIMIT );
+                    if ( ! is_wp_error( $download_limit ) && $download_limit->is_visible() ) :
+                        ?>
+                        <div class="content-half-part">
+                            <label for="<?php echo esc_attr( $download_limit->get_name() ); ?>" class="form-label">
+                                <?php echo esc_html( $download_limit->get_title() ); ?>
+                                <i
+                                    class="fas fa-question-circle tips"
+                                    aria-hidden="true"
+                                    data-title="<?php echo esc_attr( $download_limit->get_description() ); ?>">
+                                </i>
+                            </label>
+                            <?php
+                            dokan_post_input_box(
+                                $product->get_id(),
+                                $download_limit->get_name(),
+                                [
+                                    'value'       => - 1 === $product->get_download_limit( 'edit' ) ? '' : $product->get_download_limit( 'edit' ),
+                                    'placeholder' => $download_limit->get_placeholder(),
+                                    'min'         => $download_limit->get_additional_properties( 'min' ),
+                                    'step'        => $download_limit->get_additional_properties( 'step' ),
+                                ],
+                                'number'
+                            );
+                            ?>
+                        </div><!-- .content-half-part -->
+                    <?php endif; ?>
+
+                    <?php
+                    $download_expiry = $section->get_field( Elements::DOWNLOAD_EXPIRY );
+                    if ( ! is_wp_error( $download_expiry ) && $download_expiry->is_visible() ) :
+                        ?>
+                        <div class="content-half-part">
+                            <label for="<?php echo esc_attr( $download_expiry->get_name() ); ?>" class="form-label">
+                                <?php echo esc_html( $download_expiry->get_title() ); ?>
+                                <i
+                                    class="fas fa-question-circle tips"
+                                    aria-hidden="true"
+                                    data-title="<?php echo esc_attr( $download_expiry->get_description() ); ?>">
+                                </i>
+                            </label>
+                            <?php
+                            dokan_post_input_box(
+                                $product->get_id(),
+                                $download_expiry->get_name(),
+                                [
+                                    'value'       => - 1 === $product->get_download_expiry( 'edit' ) ? '' : $product->get_download_expiry( 'edit' ),
+                                    'placeholder' => __( 'Never', 'dokan-lite' ),
+                                    'min'         => $download_expiry->get_additional_properties( 'min' ),
+                                    'step'        => $download_expiry->get_additional_properties( 'step' ),
+                                ],
+                                'number'
+                            );
+                            ?>
+                        </div><!-- .content-half-part -->
+                    <?php endif; ?>
+                </div>
+            </div> <!-- .dokan-side-body -->
+        </div> <!-- .downloadable -->
+    </div>
+
+    <?php do_action( 'dokan_product_edit_after_downloadable', $post, $product->get_id(), $product ); ?>
+</div>
+
+
diff --git a/templates/products/edit/sections/general.php b/templates/products/edit/sections/general.php
new file mode 100644
index 0000000000..a2062a5be1
--- /dev/null
+++ b/templates/products/edit/sections/general.php
@@ -0,0 +1,383 @@
+<?php
+/**
+ * Product general data panel.
+ *
+ * @var WC_Product $product
+ *
+ * @package Dokan\Templates
+ */
+
+use WeDevs\Dokan\ProductCategory\Helper;
+use WeDevs\Dokan\ProductForm\Elements;
+use WeDevs\Dokan\ProductForm\Factory as ProductFormFactory;
+
+defined( 'ABSPATH' ) || exit;
+
+$section = ProductFormFactory::get_section( 'general' );
+if ( is_wp_error( $section ) ) {
+    return;
+}
+?>
+<form class="dokan-product-edit-form" role="form" method="post" id="post">
+
+    <?php do_action( 'dokan_product_data_panel_tabs', $post, $product->get_id(), $product ); ?>
+    <?php do_action( 'dokan_product_edit_before_main', $post, $product->get_id(), $product ); ?>
+
+    <div class="dokan-form-top-area">
+
+        <div class="content-half-part dokan-product-meta">
+            <?php
+            $product_title = $section->get_field( Elements::NAME );
+            if ( ! is_wp_error( $product_title ) && $product_title->is_visible() ) :
+                ?>
+                <div id="dokan-product-title-area" class="dokan-form-group">
+                    <input type="hidden" name="dokan_product_id" id="dokan-edit-product-id" value="<?php echo esc_attr( $product->get_id() ); ?>" />
+
+                    <label for="<?php echo esc_attr( $product_title->get_name() ); ?>" class="form-label"><?php echo esc_html( $product_title->get_title() ); ?></label>
+                    <?php
+                    dokan_post_input_box(
+                        $product->get_id(),
+                        $product_title->get_name(),
+                        [
+                            'name'        => $product_title->get_name(),
+                            'placeholder' => $product_title->get_placeholder(),
+                            'value'       => $product->get_name(),
+                        ]
+                    );
+                    ?>
+                    <div class="dokan-product-title-alert dokan-hide">
+                        <?php echo wp_kses_post( $product_title->get_error_message() ); ?>
+                    </div>
+
+                    <div id="edit-slug-box" class="hide-if-no-js"></div>
+                    <?php wp_nonce_field( 'samplepermalink', 'samplepermalinknonce', false ); ?>
+                    <input type="hidden" name="editable-post-name" class="dokan-hide" id="editable-post-name-full-dokan">
+                    <input type="hidden" value="<?php echo esc_attr( $product->get_slug() ); ?>" name="edited-post-name" class="dokan-hide" id="edited-post-name-dokan">
+                </div>
+            <?php endif; ?>
+
+            <?php
+            $product_type = $section->get_field( Elements::TYPE );
+            if ( ! is_wp_error( $product_type ) && $product_type->is_visible() ) :
+                $product_type_options = $product_type->get_options();
+                ?>
+                <?php if ( count( $product_type_options ) === 1 && array_key_exists( 'simple', $product_type_options ) ) : ?>
+                <input type="hidden" id="<?php echo esc_attr( $product_type->get_name() ); ?>" name="<?php echo esc_attr( $product_type->get_name() ); ?>" value="simple">
+            <?php else : ?>
+                <div class="dokan-form-group">
+                    <label for="<?php echo esc_attr( $product_type->get_name() ); ?>" class="form-label">
+                        <?php echo esc_html( $product_type->get_title() ); ?>
+                        <i
+                            class="fas fa-question-circle tips"
+                            aria-hidden="true"
+                            data-title="<?php echo esc_attr( $product_type->get_help_content() ); ?>">
+                        </i>
+                    </label>
+                    <select name="<?php echo esc_attr( $product_type->get_name() ); ?>" class="dokan-form-control" id="<?php echo esc_attr( $product_type->get_name() ); ?>">
+                        <?php foreach ( $product_type_options as $key => $value ) : ?>
+                            <option value="<?php echo esc_attr( $key ); ?>" <?php selected( $product->get_type(), $key ); ?>><?php echo esc_html( $value ); ?></option>
+                        <?php endforeach; ?>
+                    </select>
+                </div>
+            <?php endif; ?>
+            <?php endif; ?>
+
+            <?php do_action( 'dokan_product_edit_after_title', $post, $product->get_id(), $product ); ?>
+
+            <div class="show_if_simple dokan-clearfix show_if_external">
+                <div class="dokan-form-group dokan-clearfix dokan-price-container">
+                    <?php
+                    $regular_price = $section->get_field( Elements::REGULAR_PRICE );
+                    if ( ! is_wp_error( $regular_price ) && $regular_price->is_visible() ) :
+                        ?>
+                        <div class="content-half-part regular-price">
+                            <label for="<?php echo esc_attr( $regular_price->get_name() ); ?>" class="form-label"><?php echo esc_html( $regular_price->get_title() ); ?></label>
+                            <div class="dokan-input-group">
+                                <span class="dokan-input-group-addon"><?php echo esc_html( get_woocommerce_currency_symbol() ); ?></span>
+                                <?php
+                                dokan_post_input_box(
+                                    $product->get_id(),
+                                    $regular_price->get_name(),
+                                    [
+                                        'value'       => $product->get_regular_price(),
+                                        'class'       => 'dokan-product-regular-price',
+                                        'placeholder' => $regular_price->get_placeholder(),
+                                    ],
+                                    'price'
+                                );
+                                ?>
+                            </div>
+                        </div>
+                    <?php endif; ?>
+
+                    <?php
+                    $sale_price                      = $section->get_field( Elements::SALE_PRICE );
+                    $sale_price_dates_from_timestamp = $product->get_date_on_sale_from( 'edit' ) ? $product->get_date_on_sale_from( 'edit' )->getOffsetTimestamp() : false;
+                    $sale_price_dates_to_timestamp   = $product->get_date_on_sale_to( 'edit' ) ? $product->get_date_on_sale_to( 'edit' )->getOffsetTimestamp() : false;
+
+                    $sale_price_dates_from = $sale_price_dates_from_timestamp ? date_i18n( 'Y-m-d', $sale_price_dates_from_timestamp ) : '';
+                    $sale_price_dates_to   = $sale_price_dates_to_timestamp ? date_i18n( 'Y-m-d', $sale_price_dates_to_timestamp ) : '';
+                    $show_schedule         = $sale_price_dates_from && $sale_price_dates_to;
+                    if ( ! is_wp_error( $sale_price ) && $sale_price->is_visible() ) :
+                        ?>
+                        <div class="content-half-part sale-price">
+                            <label for="<?php echo esc_attr( $sale_price->get_name() ); ?>" class="form-label">
+                                <?php echo esc_html( $sale_price->get_title() ); ?>
+                                <a href="#" class="sale_schedule <?php echo $show_schedule ? 'dokan-hide' : ''; ?>"><?php esc_html_e( 'Schedule', 'dokan-lite' ); ?></a>
+                                <a href="#" class="cancel_sale_schedule <?php echo ( ! $show_schedule ) ? 'dokan-hide' : ''; ?>"><?php esc_html_e( 'Cancel', 'dokan-lite' ); ?></a>
+                            </label>
+
+                            <div class="dokan-input-group">
+                                <span class="dokan-input-group-addon"><?php echo esc_html( get_woocommerce_currency_symbol() ); ?></span>
+                                <?php
+                                dokan_post_input_box(
+                                    $product->get_id(),
+                                    $sale_price->get_name(),
+                                    [
+                                        'name'        => $sale_price->get_name(),
+                                        'value'       => $product->get_sale_price(),
+                                        'class'       => 'dokan-product-sales-price',
+                                        'placeholder' => $sale_price->get_placeholder(),
+                                    ],
+                                    'price'
+                                );
+                                ?>
+                            </div>
+                        </div>
+                    <?php endif; ?>
+                </div>
+
+                <div class="dokan-form-group dokan-clearfix dokan-price-container">
+                    <div class="dokan-product-less-price-alert dokan-hide">
+                        <?php esc_html_e( 'Product price can\'t be less than the vendor fee!', 'dokan-lite' ); ?>
+                    </div>
+                </div>
+
+                <?php
+                if ( ! is_wp_error( $sale_price ) && $sale_price->is_visible() ) :
+                    $date_on_sale_from = $section->get_field( Elements::DATE_ON_SALE_FROM );
+                    $date_on_sale_to = $section->get_field( Elements::DATE_ON_SALE_TO );
+                    ?>
+                    <div class="sale_price_dates_fields dokan-clearfix dokan-form-group <?php echo ( ! $show_schedule ) ? 'dokan-hide' : ''; ?>">
+                        <div class="content-half-part from">
+                            <div class="dokan-input-group">
+                                <span class="dokan-input-group-addon"><?php echo esc_html( $date_on_sale_from->get_title() ); ?></span>
+                                <input
+                                    type="text"
+                                    name="<?php echo esc_attr( $date_on_sale_from->get_name() ); ?>"
+                                    class="dokan-form-control dokan-start-date"
+                                    value="<?php echo esc_attr( $sale_price_dates_from ); ?>"
+                                    maxlength="10"
+                                    pattern="[0-9]{4}-(0[1-9]|1[012])-(0[1-9]|1[0-9]|2[0-9]|3[01])"
+                                    placeholder="<?php echo esc_attr( $date_on_sale_from->get_placeholder() ); ?>">
+                            </div>
+                        </div>
+
+                        <div class="content-half-part to">
+                            <div class="dokan-input-group">
+                                <span class="dokan-input-group-addon"><?php echo esc_html( $date_on_sale_to->get_title() ); ?></span>
+                                <input
+                                    type="text"
+                                    name="<?php echo esc_attr( $date_on_sale_to->get_name() ); ?>"
+                                    class="dokan-form-control dokan-end-date"
+                                    value="<?php echo esc_attr( $sale_price_dates_to ); ?>"
+                                    maxlength="10"
+                                    pattern="[0-9]{4}-(0[1-9]|1[012])-(0[1-9]|1[0-9]|2[0-9]|3[01])"
+                                    placeholder="<?php echo esc_attr( $date_on_sale_to->get_placeholder() ); ?>">
+                            </div>
+                        </div>
+                    </div><!-- .sale-schedule-container -->
+                <?php endif; ?>
+            </div>
+
+            <?php do_action( 'dokan_product_edit_after_pricing', $post, $product->get_id(), $product ); ?>
+
+            <?php
+            $category = $section->get_field( Elements::CATEGORIES );
+            if ( ! is_wp_error( $category ) && $category->is_visible() ) :
+                ?>
+                <div class="dokan-form-group">
+                    <?php
+                    $data         = Helper::get_saved_products_category( $product->get_id() );
+                    $data['from'] = 'edit_product';
+
+                    dokan_get_template_part( 'products/dokan-category-header-ui', '', $data );
+                    ?>
+                </div>
+            <?php endif; ?>
+
+            <?php
+            $tags = $section->get_field( Elements::TAGS );
+            if ( ! is_wp_error( $tags ) && $tags->is_visible() ) :
+                $terms = $product->get_tag_ids();
+                ?>
+                <div class="dokan-form-group">
+                    <label for="product_tag_edit" class="form-label"><?php echo $tags->get_title(); ?></label>
+                    <select multiple="multiple" id="product_tag_edit" name="<?php echo esc_attr( $tags->get_name() ); ?>" class="product_tag_search dokan-form-control" data-placeholder="<?php echo esc_attr( $tags->get_placeholder() ); ?>">
+                        <?php if ( ! empty( $terms ) ) : ?>
+                            <?php
+                            foreach ( $terms as $tax_term ) :
+                                $tax_term = get_term_by( 'id', $tax_term, 'product_tag' );
+                                if ( is_wp_error( $tax_term ) ) {
+                                    continue;
+                                }
+                                ?>
+                                <option value="<?php echo esc_attr( $tax_term->term_id ); ?>" selected="selected"><?php echo esc_html( $tax_term->name ); ?></option>
+                            <?php endforeach ?>
+                        <?php endif ?>
+                    </select>
+                </div>
+                <?php do_action( 'dokan_product_edit_after_product_tags', $post, $product->get_id(), $product ); ?>
+            <?php endif; ?>
+        </div><!-- .content-half-part -->
+
+        <div class="content-half-part featured-image">
+            <?php
+            $featured_image = $section->get_field( Elements::FEATURED_IMAGE_ID );
+            if ( ! is_wp_error( $featured_image ) && $featured_image->is_visible() ) :
+                ?>
+                <div class="dokan-feat-image-upload dokan-new-product-featured-img">
+                    <?php
+                    $wrap_class        = ' dokan-hide';
+                    $instruction_class = '';
+                    $feat_image_id     = $product->get_image_id();
+
+                    if ( $feat_image_id ) {
+                        $wrap_class        = '';
+                        $instruction_class = ' dokan-hide';
+                    }
+                    ?>
+
+                    <div class="instruction-inside<?php echo esc_attr( $instruction_class ); ?>">
+                        <input type="hidden" name="<?php echo esc_attr( $featured_image->get_name() ); ?>" class="dokan-feat-image-id" value="<?php echo esc_attr( $feat_image_id ); ?>">
+
+                        <i class="fas fa-cloud-upload-alt"></i>
+                        <a href="#" class="dokan-feat-image-btn btn btn-sm"><?php esc_html_e( 'Upload a product cover image', 'dokan-lite' ); ?></a>
+                    </div>
+
+                    <div class="image-wrap<?php echo esc_attr( $wrap_class ); ?>">
+                        <a class="close dokan-remove-feat-image">&times;</a>
+                        <?php if ( $feat_image_id ) : ?>
+                            <?php
+                            // todo: need to change this with $product->get_image()
+                            echo get_the_post_thumbnail(
+                                $product->get_id(),
+                                apply_filters( 'single_product_large_thumbnail_size', 'shop_single' ),
+                                [
+                                    'height' => '',
+                                    'width'  => '',
+                                ]
+                            );
+                            ?>
+                        <?php else : ?>
+                            <img height="" width="" src="" alt="">
+                        <?php endif; ?>
+                    </div>
+                </div><!-- .dokan-feat-image-upload -->
+            <?php endif; ?>
+
+            <?php
+            $gallery_images = $section->get_field( Elements::GALLERY_IMAGE_IDS );
+            if ( ! is_wp_error( $gallery_images ) && $gallery_images->is_visible() ) :
+                ?>
+                <div class="dokan-product-gallery">
+                    <div class="dokan-side-body" id="dokan-product-images">
+                        <div id="product_images_container">
+                            <ul class="product_images dokan-clearfix">
+                                <?php
+                                $product_images = $product->get_gallery_image_ids();
+
+                                if ( $product_images ) :
+                                    foreach ( $product_images as $image_id ) :
+                                        if ( empty( $image_id ) ) :
+                                            continue;
+                                        endif;
+
+                                        $attachment_image = wp_get_attachment_image_src( $image_id, 'thumbnail' );
+                                        ?>
+                                        <li class="image" data-attachment_id="<?php echo esc_attr( $image_id ); ?>">
+                                            <img src="<?php echo esc_url( $attachment_image[0] ); ?>" alt="">
+                                            <a href="#" class="action-delete" title="<?php esc_attr_e( 'Delete image', 'dokan-lite' ); ?>">&times;</a>
+                                        </li>
+                                        <?php
+                                    endforeach;
+                                endif;
+                                ?>
+                                <li class="add-image add-product-images tips" data-title="<?php esc_html_e( 'Add gallery image', 'dokan-lite' ); ?>">
+                                    <a href="#" class="add-product-images"><i class="fas fa-plus" aria-hidden="true"></i></a>
+                                </li>
+                            </ul>
+
+                            <input type="hidden" id="product_image_gallery" name="<?php echo esc_attr( $gallery_images->get_name() ); ?>" value="<?php echo esc_attr( implode( ',', $product_images ) ); ?>">
+                        </div>
+                    </div>
+                </div> <!-- .product-gallery -->
+
+                <?php do_action( 'dokan_product_gallery_image_count' ); ?>
+            <?php endif; ?>
+
+        </div><!-- .content-half-part -->
+    </div><!-- .dokan-form-top-area -->
+
+    <?php
+    $short_description = $section->get_field( Elements::SHORT_DESCRIPTION );
+    if ( ! is_wp_error( $short_description ) && $short_description->is_visible() ) :
+        ?>
+        <div class="dokan-product-short-description">
+            <label for="<?php echo esc_attr( $short_description->get_name() ); ?>" class="form-label"><?php echo esc_html( $short_description->get_title() ); ?></label>
+            <?php
+            wp_editor(
+                $product->get_short_description(),
+                $short_description->get_name(),
+                apply_filters(
+                    'dokan_product_short_description',
+                    [
+                        'editor_height' => 50,
+                        'quicktags'     => true,
+                        'media_buttons' => false,
+                        'teeny'         => false,
+                        'editor_class'  => 'post_excerpt',
+                    ]
+                )
+            );
+            ?>
+        </div>
+    <?php endif; ?>
+
+    <?php
+    $description = $section->get_field( Elements::DESCRIPTION );
+    if ( ! is_wp_error( $description ) && $description->is_visible() ) :
+        ?>
+        <div class="dokan-product-description">
+            <label for="<?php echo esc_attr( $description->get_name() ); ?>" class="form-label"><?php echo esc_html( $description->get_title() ); ?></label>
+            <?php
+            wp_editor(
+                $product->get_description(),
+                $description->get_name(),
+                apply_filters(
+                    'dokan_product_description',
+                    [
+                        'editor_height' => 50,
+                        'quicktags'     => true,
+                        'media_buttons' => false,
+                        'teeny'         => false,
+                        'editor_class'  => 'post_content',
+                    ]
+                )
+            );
+            ?>
+        </div>
+    <?php endif; ?>
+
+    <?php do_action( 'dokan_product_edit_form', $post, $product->get_id(), $product ); ?>
+    <?php do_action( 'dokan_product_edit_after_main', $post, $product->get_id(), $product ); ?>
+    <?php do_action( 'dokan_product_edit_after_inventory_variants', $post, $product->get_id(), $product ); ?>
+    <?php do_action( 'dokan_product_edit_after_options', $product->get_id(), $product ); ?>
+
+    <?php wp_nonce_field( 'dokan_edit_product', 'dokan_edit_product_nonce' ); ?>
+    <input type="hidden" name="dokan_product_id" id="dokan_product_id" value="<?php echo esc_attr( $product->get_id() ); ?>" />
+    <!--hidden input for Firefox issue-->
+    <input type="hidden" name="dokan_update_product" value="<?php esc_attr_e( 'Save Product', 'dokan-lite' ); ?>" />
+    <input type="submit" name="dokan_update_product" id="publish" class="dokan-btn dokan-btn-theme dokan-btn-lg dokan-right" value="<?php esc_attr_e( 'Save Product', 'dokan-lite' ); ?>" />
+    <div class="dokan-clearfix"></div>
+</form>
diff --git a/templates/products/edit/sections/inventory.php b/templates/products/edit/sections/inventory.php
new file mode 100644
index 0000000000..7b76927d72
--- /dev/null
+++ b/templates/products/edit/sections/inventory.php
@@ -0,0 +1,229 @@
+<?php
+/**
+ * Dokan Dashboard Product Edit Template
+ *
+ * @since   2.4
+ *
+ * @var WC_Product $product instance of WC_Product object
+ * @var Section    $section
+ * @var string     $class
+ *
+ * @package dokan
+ */
+
+use WeDevs\Dokan\ProductForm\Elements;
+use WeDevs\Dokan\ProductForm\Section;
+
+defined( 'ABSPATH' ) || exit;
+
+$post = get_post( $product->get_id() ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
+?>
+
+<div class="dokan-product-inventory dokan-edit-row <?php echo esc_attr( $class ); ?>">
+    <div class="dokan-section-heading" data-togglehandler="dokan_product_inventory">
+        <h2><i class="fas fa-cubes" aria-hidden="true"></i> <?php echo esc_html( $section->get_title() ); ?></h2>
+        <p><?php echo esc_html( $section->get_description() ); ?></p>
+        <a href="#" class="dokan-section-toggle">
+            <i class="fas fa-sort-down fa-flip-vertical" aria-hidden="true"></i>
+        </a>
+        <div class="dokan-clearfix"></div>
+    </div>
+
+    <div class="dokan-section-content">
+        <?php
+        $sku = $section->get_field( Elements::SKU );
+        if ( ! is_wp_error( $sku ) && $sku->is_visible() ) :
+            ?>
+            <div class="content-half-part dokan-form-group">
+                <label for="<?php echo esc_attr( $sku->get_name() ); ?>" class="form-label">
+                    <?php echo wp_kses_post( $sku->get_title() ); ?>
+                    <i
+                        class="fas fa-question-circle tips"
+                        aria-hidden="true"
+                        data-title="<?php echo esc_attr( $sku->get_description() ); ?>">
+                    </i>
+                </label>
+                <?php
+                dokan_post_input_box(
+                    $product->get_id(),
+                    $sku->get_name(),
+                    [
+                        'value' => $product->get_sku(),
+                        'class' => 'dokan-form-control',
+                    ]
+                );
+                ?>
+            </div>
+        <?php endif; ?>
+
+        <?php
+        $stock_status = $section->get_field( Elements::STOCK_STATUS );
+        if ( ! is_wp_error( $stock_status ) && $stock_status->is_visible() ) :
+            ?>
+            <div class="content-half-part hide_if_variable hide_if_external hide_if_stock_global">
+                <label for="<?php echo esc_attr( $stock_status->get_name() ); ?>" class="form-label">
+                    <?php echo esc_html( $stock_status->get_title() ); ?>
+                    <i
+                        class="fas fa-question-circle tips"
+                        aria-hidden="true"
+                        data-title="<?php echo esc_attr( $stock_status->get_description() ); ?>">
+                    </i>
+                </label>
+                <?php
+                dokan_post_input_box(
+                    $product->get_id(),
+                    $stock_status->get_name(),
+                    [
+                        'name'    => $product->get_name(),
+                        'value'   => $product->get_stock_status(),
+                        'options' => $stock_status->get_options(),
+                    ],
+                    'select'
+                );
+                ?>
+            </div>
+        <?php endif; ?>
+
+        <div class="dokan-clearfix"></div>
+
+        <?php if ( 'yes' === get_option( 'woocommerce_manage_stock' ) ) : ?>
+            <?php
+            $manage_stock = $section->get_field( Elements::MANAGE_STOCK );
+            if ( ! is_wp_error( $manage_stock ) && $manage_stock->is_visible() ) :
+                ?>
+                <div class="dokan-form-group hide_if_grouped hide_if_external">
+                    <?php
+                    dokan_post_input_box(
+                        $product->get_id(),
+                        $manage_stock->get_name(),
+                        [
+                            'value' => $product->get_manage_stock( 'edit' ) ? 'yes' : 'no',
+                            'label' => $manage_stock->get_title(),
+                        ],
+                        'checkbox'
+                    );
+                    ?>
+                </div>
+            <?php endif; ?>
+
+            <div class="show_if_stock dokan-stock-management-wrapper dokan-form-group dokan-clearfix">
+                <?php
+                $stock_quantity = $section->get_field( Elements::STOCK_QUANTITY );
+                if ( ! is_wp_error( $stock_quantity ) && $stock_quantity->is_visible() ) :
+                    ?>
+                    <div class="content-half-part">
+                        <label for="<?php echo esc_attr( $stock_quantity->get_name() ); ?>" class="form-label">
+                            <?php echo esc_html( $stock_quantity->get_title() ); ?>
+                            <i
+                                class="fas fa-question-circle tips"
+                                aria-hidden="true"
+                                data-title="<?php echo esc_attr( $stock_quantity->get_description() ); ?>">
+                            </i>
+                        </label>
+                        <?php
+                        dokan_post_input_box(
+                            $product->get_id(),
+                            $stock_quantity->get_name(),
+                            [
+                                'name'        => $stock_quantity->get_name(),
+                                'value'       => wc_stock_amount( $product->get_stock_quantity( 'edit' ) ),
+                                'class'       => 'dokan-form-control',
+                                'placeholder' => $stock_quantity->get_placeholder(),
+                                'min'         => $stock_quantity->get_additional_properties( 'min' ),
+                                'step'        => $stock_quantity->get_additional_properties( 'step' ),
+                            ],
+                            'number'
+                        );
+                        ?>
+                    </div>
+                    <input type="hidden" name="_original_stock" value="<?php echo esc_attr( wc_stock_amount( $product->get_stock_quantity( 'edit' ) ) ); ?>"/>
+                <?php endif; ?>
+
+                <?php
+                $low_stock_amount = $section->get_field( Elements::LOW_STOCK_AMOUNT );
+                if ( ! is_wp_error( $low_stock_amount ) && $low_stock_amount->is_visible() ) :
+                    ?>
+                    <div class="content-half-part">
+                        <label for="<?php echo esc_attr( $low_stock_amount->get_name() ); ?>" class="form-label">
+                            <?php echo esc_html( $low_stock_amount->get_title() ); ?>
+                            <i
+                                class="fas fa-question-circle tips"
+                                aria-hidden="true"
+                                data-title="<?php echo esc_attr( $low_stock_amount->get_description() ); ?>">
+                            </i>
+                        </label>
+                        <?php
+                        dokan_post_input_box(
+                            $product->get_id(),
+                            $stock_quantity->get_name(),
+                            [
+                                'value'       => $product->get_low_stock_amount( 'edit' ),
+                                'class'       => 'dokan-form-control',
+                                'placeholder' => $stock_quantity->get_placeholder(),
+                                'min'         => $stock_quantity->get_additional_properties( 'min' ),
+                                'step'        => $stock_quantity->get_additional_properties( 'step' ),
+                            ],
+                            'number'
+                        );
+                        ?>
+                    </div>
+                <?php endif; ?>
+
+                <?php
+                $backorders = $section->get_field( Elements::BACKORDERS );
+                if ( is_wp_error( $backorders ) && $backorders->is_visible() ) :
+                    ?>
+                    <div class="content-half-part last-child">
+                        <label for="<?php echo esc_attr( $backorders->get_name() ); ?>" class="form-label">
+                            <?php echo esc_html( $backorders->get_title() ); ?>
+                            <i
+                                class="fas fa-question-circle tips"
+                                aria-hidden="true"
+                                data-title="<?php echo esc_attr( $backorders->get_description() ); ?>">
+                            </i>
+                        </label>
+
+                        <?php
+                        dokan_post_input_box(
+                            $product->get_id(),
+                            $backorders->get_name(),
+                            [
+                                'options' => $backorders->get_options(),
+                            ],
+                            'select'
+                        );
+                        ?>
+                    </div>
+                <?php endif; ?>
+
+                <div class="dokan-clearfix"></div>
+            </div><!-- .show_if_stock -->
+        <?php endif; ?>
+
+        <?php
+        $sold_individually = $section->get_field( Elements::SOLD_INDIVIDUALLY );
+        if ( ! is_wp_error( $sold_individually ) && $sold_individually->is_visible() ) :
+            ?>
+            <div class="dokan-form-group hide_if_grouped hide_if_external">
+                <label class="" for="<?php echo esc_attr( $sold_individually->get_name() ); ?>">
+                    <input
+                        name="<?php echo esc_attr( $sold_individually->get_name() ); ?>"
+                        id="<?php echo esc_attr( $sold_individually->get_name() ); ?>"
+                        value="yes"
+                        type="checkbox" <?php checked( $product->get_sold_individually( 'edit' ), true ); ?>>
+                    <i
+                        class="fas fa-question-circle tips"
+                        aria-hidden="true"
+                        data-title="<?php echo esc_attr( $sold_individually->get_description() ); ?>">
+                    </i>
+                    <?php esc_html_e( 'Allow only one quantity of this product to be bought in a single order', 'dokan-lite' ); ?>
+                </label>
+            </div>
+        <?php endif; ?>
+
+        <?php do_action( 'dokan_product_edit_after_inventory' ); ?>
+        <?php do_action( 'dokan_product_edit_after_sidebar', $post, $product->get_id(), $product ); ?>
+        <?php do_action( 'dokan_single_product_edit_after_sidebar', $post, $product->get_id(), $product ); ?>
+
+    </div><!-- .dokan-side-right -->
+</div><!-- .dokan-product-inventory -->
diff --git a/templates/products/edit/sections/others.php b/templates/products/edit/sections/others.php
new file mode 100644
index 0000000000..0dc022a092
--- /dev/null
+++ b/templates/products/edit/sections/others.php
@@ -0,0 +1,112 @@
+<?php
+/**
+ * Product other data panel.
+ *
+ * @since   2.4
+ *
+ * @var Section    $section
+ * @var WC_Product $product
+ * @var string     $post_status
+ * @var string     $class
+ *
+ * @package WeDevs\Dokan
+ */
+
+use WeDevs\Dokan\ProductForm\Elements;
+use WeDevs\Dokan\ProductForm\Section;
+
+?>
+
+<div class="dokan-other-options dokan-edit-row dokan-clearfix <?php echo esc_attr( $class ); ?>">
+    <div class="dokan-section-heading" data-togglehandler="dokan_other_options">
+        <h2><i class="fas fa-cog" aria-hidden="true"></i> <?php echo esc_html( $section->get_title() ); ?></h2>
+        <p><?php echo esc_html( $section->get_description() ); ?></p>
+        <a href="#" class="dokan-section-toggle">
+            <i class="fas fa-sort-down fa-flip-vertical" aria-hidden="true"></i>
+        </a>
+        <div class="dokan-clearfix"></div>
+    </div>
+
+    <div class="dokan-section-content">
+        <?php
+        $product_status = $section->get_field( Elements::STATUS );
+        if ( ! is_wp_error( $product_status ) && $product_status->is_visible() ) :
+            ?>
+            <div class="dokan-form-group content-half-part">
+                <label for="<?php echo esc_attr( $product_status->get_name() ); ?>" class="form-label">
+                    <?php echo esc_html( $product_status->get_title() ); ?>
+                </label>
+                <select id="<?php echo esc_attr( $product_status->get_name() ); ?>" class="dokan-form-control" name="<?php echo esc_attr( $product_status->get_name() ); ?>">
+                    <?php foreach ( $product_status->get_options() as $status => $label ) : // phpcs:ignore ?>
+                        <option value="<?php echo esc_attr( $status ); ?>" <?php selected( $status, $post_status ); ?>>
+                            <?php echo esc_html( $label ); ?>
+                        </option>
+                    <?php endforeach; ?>
+                </select>
+            </div>
+        <?php endif; ?>
+
+        <?php
+        $catalog_visibility = $section->get_field( Elements::CATALOG_VISIBILITY );
+        if ( ! is_wp_error( $catalog_visibility ) && $catalog_visibility->is_visible() ) :
+            ?>
+            <div class="dokan-form-group content-half-part">
+                <label for="<?php echo esc_attr( $catalog_visibility->get_name() ); ?>" class="form-label">
+                    <?php echo esc_html( $catalog_visibility->get_title() ); ?>
+                </label>
+                <select name="<?php echo esc_attr( $catalog_visibility->get_name() ); ?>" id="<?php echo esc_attr( $catalog_visibility->get_name() ); ?>" class="dokan-form-control">
+                    <?php foreach ( $catalog_visibility->get_options() as $name => $label ) : ?>
+                        <option value="<?php echo esc_attr( $name ); ?>" <?php selected( $product->get_catalog_visibility( 'edit' ), $name ); ?>>
+                            <?php echo esc_html( $label ); ?>
+                        </option>
+                    <?php endforeach; ?>
+                </select>
+            </div>
+        <?php endif; ?>
+
+        <div class="dokan-clearfix"></div>
+
+        <?php
+        $purchase_note = $section->get_field( Elements::PURCHASE_NOTE );
+        if ( ! is_wp_error( $purchase_note ) && $purchase_note->is_visible() ) :
+            ?>
+            <div class="dokan-form-group">
+                <label for="<?php echo esc_attr( $purchase_note->get_name() ); ?>" class="form-label">
+                    <?php echo esc_html( $purchase_note->get_title() ); ?>
+                </label>
+                <?php
+                dokan_post_input_box(
+                    $product->get_id(),
+                    $purchase_note->get_name(),
+                    [
+                        'name' => $purchase_note->get_name(),
+                        'value'       => $product->get_purchase_note( 'edit' ),
+                        'placeholder' => $purchase_note->get_placeholder(),
+                    ],
+                    'textarea'
+                );
+                ?>
+            </div>
+        <?php endif; ?>
+
+        <?php
+        $enable_reviews = $section->get_field( Elements::REVIEWS_ALLOWED );
+        if ( ! is_wp_error( $enable_reviews ) && $enable_reviews->is_visible() && post_type_supports( 'product', 'comments' ) ) :
+            ?>
+            <div class="dokan-form-group">
+                <?php
+                dokan_post_input_box(
+                    $product->get_id(),
+                    $enable_reviews->get_name(),
+                    [
+                        'name' => $enable_reviews->get_name(),
+                        'value' => $product->get_reviews_allowed( 'edit' ) ? 'yes' : 'no',
+                        'label' => __( 'Enable product reviews', 'dokan-lite' ),
+                    ],
+                    'checkbox'
+                );
+                ?>
+            </div>
+        <?php endif; ?>
+    </div>
+</div><!-- .dokan-other-options -->
diff --git a/templates/products/inventory.php b/templates/products/inventory.php
deleted file mode 100644
index ec3c665baf..0000000000
--- a/templates/products/inventory.php
+++ /dev/null
@@ -1,96 +0,0 @@
-<div class="dokan-product-inventory dokan-edit-row <?php echo esc_attr( $class ); ?>">
-    <div class="dokan-section-heading" data-togglehandler="dokan_product_inventory">
-        <h2><i class="fas fa-cubes" aria-hidden="true"></i> <?php esc_html_e( 'Inventory', 'dokan-lite' ); ?></h2>
-        <p><?php esc_html_e( 'Manage inventory for this product.', 'dokan-lite' ); ?></p>
-        <a href="#" class="dokan-section-toggle">
-            <i class="fas fa-sort-down fa-flip-vertical" aria-hidden="true"></i>
-        </a>
-        <div class="dokan-clearfix"></div>
-    </div>
-
-    <div class="dokan-section-content">
-
-        <div class="content-half-part dokan-form-group">
-            <label for="_sku" class="form-label"><?php esc_html_e( 'SKU', 'dokan-lite' ); ?> <span><?php esc_html_e( '(Stock Keeping Unit)', 'dokan-lite' ); ?></span></label>
-            <?php dokan_post_input_box( $post_id, '_sku' ); ?>
-        </div>
-
-        <div class="content-half-part hide_if_variable hide_if_external hide_if_stock_global">
-            <label for="_stock_status" class="form-label"><?php esc_html_e( 'Stock Status', 'dokan-lite' ); ?></label>
-
-            <?php
-            dokan_post_input_box(
-                $post_id,
-                '_stock_status',
-                [
-                    'options' => [
-                        'instock'     => __( 'In Stock', 'dokan-lite' ),
-                        'outofstock'  => __( 'Out of Stock', 'dokan-lite' ),
-                        'onbackorder' => __( 'On Backorder', 'dokan-lite' ),
-                    ],
-                ],
-                'select'
-            );
-            ?>
-        </div>
-
-        <div class="dokan-clearfix"></div>
-
-        <?php if ( 'yes' === get_option( 'woocommerce_manage_stock' ) ) : ?>
-        <div class="dokan-form-group hide_if_grouped hide_if_external">
-            <?php dokan_post_input_box( $post_id, '_manage_stock', array( 'label' => __( 'Enable product stock management', 'dokan-lite' ) ), 'checkbox' ); ?>
-        </div>
-
-        <div class="show_if_stock dokan-stock-management-wrapper dokan-form-group dokan-clearfix">
-
-            <div class="content-half-part">
-                <label for="_stock" class="form-label"><?php esc_html_e( 'Stock quantity', 'dokan-lite' ); ?></label>
-                <input type="number" class="dokan-form-control" name="_stock" placeholder="<?php esc_attr__( '1', 'dokan-lite' ); ?>" value="<?php echo esc_attr( wc_stock_amount( $_stock ) ); ?>" min="0" step="1">
-            </div>
-
-            <?php if ( version_compare( WC_VERSION, '3.4.7', '>' ) ) : ?>
-            <div class="content-half-part">
-                <label for="_low_stock_amount" class="form-label"><?php esc_html_e( 'Low stock threshold', 'dokan-lite' ); ?></label>
-                <input type="number" class="dokan-form-control" name="_low_stock_amount" placeholder="<?php esc_attr__( '1', 'dokan-lite' ); ?>" value="<?php echo esc_attr( wc_stock_amount( $_low_stock_amount ) ); ?>" min="0" step="1">
-            </div>
-            <?php endif; ?>
-
-            <div class="content-half-part last-child">
-                <label for="_backorders" class="form-label"><?php esc_html_e( 'Allow Backorders', 'dokan-lite' ); ?></label>
-
-                <?php
-                dokan_post_input_box(
-                    $post_id,
-                    '_backorders',
-                    [
-                        'options' => [
-                            'no'     => __( 'Do not allow', 'dokan-lite' ),
-                            'notify' => __( 'Allow but notify customer', 'dokan-lite' ),
-                            'yes'    => __( 'Allow', 'dokan-lite' ),
-                        ],
-                    ],
-                    'select'
-                );
-                ?>
-            </div>
-            <div class="dokan-clearfix"></div>
-        </div><!-- .show_if_stock -->
-        <?php endif; ?>
-
-        <div class="dokan-form-group hide_if_grouped hide_if_external">
-            <label class="" for="_sold_individually">
-                <input name="_sold_individually" id="_sold_individually" value="yes" type="checkbox" <?php checked( $_sold_individually, 'yes' ); ?>>
-                <?php esc_html_e( 'Allow only one quantity of this product to be bought in a single order', 'dokan-lite' ); ?>
-            </label>
-        </div>
-
-        <?php if ( $post_id ) : ?>
-            <?php do_action( 'dokan_product_edit_after_inventory' ); ?>
-        <?php endif; ?>
-
-        <?php do_action( 'dokan_product_edit_after_downloadable', $post, $post_id ); ?>
-        <?php do_action( 'dokan_product_edit_after_sidebar', $post, $post_id ); ?>
-        <?php do_action( 'dokan_single_product_edit_after_sidebar', $post, $post_id ); ?>
-
-    </div><!-- .dokan-side-right -->
-</div><!-- .dokan-product-inventory -->
diff --git a/templates/products/new-product.php b/templates/products/new-product.php
deleted file mode 100755
index 65b379528c..0000000000
--- a/templates/products/new-product.php
+++ /dev/null
@@ -1,367 +0,0 @@
-<?php
-
-use WeDevs\Dokan\ProductCategory\Helper;
-
-$created_product      = null;
-$feat_image_id        = null;
-$regular_price        = '';
-$sale_price           = '';
-$sale_price_date_from = '';
-$sale_price_date_to   = '';
-$post_content         = '';
-$post_excerpt         = '';
-$product_images       = '';
-$post_title           = '';
-$terms                = [];
-$currency_symbol      = get_woocommerce_currency_symbol();
-
-if ( isset( $_REQUEST['_dokan_add_product_nonce'] ) && wp_verify_nonce( sanitize_key( $_REQUEST['_dokan_add_product_nonce'] ), 'dokan_add_product_nonce' ) ) {
-    if ( ! empty( $_REQUEST['created_product'] ) ) {
-        $created_product = intval( $_REQUEST['created_product'] );
-    }
-
-    if ( ! empty( $_REQUEST['feat_image_id'] ) ) {
-        $feat_image_id = intval( $_REQUEST['feat_image_id'] );
-    }
-
-    if ( ! empty( $_REQUEST['_regular_price'] ) ) {
-        $regular_price = floatval( $_REQUEST['_regular_price'] );
-    }
-
-    if ( ! empty( $_REQUEST['_sale_price'] ) ) {
-        $sale_price = floatval( $_REQUEST['_sale_price'] );
-    }
-
-    if ( ! empty( $_REQUEST['_sale_price_dates_from'] ) ) {
-        $sale_price_date_from = sanitize_text_field( wp_unslash( $_REQUEST['_sale_price_dates_from'] ) );
-    }
-
-    if ( ! empty( $_REQUEST['_sale_price_dates_to'] ) ) {
-        $sale_price_date_to = sanitize_text_field( wp_unslash( $_REQUEST['_sale_price_dates_to'] ) );
-    }
-
-    if ( ! empty( $_REQUEST['post_content'] ) ) {
-        $post_content = wp_kses_post( wp_unslash( $_REQUEST['post_content'] ) );
-    }
-
-    if ( ! empty( $_REQUEST['post_excerpt'] ) ) {
-        $post_excerpt = sanitize_textarea_field( wp_unslash( $_REQUEST['post_excerpt'] ) );
-    }
-
-    if ( ! empty( $_REQUEST['post_title'] ) ) {
-        $post_title = sanitize_text_field( wp_unslash( $_REQUEST['post_title'] ) );
-    }
-
-    if ( ! empty( $_REQUEST['product_image_gallery'] ) ) {
-        $product_images = sanitize_text_field( wp_unslash( $_REQUEST['product_image_gallery'] ) );
-    }
-
-    if ( ! empty( $_REQUEST['product_tag'] ) ) {
-        $terms = array_map( 'intval', (array) wp_unslash( $_REQUEST['product_tag'] ) );
-    }
-}
-
-/**
- * Action hook to fire before new product wrap.
- *
- *  @since 2.4
- */
-do_action( 'dokan_new_product_wrap_before' );
-?>
-
-<?php do_action( 'dokan_dashboard_wrap_start' ); ?>
-
-    <div class="dokan-dashboard-wrap">
-        <?php
-        /**
-         * Action hook to fire before rendering dashboard content.
-         *
-         *  @hooked get_dashboard_side_navigation
-         *
-         *  @since 2.4
-         */
-        do_action( 'dokan_dashboard_content_before' );
-
-        /**
-         * Action hook to fire before rendering product content
-         *
-         * @since 2.4
-         */
-        do_action( 'dokan_before_new_product_content_area' );
-        ?>
-
-
-        <div class="dokan-dashboard-content">
-
-            <?php
-            /**
-             *  Action hook to fire inside new product content before
-             *
-             *  @since 2.4
-             */
-            do_action( 'dokan_before_new_product_inside_content_area' );
-            ?>
-
-            <header class="dokan-dashboard-header dokan-clearfix">
-                <h1 class="entry-title">
-                    <?php esc_html_e( 'Add New Product', 'dokan-lite' ); ?>
-                </h1>
-            </header><!-- .entry-header -->
-
-            <?php do_action( 'dokan_new_product_before_product_area' ); ?>
-
-            <div class="dokan-new-product-area">
-                <?php if ( dokan()->dashboard->templates->products->has_errors() ) : ?>
-                    <div class="dokan-alert dokan-alert-danger">
-                        <a class="dokan-close" data-dismiss="alert">&times;</a>
-
-                        <?php foreach ( dokan()->dashboard->templates->products->get_errors() as $error_msg ) : ?>
-                            <strong><?php esc_html_e( 'Error!', 'dokan-lite' ); ?></strong> <?php echo wp_kses_post( $error_msg ); ?>.<br>
-                        <?php endforeach; ?>
-                    </div>
-                <?php endif; ?>
-
-                <?php if ( ! empty( $created_product ) ) : ?>
-                    <div class="dokan-alert dokan-alert-success">
-                        <a class="dokan-close" data-dismiss="alert">&times;</a>
-                        <strong><?php esc_html_e( 'Success!', 'dokan-lite' ); ?></strong>
-                        <?php
-                        printf(
-                            /* translators: %s: product title with edit link */
-                            __( 'You have successfully created %s product', 'dokan-lite' ),
-                            sprintf(
-                                '<a href="%s"><strong>%s</strong></a>',
-                                esc_url( dokan_edit_product_url( $created_product ) ),
-                                get_the_title( $created_product )
-                            )
-                        );
-                        ?>
-                    </div>
-                <?php endif ?>
-
-                <?php
-                if ( apply_filters( 'dokan_can_post', true ) ) :
-                    $feat_image_url   = '';
-                    $hide_instruction = '';
-                    $hide_img_wrap    = 'dokan-hide';
-
-                    if ( ! empty( $feat_image_id ) ) {
-                        $feat_image_url   = wp_get_attachment_url( $image_id );
-                        $hide_instruction = 'dokan-hide';
-                        $hide_img_wrap    = '';
-                    }
-
-                    if ( dokan_is_seller_enabled( get_current_user_id() ) ) :
-                        ?>
-                        <form class="dokan-form-container" method="post">
-                            <div class="product-edit-container dokan-clearfix">
-                                <div class="content-half-part featured-image">
-                                    <div class="featured-image">
-                                        <div class="dokan-feat-image-upload">
-                                            <div class="instruction-inside <?php echo esc_attr( $hide_instruction ); ?>">
-                                                <input type="hidden" name="feat_image_id" class="dokan-feat-image-id" value="<?php echo esc_attr( $feat_image_id ); ?>">
-                                                <i class="fas fa-cloud-upload-alt"></i>
-                                                <a href="#" class="dokan-feat-image-btn dokan-btn"><?php esc_html_e( 'Upload Product Image', 'dokan-lite' ); ?></a>
-                                            </div>
-
-                                            <div class="image-wrap <?php echo esc_attr( $hide_img_wrap ); ?>">
-                                                <a class="close dokan-remove-feat-image">&times;</a>
-                                                    <img src="<?php echo esc_url( $feat_image_url ); ?>" alt="">
-                                            </div>
-                                        </div>
-                                    </div>
-
-                                    <div class="dokan-product-gallery">
-                                        <div class="dokan-side-body" id="dokan-product-images">
-                                            <div id="product_images_container">
-                                                <ul class="product_images dokan-clearfix">
-                                                    <?php
-                                                    if ( ! empty( $product_images ) ) :
-                                                        $gallery = explode( ',', $product_images );
-                                                        if ( $gallery ) :
-                                                            foreach ( $gallery as $image_id ) :
-                                                                if ( empty( $image_id ) ) :
-                                                                    continue;
-                                                                endif;
-
-                                                                $attachment_image = wp_get_attachment_image_src( $image_id );
-
-                                                                if ( ! $attachment_image ) :
-                                                                    continue;
-                                                                endif;
-                                                                ?>
-                                                                <li class="image" data-attachment_id="<?php echo esc_attr( $image_id ); ?>">
-                                                                    <img src="<?php echo esc_url( $attachment_image[0] ); ?>" alt="">
-                                                                    <a href="#" class="action-delete" title="<?php esc_attr_e( 'Delete image', 'dokan-lite' ); ?>">&times;</a>
-                                                                </li>
-                                                                <?php
-                                                            endforeach;
-                                                        endif;
-                                                    endif;
-                                                    ?>
-                                                    <li class="add-image add-product-images tips" data-title="<?php esc_attr_e( 'Add gallery image', 'dokan-lite' ); ?>">
-                                                        <a href="#" class="add-product-images"><i class="fas fa-plus" aria-hidden="true"></i></a>
-                                                    </li>
-                                                </ul>
-                                                <input type="hidden" id="product_image_gallery" name="product_image_gallery" value="">
-                                            </div>
-                                        </div>
-                                    </div> <!-- .product-gallery -->
-                                    <?php do_action( 'dokan_product_gallery_image_count' ); ?>
-                                </div>
-
-                                <div class="content-half-part dokan-product-meta">
-                                    <div class="dokan-form-group">
-                                        <input class="dokan-form-control" name="post_title" id="post-title" type="text" placeholder="<?php esc_attr_e( 'Product name..', 'dokan-lite' ); ?>" value="<?php echo esc_attr( $post_title ); ?>">
-                                    </div>
-
-                                    <div class="dokan-form-group">
-                                        <div class="dokan-form-group dokan-clearfix dokan-price-container">
-                                            <div class="content-half-part">
-                                                <label for="_regular_price" class="dokan-form-label"><?php esc_html_e( 'Price', 'dokan-lite' ); ?></label>
-                                                <div class="dokan-input-group">
-                                                    <span class="dokan-input-group-addon"><?php echo esc_attr( $currency_symbol ); ?></span>
-                                                    <input type="text" class="dokan-form-control wc_input_price dokan-product-regular-price" name="_regular_price" placeholder="0.00" id="_regular_price" value="<?php echo esc_attr( $regular_price ); ?>">
-                                                </div>
-                                            </div>
-
-                                            <div class="content-half-part sale-price">
-                                                <label for="_sale_price" class="form-label">
-                                                    <?php esc_html_e( 'Discounted Price', 'dokan-lite' ); ?>
-                                                    <a href="#" class="sale_schedule"><?php esc_html_e( 'Schedule', 'dokan-lite' ); ?></a>
-                                                    <a href="#" class="cancel_sale_schedule dokan-hide"><?php esc_html_e( 'Cancel', 'dokan-lite' ); ?></a>
-                                                </label>
-
-                                                <div class="dokan-input-group">
-                                                    <span class="dokan-input-group-addon"><?php echo esc_attr( $currency_symbol ); ?></span>
-                                                    <input type="text" class="dokan-form-control wc_input_price dokan-product-sales-price" name="_sale_price" placeholder="0.00" id="_sale_price" value="<?php echo esc_attr( $sale_price ); ?>">
-                                                </div>
-                                            </div>
-                                        </div>
-
-                                        <div class="dokan-hide sale-schedule-container sale_price_dates_fields dokan-clearfix dokan-form-group">
-                                            <div class="content-half-part from">
-                                                <div class="dokan-input-group">
-                                                    <span class="dokan-input-group-addon"><?php esc_html_e( 'From', 'dokan-lite' ); ?></span>
-                                                    <input type="text" name="_sale_price_dates_from" class="dokan-form-control datepicker sale_price_dates_from" maxlength="10" value="<?php echo esc_attr( $sale_price_date_from ); ?>" pattern="[0-9]{4}-(0[1-9]|1[012])-(0[1-9]|1[0-9]|2[0-9]|3[01])" placeholder="<?php esc_attr_e( 'YYYY-MM-DD', 'dokan-lite' ); ?>">
-                                                </div>
-                                            </div>
-
-                                            <div class="content-half-part to">
-                                                <div class="dokan-input-group">
-                                                    <span class="dokan-input-group-addon"><?php esc_html_e( 'To', 'dokan-lite' ); ?></span>
-                                                    <input type="text" name="_sale_price_dates_to" class="dokan-form-control datepicker sale_price_dates_to" value="<?php echo esc_attr( $sale_price_date_to ); ?>" maxlength="10" pattern="[0-9]{4}-(0[1-9]|1[012])-(0[1-9]|1[0-9]|2[0-9]|3[01])" placeholder="<?php esc_attr_e( 'YYYY-MM-DD', 'dokan-lite' ); ?>">
-                                                </div>
-                                            </div>
-                                        </div><!-- .sale-schedule-container -->
-                                    </div>
-
-                                    <div class="dokan-form-group">
-                                        <textarea name="post_excerpt" id="post-excerpt" rows="5" class="dokan-form-control" placeholder="<?php esc_attr_e( 'Short description of the product...', 'dokan-lite' ); ?>"><?php echo esc_attr( $post_excerpt ); ?></textarea>
-                                    </div>
-
-                                    <?php
-                                    $can_create_tags        = 'on' === dokan_get_option( 'product_vendors_can_create_tags', 'dokan_selling' );
-                                    $saved_product_cat_data = array_merge( (array) Helper::get_saved_products_category(), [ 'from' => 'new_product' ] );
-                                    dokan_get_template_part( 'products/dokan-category-header-ui', '', $saved_product_cat_data );
-                                    ?>
-
-                                    <div class="dokan-form-group">
-                                        <label for="product_tag" class="form-label"><?php esc_html_e( 'Tags', 'dokan-lite' ); ?></label>
-                                        <select multiple="multiple" name="product_tag[]" id="product_tag_search" class="product_tag_search product_tags dokan-form-control dokan-select2" data-placeholder="<?php echo $can_create_tags ? esc_attr__( 'Select tags/Add tags', 'dokan-lite' ) : esc_attr__( 'Select product tags', 'dokan-lite' ); ?>">
-                                            <?php if ( ! empty( $terms ) ) : ?>
-                                                <?php foreach ( $terms as $product_term_id ) : ?>
-                                                    <?php $product_term = get_term( $product_term_id ); ?>
-                                                    <option value="<?php echo esc_attr( $product_term->term_id ); ?>" selected="selected" ><?php echo esc_html( $product_term->name ); ?></option>
-                                                <?php endforeach ?>
-                                            <?php endif ?>
-                                        </select>
-                                    </div>
-
-                                    <?php do_action( 'dokan_new_product_after_product_tags' ); ?>
-                                </div>
-                            </div>
-
-                            <div class="dokan-form-group">
-                                <label for="post_content" class="control-label"><?php esc_html_e( 'Description', 'dokan-lite' ); ?> <i class="fas fa-question-circle tips" data-title="<?php esc_attr_e( 'Add your product description', 'dokan-lite' ); ?>" aria-hidden="true"></i></label>
-                                <?php
-                                wp_editor(
-                                    htmlspecialchars_decode( $post_content, ENT_QUOTES ),
-                                    'post_content',
-                                    [
-                                        'editor_height' => 50,
-                                        'quicktags'     => false,
-                                        'media_buttons' => false,
-                                        'teeny'         => true,
-                                        'editor_class'  => 'post_content',
-                                    ]
-                                );
-                                ?>
-                            </div>
-
-                            <?php do_action( 'dokan_new_product_form' ); ?>
-
-                            <hr>
-
-                            <div class="dokan-form-group dokan-right">
-                                <?php
-                                wp_nonce_field( 'dokan_add_new_product', 'dokan_add_new_product_nonce' );
-
-                                $show_add_new_button = ! function_exists( 'dokan_pro' ) || ! dokan_pro()->module->is_active( 'product_subscription' ) || \DokanPro\Modules\Subscription\Helper::get_vendor_remaining_products( dokan_get_current_user_id() ) !== 1;
-
-                                if ( $show_add_new_button ) :
-                                    ?>
-                                    <button type="submit" name="add_product" class="dokan-btn dokan-btn-default" value="create_and_add_new">
-                                        <?php esc_attr_e( 'Create & Add New', 'dokan-lite' ); ?>
-                                    </button>
-                                <?php endif; ?>
-                                <button type="submit" name="add_product" class="dokan-btn dokan-btn-default dokan-btn-theme" value="create_new"><?php esc_attr_e( 'Create Product', 'dokan-lite' ); ?></button>
-                            </div>
-                        </form>
-                    <?php else : ?>
-                        <?php dokan_seller_not_enabled_notice(); ?>
-                    <?php endif; ?>
-                <?php else : ?>
-                    <?php do_action( 'dokan_can_post_notice' ); ?>
-                <?php endif; ?>
-            </div>
-            <?php
-            /**
-             * Action hook to fire inside new product content after
-             *
-             *  @since 2.4
-             */
-            do_action( 'dokan_after_new_product_inside_content_area' );
-            ?>
-        </div> <!-- #primary .content-area -->
-        <?php
-        /**
-         * Action hook to fire after rendering dashboard content.
-         *
-         *  @since 2.4
-         */
-        do_action( 'dokan_dashboard_content_after' );
-
-        /**
-         * Action hook to fire after rendering product content.
-         *
-         *  @since 2.4
-         */
-        do_action( 'dokan_after_new_product_content_area' );
-        ?>
-    </div><!-- .dokan-dashboard-wrap -->
-<?php
-/**
- * Action hook to fire at end of the dahboard wrap.
- *
- * @since 2.4
- */
-do_action( 'dokan_dashboard_wrap_end' );
-
-/**
- * Action hook to fire after new product wrap.
- *
- *  @since 2.4
- */
-do_action( 'dokan_new_product_wrap_after' );
-?>
diff --git a/templates/products/others.php b/templates/products/others.php
deleted file mode 100644
index 165f041ba1..0000000000
--- a/templates/products/others.php
+++ /dev/null
@@ -1,59 +0,0 @@
-<?php
-$post_statuses = dokan_get_available_post_status( $post->ID );
-?>
-
-<div class="dokan-other-options dokan-edit-row dokan-clearfix <?php echo esc_attr( $class ); ?>">
-    <div class="dokan-section-heading" data-togglehandler="dokan_other_options">
-        <h2><i class="fas fa-cog" aria-hidden="true"></i> <?php esc_html_e( 'Other Options', 'dokan-lite' ); ?></h2>
-        <p><?php esc_html_e( 'Set your extra product options', 'dokan-lite' ); ?></p>
-        <a href="#" class="dokan-section-toggle">
-            <i class="fas fa-sort-down fa-flip-vertical" aria-hidden="true"></i>
-        </a>
-        <div class="dokan-clearfix"></div>
-    </div>
-
-    <div class="dokan-section-content">
-        <div class="dokan-form-group content-half-part">
-            <label for="post_status" class="form-label"><?php esc_html_e( 'Product Status', 'dokan-lite' ); ?></label>
-            <select id="post_status" class="dokan-form-control" name="post_status">
-                <?php foreach ( $post_statuses as $status => $label ) : // phpcs:ignore ?>
-                    <option value="<?php echo esc_attr( $status ); ?>" <?php selected( $status, $post_status ); ?>>
-                        <?php echo esc_html( $label ); ?>
-                    </option>
-                <?php endforeach; ?>
-            </select>
-        </div>
-
-        <div class="dokan-form-group content-half-part">
-            <label for="_visibility" class="form-label"><?php esc_html_e( 'Visibility', 'dokan-lite' ); ?></label>
-            <select name="_visibility" id="_visibility" class="dokan-form-control">
-                <?php foreach ( $visibility_options as $name => $label ) : ?>
-                    <option value="<?php echo esc_attr( $name ); ?>" <?php selected( $_visibility, $name ); ?>>
-                        <?php echo esc_html( $label ); ?>
-                    </option>
-                <?php endforeach; ?>
-            </select>
-        </div>
-
-        <div class="dokan-clearfix"></div>
-
-        <div class="dokan-form-group">
-            <label for="_purchase_note" class="form-label"><?php esc_html_e( 'Purchase Note', 'dokan-lite' ); ?></label>
-            <?php dokan_post_input_box( $post_id, '_purchase_note', array( 'placeholder' => __( 'Customer will get this info in their order email', 'dokan-lite' ) ), 'textarea' ); ?>
-        </div>
-
-        <div class="dokan-form-group">
-            <?php
-            dokan_post_input_box(
-                $post_id,
-                '_enable_reviews',
-                [
-                    'value' => 'open' === $post->comment_status ? 'yes' : 'no',
-                    'label' => __( 'Enable product reviews', 'dokan-lite' ),
-                ],
-                'checkbox'
-            );
-            ?>
-        </div>
-    </div>
-</div><!-- .dokan-other-options -->
diff --git a/templates/products/tmpl-add-product-popup.php b/templates/products/tmpl-add-product-popup.php
deleted file mode 100644
index 119db898b7..0000000000
--- a/templates/products/tmpl-add-product-popup.php
+++ /dev/null
@@ -1,145 +0,0 @@
-<?php
-
-use WeDevs\Dokan\ProductCategory\Helper;
-use WeDevs\Dokan\Walkers\TaxonomyDropdown;
-
-?>
-<script type="text/html" id="tmpl-dokan-add-new-product">
-    <div id="dokan-add-new-product-popup" class="white-popup dokan-add-new-product-popup">
-        <?php do_action( 'dokan_new_product_before_product_area' ); ?>
-        <form action="" method="post" id="dokan-add-new-product-form">
-            <div class="product-form-container">
-                <div class="content-half-part dokan-feat-image-content">
-                    <div class="dokan-feat-image-upload">
-                        <?php
-                        $wrap_class        = ' dokan-hide';
-                        $instruction_class = '';
-                        $feat_image_id     = 0;
-                        $can_create_tags   = dokan_get_option( 'product_vendors_can_create_tags', 'dokan_selling' );
-                        $tags_placeholder  = 'on' === $can_create_tags ? __( 'Select tags/Add tags', 'dokan-lite' ) : __( 'Select product tags', 'dokan-lite' );
-                        ?>
-                        <div class="instruction-inside<?php echo esc_attr( $instruction_class ); ?>">
-                            <input type="hidden" name="feat_image_id" class="dokan-feat-image-id" value="<?php echo esc_attr( $feat_image_id ); ?>">
-
-                            <i class="fas fa-cloud-upload-alt"></i>
-                            <a href="#" class="dokan-feat-image-btn btn btn-sm"><?php esc_html_e( 'Upload a product cover image', 'dokan-lite' ); ?></a>
-                        </div>
-
-                        <div class="image-wrap<?php echo esc_attr( $wrap_class ); ?>">
-                            <a class="close dokan-remove-feat-image">&times;</a>
-                            <img height="" width="" src="" alt="">
-                        </div>
-                    </div>
-
-                        <div class="dokan-product-gallery">
-                            <div class="dokan-side-body" id="dokan-product-images">
-                                <div id="product_images_container">
-                                    <ul class="product_images dokan-clearfix">
-
-                                        <li class="add-image add-product-images tips" data-title="<?php esc_attr_e( 'Add gallery image', 'dokan-lite' ); ?>">
-                                            <a href="#" class="add-product-images"><i class="fas fa-plus" aria-hidden="true"></i></a>
-                                        </li>
-                                    </ul>
-                                    <input type="hidden" id="product_image_gallery" name="product_image_gallery" value="">
-                                </div>
-                            </div>
-                        </div>
-
-                </div>
-                <div class="content-half-part dokan-product-field-content">
-                    <div class="dokan-form-group">
-                        <input type="text" class="dokan-form-control" name="post_title" placeholder="<?php esc_attr_e( 'Product name..', 'dokan-lite' ); ?>">
-                    </div>
-
-                    <div class="dokan-clearfix">
-                        <div class="dokan-form-group dokan-clearfix dokan-price-container">
-                            <div class="content-half-part">
-                                <label for="_regular_price" class="form-label"><?php esc_html_e( 'Price', 'dokan-lite' ); ?></label>
-                                <div class="dokan-input-group">
-                                    <span class="dokan-input-group-addon"><?php echo esc_html( get_woocommerce_currency_symbol() ); ?></span>
-                                    <input type="text" class="dokan-product-regular-price wc_input_price dokan-form-control" name="_regular_price" id="_regular_price" placeholder="0.00">
-                                </div>
-                            </div>
-
-                            <div class="content-half-part sale-price">
-                                <label for="_sale_price" class="form-label">
-                                    <?php esc_html_e( 'Discounted Price', 'dokan-lite' ); ?>
-                                    <a href="#" class="sale_schedule"><?php esc_html_e( 'Schedule', 'dokan-lite' ); ?></a>
-                                    <a href="#" class="cancel_sale_schedule dokan-hide"><?php esc_html_e( 'Cancel', 'dokan-lite' ); ?></a>
-                                </label>
-
-                                <div class="dokan-input-group">
-                                    <span class="dokan-input-group-addon"><?php echo esc_html( get_woocommerce_currency_symbol() ); ?></span>
-                                    <input type="text" class="dokan-product-sales-price wc_input_price dokan-form-control"  name="_sale_price" placeholder="0.00" id="_sale_price">
-                                </div>
-                            </div>
-                        </div>
-
-                        <div class="dokan-hide sale-schedule-container sale_price_dates_fields dokan-clearfix dokan-form-group">
-                            <div class="content-half-part from">
-                                <div class="dokan-input-group">
-                                    <span class="dokan-input-group-addon"><?php esc_html_e( 'From', 'dokan-lite' ); ?></span>
-                                    <input type="text" name="_sale_price_dates_from" class="dokan-form-control" value="" maxlength="10" pattern="[0-9]{4}-(0[1-9]|1[012])-(0[1-9]|1[0-9]|2[0-9]|3[01])" placeholder="<?php esc_attr_e( 'YYYY-MM-DD', 'dokan-lite' ); ?>">
-                                </div>
-                            </div>
-
-                            <div class="content-half-part to">
-                                <div class="dokan-input-group">
-                                    <span class="dokan-input-group-addon"><?php esc_html_e( 'To', 'dokan-lite' ); ?></span>
-                                    <input type="text" name="_sale_price_dates_to" class="dokan-form-control" value="" maxlength="10" pattern="[0-9]{4}-(0[1-9]|1[012])-(0[1-9]|1[0-9]|2[0-9]|3[01])" placeholder="<?php esc_attr_e( 'YYYY-MM-DD', 'dokan-lite' ); ?>">
-                                </div>
-                            </div>
-                        </div>
-                    </div>
-                </div>
-                <div class="dokan-clearfix"></div>
-                <div class="product-full-container">
-
-                    <?php
-                        $data = Helper::get_saved_products_category();
-                        $data['from'] = 'new_product_popup';
-                        dokan_get_template_part( 'products/dokan-category-header-ui', '', $data );
-                    ?>
-
-                    <div class="dokan-form-group">
-                        <label for="product_tag_search" class="form-label"><?php esc_html_e( 'Tags', 'dokan-lite' ); ?></label>
-                        <select multiple="multiple" name="product_tag[]" id="product_tag_search" class="product_tag_search product_tags" data-placeholder="<?php echo esc_attr( $tags_placeholder ); ?>" style="width: 100%;"></select>
-                    </div>
-
-                    <?php do_action( 'dokan_new_product_after_product_tags' ); ?>
-
-                    <div class="dokan-form-group">
-                        <textarea name="post_excerpt" id="" class="dokan-form-control" rows="5" placeholder="<?php esc_attr_e( 'Enter some short description about this product...' , 'dokan-lite' ) ?>"></textarea>
-                    </div>
-                </d>
-            </div>
-            <div class="product-container-footer">
-                <span class="dokan-show-add-product-error"></span>
-                <span class="dokan-show-add-product-success"></span>
-                <span class="dokan-spinner dokan-add-new-product-spinner dokan-hide"></span>
-                <input type="submit" id="dokan-create-new-product-btn" class="dokan-btn dokan-btn-default" data-btn_id="create_new" value="<?php esc_attr_e( 'Create product', 'dokan-lite' ) ?>">
-                <?php
-                $display_create_and_add_new_button = true;
-                if ( function_exists( 'dokan_pro' ) && dokan_pro()->module->is_active( 'product_subscription' ) ) {
-                    if ( \DokanPro\Modules\Subscription\Helper::get_vendor_remaining_products( dokan_get_current_user_id() ) === 1 ) {
-                        $display_create_and_add_new_button = false;
-                    }
-                }
-                if ( $display_create_and_add_new_button ) :
-                ?>
-                <input type="submit" id="dokan-create-and-add-new-product-btn" class="dokan-btn dokan-btn-theme" data-btn_id="create_and_new" value="<?php esc_attr_e( 'Create & add new', 'dokan-lite' ) ?>">
-                <?php endif; ?>
-            </div>
-        </form>
-    </div>
-    <style>
-        .select2-container--open .select2-dropdown--below {
-            margin-top: 0px;
-        }
-
-        .select2-container--open .select2-dropdown--above {
-            margin-top: 0px;
-        }
-    </style>
-</script>
-<?php do_action( 'dokan_add_product_js_template_end' );?>

From a634761056af75fbf67960ffb9d51cd21a8a10ca Mon Sep 17 00:00:00 2001
From: Nurul Umbhiya <zikubd@gmail.com>
Date: Thu, 7 Dec 2023 19:24:31 +0600
Subject: [PATCH 3/5] refactor: replace action hook: dokan_product_updated with
 filter hook dokan_product_edit_meta_data, this will save some executions
 refactor: removed update_post_meta and get_post_meta references

---
 .../CatalogMode/Dashboard/ProductBulkEdit.php | 12 +++++++++--
 includes/CatalogMode/Dashboard/Products.php   | 16 ++++++++-------
 includes/CatalogMode/Helper.php               |  9 +++------
 includes/Dashboard/Templates/Products.php     |  7 +++----
 includes/Product/Hooks.php                    | 20 ++++++++++---------
 5 files changed, 36 insertions(+), 28 deletions(-)

diff --git a/includes/CatalogMode/Dashboard/ProductBulkEdit.php b/includes/CatalogMode/Dashboard/ProductBulkEdit.php
index 661e40c000..093694d63c 100644
--- a/includes/CatalogMode/Dashboard/ProductBulkEdit.php
+++ b/includes/CatalogMode/Dashboard/ProductBulkEdit.php
@@ -72,6 +72,12 @@ public function save_bulk_edit_catalog_mode_data( $status, $product_ids ) {
         // loop through the products and update the status
         if ( ! empty( $product_ids ) ) {
             foreach ( $product_ids as $product_id ) {
+                // get product object
+                $product = wc_get_product( $product_id );
+                if ( ! $product ) {
+                    continue;
+                }
+
                 // get existing product data
                 $catalog_mode_data = Helper::get_catalog_mode_data_by_product( $product_id );
                 $count++;
@@ -81,7 +87,7 @@ public function save_bulk_edit_catalog_mode_data( $status, $product_ids ) {
                         if ( Helper::hide_add_to_cart_button_option_is_enabled_by_admin() ) {
                             $catalog_mode_data['hide_add_to_cart_button'] = 'on';
                         }
-                        // if admin didn't enabled hide product price, set this value to off
+                        // if admin didn't enable hide product price, set this value to off
                         if ( ! Helper::hide_product_price_option_is_enabled_by_admin() ) {
                             $catalog_mode_data['hide_product_price'] = 'off';
                         }
@@ -92,8 +98,10 @@ public function save_bulk_edit_catalog_mode_data( $status, $product_ids ) {
                         $catalog_mode_data['hide_product_price']      = 'off';
                         break;
                 }
+
                 // finally save catalog mode data
-                update_post_meta( $product_id, '_dokan_catalog_mode', $catalog_mode_data );
+                $product->add_meta_data( '_dokan_catalog_mode', $catalog_mode_data, true );
+                $product->save();
             }
         }
         wp_safe_redirect(
diff --git a/includes/CatalogMode/Dashboard/Products.php b/includes/CatalogMode/Dashboard/Products.php
index fd6aec805d..8acef768a2 100644
--- a/includes/CatalogMode/Dashboard/Products.php
+++ b/includes/CatalogMode/Dashboard/Products.php
@@ -29,7 +29,7 @@ public function __construct() {
         // render catalog mode section under single product edit page
         add_action( 'dokan_product_edit_after_options', [ $this, 'render_product_section' ], 99, 2 );
         // save catalog mode section data
-        add_action( 'dokan_product_updated', [ $this, 'save_catalog_mode_data' ], 13 );
+        add_filter( 'dokan_product_edit_meta_data', [ $this, 'save_catalog_mode_data' ], 13, 1 );
     }
 
     /**
@@ -71,17 +71,17 @@ public function render_product_section( $product_id, $product ) {
      *
      * @since 3.6.4
      *
-     * @param $product_id int
+     * @param array $meta_data
      *
-     * @return void
+     * @return array
      */
-    public function save_catalog_mode_data( $product_id ) {
+    public function save_catalog_mode_data( array $meta_data ) {
         if ( ! isset( $_POST['_dokan_catalog_mode_frontend_nonce'] ) || ! wp_verify_nonce( sanitize_key( $_POST['_dokan_catalog_mode_frontend_nonce'] ), 'dokan_catalog_mode_frontend' ) ) {
-            return;
+            return $meta_data;
         }
 
         if ( ! dokan_is_user_seller( dokan_get_current_user_id() ) ) {
-            return;
+            return $meta_data;
         }
 
         $catalog_mode_data = [
@@ -94,6 +94,8 @@ public function save_catalog_mode_data( $product_id ) {
             $catalog_mode_data['hide_product_price'] = 'off';
         }
 
-        update_post_meta( $product_id, '_dokan_catalog_mode', $catalog_mode_data );
+        $meta_data['_dokan_catalog_mode'] = $catalog_mode_data;
+
+        return $meta_data;
     }
 }
diff --git a/includes/CatalogMode/Helper.php b/includes/CatalogMode/Helper.php
index 2009b90bf4..254139804e 100644
--- a/includes/CatalogMode/Helper.php
+++ b/includes/CatalogMode/Helper.php
@@ -174,15 +174,12 @@ public static function get_catalog_mode_data_by_product( $product ) {
 
         // get product id
         if ( 'variation' === $product->get_type() ) {
-            $product_id = $product->get_parent_id();
-        } else {
-            $product_id = $product->get_id();
+            $product = wc_get_product( $product->get_parent_id() );
         }
 
         // check for saved values
-        $catalog_mode_data = get_post_meta( $product_id, '_dokan_catalog_mode', true );
-        $catalog_mode_data = is_array( $catalog_mode_data ) && ! empty( $catalog_mode_data ) ? $catalog_mode_data : $defaults;
+        $catalog_mode_data = $product->get_meta( '_dokan_catalog_mode', true );
 
-        return $catalog_mode_data;
+        return is_array( $catalog_mode_data ) && ! empty( $catalog_mode_data ) ? $catalog_mode_data : $defaults;
     }
 }
diff --git a/includes/Dashboard/Templates/Products.php b/includes/Dashboard/Templates/Products.php
index 437a7f23c2..e7c7a08a68 100644
--- a/includes/Dashboard/Templates/Products.php
+++ b/includes/Dashboard/Templates/Products.php
@@ -409,8 +409,7 @@ public function handle_product_update() {
 
         $props               = [];
         $meta_data           = [];
-        $current_post_status = $product->get_status();
-        $is_new_product      = 'auto-draft' === $current_post_status;
+        $is_new_product      = 'auto-draft' === $product->get_status();
 
         foreach ( ProductFormFactory::get_fields() as $field_id => $field ) {
             if ( $field->is_other_type() ) {
@@ -486,8 +485,8 @@ public function handle_product_update() {
         }
 
         // apply hooks before saving product
-        $props     = apply_filters( 'dokan_product_edit_props', $props, $product );
-        $meta_data = apply_filters( 'dokan_product_edit_meta_data', $meta_data, $product );
+        $props     = apply_filters( 'dokan_product_edit_props', $props, $product, $is_new_product );
+        $meta_data = apply_filters( 'dokan_product_edit_meta_data', $meta_data, $product, $is_new_product );
 
         $errors = $product->set_props( $props );
         if ( is_wp_error( $errors ) ) {
diff --git a/includes/Product/Hooks.php b/includes/Product/Hooks.php
index 96e74e0275..27a261ed05 100644
--- a/includes/Product/Hooks.php
+++ b/includes/Product/Hooks.php
@@ -30,7 +30,7 @@ public function __construct() {
         add_action( 'woocommerce_new_product', [ $this, 'update_category_data_for_new_and_update_product' ], 10, 1 );
         add_action( 'woocommerce_update_product', [ $this, 'update_category_data_for_new_and_update_product' ], 10, 1 );
         add_filter( 'dokan_post_status', [ $this, 'set_product_status' ], 1, 2 );
-        add_action( 'dokan_new_product_added', [ $this, 'set_new_product_email_status' ], 1, 1 );
+        add_filter( 'dokan_product_edit_meta_data', [ $this, 'set_new_product_email_status' ], 1, 3 );
 
         // Remove product type filter if pro not exists.
         add_filter( 'dokan_product_listing_filter_args', [ $this, 'remove_product_type_filter' ] );
@@ -362,18 +362,20 @@ public function set_product_status( $all_statuses, int $product_id ) {
      *
      * @since 3.8.2
      *
-     * @param int $product_id
+     * @param array $meta_data
+     * @param WC_Product $product
+     * @param bool $is_new_product
      *
-     * @return void
+     * @return array
      */
-    public function set_new_product_email_status( $product_id ) {
-        $product = wc_get_product( $product_id );
-        if ( ! $product ) {
-            return;
+    public function set_new_product_email_status( array $meta_data, WC_Product $product, bool $is_new_product ) {
+        if ( ! $is_new_product ) {
+            return $meta_data;
         }
 
-        $product->add_meta_data( '_dokan_new_product_email_sent', 'no', true );
-        $product->save();
+        $meta_data['_dokan_new_product_email_sent'] = 'no';
+
+        return $meta_data;
     }
 
     /**

From b869ff8c0064818147e834b88c01b386b986cfb3 Mon Sep 17 00:00:00 2001
From: Nurul Umbhiya <zikubd@gmail.com>
Date: Thu, 7 Dec 2023 22:29:39 +0600
Subject: [PATCH 4/5] fix: fixed some minor issues

---
 includes/Dashboard/Templates/Products.php     | 42 ++++++++++---------
 includes/ProductForm/Field.php                |  6 +--
 includes/ProductForm/Init.php                 | 24 +++++++++--
 .../products/edit/edit-product-single.php     |  3 +-
 templates/products/edit/sections/general.php  |  4 +-
 .../products/edit/sections/inventory.php      | 35 ++++++++--------
 6 files changed, 66 insertions(+), 48 deletions(-)

diff --git a/includes/Dashboard/Templates/Products.php b/includes/Dashboard/Templates/Products.php
index e7c7a08a68..f1afebdf2b 100644
--- a/includes/Dashboard/Templates/Products.php
+++ b/includes/Dashboard/Templates/Products.php
@@ -185,8 +185,8 @@ public function load_product_edit_template() {
      *
      * @since 1.0.0
      *
-     * @param WP_Post    $post
-     * @param int        $post_id
+     * @param WP_Post $post
+     * @param int     $post_id
      *
      * @return void
      */
@@ -222,8 +222,8 @@ public static function load_download_virtual_template( $post, $post_id ) {
      *
      * @since 2.9.2
      *
-     * @param WP_Post    $post
-     * @param int        $post_id
+     * @param WP_Post $post
+     * @param int     $post_id
      *
      * @return void
      */
@@ -253,8 +253,8 @@ public static function load_inventory_template( $post, $post_id ) {
      *
      * @since 2.9.2
      *
-     * @param WP_Post    $post
-     * @param int        $post_id
+     * @param WP_Post $post
+     * @param int     $post_id
      *
      * @return void
      */
@@ -289,8 +289,8 @@ public static function load_downloadable_template( $post, $post_id ) {
      *
      * @since 2.9.2
      *
-     * @param WP_Post    $post
-     * @param int        $post_id
+     * @param WP_Post $post
+     * @param int     $post_id
      *
      * @return void
      */
@@ -407,9 +407,12 @@ public function handle_product_update() {
             return;
         }
 
-        $props               = [];
-        $meta_data           = [];
-        $is_new_product      = 'auto-draft' === $product->get_status();
+        $props          = [];
+        $meta_data      = [];
+        $is_new_product = 'auto-draft' === $product->get_status();
+
+        // stock quantity
+        $props[ ProductFormElements::STOCK_QUANTITY ] = null;
 
         foreach ( ProductFormFactory::get_fields() as $field_id => $field ) {
             if ( $field->is_other_type() ) {
@@ -421,17 +424,19 @@ public function handle_product_update() {
                 continue;
             }
 
+            $field_name = sanitize_key( $field->get_name() );
+
             // check if field is required
-            if ( $field->is_required() && empty( $_POST[ $field->get_name() ] ) ) {
-                self::$errors[ $field->get_id() ][] = ! empty( $field->get_error_message() )
+            if ( $field->is_required() && empty( $_POST[ $field_name ] ) ) {
+                self::$errors[ $field->get_id() ] = ! empty( $field->get_error_message() )
                     ? $field->get_error_message()
                     // translators: 1) field title
-                    : sprintf( __( '%1$s is a required field.', 'dokan-lite' ), $field->get_title() );
+                    : sprintf( __( '<strong>%1$s</strong> is a required field.', 'dokan-lite' ), $field->get_title() );
                 continue;
             }
 
             // check if the field is present
-            if ( ! isset( $_POST[ $field->get_name() ] ) ) {
+            if ( ! isset( $_POST[ $field_name ] ) ) {
                 continue;
             }
 
@@ -450,14 +455,14 @@ public function handle_product_update() {
 
                 case ProductFormElements::STOCK_QUANTITY:
                     $original_stock = isset( $_POST['_original_stock'] ) ? wc_stock_amount( wp_unslash( $_POST['_original_stock'] ) ) : false; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
-                    $field_value    = $field->sanitize( wp_unslash( $_POST[ $field->get_name() ] ), $original_stock, $product ); //phpcs:ignore
+                    $field_value    = $field->sanitize( wp_unslash( $_POST[ $field_name ] ), $original_stock, $product ); //phpcs:ignore
                     break;
 
                 default:
                     // give a chance to other plugins to sanitize their data
-                    $field_value = apply_filters( 'dokan_product_update_field_value', null, $field, wp_unslash( $_POST[ $field->get_name() ] ), $product ); // phpcs:ignore
+                    $field_value = apply_filters( 'dokan_product_update_field_value', null, $field, wp_unslash( $_POST[ $field_name ] ), $product ); // phpcs:ignore
                     if ( null === $field_value ) {
-                        $field_value = $field->sanitize( wp_unslash( $_POST[ $field->get_name() ] ) ); //phpcs:ignore
+                        $field_value = $field->sanitize( wp_unslash( $_POST[ $field_name ] ) ); //phpcs:ignore
                     }
                     break;
             }
@@ -491,7 +496,6 @@ public function handle_product_update() {
         $errors = $product->set_props( $props );
         if ( is_wp_error( $errors ) ) {
             self::$errors = array_merge( self::$errors, $errors->get_error_messages() );
-            exit;
         }
 
         // return early for validation errors
diff --git a/includes/ProductForm/Field.php b/includes/ProductForm/Field.php
index d3f65b2452..1e0c0a4e17 100644
--- a/includes/ProductForm/Field.php
+++ b/includes/ProductForm/Field.php
@@ -80,18 +80,18 @@ public function get_name(): string {
     }
 
     /**
-     * Set field name, validated by sanitize_title()
+     * Set field name, validated by sanitize_text_field()
      *
      * @since DOKAN_SINCE
      *
      * @param string $name
      *
-     * @see   sanitize_title()
+     * @see   sanitize_text_field()
      *
      * @return $this
      */
     public function set_name( string $name ): Field {
-        $this->data['name'] = sanitize_title( $name );
+        $this->data['name'] = sanitize_text_field( $name );
 
         return $this;
     }
diff --git a/includes/ProductForm/Init.php b/includes/ProductForm/Init.php
index 6c3c977bc9..a28e171a58 100644
--- a/includes/ProductForm/Init.php
+++ b/includes/ProductForm/Init.php
@@ -387,7 +387,13 @@ public function init_inventory_fields() {
                     'min'  => 0,
                     'step' => 'any',
                 ],
-                'sanitize_callback'     => 'wc_stock_amount',
+                'sanitize_callback'     => function ( $value ) {
+                    if ( '' !== $value ) {
+                        return wc_stock_amount( $value );
+                    }
+
+                    return '';
+                },
             ]
         );
 
@@ -403,7 +409,7 @@ public function init_inventory_fields() {
 
         $section->add_field(
             Elements::SOLD_INDIVIDUALLY, [
-                'title'                 => __( 'Sold Individually', 'dokan-lite' ),
+                'title'                 => __( 'Allow only one quantity of this product to be bought in a single order', 'dokan-lite' ),
                 'description'           => __( 'Check to let customers to purchase only 1 item in a single order. This is particularly useful for items that have limited quantity, for example art or handmade goods.', 'dokan-lite' ),
                 'field_type'            => 'checkbox',
                 'name'                  => '_sold_individually',
@@ -457,7 +463,12 @@ public function init_downloadable_fields() {
                     'min'  => 0,
                     'step' => 1,
                 ],
-                'sanitize_callback'     => 'absint',
+                'sanitize_callback'     => function ( $value ) {
+                    if ( '' !== $value ) {
+                        return absint( $value );
+                    }
+                    return '';
+                },
             ]
         );
 
@@ -472,7 +483,12 @@ public function init_downloadable_fields() {
                     'min'  => 0,
                     'step' => 1,
                 ],
-                'sanitize_callback'     => 'absint',
+                'sanitize_callback'     => function ( $value ) {
+                    if ( '' !== $value ) {
+                        return absint( $value );
+                    }
+                    return '';
+                },
             ]
         );
     }
diff --git a/templates/products/edit/edit-product-single.php b/templates/products/edit/edit-product-single.php
index bea7d138c8..3fa1855678 100755
--- a/templates/products/edit/edit-product-single.php
+++ b/templates/products/edit/edit-product-single.php
@@ -135,9 +135,8 @@
                 <?php if ( dokan()->dashboard->templates->products->has_errors() ) : ?>
                     <div class="dokan-alert dokan-alert-danger">
                         <a class="dokan-close" data-dismiss="alert">&times;</a>
-
                         <?php foreach ( dokan()->dashboard->templates->products->get_errors() as $error ) : ?>
-                            <strong><?php esc_html_e( 'Error!', 'dokan-lite' ); ?></strong> <?php echo esc_html( $error ); ?>.<br>
+                            <strong><?php esc_html_e( 'Error!', 'dokan-lite' ); ?></strong> <?php echo wp_kses_post( $error ); ?>.<br>
                         <?php endforeach; ?>
                     </div>
                 <?php endif; ?>
diff --git a/templates/products/edit/sections/general.php b/templates/products/edit/sections/general.php
index a2062a5be1..140445cbaa 100644
--- a/templates/products/edit/sections/general.php
+++ b/templates/products/edit/sections/general.php
@@ -212,8 +212,8 @@ class="dokan-form-control dokan-end-date"
                 $terms = $product->get_tag_ids();
                 ?>
                 <div class="dokan-form-group">
-                    <label for="product_tag_edit" class="form-label"><?php echo $tags->get_title(); ?></label>
-                    <select multiple="multiple" id="product_tag_edit" name="<?php echo esc_attr( $tags->get_name() ); ?>" class="product_tag_search dokan-form-control" data-placeholder="<?php echo esc_attr( $tags->get_placeholder() ); ?>">
+                    <label for="<?php echo esc_attr( $tags->get_name() ); ?>" class="form-label"><?php echo $tags->get_title(); ?></label>
+                    <select multiple="multiple" id="<?php echo esc_attr( $tags->get_name() ); ?>" name="<?php echo esc_attr( $tags->get_name() ); ?>" class="product_tag_search dokan-form-control" data-placeholder="<?php echo esc_attr( $tags->get_placeholder() ); ?>">
                         <?php if ( ! empty( $terms ) ) : ?>
                             <?php
                             foreach ( $terms as $tax_term ) :
diff --git a/templates/products/edit/sections/inventory.php b/templates/products/edit/sections/inventory.php
index 7b76927d72..71500dd36e 100644
--- a/templates/products/edit/sections/inventory.php
+++ b/templates/products/edit/sections/inventory.php
@@ -155,13 +155,13 @@ class="fas fa-question-circle tips"
                         <?php
                         dokan_post_input_box(
                             $product->get_id(),
-                            $stock_quantity->get_name(),
+                            $low_stock_amount->get_name(),
                             [
                                 'value'       => $product->get_low_stock_amount( 'edit' ),
                                 'class'       => 'dokan-form-control',
-                                'placeholder' => $stock_quantity->get_placeholder(),
-                                'min'         => $stock_quantity->get_additional_properties( 'min' ),
-                                'step'        => $stock_quantity->get_additional_properties( 'step' ),
+                                'placeholder' => $low_stock_amount->get_placeholder(),
+                                'min'         => $low_stock_amount->get_additional_properties( 'min' ),
+                                'step'        => $low_stock_amount->get_additional_properties( 'step' ),
                             ],
                             'number'
                         );
@@ -171,7 +171,7 @@ class="fas fa-question-circle tips"
 
                 <?php
                 $backorders = $section->get_field( Elements::BACKORDERS );
-                if ( is_wp_error( $backorders ) && $backorders->is_visible() ) :
+                if ( ! is_wp_error( $backorders ) && $backorders->is_visible() ) :
                     ?>
                     <div class="content-half-part last-child">
                         <label for="<?php echo esc_attr( $backorders->get_name() ); ?>" class="form-label">
@@ -205,19 +205,18 @@ class="fas fa-question-circle tips"
         if ( ! is_wp_error( $sold_individually ) && $sold_individually->is_visible() ) :
             ?>
             <div class="dokan-form-group hide_if_grouped hide_if_external">
-                <label class="" for="<?php echo esc_attr( $sold_individually->get_name() ); ?>">
-                    <input
-                        name="<?php echo esc_attr( $sold_individually->get_name() ); ?>"
-                        id="<?php echo esc_attr( $sold_individually->get_name() ); ?>"
-                        value="yes"
-                        type="checkbox" <?php checked( $product->get_sold_individually( 'edit' ), true ); ?>>
-                    <i
-                        class="fas fa-question-circle tips"
-                        aria-hidden="true"
-                        data-title="<?php echo esc_attr( $sold_individually->get_description() ); ?>">
-                    </i>
-                    <?php esc_html_e( 'Allow only one quantity of this product to be bought in a single order', 'dokan-lite' ); ?>
-                </label>
+                <?php
+                dokan_post_input_box(
+                    $product->get_id(),
+                    $sold_individually->get_name(),
+                    [
+                        'value' => $product->get_sold_individually() ? 'yes' : 'no',
+                        'label' => $sold_individually->get_title(),
+                        'desc'  => $sold_individually->get_description(),
+                    ],
+                    'checkbox'
+                );
+                ?>
             </div>
         <?php endif; ?>
 

From fc5ff41ce842105205177222065b5d9774e09ccc Mon Sep 17 00:00:00 2001
From: Nurul Umbhiya <zikubd@gmail.com>
Date: Fri, 8 Dec 2023 15:00:31 +0600
Subject: [PATCH 5/5] =?UTF-8?q?=F0=9F=94=92=20chore(Product):=20add=20defi?=
 =?UTF-8?q?ned(=20'ABSPATH'=20)=20||=20exit;=20to=20Hooks.php=20and=20Mana?=
 =?UTF-8?q?ger.php=20to=20prevent=20direct=20access=20to=20the=20files?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 includes/Product/Hooks.php   | 2 ++
 includes/Product/Manager.php | 2 ++
 2 files changed, 4 insertions(+)

diff --git a/includes/Product/Hooks.php b/includes/Product/Hooks.php
index 27a261ed05..3ea9e40882 100644
--- a/includes/Product/Hooks.php
+++ b/includes/Product/Hooks.php
@@ -5,6 +5,8 @@
 use WeDevs\Dokan\ProductCategory\Helper;
 use WC_Product;
 
+defined( 'ABSPATH' ) || exit;
+
 /**
  * Admin Hooks
  *
diff --git a/includes/Product/Manager.php b/includes/Product/Manager.php
index 623acf4344..bb5e844d5c 100644
--- a/includes/Product/Manager.php
+++ b/includes/Product/Manager.php
@@ -10,6 +10,8 @@
 use WP_Error;
 use WeDevs\Dokan\ProductForm\Elements as FormElements;
 
+defined( 'ABSPATH' ) || exit;
+
 /**
  * Product manager Class
  *