0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-04-08 02:52:39 -05:00

Added save states for setting groups in admin-x

refs https://github.com/TryGhost/Team/issues/3151

- adds Save -> Saving -> Saved sequence to save buttons on all setting groups
- uses common hook state to manage state across all Settings
This commit is contained in:
Rishabh 2023-05-31 22:03:28 +05:30
parent 19b2112f8b
commit 9ca82490c2
13 changed files with 56 additions and 10 deletions

View file

@ -4,12 +4,14 @@ import SettingGroupHeader from './SettingGroupHeader';
import {IButton} from '../global/Button';
export type TSettingGroupStates = 'view' | 'edit' | 'unsaved';
export type SaveState = 'saving' | 'saved' | 'error' | '';
interface SettingGroupProps {
navid?:string;
title?: string;
description?: React.ReactNode;
state?: TSettingGroupStates;
saveState?: SaveState;
customHeader?: React.ReactNode;
customButtons?: React.ReactNode;
children?: React.ReactNode;
@ -35,6 +37,7 @@ const SettingGroup: React.FC<SettingGroupProps> = ({
title,
description,
state,
saveState,
customHeader,
customButtons,
children,
@ -59,7 +62,6 @@ const SettingGroup: React.FC<SettingGroupProps> = ({
const handleSave = () => {
onSave?.();
onStateChange?.('view');
};
switch (state) {
@ -79,9 +81,13 @@ const SettingGroup: React.FC<SettingGroupProps> = ({
let viewButtons = [];
if (!hideEditButton) {
let label = 'Edit';
if (saveState === 'saved') {
label = 'Saved';
}
viewButtons.push(
{
label: 'Edit',
label,
key: 'edit',
color: 'green',
onClick: handleEdit
@ -98,9 +104,13 @@ const SettingGroup: React.FC<SettingGroupProps> = ({
];
if (state === 'unsaved' || alwaysShowSaveButton) {
let label = 'Save';
if (saveState === 'saving') {
label = 'Saving...';
}
editButtons.push(
{
label: 'Save',
label,
key: 'save',
color: 'green',
onClick: handleSave
@ -112,7 +122,7 @@ const SettingGroup: React.FC<SettingGroupProps> = ({
<div className={`flex flex-col gap-6 rounded ${border && 'border p-5 md:p-7'} ${styles}`} id={navid && navid}>
{customHeader ? customHeader :
<SettingGroupHeader description={description} title={title!}>
{customButtons ? customButtons :
{customButtons ? customButtons :
(onStateChange && <ButtonGroup buttons={state === 'view' ? viewButtons : editButtons} link={true} />)}
</SettingGroupHeader>
}

View file

@ -5,7 +5,7 @@ import {settingsApi} from '../../utils/api';
// Define the Settings Context
interface SettingsContextProps {
settings: Setting[] | null;
saveSettings: (updatedSettings: Setting[]) => void;
saveSettings: (updatedSettings: Setting[]) => Promise<void>;
}
interface SettingsProviderProps {
@ -14,7 +14,7 @@ interface SettingsProviderProps {
const SettingsContext = createContext<SettingsContextProps>({
settings: null,
saveSettings: () => []
saveSettings: async () => {}
});
// Create a Settings Provider component

View file

@ -15,6 +15,7 @@ const MAILGUN_REGIONS = [
const MailGun: React.FC = () => {
const {
currentState,
saveState,
handleSave,
handleCancel,
updateSetting,
@ -96,6 +97,7 @@ const MailGun: React.FC = () => {
return (
<SettingGroup
description={groupDescription}
saveState={saveState}
state={currentState}
title='Mailgun'
onCancel={handleCancel}

View file

@ -7,6 +7,7 @@ import useSettingGroup from '../../../hooks/useSettingGroup';
const Facebook: React.FC = () => {
const {
currentState,
saveState,
focusRef,
handleSave,
handleCancel,
@ -51,6 +52,7 @@ const Facebook: React.FC = () => {
<SettingGroup
description='Customize structured data of your site'
navid='facebook'
saveState={saveState}
state={currentState}
title='Facebook card'
onCancel={handleCancel}

View file

@ -10,6 +10,7 @@ import useSettingGroup from '../../../hooks/useSettingGroup';
const LockSite: React.FC = () => {
const {
currentState,
saveState,
handleSave,
handleCancel,
updateSetting,
@ -76,6 +77,7 @@ const LockSite: React.FC = () => {
return (
<SettingGroup
description='Enable protection with a simple shared password.'
saveState={saveState}
state={currentState}
title='Make site private'
onCancel={handleCancel}

View file

@ -10,6 +10,7 @@ import {ReactComponent as MagnifyingGlass} from '../../../admin-x-ds/assets/icon
const Metadata: React.FC = () => {
const {
currentState,
saveState,
focusRef,
handleSave,
handleCancel,
@ -79,6 +80,7 @@ const Metadata: React.FC = () => {
<SettingGroup
description='Extra content for search engines'
navid='metadata'
saveState={saveState}
state={currentState}
title='Metadata'
onCancel={handleCancel}

View file

@ -7,6 +7,7 @@ import useSettingGroup from '../../../hooks/useSettingGroup';
const PublicationLanguage: React.FC = () => {
const {
currentState,
saveState,
handleSave,
handleCancel,
updateSetting,
@ -54,6 +55,7 @@ const PublicationLanguage: React.FC = () => {
return (
<SettingGroup
description="Set the language/locale which is used on your site"
saveState={saveState}
state={currentState}
title="Publication Language"
onCancel={handleCancel}

View file

@ -37,6 +37,7 @@ const Hint: React.FC<HintProps> = ({timezone}) => {
const TimeZone: React.FC = () => {
const {
currentState,
saveState,
handleSave,
handleCancel,
updateSetting,
@ -83,6 +84,7 @@ const TimeZone: React.FC = () => {
return (
<SettingGroup
description='Set the time and date of your publication, used for all published posts'
saveState={saveState}
state={currentState}
title='Site timezone'
onCancel={handleCancel}

View file

@ -8,6 +8,7 @@ import useSettingGroup from '../../../hooks/useSettingGroup';
const TitleAndDescription: React.FC = () => {
const {
currentState,
saveState,
focusRef,
handleSave,
handleCancel,
@ -67,6 +68,7 @@ const TitleAndDescription: React.FC = () => {
<SettingGroup
description='The details used to identify your publication around the web'
navid='title-and-description'
saveState={saveState}
state={currentState}
title='Title & description'
onCancel={handleCancel}

View file

@ -7,6 +7,7 @@ import useSettingGroup from '../../../hooks/useSettingGroup';
const Twitter: React.FC = () => {
const {
currentState,
saveState,
focusRef,
handleSave,
handleCancel,
@ -51,6 +52,7 @@ const Twitter: React.FC = () => {
<SettingGroup
description='Customize structured data of your site'
navid='twitter'
saveState={saveState}
state={currentState}
title='Twitter card'
onCancel={handleCancel}

View file

@ -27,6 +27,7 @@ const COMMENTS_ENABLED_OPTIONS = [
const Access: React.FC = () => {
const {
currentState,
saveState,
handleSave,
handleCancel,
updateSetting,
@ -99,6 +100,7 @@ const Access: React.FC = () => {
return (
<SettingGroup
description='Set up default access options for subscription and posts'
saveState={saveState}
state={currentState}
title='Access'
onCancel={handleCancel}

View file

@ -8,6 +8,7 @@ import useSettingGroup from '../../../hooks/useSettingGroup';
const Analytics: React.FC = () => {
const {
currentState,
saveState,
handleSave,
handleCancel,
updateSetting,
@ -72,6 +73,7 @@ const Analytics: React.FC = () => {
<SettingGroup
description='Decide what data you collect from your members'
hideEditButton={true}
saveState={saveState}
state={currentState}
title='Analytics'
onCancel={handleCancel}

View file

@ -1,7 +1,7 @@
import React, {useEffect} from 'react';
import {SaveState, TSettingGroupStates} from '../admin-x-ds/settings/SettingGroup';
import {Setting, SettingValue} from '../types/api';
import {SettingsContext} from '../components/providers/SettingsProvider';
import {TSettingGroupStates} from '../admin-x-ds/settings/SettingGroup';
import {useContext, useReducer, useRef, useState} from 'react';
interface LocalSetting extends Setting {
@ -10,6 +10,7 @@ interface LocalSetting extends Setting {
export interface SettingGroupHook {
currentState: TSettingGroupStates;
saveState: SaveState;
focusRef: React.RefObject<HTMLInputElement>;
handleSave: () => void;
handleCancel: () => void;
@ -66,6 +67,8 @@ const useSettingGroup = (): SettingGroupHook => {
// create a state to track the current state of the setting group
const [currentState, setCurrentState] = useState<TSettingGroupStates>('view');
const [saveState, setSaveState] = useState<SaveState>('');
// focus the input field when the state changes to edit
useEffect(() => {
if (currentState === 'edit' && focusRef.current) {
@ -73,8 +76,17 @@ const useSettingGroup = (): SettingGroupHook => {
}
}, [currentState]);
// Reset saved state after 2 seconds
useEffect(() => {
if (saveState === 'saved') {
setTimeout(() => {
setSaveState('');
}, 2000);
}
}, [saveState]);
// function to save the changed settings via API
const handleSave = () => {
const handleSave = async () => {
const changedSettings = localSettings?.filter(setting => setting.dirty)
?.map((setting) => {
return {
@ -82,9 +94,12 @@ const useSettingGroup = (): SettingGroupHook => {
value: setting.value
};
});
if (changedSettings.length) {
saveSettings?.(changedSettings);
if (!changedSettings?.length) {
return;
}
setSaveState('saving');
await saveSettings?.(changedSettings);
setSaveState('saved');
setCurrentState('view');
};
@ -123,6 +138,7 @@ const useSettingGroup = (): SettingGroupHook => {
return {
currentState,
saveState,
focusRef,
handleSave,
handleCancel,