From 1160e4e0cd18bf0f9581e9806bbe233fa04cb08e Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Mon, 23 Dec 2024 22:25:13 +0530 Subject: [PATCH 1/4] Add save settings snackbar notifications to inform users settings saved successfully. --- .../components/classifai-settings/index.js | 34 ++++++++- .../feature-settings/save-settings-button.js | 14 +++- .../components/service-settings/index.js | 69 +++++++++++++++---- src/scss/settings.scss | 10 +++ 4 files changed, 111 insertions(+), 16 deletions(-) diff --git a/src/js/settings/components/classifai-settings/index.js b/src/js/settings/components/classifai-settings/index.js index bbbffe56c..449255413 100644 --- a/src/js/settings/components/classifai-settings/index.js +++ b/src/js/settings/components/classifai-settings/index.js @@ -14,11 +14,12 @@ import { /** * WordPress dependencies */ -import { useDispatch } from '@wordpress/data'; -import { SlotFillProvider } from '@wordpress/components'; +import { useSelect, useDispatch } from '@wordpress/data'; +import { SlotFillProvider, SnackbarList } from '@wordpress/components'; import { __, sprintf } from '@wordpress/i18n'; import { useEffect, useState } from '@wordpress/element'; import apiFetch from '@wordpress/api-fetch'; +import { store as noticeStore } from '@wordpress/notices'; /** * Internal dependencies @@ -28,6 +29,7 @@ import { STORE_NAME } from '../../data/store'; import { FeatureContext } from '../feature-settings/context'; import { ClassifAIRegistration } from '../classifai-registration'; import { ClassifAIWelcomeGuide } from './welcome-guide'; +import { Notices } from '../feature-settings/notices'; const { services, features } = window.classifAISettings; @@ -121,6 +123,32 @@ export const ServiceNavigation = () => { ); }; +/** + * Snackbar component to render the snackbar notifications. + * + * @return {React.ReactElement} The Snackbar component. + */ +export const SnackbarNotifications = () => { + const { removeNotice } = useDispatch( noticeStore ); + + const { notices } = useSelect( ( select ) => { + const allNotices = select( noticeStore ).getNotices(); + return { + notices: allNotices.filter( + ( notice ) => notice.type === 'snackbar' + ), + }; + }, [] ); + + return ( + removeNotice( notice ) } + /> + ); +}; + /** * Main ClassifAI Settings Component. * @@ -188,6 +216,7 @@ export const ClassifAISettings = () => {
+ { />
+ ); diff --git a/src/js/settings/components/feature-settings/save-settings-button.js b/src/js/settings/components/feature-settings/save-settings-button.js index 185c68201..5210410cc 100644 --- a/src/js/settings/components/feature-settings/save-settings-button.js +++ b/src/js/settings/components/feature-settings/save-settings-button.js @@ -33,8 +33,12 @@ export const SaveSettingsButton = ( { label = __( 'Save Settings', 'classifai' ), } ) => { const { featureName } = useFeatureSettings(); - const { createErrorNotice, removeNotices, removeNotice } = - useDispatch( noticesStore ); + const { + createSuccessNotice, + createErrorNotice, + removeNotices, + removeNotice, + } = useDispatch( noticesStore ); const notices = useSelect( ( select ) => select( noticesStore ).getNotices() ); @@ -82,6 +86,12 @@ export const SaveSettingsButton = ( { return; } onSaveSuccess(); + createSuccessNotice( + __( 'Settings saved successfully.', 'classifai' ), + { + type: 'snackbar', + } + ); setSettings( res.settings ); setIsSaving( false ); } ) diff --git a/src/js/settings/components/service-settings/index.js b/src/js/settings/components/service-settings/index.js index f175f29db..fe34f120c 100644 --- a/src/js/settings/components/service-settings/index.js +++ b/src/js/settings/components/service-settings/index.js @@ -19,8 +19,9 @@ import { Notice, Icon, } from '@wordpress/components'; -import { useEffect, useRef } from '@wordpress/element'; +import { useEffect, useRef, useState } from '@wordpress/element'; import apiFetch from '@wordpress/api-fetch'; +import { store as noticesStore } from '@wordpress/notices'; /** * Internal dependencies @@ -54,8 +55,18 @@ const ConfigureProviderNotice = () => ( * @return {Object} The ServiceSettings component. */ export const ServiceSettings = () => { + const [ enabled, setEnabled ] = useState( false ); const { setCurrentService, setIsSaving, setSettings } = useDispatch( STORE_NAME ); + const { + createSuccessNotice, + createErrorNotice, + removeNotices, + removeNotice, + } = useDispatch( noticesStore ); + const notices = useSelect( ( select ) => + select( noticesStore ).getNotices() + ); const { service } = useParams(); const isInitialPageLoad = useRef( true ); @@ -77,6 +88,13 @@ export const ServiceSettings = () => { const serviceFeatures = features[ service ] || {}; const saveSettings = () => { + // Remove existing notices. + if ( removeNotices ) { + removeNotices( notices.map( ( { id } ) => id ) ); + } else if ( removeNotice ) { + notices.forEach( ( { id } ) => removeNotice( id ) ); + } + setIsSaving( true ); apiFetch( { path: '/classifai/v1/settings/', @@ -86,15 +104,41 @@ export const ServiceSettings = () => { is_setup: true, step: 'enable_features', }, - } ).then( ( res ) => { - if ( res.errors && res.errors.length ) { - setIsSaving( false ); - return; - } + } ) + .then( ( res ) => { + if ( res.errors && res.errors.length ) { + res.errors.forEach( ( error ) => { + createErrorNotice( error.message, { + id: 'error-generic-notices', + } ); + } ); + setIsSaving( false ); + return; + } - setSettings( res.settings ); - setIsSaving( false ); - } ); + const message = enabled + ? __( 'Feature enabled successfully.', 'classifai' ) + : __( 'Feature disabled successfully.', 'classifai' ); + + createSuccessNotice( message, { + type: 'snackbar', + } ); + setSettings( res.settings ); + setIsSaving( false ); + } ) + .catch( ( error ) => { + createErrorNotice( + error.message || + __( + 'An error occurred while saving settings.', + 'classifai' + ), + { + id: 'error-generic-notices', + } + ); + setIsSaving( false ); + } ); }; const statuses = Object.keys( settings ) @@ -126,7 +170,8 @@ export const ServiceSettings = () => { feature ) } - onChange={ ( value ) => + onChange={ ( value ) => { + setEnabled( value ); wp.data .dispatch( STORE_NAME ) .setFeatureSettings( @@ -136,8 +181,8 @@ export const ServiceSettings = () => { : '0', }, feature - ) - } + ); + } } __nextHasNoMarginBottom /> diff --git a/src/scss/settings.scss b/src/scss/settings.scss index f13801452..35fe31414 100644 --- a/src/scss/settings.scss +++ b/src/scss/settings.scss @@ -430,6 +430,16 @@ .classifai-admin-notices { margin-right: 0px; } + + .classifai-settings-snackbar-notices { + position: fixed; + left: auto; + bottom: 40px; + + @media screen and (max-width: 600px) { + padding: 0px 20px 0px 0px; + } + } } .classifai-onboarding { From d9ed9f41e1d6030bc33db48e511ca4c19ffa9d5d Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Mon, 23 Dec 2024 22:26:57 +0530 Subject: [PATCH 2/4] Add snackbar notice in classifai registration settings as well. --- .../settings/components/classifai-registration/index.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/js/settings/components/classifai-registration/index.js b/src/js/settings/components/classifai-registration/index.js index e51f00b5e..2c9dbac86 100644 --- a/src/js/settings/components/classifai-registration/index.js +++ b/src/js/settings/components/classifai-registration/index.js @@ -174,7 +174,8 @@ export const SaveSettingsButton = ( { setSettings, onSaveSuccess = () => {}, } ) => { - const { createErrorNotice, removeNotices } = useDispatch( noticesStore ); + const { createErrorNotice, createSuccessNotice, removeNotices } = + useDispatch( noticesStore ); const notices = useSelect( ( select ) => select( noticesStore ).getNotices() ); @@ -205,6 +206,12 @@ export const SaveSettingsButton = ( { setSettings( res.settings ); onSaveSuccess(); + createSuccessNotice( + __( 'Settings saved successfully.', 'classifai' ), + { + type: 'snackbar', + } + ); setIsSaving( false ); } ) .catch( ( error ) => { From 4c0282ffc674c9093e357e812113df2b6ef7fe0f Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Fri, 10 Jan 2025 17:08:57 +0530 Subject: [PATCH 3/4] Add scroll to error notice. --- .../settings/components/classifai-registration/index.js | 8 ++++++++ .../components/feature-settings/save-settings-button.js | 8 ++++++++ src/js/settings/components/service-settings/index.js | 8 ++++++++ 3 files changed, 24 insertions(+) diff --git a/src/js/settings/components/classifai-registration/index.js b/src/js/settings/components/classifai-registration/index.js index 2c9dbac86..aedb07680 100644 --- a/src/js/settings/components/classifai-registration/index.js +++ b/src/js/settings/components/classifai-registration/index.js @@ -201,6 +201,10 @@ export const SaveSettingsButton = ( { ); setSettings( res.settings ); setIsSaving( false ); + window.scrollTo( { + top: 0, + behavior: 'smooth', + } ); return; } @@ -226,6 +230,10 @@ export const SaveSettingsButton = ( { } ); setIsSaving( false ); + window.scrollTo( { + top: 0, + behavior: 'smooth', + } ); } ); }; diff --git a/src/js/settings/components/feature-settings/save-settings-button.js b/src/js/settings/components/feature-settings/save-settings-button.js index 5210410cc..3f83e264b 100644 --- a/src/js/settings/components/feature-settings/save-settings-button.js +++ b/src/js/settings/components/feature-settings/save-settings-button.js @@ -83,6 +83,10 @@ export const SaveSettingsButton = ( { } ); setSettings( res.settings ); setIsSaving( false ); + window.scrollTo( { + top: 0, + behavior: 'smooth', + } ); return; } onSaveSuccess(); @@ -107,6 +111,10 @@ export const SaveSettingsButton = ( { } ); setIsSaving( false ); + window.scrollTo( { + top: 0, + behavior: 'smooth', + } ); } ); }; diff --git a/src/js/settings/components/service-settings/index.js b/src/js/settings/components/service-settings/index.js index fe34f120c..3f41e9ce6 100644 --- a/src/js/settings/components/service-settings/index.js +++ b/src/js/settings/components/service-settings/index.js @@ -113,6 +113,10 @@ export const ServiceSettings = () => { } ); } ); setIsSaving( false ); + window.scrollTo( { + top: 0, + behavior: 'smooth', + } ); return; } @@ -138,6 +142,10 @@ export const ServiceSettings = () => { } ); setIsSaving( false ); + window.scrollTo( { + top: 0, + behavior: 'smooth', + } ); } ); }; From 4082203dc02cb450444fe43cfd057a06fffcc313 Mon Sep 17 00:00:00 2001 From: Dharmesh Patel Date: Mon, 13 Jan 2025 12:57:57 +0530 Subject: [PATCH 4/4] Remove snackbar location change. --- .../settings/components/classifai-settings/index.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/js/settings/components/classifai-settings/index.js b/src/js/settings/components/classifai-settings/index.js index 449255413..f821ea4c9 100644 --- a/src/js/settings/components/classifai-settings/index.js +++ b/src/js/settings/components/classifai-settings/index.js @@ -129,7 +129,8 @@ export const ServiceNavigation = () => { * @return {React.ReactElement} The Snackbar component. */ export const SnackbarNotifications = () => { - const { removeNotice } = useDispatch( noticeStore ); + const { removeNotice, removeNotices } = useDispatch( noticeStore ); + const location = useLocation(); const { notices } = useSelect( ( select ) => { const allNotices = select( noticeStore ).getNotices(); @@ -140,6 +141,16 @@ export const SnackbarNotifications = () => { }; }, [] ); + useEffect( () => { + // Remove existing snackbar notices on location change. + if ( removeNotices ) { + removeNotices( notices.map( ( { id } ) => id ) ); + } else if ( removeNotice ) { + notices.forEach( ( { id } ) => removeNotice( id ) ); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [ location, removeNotice, removeNotices ] ); + return (