diff --git a/src/js/settings/components/feature-additional-settings/moderation.js b/src/js/settings/components/feature-additional-settings/moderation.js index bf0d7c2c1..13f82e4a3 100644 --- a/src/js/settings/components/feature-additional-settings/moderation.js +++ b/src/js/settings/components/feature-additional-settings/moderation.js @@ -11,6 +11,7 @@ import { __ } from '@wordpress/i18n'; */ import { SettingsRow } from '../settings-row'; import { STORE_NAME } from '../../data/store'; +import { moderationHelperText } from '../../utils/helper-text'; /** * Component for Moderation feature settings. @@ -29,36 +30,50 @@ export const ModerationSettings = () => { }; return ( - - { Object.keys( contentTypes ).map( ( contentType ) => { - return ( - { - setFeatureSettings( { - content_types: { - ...featureSettings.content_types, - [ contentType ]: value ? contentType : '0', - }, - } ); + <> + + { Object.keys( contentTypes ).map( ( contentType ) => { + return ( + { + setFeatureSettings( { + content_types: { + ...featureSettings.content_types, + [ contentType ]: value + ? contentType + : '0', + }, + } ); + } } + __nextHasNoMarginBottom + /> + ); + } ) } + +
+
+
- ); - } ) } - +
+
+ ); }; diff --git a/src/js/settings/components/feature-additional-settings/nlu-feature.js b/src/js/settings/components/feature-additional-settings/nlu-feature.js index e66abf75e..1b58b5165 100644 --- a/src/js/settings/components/feature-additional-settings/nlu-feature.js +++ b/src/js/settings/components/feature-additional-settings/nlu-feature.js @@ -8,6 +8,7 @@ import { __experimentalInputControl as InputControl, // eslint-disable-line @wordpress/no-unsafe-wp-apis } from '@wordpress/components'; import { __, sprintf } from '@wordpress/i18n'; +import { useState } from '@wordpress/element'; /** * Internal dependencies @@ -15,6 +16,7 @@ import { __, sprintf } from '@wordpress/i18n'; import { SettingsRow } from '../settings-row'; import { STORE_NAME } from '../../data/store'; import { getFeature } from '../../utils/utils'; +import { thresholdInfo, nluHelperText } from '../../utils/helper-text'; /** * Component for render settings fields when IBM Watson NLU is selected as the provider. @@ -24,6 +26,7 @@ import { getFeature } from '../../utils/utils'; * @return {React.ReactElement} NLUFeatureSettings component. */ export const NLUFeatureSettings = () => { + const [ thresholdInfoStates, setThresholdInfoStates ] = useState( {} ); const featureSettings = useSelect( ( select ) => select( STORE_NAME ).getFeatureSettings() ); @@ -36,18 +39,22 @@ export const NLUFeatureSettings = () => { category: { label: __( 'Category', 'classifai' ), defaultThreshold: 70, + helperText: nluHelperText.category, }, keyword: { label: __( 'Keyword', 'classifai' ), defaultThreshold: 70, + helperText: nluHelperText.keyword, }, entity: { label: __( 'Entity', 'classifai' ), defaultThreshold: 70, + helperText: nluHelperText.entity, }, concept: { label: __( 'Concept', 'classifai' ), defaultThreshold: 70, + helperText: nluHelperText.concept, }, }; @@ -91,15 +98,24 @@ export const NLUFeatureSettings = () => { } ); } + const toggleThresholdInfo = ( feature ) => { + setThresholdInfoStates( ( prev ) => ( { + ...prev, + [ feature ]: ! prev[ feature ], + } ) ); + }; + return ( <> { Object.keys( features ).map( ( feature ) => { - const { defaultThreshold, label } = features[ feature ]; + const { defaultThreshold, label, helperText } = + features[ feature ]; return ( { /> { } ); } } /> +
+ + { thresholdInfoStates[ feature ] && + thresholdInfo.helper } +
+ { 'ibm_watson_nlu' === featureSettings.provider && ( { ); const { setFeatureSettings } = useDispatch( STORE_NAME ); const { taxonomies = {} } = getFeature( featureName ); - + const [ thresholdInfoStates, setThresholdInfoStates ] = useState( {} ); const options = Object.keys( taxonomies ).map( ( slug ) => { return { value: slug, @@ -47,6 +49,13 @@ export const TermCleanupSettings = () => { }; } ); + const toggleThresholdInfo = ( feature ) => { + setThresholdInfoStates( ( prev ) => ( { + ...prev, + [ feature ]: ! prev[ feature ], + } ) ); + }; + const Description = () => { if ( window.classifAISettings?.isEPinstalled ) { return __( @@ -123,7 +132,10 @@ export const TermCleanupSettings = () => { /> { } ); } } /> +
+ + { thresholdInfoStates[ feature ] && + thresholdInfo.helper } +
); } ) } diff --git a/src/js/settings/components/settings-row/index.js b/src/js/settings/components/settings-row/index.js index 341d65999..4299728aa 100644 --- a/src/js/settings/components/settings-row/index.js +++ b/src/js/settings/components/settings-row/index.js @@ -2,6 +2,7 @@ * External dependencies */ import classNames from 'classnames'; +import { useState } from '@wordpress/element'; /** * Settings row component. @@ -11,9 +12,31 @@ import classNames from 'classnames'; * @param {Object} props.children The children of the component. */ export const SettingsRow = ( props ) => { + const [ showTooltip, setShowTooltip ] = useState( false ); + return (
-
{ props.label }
+
+ { props.label } + { props.helperText && ( +
setShowTooltip( true ) } + onMouseLeave={ () => setShowTooltip( false ) } + > + + { showTooltip && ( +
+
+
+ ) } +
+ ) } +
{ props.children }
diff --git a/src/js/settings/utils/helper-text.js b/src/js/settings/utils/helper-text.js new file mode 100644 index 000000000..904b2dfdf --- /dev/null +++ b/src/js/settings/utils/helper-text.js @@ -0,0 +1,71 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; + +export const thresholdInfo = { + helper: ( +
+
+

+ { __( + 'Determines how confident the AI must be before suggesting a term.', + 'classifai' + ) } +

+

+ { __( + 'Higher % = More precise (fewer, more accurate terms)', + 'classifai' + ) } +

+

+ { __( + 'Lower % = More verbose (more suggestions, including lower-confidence terms)', + 'classifai' + ) } +

+
+
+ ), +}; + +export const nluHelperText = { + category: __( + '

IBM Watson analyzes your content and assigns a broad topic hierarchy that best describes the overall subject.

' + + '

Example:/technology and computing/software

' + + '

Categories are useful for general classification and site-wide content grouping.

' + + '

Learn more

', + 'classifai' + ), + keyword: __( + '

Keywords represent important terms in your content that are contextually significant.

' + + '

Watson extracts these to help identify core concepts, topics, and SEO-friendly tags.

' + + '

Keywords often map well to WordPress tags.

' + + '

Learn more

', + 'classifai' + ), + entity: __( + '

Entities are named people, places, brands, and other proper nouns mentioned in your content.

' + + '

Watson identifies and classifies these by type (e.g., Person, Company, Location) and optionally links them to known databases like Wikipedia.

' + + '

Entities are helpful for structured data and enhancing rich snippets or metadata.

' + + '

Learn more

', + 'classifai' + ), + concept: __( + "

Concepts reflect high-level abstract ideas Watson identifies in your content, even if the term isn't explicitly used.

" + + '

For example, an article about "the iPhone" might be linked to the concept of "Apple Inc."

' + + '

Concepts are great for semantic tagging and content recommendation systems.

' + + '

Learn more

', + 'classifai' + ), +}; + +export const moderationHelperText = { + content_types: __( + '

The Moderation endpoint provides a simple interface to classify user-generated content into specific content categories.

' + + '

Categories include: hate, threatening, harassment, self-harm, sexual, violence

' + + '

Learn more

', + 'classifai' + ), +}; diff --git a/src/scss/settings.scss b/src/scss/settings.scss index 35fe31414..d63cbe3f1 100644 --- a/src/scss/settings.scss +++ b/src/scss/settings.scss @@ -128,6 +128,85 @@ .classifai-panel-header-close { margin-right: -10px; } + + .display-container-wrapper { + margin-top: 8px; + margin-bottom: 8px; + + .helper-text-icon { + cursor: pointer; + background: none; + border: none; + padding: 0; + margin: 0; + } + + .helper-text-content { + margin-top: 8px; + padding: 4px 20px; + background-color: #f0f0f1; + } + } + + // Tooltip styles for helper text + .tooltip-container { + position: relative; + display: inline-block; + + .helper-text-icon { + position: relative; + top: 1px; + cursor: pointer; + color: #666; + margin-left: 4px; + font-size: 18px; + transition: color 0.2s ease; + &:hover { + color: var(--wp-admin-theme-color, #3858e9); + } + } + + .settings-helper-text.tooltip { + position: absolute; + left: 0; + transform: translateX(-14%); + background: #FFF; + color: #333; + padding: 8px 12px; + border-radius: 4px; + border: 1px solid #333; + font-size: 10px; + font-weight: normal; + z-index: 1000; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); + margin-top: 1px; + width: 400px; + + // Arrow pointing up + &::before { + content: ''; + position: absolute; + top: -4px; + left: 4.2rem; + border-left: 4px solid transparent; + border-right: 4px solid transparent; + border-bottom: 4px solid #333; + } + + // Ensure tooltip doesn't go off-screen + @media (max-width: 600px) { + left: 0; + transform: none; + white-space: normal; + max-width: 250px; + + &::before { + left: 20px; + transform: none; + } + } + } + } } .classifai-settings-wrapper {