Adding basic Woo AI settings screen (#38920)

This commit is contained in:
Joel Thiessen 2023-07-12 17:12:03 -07:00 committed by GitHub
parent c6d6a271c0
commit b7e7d66de8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 1471 additions and 701 deletions

View File

@ -0,0 +1,4 @@
Significance: minor
Type: add
Adding settings screen for AI centric settings.

View File

@ -56,13 +56,6 @@ class Woo_AI_Product_Text_Generation {
$css_file_version = filemtime( dirname( __FILE__ ) . '/../build/index.css' );
wp_register_style(
'wp-components',
plugins_url( 'dist/components/style.css', __FILE__ ),
array(),
$css_file_version
);
wp_register_style(
'woo-ai',
plugins_url( '/../build/index.css', __FILE__ ),

View File

@ -0,0 +1,220 @@
<?php
/**
* Woo AI settings page module.
*
* @package Woo_AI
*/
defined( 'ABSPATH' ) || exit;
use Automattic\Jetpack\Constants;
/**
* Woo_AI_Settings Class.
*/
class Woo_AI_Settings {
/**
* Plugin instance.
*
* @var Woo_AI_Settings
*/
protected static $instance = null;
/**
* Settings ID.
*
* @var id
*/
protected $id = 'woo-ai-settings-tab';
/**
* Main Instance.
*/
public static function instance() {
self::$instance = is_null( self::$instance ) ? new self() : self::$instance;
return self::$instance;
}
/**
* Constructor
*/
public function __construct() {
add_action( 'admin_enqueue_scripts', array( $this, 'add_woo_ai_settings_script' ) );
add_action( 'woocommerce_settings_save_advanced', array( $this, 'action_save_woo_ai_settings_tab' ) );
add_action( 'woocommerce_settings_page_init', array( $this, 'add_ui' ) );
$this->add_sanitization_hooks();
}
/**
* Add UI related hooks.
*/
public function add_ui() {
add_filter( 'woocommerce_get_settings_advanced', array( $this, 'add_woo_ai_settings' ), 10, 2 );
}
/**
* Save settings.
*/
public function action_save_woo_ai_settings_tab() {
WC_Admin_Settings::save_fields(
$this->get_woo_ai_settings()
);
}
/**
* Add sanitization hooks.
*/
public function add_sanitization_hooks() {
$settings = $this->get_woo_ai_settings();
foreach ( $settings as $setting ) {
if ( in_array( $setting['type'], array( 'text', 'textarea' ), true ) ) {
add_filter( 'woocommerce_admin_settings_sanitize_option_' . $setting['id'], array( $this, 'strip_tags_field_value' ) );
}
}
}
/**
* Sanitize field value.
*
* @param string $raw_value The current section.
*/
public function strip_tags_field_value( $raw_value ) {
return wp_strip_all_tags( $raw_value ?? '' );
}
/**
* Add settings to the AI section.
*
* @param array $settings The original settings array.
* @param string $current_section The current section.
*/
public function add_woo_ai_settings( $settings = array(), $current_section = null ) {
if ( 'features' === $current_section ) {
$settings = array_merge(
$this->get_woo_ai_settings(),
$settings
);
}
return $settings;
}
/**
* Return array describing new settings.
*
* @return array Describing new AI settings.
*/
public function get_woo_ai_settings() {
// These are made available in the DOM and actually toggled client-side.
$tone_desc = array(
'informal' => __( 'Relaxed and friendly, as if you were having a conversation with a friend.', 'woocommerce' ),
'humorous' => __( 'Engage customers with a light-hearted and fun communication style.', 'woocommerce' ),
'neutral' => __( 'A balanced and impartial tone that uses casual expressions without slang.', 'woocommerce' ),
'youthful' => __( 'Bring energy and enthusiasm to your store with a friendly and cheeky tone.', 'woocommerce' ),
'formal' => __( 'Direct yet respectful, a formal tone sounds serious and professional.', 'woocommerce' ),
'motivational' => __( 'Passionate and inspiring, engage users with this upbeat tone.', 'woocommerce' ),
);
$markup_str = array_reduce(
array_keys( $tone_desc ),
function( $acc, $key ) use ( $tone_desc ) {
$value = $tone_desc[ $key ];
return $acc . '<span class="woo-ai-settings-tone-option" data-option="' . htmlspecialchars( $key ) . '">' . htmlspecialchars( $value ) . '</span>';
},
''
);
return array(
array(
'id' => 'woo_ai_title',
'type' => 'title',
'title' => __( 'Artificial Intelligence', 'woocommerce' ),
'desc' => __( "Save time by automating mundane parts of store management. This information will make AI-generated content, visuals, and setting more aligned with your store's goals and identity.", 'woocommerce' ),
),
array(
'title' => __( 'Enable AI', 'woocommerce' ),
'desc' => __( 'Enable AI features in your store', 'woocommerce' ),
'id' => 'woo_ai_enable_checkbox',
'default' => 'yes',
'type' => 'checkbox',
),
array(
'title' => __( 'Tone of voice', 'woocommerce' ),
'id' => 'woo_ai_tone_of_voice_select',
'css' => 'min-width:300px;',
'default' => 'neutral',
'type' => 'select',
'desc' => $markup_str,
'desc_tip' => __( 'Choose the language style that best resonates with your customers. It\'ll be used in text-based content, like product descriptions.', 'woocommerce' ),
'custom_attributes' => array( 'disabled' => 'true' ),
'options' => array(
'informal' => esc_html__( 'Informal', 'woocommerce' ),
'humorous' => esc_html__( 'Humorous', 'woocommerce' ),
'neutral' => esc_html__( 'Neutral', 'woocommerce' ),
'youthful' => esc_html__( 'Youthful', 'woocommerce' ),
'formal' => esc_html__( 'Formal', 'woocommerce' ),
'motivational' => esc_html__( 'Motivational', 'woocommerce' ),
),
),
array(
'id' => 'woo_ai_describe_store_description',
'type' => 'textarea',
'custom_attributes' => array( 'disabled' => 'true' ),
'title' => __( 'Describe your business', 'woocommerce' ),
'desc_tip' => __( 'Tell us what makes your business unique to further improve accuracy of the AI-generated content. This will not be shown to customers.', 'woocommerce' ),
'placeholder' => __( 'e.g. Marianne Renoir is a greengrocery taken over by a ten generations Parisian family who wants to keep quality and tradition in the quarter of Montmartre', 'woocommerce' ),
'css' => 'min-width:300px;min-height: 130px;',
),
array(
'type' => 'sectionend',
'id' => 'woo_ai_title',
),
);
}
/**
* Enqueue the styles and JS
*/
public function add_woo_ai_settings_script() {
global $pagenow;
// phpcs:disable WordPress.Security.NonceVerification.Recommended
if ( 'admin.php' !== $pagenow || ( isset( $_GET['page'] ) && 'wc-settings' !== $_GET['page'] ) ) {
return;
}
$script_path = '/../build/settings.js';
$script_url = plugins_url( $script_path, __FILE__ );
$version = Constants::get_constant( 'WC_VERSION' );
wp_register_script(
'woo-ai-settings',
$script_url,
array( 'jquery' ),
$version,
true
);
wp_enqueue_script( 'woo-ai-settings' );
$css_file_version = filemtime( dirname( __FILE__ ) . '/../build/settings.css' );
wp_register_style(
'woo-ai-settings',
plugins_url( '/../build/settings.css', __FILE__ ),
array(),
$css_file_version
);
wp_enqueue_style( 'woo-ai-settings' );
}
}

View File

@ -80,7 +80,6 @@ class Woo_AI {
}
}
/**
* Get plugin download URL.
*

View File

@ -45,9 +45,9 @@
"peerDependencies": {
"@types/react": "^17.0.2",
"@types/react-dom": "^17.0.2",
"@wordpress/data": "wp-6.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"@wordpress/data": "wp-6.0"
"react-dom": "^17.0.2"
},
"scripts": {
"postinstall": "composer install",

View File

@ -0,0 +1,11 @@
.woo-ai-settings-tone-option {
&.selected {
display: block;
}
display: none;
}
#woo_ai_describe_store_description:disabled::placeholder {
color: #ccc;
}

View File

@ -0,0 +1,70 @@
/**
* Internal dependencies
*/
import './settings.scss';
const fieldMap = {
enabled: document.getElementById(
'woo_ai_enable_checkbox'
) as HTMLInputElement,
tone: document.getElementById(
'woo_ai_tone_of_voice_select'
) as HTMLSelectElement,
describeBusiness: document.getElementById(
'woo_ai_describe_store_description'
) as HTMLInputElement,
};
const getSelectedOption = (): Element | null =>
document.querySelector( `.woo-ai-settings-tone-option.selected` );
const setSelectedDescription = ( value: string ): void => {
const selected = document.querySelector(
`.woo-ai-settings-tone-option[data-option="${ value }"]`
);
if ( ! selected ) {
return;
}
const previousSelected = getSelectedOption();
if ( previousSelected ) {
previousSelected.classList.remove( 'selected' );
}
selected.classList.add( 'selected' );
};
const setDisabledState = ( disabled: boolean ): void => {
if ( fieldMap.tone && fieldMap.describeBusiness ) {
fieldMap.tone.disabled = disabled;
fieldMap.describeBusiness.disabled = disabled;
}
};
( () => {
if ( fieldMap.enabled?.checked ) {
setSelectedDescription( fieldMap.tone?.value || '' );
setDisabledState( false );
}
fieldMap.enabled?.addEventListener( 'change', ( { target } ) => {
const checked = ( target as HTMLInputElement )?.checked;
if ( checked ) {
setSelectedDescription( fieldMap.tone?.value || '' );
} else {
const selected = getSelectedOption();
if ( selected ) {
selected.classList.remove( 'selected' );
}
}
setDisabledState( ! checked );
} );
fieldMap.tone?.addEventListener( 'change', ( { target } ) => {
const value = ( target as HTMLSelectElement )?.value;
setSelectedDescription( value );
} );
} )();

View File

@ -5,6 +5,7 @@ module.exports = {
...defaultConfig,
entry: {
...defaultConfig.entry,
settings: './src/settings.ts',
},
module: {
...defaultConfig.module,

View File

@ -79,6 +79,11 @@ function _woo_ai_bootstrap(): void {
add_action( 'admin_init', array( 'Woo_AI', 'instance' ) );
}
if ( ! class_exists( 'Woo_AI_Settings' ) ) {
include dirname( __FILE__ ) . '/includes/class-woo-ai-settings.php';
add_action( 'wp_loaded', array( 'Woo_AI_Settings', 'instance' ), 10 );
}
}
add_action( 'plugins_loaded', '_woo_ai_bootstrap' );

View File

@ -5,8 +5,8 @@ const { get } = require( 'lodash' );
const path = require( 'path' );
const CopyWebpackPlugin = require( 'copy-webpack-plugin' );
const CustomTemplatedPathPlugin = require( '@wordpress/custom-templated-path-webpack-plugin' );
const BundleAnalyzerPlugin = require( 'webpack-bundle-analyzer' )
.BundleAnalyzerPlugin;
const BundleAnalyzerPlugin =
require( 'webpack-bundle-analyzer' ).BundleAnalyzerPlugin;
const MomentTimezoneDataPlugin = require( 'moment-timezone-data-webpack-plugin' );
const ForkTsCheckerWebpackPlugin = require( 'fork-ts-checker-webpack-plugin' );
const ReactRefreshWebpackPlugin = require( '@pmmmwh/react-refresh-webpack-plugin' );

View File

@ -0,0 +1,4 @@
Significance: patch
Type: fix
Linting fix to webpack config.

File diff suppressed because it is too large Load Diff