Remove duplicated src folder/
This commit is contained in:
parent
979fa45627
commit
2dce7b5bba
|
@ -1,136 +0,0 @@
|
|||
/**
|
||||
* External dependencies.
|
||||
*/
|
||||
import { useState } from '@wordpress/element';
|
||||
import { Button, SelectControl } from '@wordpress/components';
|
||||
import apiFetch from '@wordpress/api-fetch';
|
||||
|
||||
|
||||
export const AddNote = () => {
|
||||
const [ isAdding, setIsAdding ] = useState( false );
|
||||
const [ hasAdded, setHasAdded ] = useState( false );
|
||||
const [ errorMessage, setErrorMessage ] = useState( false );
|
||||
const [ noteType, setNoteType ] = useState( 'info' );
|
||||
const [ noteLayout, setNoteLayout ] = useState( 'plain' );
|
||||
|
||||
async function triggerAddNote() {
|
||||
setIsAdding( true );
|
||||
setHasAdded( false );
|
||||
setErrorMessage( false );
|
||||
|
||||
const name = prompt( 'Enter the note name' );
|
||||
if ( ! name ) {
|
||||
setIsAdding( false );
|
||||
return;
|
||||
}
|
||||
|
||||
const title = prompt( 'Enter the note title' );
|
||||
if ( ! title ) {
|
||||
setIsAdding( false );
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await apiFetch( {
|
||||
path: '/wc-admin-test-helper/admin-notes/add-note/v1',
|
||||
method: 'POST',
|
||||
data: {
|
||||
name,
|
||||
type: noteType,
|
||||
layout: noteLayout,
|
||||
title,
|
||||
},
|
||||
} );
|
||||
setHasAdded( true );
|
||||
}
|
||||
catch ( ex ) {
|
||||
setErrorMessage( ex.message );
|
||||
}
|
||||
|
||||
setIsAdding( false );
|
||||
}
|
||||
|
||||
function onTypeChange( val ) {
|
||||
setNoteType( val );
|
||||
if ( val !== 'info' ) {
|
||||
setNoteLayout( 'plain' );
|
||||
}
|
||||
}
|
||||
|
||||
function onLayoutChange( val ) {
|
||||
setNoteLayout( val );
|
||||
}
|
||||
|
||||
function getAddNoteDescription() {
|
||||
switch ( noteType ){
|
||||
case 'email':
|
||||
return (
|
||||
<>
|
||||
This will add a new <strong>email</strong> note. Enable email insights{' '}
|
||||
<a href="/wp-admin/admin.php?page=wc-settings&tab=email">
|
||||
here
|
||||
</a>{' '}
|
||||
and run the cron to send the note by email.
|
||||
</>);
|
||||
default:
|
||||
return (
|
||||
<>
|
||||
This will add a new note. Currently only the note name
|
||||
and title will be used to create the note.
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<p><strong>Add a note</strong></p>
|
||||
<div>
|
||||
{ getAddNoteDescription() }
|
||||
<br/>
|
||||
<div className="woocommerce-admin-test-helper__add-notes">
|
||||
<Button
|
||||
onClick={ triggerAddNote }
|
||||
disabled={ isAdding }
|
||||
isPrimary
|
||||
>
|
||||
Add admin note
|
||||
</Button>
|
||||
<SelectControl
|
||||
label="Type"
|
||||
onChange={ onTypeChange }
|
||||
labelPosition="side"
|
||||
options={ [
|
||||
{ label: 'Info', value: 'info' },
|
||||
{ label: 'Update', value: 'update' },
|
||||
{ label: 'Email', value: 'email' },
|
||||
] }
|
||||
value={ noteType }
|
||||
/>
|
||||
<SelectControl
|
||||
label="Layout"
|
||||
onChange={ onLayoutChange }
|
||||
labelPosition="side"
|
||||
options={ [
|
||||
{ label: 'Plain', value: 'plain' },
|
||||
{ label: 'Banner', value: 'banner' },
|
||||
{ label: 'Thumbnail', value: 'thumbnail' },
|
||||
] }
|
||||
disabled={ noteType !== 'info' }
|
||||
value={ noteLayout }
|
||||
/>
|
||||
</div>
|
||||
<br/>
|
||||
<span className="woocommerce-admin-test-helper__action-status">
|
||||
{ isAdding && 'Adding, please wait' }
|
||||
{ hasAdded && 'Note added' }
|
||||
{ errorMessage && (
|
||||
<>
|
||||
<strong>Error:</strong> { errorMessage }
|
||||
</>
|
||||
) }
|
||||
</span>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -1,16 +0,0 @@
|
|||
/**
|
||||
* Internal dependencies.
|
||||
*/
|
||||
import { DeleteAllNotes } from './delete-all-notes';
|
||||
import { AddNote } from './add-note';
|
||||
|
||||
export const AdminNotes = () => {
|
||||
return (
|
||||
<>
|
||||
<h2>Admin notes</h2>
|
||||
<p>This section contains tools for managing admin notes.</p>
|
||||
<AddNote/>
|
||||
<DeleteAllNotes/>
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -1,69 +0,0 @@
|
|||
/**
|
||||
* External dependencies.
|
||||
*/
|
||||
import { useState } from '@wordpress/element';
|
||||
import { Button } from '@wordpress/components';
|
||||
import apiFetch from '@wordpress/api-fetch';
|
||||
|
||||
export const DeleteAllNotes = () => {
|
||||
const [ isDeleting, setIsDeleting ] = useState( false );
|
||||
const [ deleteStatus, setDeleteStatus ] = useState( false );
|
||||
const [ errorMessage, setErrorMessage ] = useState( false );
|
||||
|
||||
async function triggerDeleteAllNotes() {
|
||||
setIsDeleting( true );
|
||||
setErrorMessage( false );
|
||||
setDeleteStatus( false );
|
||||
|
||||
try {
|
||||
const response = await apiFetch( {
|
||||
path: '/wc-admin-test-helper/admin-notes/delete-all-notes/v1',
|
||||
method: 'POST',
|
||||
} );
|
||||
|
||||
setDeleteStatus( response );
|
||||
} catch ( ex ) {
|
||||
setErrorMessage( ex.message );
|
||||
}
|
||||
|
||||
setIsDeleting( false );
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<p><strong>Delete all admin notes</strong></p>
|
||||
<p>
|
||||
This will delete all notes from the <code>wp_wc_admin_notes</code>
|
||||
table, and actions from the <code>wp_wc_admin_note_actions</code>
|
||||
table.
|
||||
<br/>
|
||||
<Button
|
||||
onClick={ triggerDeleteAllNotes }
|
||||
disabled={ isDeleting }
|
||||
isPrimary
|
||||
>
|
||||
Delete all notes
|
||||
</Button>
|
||||
<br/>
|
||||
<span className="woocommerce-admin-test-helper__action-status">
|
||||
{ isDeleting && 'Deleting, please wait.' }
|
||||
{ deleteStatus && (
|
||||
<>
|
||||
Deleted{ ' ' }
|
||||
<strong>{ deleteStatus.deleted_note_count }</strong>{ ' ' }
|
||||
admin notes and{ ' ' }
|
||||
<strong>{ deleteStatus.deleted_action_count }</strong>{ ' ' }
|
||||
actions.
|
||||
</>
|
||||
) }
|
||||
{ errorMessage && (
|
||||
<>
|
||||
<strong>Error: </strong>
|
||||
{ errorMessage }
|
||||
</>
|
||||
) }
|
||||
</span>
|
||||
</p>
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -1 +0,0 @@
|
|||
export { AdminNotes } from './admin-notes.js';
|
|
@ -1,72 +0,0 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { TabPanel } from '@wordpress/components';
|
||||
import { applyFilters } from '@wordpress/hooks';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { AdminNotes } from '../admin-notes';
|
||||
import { default as Tools } from '../tools';
|
||||
import { default as Options } from '../options';
|
||||
import { default as Experiments } from '../experiments';
|
||||
import { default as Features } from '../features';
|
||||
import { default as RestAPIFilters } from '../rest-api-filters';
|
||||
|
||||
const tabs = applyFilters( 'woocommerce_admin_test_helper_tabs', [
|
||||
{
|
||||
name: 'options',
|
||||
title: 'Options',
|
||||
content: <Options />,
|
||||
},
|
||||
{
|
||||
name: 'admin-notes',
|
||||
title: 'Admin notes',
|
||||
content: <AdminNotes />,
|
||||
},
|
||||
{
|
||||
name: 'tools',
|
||||
title: 'Tools',
|
||||
content: <Tools />,
|
||||
},
|
||||
{
|
||||
name: 'experiments',
|
||||
title: 'Experiments',
|
||||
content: <Experiments />,
|
||||
},
|
||||
{
|
||||
name: 'features',
|
||||
title: 'Features',
|
||||
content: <Features />,
|
||||
},
|
||||
{
|
||||
name: 'rest-api-filters',
|
||||
title: 'REST API FIlters',
|
||||
content: <RestAPIFilters />,
|
||||
},
|
||||
] );
|
||||
|
||||
export function App() {
|
||||
return (
|
||||
<div className="wrap">
|
||||
<h1>WooCommerce Admin Test Helper</h1>
|
||||
<TabPanel
|
||||
className="woocommerce-admin-test-helper__main-tab-panel"
|
||||
activeClass="active-tab"
|
||||
tabs={ tabs }
|
||||
initialTabName={ tabs[ 0 ].name }
|
||||
>
|
||||
{ ( tab ) => (
|
||||
<>
|
||||
{ tab.content }
|
||||
{ applyFilters(
|
||||
`woocommerce_admin_test_helper_tab_${ tab.name }`,
|
||||
[]
|
||||
) }
|
||||
</>
|
||||
) }
|
||||
</TabPanel>
|
||||
</div>
|
||||
);
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
export { App } from './app';
|
|
@ -1,56 +0,0 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { withDispatch } from '@wordpress/data';
|
||||
import { compose } from '@wordpress/compose';
|
||||
import { Button } from '@wordpress/components';
|
||||
import { useState } from '@wordpress/element';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { STORE_KEY } from './data/constants';
|
||||
import './data';
|
||||
|
||||
function NewExperimentForm( { addExperiment } ) {
|
||||
const [ experimentName, setExperimentName ] = useState( null );
|
||||
const [ variation, setVariation ] = useState( 'treatment' );
|
||||
|
||||
const getInputValue = ( event ) => {
|
||||
setExperimentName( event.target.value );
|
||||
};
|
||||
|
||||
const getVariationInput = ( event ) => {
|
||||
setVariation( event.target.value );
|
||||
};
|
||||
|
||||
const AddNewExperiment = () => {
|
||||
addExperiment( experimentName, variation );
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="manual-input">
|
||||
<div className="description">
|
||||
Don't see an experiment you want to test? Add it manually.
|
||||
</div>
|
||||
<input type="text" onChange={ getInputValue } />
|
||||
<select value={ variation } onChange={ getVariationInput }>
|
||||
<option value="treatment">treatment</option>
|
||||
<option value="control">control</option>
|
||||
</select>
|
||||
|
||||
<Button isPrimary onClick={ AddNewExperiment }>
|
||||
Add
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withDispatch( ( dispatch ) => {
|
||||
const { addExperiment } = dispatch( STORE_KEY );
|
||||
return {
|
||||
addExperiment,
|
||||
};
|
||||
} )
|
||||
)( NewExperimentForm );
|
|
@ -1,8 +0,0 @@
|
|||
const TYPES = {
|
||||
TOGGLE_EXPERIMENT: 'TOGGLE_EXPERIMENT',
|
||||
SET_EXPERIMENTS: 'SET_EXPERIMENTS',
|
||||
ADD_EXPERIMENT: 'ADD_EXPERIMENT',
|
||||
DELETE_EXPERIMENT: 'DELETE_EXPERIMENT',
|
||||
};
|
||||
|
||||
export default TYPES;
|
|
@ -1,105 +0,0 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { apiFetch } from '@wordpress/data-controls';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import TYPES from './action-types';
|
||||
import {
|
||||
EXPERIMENT_NAME_PREFIX,
|
||||
TRANSIENT_NAME_PREFIX,
|
||||
TRANSIENT_TIMEOUT_NAME_PREFIX,
|
||||
} from './constants';
|
||||
|
||||
function toggleFrontendExperiment( experimentName, newVariation ) {
|
||||
let storageItem = JSON.parse(
|
||||
window.localStorage.getItem( EXPERIMENT_NAME_PREFIX + experimentName )
|
||||
);
|
||||
|
||||
// If the experiment is not in localStorage, consider it as a new.
|
||||
if ( storageItem === null ) {
|
||||
storageItem = {
|
||||
experimentName,
|
||||
retrievedTimestamp: Date.now(),
|
||||
};
|
||||
}
|
||||
|
||||
storageItem.variationName = newVariation;
|
||||
storageItem.ttl = 3600;
|
||||
|
||||
window.localStorage.setItem(
|
||||
EXPERIMENT_NAME_PREFIX + experimentName,
|
||||
JSON.stringify( storageItem )
|
||||
);
|
||||
}
|
||||
|
||||
function* toggleBackendExperiment( experimentName, newVariation ) {
|
||||
try {
|
||||
const payload = {};
|
||||
payload[ TRANSIENT_NAME_PREFIX + experimentName ] = newVariation;
|
||||
payload[ TRANSIENT_TIMEOUT_NAME_PREFIX + experimentName ] =
|
||||
Math.round( Date.now() / 1000 ) + 3600;
|
||||
|
||||
yield apiFetch( {
|
||||
method: 'POST',
|
||||
path: '/wc-admin/options',
|
||||
headers: { 'content-type': 'application/json' },
|
||||
body: JSON.stringify( payload ),
|
||||
} );
|
||||
} catch ( error ) {
|
||||
throw new Error();
|
||||
}
|
||||
}
|
||||
|
||||
export function* toggleExperiment( experimentName, currentVariation ) {
|
||||
const newVariation =
|
||||
currentVariation === 'control' ? 'treatment' : 'control';
|
||||
|
||||
toggleFrontendExperiment( experimentName, newVariation );
|
||||
yield toggleBackendExperiment( experimentName, newVariation );
|
||||
|
||||
return {
|
||||
type: TYPES.TOGGLE_EXPERIMENT,
|
||||
experimentName,
|
||||
newVariation,
|
||||
};
|
||||
}
|
||||
|
||||
export function setExperiments( experiments ) {
|
||||
return {
|
||||
type: TYPES.SET_EXPERIMENTS,
|
||||
experiments,
|
||||
};
|
||||
}
|
||||
|
||||
export function* addExperiment( experimentName, variation ) {
|
||||
toggleFrontendExperiment( experimentName, variation );
|
||||
yield toggleBackendExperiment( experimentName, variation );
|
||||
|
||||
return {
|
||||
type: TYPES.ADD_EXPERIMENT,
|
||||
experimentName,
|
||||
variation,
|
||||
};
|
||||
}
|
||||
|
||||
export function* deleteExperiment( experimentName ) {
|
||||
window.localStorage.removeItem( EXPERIMENT_NAME_PREFIX + experimentName );
|
||||
|
||||
const optionNames = [
|
||||
TRANSIENT_NAME_PREFIX + experimentName,
|
||||
TRANSIENT_TIMEOUT_NAME_PREFIX + experimentName,
|
||||
];
|
||||
|
||||
yield apiFetch( {
|
||||
method: 'DELETE',
|
||||
path: '/wc-admin-test-helper/options/' + optionNames.join( ',' ),
|
||||
} );
|
||||
|
||||
return {
|
||||
type: TYPES.DELETE_EXPERIMENT,
|
||||
experimentName,
|
||||
};
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
export const STORE_KEY = 'wc-admin-helper/experiments';
|
||||
export const EXPERIMENT_NAME_PREFIX = 'explat-experiment--';
|
||||
export const TRANSIENT_NAME_PREFIX = '_transient_abtest_variation_';
|
||||
export const TRANSIENT_TIMEOUT_NAME_PREFIX =
|
||||
'_transient_timeout_abtest_variation';
|
||||
export const API_NAMESPACE = '/wc-admin-test-helper';
|
|
@ -1,22 +0,0 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { registerStore } from '@wordpress/data';
|
||||
import { controls } from '@wordpress/data-controls';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import * as actions from './actions';
|
||||
import * as resolvers from './resolvers';
|
||||
import * as selectors from './selectors';
|
||||
import reducer from './reducer';
|
||||
import { STORE_KEY } from './constants';
|
||||
|
||||
export default registerStore( STORE_KEY, {
|
||||
actions,
|
||||
selectors,
|
||||
resolvers,
|
||||
controls,
|
||||
reducer,
|
||||
} );
|
|
@ -1,72 +0,0 @@
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import TYPES from './action-types';
|
||||
|
||||
const DEFAULT_STATE = {
|
||||
experiments: [],
|
||||
};
|
||||
|
||||
const reducer = ( state = DEFAULT_STATE, action ) => {
|
||||
switch ( action.type ) {
|
||||
case TYPES.DELETE_EXPERIMENT:
|
||||
return {
|
||||
...state,
|
||||
experiments: state.experiments.filter( ( experiment ) => {
|
||||
return experiment.name !== action.experimentName;
|
||||
} ),
|
||||
};
|
||||
case TYPES.ADD_EXPERIMENT:
|
||||
const existingExperimentIndex = state.experiments.findIndex(
|
||||
( element ) => {
|
||||
return element.name === action.experimentName;
|
||||
}
|
||||
);
|
||||
const newExperiment = {
|
||||
name: action.experimentName,
|
||||
variation: action.variation,
|
||||
};
|
||||
const newExperiments =
|
||||
existingExperimentIndex !== -1
|
||||
? state.experiments
|
||||
.slice( 0, existingExperimentIndex )
|
||||
.concat( newExperiment )
|
||||
.concat(
|
||||
state.experiments.slice(
|
||||
existingExperimentIndex + 1
|
||||
)
|
||||
)
|
||||
: [
|
||||
...state.experiments,
|
||||
{
|
||||
name: action.experimentName,
|
||||
variation: action.variation,
|
||||
},
|
||||
];
|
||||
|
||||
return {
|
||||
...state,
|
||||
experiments: newExperiments,
|
||||
};
|
||||
case TYPES.TOGGLE_EXPERIMENT:
|
||||
return {
|
||||
...state,
|
||||
experiments: state.experiments.map( ( experiment ) => ( {
|
||||
...experiment,
|
||||
variation:
|
||||
experiment.name === action.experimentName
|
||||
? action.newVariation
|
||||
: experiment.variation,
|
||||
} ) ),
|
||||
};
|
||||
case TYPES.SET_EXPERIMENTS:
|
||||
return {
|
||||
...state,
|
||||
experiments: action.experiments,
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export default reducer;
|
|
@ -1,68 +0,0 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { apiFetch } from '@wordpress/data-controls';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { setExperiments } from './actions';
|
||||
import {
|
||||
EXPERIMENT_NAME_PREFIX,
|
||||
TRANSIENT_NAME_PREFIX,
|
||||
API_NAMESPACE,
|
||||
} from './constants';
|
||||
|
||||
function getExperimentsFromFrontend() {
|
||||
const storageItems = Object.entries( { ...window.localStorage } ).filter(
|
||||
( item ) => {
|
||||
return item[ 0 ].indexOf( EXPERIMENT_NAME_PREFIX ) === 0;
|
||||
}
|
||||
);
|
||||
|
||||
return storageItems.map( ( storageItem ) => {
|
||||
const [ key, value ] = storageItem;
|
||||
const objectValue = JSON.parse( value );
|
||||
return {
|
||||
name: key.replace( EXPERIMENT_NAME_PREFIX, '' ),
|
||||
variation: objectValue.variationName || 'control',
|
||||
};
|
||||
} );
|
||||
}
|
||||
|
||||
export function* getExperiments() {
|
||||
try {
|
||||
const response = yield apiFetch( {
|
||||
path: `${ API_NAMESPACE }/options?search=_transient_abtest_variation_`,
|
||||
} );
|
||||
|
||||
const experimentsFromBackend = response.map( ( experiment ) => {
|
||||
return {
|
||||
name: experiment.option_name.replace(
|
||||
TRANSIENT_NAME_PREFIX,
|
||||
''
|
||||
),
|
||||
variation:
|
||||
experiment.option_value === 'control'
|
||||
? 'control'
|
||||
: 'treatment',
|
||||
};
|
||||
} );
|
||||
|
||||
// Remove duplicate.
|
||||
const experiments = getExperimentsFromFrontend()
|
||||
.concat( experimentsFromBackend )
|
||||
.filter(
|
||||
( value, index, self ) =>
|
||||
index ===
|
||||
self.findIndex(
|
||||
( t ) =>
|
||||
t.place === value.place && t.name === value.name
|
||||
)
|
||||
);
|
||||
|
||||
yield setExperiments( experiments );
|
||||
} catch ( error ) {
|
||||
throw new Error();
|
||||
}
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
export function getExperiments( state ) {
|
||||
return state.experiments;
|
||||
}
|
|
@ -1,110 +0,0 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { withDispatch, withSelect } from '@wordpress/data';
|
||||
import { compose } from '@wordpress/compose';
|
||||
import { Button } from '@wordpress/components';
|
||||
import { OPTIONS_STORE_NAME } from '@woocommerce/data';
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { STORE_KEY } from './data/constants';
|
||||
import './data';
|
||||
import NewExperimentForm from './NewExperimentForm';
|
||||
|
||||
function Experiments( {
|
||||
experiments,
|
||||
toggleExperiment,
|
||||
deleteExperiment,
|
||||
isTrackingEnabled,
|
||||
isResolving,
|
||||
} ) {
|
||||
if ( isResolving ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div id="wc-admin-test-helper-experiments">
|
||||
<h2>Experiments</h2>
|
||||
{ isTrackingEnabled === 'no' && (
|
||||
<p className="tracking-disabled">
|
||||
The following list might not be complete without tracking
|
||||
enabled. <br />
|
||||
Please visit
|
||||
<a
|
||||
target="_blank"
|
||||
href={
|
||||
wcSettings.adminUrl +
|
||||
'/admin.php?page=wc-settings&tab=advanced§ion=woocommerce_com'
|
||||
}
|
||||
rel="noreferrer"
|
||||
>
|
||||
WooCommerce → Settings → Advanced →
|
||||
Woocommerce.com
|
||||
</a>
|
||||
and check{ ' ' }
|
||||
<b>Allow usage of WooCommerce to be tracked</b>.
|
||||
</p>
|
||||
) }
|
||||
<NewExperimentForm />
|
||||
<table className="experiments wp-list-table striped table-view-list widefat">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Experiment</th>
|
||||
<th>Variation</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{ experiments.map( ( { name, variation }, index ) => {
|
||||
return (
|
||||
<tr key={ index }>
|
||||
<td className="experiment-name">{ name }</td>
|
||||
<td align="center">{ variation }</td>
|
||||
<td className="actions" align="center">
|
||||
<Button
|
||||
onClick={ () => {
|
||||
toggleExperiment( name, variation );
|
||||
} }
|
||||
isPrimary
|
||||
>
|
||||
Toggle
|
||||
</Button>
|
||||
<Button
|
||||
onClick={ () => {
|
||||
deleteExperiment( name );
|
||||
} }
|
||||
className="btn btn-danger"
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
} ) }
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withSelect( ( select ) => {
|
||||
const { getExperiments } = select( STORE_KEY );
|
||||
const { getOption, isResolving } = select( OPTIONS_STORE_NAME );
|
||||
|
||||
return {
|
||||
experiments: getExperiments(),
|
||||
isTrackingEnabled: getOption( 'woocommerce_allow_tracking' ),
|
||||
isResolving: isResolving( 'getOption', [ 'getExperiments' ] ),
|
||||
};
|
||||
} ),
|
||||
withDispatch( ( dispatch ) => {
|
||||
const { toggleExperiment, deleteExperiment } = dispatch( STORE_KEY );
|
||||
|
||||
return {
|
||||
toggleExperiment,
|
||||
deleteExperiment,
|
||||
};
|
||||
} )
|
||||
)( Experiments );
|
|
@ -1,7 +0,0 @@
|
|||
const TYPES = {
|
||||
TOGGLE_FEATURE: 'TOGGLE_FEATURE',
|
||||
SET_FEATURES: 'SET_FEATURES',
|
||||
SET_MODIFIED_FEATURES: 'SET_MODIFIED_FEATURES',
|
||||
};
|
||||
|
||||
export default TYPES;
|
|
@ -1,57 +0,0 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { apiFetch } from '@wordpress/data-controls';
|
||||
import { controls } from '@wordpress/data';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import TYPES from './action-types';
|
||||
import { API_NAMESPACE, STORE_KEY } from './constants';
|
||||
|
||||
export function* resetModifiedFeatures() {
|
||||
try {
|
||||
const response = yield apiFetch( {
|
||||
path: `${ API_NAMESPACE }/features/reset`,
|
||||
method: 'POST',
|
||||
} );
|
||||
|
||||
yield setModifiedFeatures( [] );
|
||||
yield setFeatures( response );
|
||||
} catch ( error ) {
|
||||
throw new Error();
|
||||
}
|
||||
}
|
||||
|
||||
export function* toggleFeature( featureName ) {
|
||||
try {
|
||||
const response = yield apiFetch( {
|
||||
method: 'POST',
|
||||
path: API_NAMESPACE + '/features/' + featureName + '/toggle',
|
||||
headers: { 'content-type': 'application/json' },
|
||||
} );
|
||||
yield setFeatures( response );
|
||||
yield controls.dispatch(
|
||||
STORE_KEY,
|
||||
'invalidateResolutionForStoreSelector',
|
||||
'getModifiedFeatures'
|
||||
);
|
||||
} catch ( error ) {
|
||||
throw new Error();
|
||||
}
|
||||
}
|
||||
|
||||
export function setFeatures( features ) {
|
||||
return {
|
||||
type: TYPES.SET_FEATURES,
|
||||
features,
|
||||
};
|
||||
}
|
||||
|
||||
export function setModifiedFeatures( modifiedFeatures ) {
|
||||
return {
|
||||
type: TYPES.SET_MODIFIED_FEATURES,
|
||||
modifiedFeatures,
|
||||
};
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
export const STORE_KEY = 'wc-admin-helper/features';
|
||||
export const OPTION_NAME_PREFIX = 'wc_admin_helper_feature_values';
|
||||
export const API_NAMESPACE = '/wc-admin-test-helper';
|
|
@ -1,22 +0,0 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { registerStore } from '@wordpress/data';
|
||||
import { controls } from '@wordpress/data-controls';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import * as actions from './actions';
|
||||
import * as resolvers from './resolvers';
|
||||
import * as selectors from './selectors';
|
||||
import reducer from './reducer';
|
||||
import { STORE_KEY } from './constants';
|
||||
|
||||
export default registerStore( STORE_KEY, {
|
||||
actions,
|
||||
selectors,
|
||||
resolvers,
|
||||
controls,
|
||||
reducer,
|
||||
} );
|
|
@ -1,28 +0,0 @@
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import TYPES from './action-types';
|
||||
|
||||
const DEFAULT_STATE = {
|
||||
features: {},
|
||||
modifiedFeatures: [],
|
||||
};
|
||||
|
||||
const reducer = ( state = DEFAULT_STATE, action ) => {
|
||||
switch ( action.type ) {
|
||||
case TYPES.SET_MODIFIED_FEATURES:
|
||||
return {
|
||||
...state,
|
||||
modifiedFeatures: action.modifiedFeatures,
|
||||
};
|
||||
case TYPES.SET_FEATURES:
|
||||
return {
|
||||
...state,
|
||||
features: action.features,
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export default reducer;
|
|
@ -1,38 +0,0 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { apiFetch } from '@wordpress/data-controls';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { setFeatures, setModifiedFeatures } from './actions';
|
||||
import { API_NAMESPACE, OPTION_NAME_PREFIX } from './constants';
|
||||
|
||||
export function* getModifiedFeatures() {
|
||||
try {
|
||||
const response = yield apiFetch( {
|
||||
path: `wc-admin/options?options=` + OPTION_NAME_PREFIX,
|
||||
} );
|
||||
|
||||
yield setModifiedFeatures(
|
||||
response && response[ OPTION_NAME_PREFIX ]
|
||||
? Object.keys( response[ OPTION_NAME_PREFIX ] )
|
||||
: []
|
||||
);
|
||||
} catch ( error ) {
|
||||
throw new Error();
|
||||
}
|
||||
}
|
||||
|
||||
export function* getFeatures() {
|
||||
try {
|
||||
const response = yield apiFetch( {
|
||||
path: `${ API_NAMESPACE }/features`,
|
||||
} );
|
||||
|
||||
yield setFeatures( response );
|
||||
} catch ( error ) {
|
||||
throw new Error();
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
export function getFeatures( state ) {
|
||||
return state.features;
|
||||
}
|
||||
|
||||
export function getModifiedFeatures( state ) {
|
||||
return state.modifiedFeatures;
|
||||
}
|
|
@ -1,72 +0,0 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { useDispatch, useSelect } from '@wordpress/data';
|
||||
import { Button } from '@wordpress/components';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { STORE_KEY } from './data/constants';
|
||||
import './data';
|
||||
|
||||
function Features() {
|
||||
const { features = {}, modifiedFeatures = [] } = useSelect( ( select ) => {
|
||||
const { getFeatures, getModifiedFeatures } = select( STORE_KEY );
|
||||
return {
|
||||
features: getFeatures(),
|
||||
modifiedFeatures: getModifiedFeatures(),
|
||||
};
|
||||
} );
|
||||
|
||||
const { toggleFeature, resetModifiedFeatures } = useDispatch( STORE_KEY );
|
||||
|
||||
return (
|
||||
<div id="wc-admin-test-helper-features">
|
||||
<h2>
|
||||
Features
|
||||
<Button
|
||||
disabled={ modifiedFeatures.length === 0 }
|
||||
onClick={ () => resetModifiedFeatures() }
|
||||
isSecondary
|
||||
style={ { marginLeft: '24px' } }
|
||||
>
|
||||
Reset to defaults
|
||||
</Button>
|
||||
</h2>
|
||||
<table className="features wp-list-table striped table-view-list widefat">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Feature Name</th>
|
||||
<th>Enabled?</th>
|
||||
<th>Toggle</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{ Object.keys( features ).map( ( feature_name ) => {
|
||||
return (
|
||||
<tr key={ feature_name }>
|
||||
<td className="feature-name">
|
||||
{ feature_name }
|
||||
</td>
|
||||
<td>{ features[ feature_name ].toString() }</td>
|
||||
<td>
|
||||
<Button
|
||||
onClick={ () => {
|
||||
toggleFeature( feature_name );
|
||||
} }
|
||||
isPrimary
|
||||
>
|
||||
Toggle
|
||||
</Button>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
} ) }
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Features;
|
|
@ -1,18 +0,0 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { render } from '@wordpress/element';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { App } from './app';
|
||||
import './index.scss';
|
||||
|
||||
const appRoot = document.getElementById(
|
||||
'woocommerce-admin-test-helper-app-root'
|
||||
);
|
||||
|
||||
if (appRoot) {
|
||||
render(<App />, appRoot);
|
||||
}
|
|
@ -1,155 +0,0 @@
|
|||
#woocommerce-admin-test-helper-app-root {
|
||||
.btn-danger {
|
||||
color: #fff;
|
||||
background-color: #dc3545;
|
||||
border-color: #dc3545;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
color: #fff;
|
||||
background-color: #007bff;
|
||||
border-color: #007bff;
|
||||
}
|
||||
}
|
||||
|
||||
.woocommerce-admin-test-helper__main-tab-panel {
|
||||
.active-tab {
|
||||
box-shadow: inset 0 1.5px #007cba;
|
||||
box-shadow: inset 0 1.5px var( --wp-admin-theme-color );
|
||||
}
|
||||
}
|
||||
|
||||
.woocommerce-admin-test-helper__action-status {
|
||||
color: #007cba;
|
||||
color: var( --wp-admin-theme-color );
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.woocommerce-admin-test-helper__add-notes {
|
||||
width: 410px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
.components-base-control__field {
|
||||
margin-bottom: 0;
|
||||
padding-top: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
#wc-admin-test-helper-options {
|
||||
div.search-box {
|
||||
float: right;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.align-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.components-notice {
|
||||
margin: 0px 0px 10px 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.wca-test-helper-option-editor {
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
}
|
||||
|
||||
.wca-test-helper-edit-btn-save {
|
||||
float: right;
|
||||
}
|
||||
|
||||
#wc-admin-test-helper-tools,
|
||||
#wc-admin-test-helper-experiments {
|
||||
table.tools,
|
||||
table.experiments {
|
||||
thead th {
|
||||
text-align: center;
|
||||
}
|
||||
tbody td {
|
||||
vertical-align: middle;
|
||||
&.command {
|
||||
white-space: nowrap;
|
||||
}
|
||||
.trigger-cron-job {
|
||||
width: 40%;
|
||||
padding-top: 4px;
|
||||
.components-base-control__field {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.components-notice {
|
||||
margin: 0px 0px 10px 0px;
|
||||
}
|
||||
.tracking-disabled {
|
||||
border: 1px solid #cc99c2;
|
||||
border-left: 4px solid #cc99c2;
|
||||
line-height: 1.5em;
|
||||
background: #fff;
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
#wc-admin-test-helper-experiments {
|
||||
.actions {
|
||||
button {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
.manual-input {
|
||||
margin-bottom: 20px;
|
||||
float: right;
|
||||
.description {
|
||||
text-align: right;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
button {
|
||||
height: 34px;
|
||||
position: relative;
|
||||
top: -1px;
|
||||
}
|
||||
input {
|
||||
height: 34px;
|
||||
width: 250px;
|
||||
}
|
||||
select {
|
||||
height: 34px;
|
||||
position: relative;
|
||||
top: -2px;
|
||||
margin-right: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#wc-admin-test-helper-rest-api-filters {
|
||||
.btn-new {
|
||||
float: right;
|
||||
}
|
||||
}
|
||||
|
||||
form.rest-api-filter-new-form {
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: max-content max-content;
|
||||
grid-gap: 5px;
|
||||
input[type='text'] {
|
||||
width: 350px;
|
||||
}
|
||||
label {
|
||||
text-align: right;
|
||||
}
|
||||
label:after {
|
||||
content: ':';
|
||||
}
|
||||
}
|
||||
|
||||
.btn-new {
|
||||
color: #fff;
|
||||
background-color: #007bff;
|
||||
border-color: #007bff;
|
||||
float: right;
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { useEffect, useState } from '@wordpress/element';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Button } from '@wordpress/components';
|
||||
|
||||
const OptionEditor = ( props ) => {
|
||||
const [ value, setValue ] = useState( props.option.content );
|
||||
|
||||
useEffect( () => {
|
||||
setValue( props.option.content );
|
||||
}, [ props.option ] );
|
||||
|
||||
const handleChange = ( event ) => {
|
||||
setValue( event.target.value );
|
||||
};
|
||||
|
||||
const handleSave = () => {
|
||||
props.onSave( props.option.name, value );
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<textarea
|
||||
className="wca-test-helper-option-editor"
|
||||
value={ value }
|
||||
onChange={ handleChange }
|
||||
></textarea>
|
||||
<Button
|
||||
className="wca-test-helper-edit-btn-save"
|
||||
isPrimary
|
||||
onClick={ handleSave }
|
||||
disabled={ props.option.isSaving === true }
|
||||
>
|
||||
{ props.option.isSaving ? 'Saving...' : 'Save' }
|
||||
</Button>
|
||||
<div className="clear"></div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
OptionEditor.propTypes = {
|
||||
option: PropTypes.object.isRequired,
|
||||
onSave: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default OptionEditor;
|
|
@ -1,9 +0,0 @@
|
|||
const TYPES = {
|
||||
SET_OPTIONS: 'SET_OPTIONS',
|
||||
SET_OPTION_FOR_EDITING: 'SET_OPTION_FOR_EDITING',
|
||||
SET_IS_LOADING: 'SET_IS_LOADING',
|
||||
SET_NOTICE: 'SET_NOTICE',
|
||||
DELETE_OPTION: 'DELETE_OPTION',
|
||||
};
|
||||
|
||||
export default TYPES;
|
|
@ -1,81 +0,0 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { apiFetch } from '@wordpress/data-controls';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import TYPES from './action-types';
|
||||
import { API_NAMESPACE } from './constants';
|
||||
|
||||
/**
|
||||
* Initialize the state
|
||||
*
|
||||
* @param {Array} options
|
||||
*/
|
||||
export function setOptions( options ) {
|
||||
return {
|
||||
type: TYPES.SET_OPTIONS,
|
||||
options,
|
||||
};
|
||||
}
|
||||
|
||||
export function setLoadingState( isLoading ) {
|
||||
return {
|
||||
type: TYPES.SET_IS_LOADING,
|
||||
isLoading,
|
||||
};
|
||||
}
|
||||
|
||||
export function setOptionForEditing( editingOption ) {
|
||||
return {
|
||||
type: TYPES.SET_OPTION_FOR_EDITING,
|
||||
editingOption,
|
||||
};
|
||||
}
|
||||
|
||||
export function setNotice( notice ) {
|
||||
return {
|
||||
type: TYPES.SET_NOTICE,
|
||||
notice,
|
||||
};
|
||||
}
|
||||
|
||||
export function* deleteOption( optionName ) {
|
||||
try {
|
||||
yield apiFetch( {
|
||||
method: 'DELETE',
|
||||
path: `${ API_NAMESPACE }/options/${ optionName }`,
|
||||
} );
|
||||
yield {
|
||||
type: TYPES.DELETE_OPTION,
|
||||
optionName,
|
||||
};
|
||||
} catch {
|
||||
throw new Error();
|
||||
}
|
||||
}
|
||||
|
||||
export function* saveOption( optionName, newOptionValue ) {
|
||||
try {
|
||||
const payload = {};
|
||||
payload[ optionName ] = JSON.parse( newOptionValue );
|
||||
yield apiFetch( {
|
||||
method: 'POST',
|
||||
path: '/wc-admin/options',
|
||||
headers: { 'content-type': 'application/json' },
|
||||
body: JSON.stringify( payload ),
|
||||
} );
|
||||
yield setNotice( {
|
||||
status: 'success',
|
||||
message: optionName + ' has been saved.',
|
||||
} );
|
||||
} catch {
|
||||
yield setNotice( {
|
||||
status: 'error',
|
||||
message: 'Unable to save ' + optionName,
|
||||
} );
|
||||
throw new Error();
|
||||
}
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
export const STORE_KEY = 'wc-admin-helper/options';
|
||||
export const API_NAMESPACE = '/wc-admin-test-helper';
|
|
@ -1,22 +0,0 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { registerStore } from '@wordpress/data';
|
||||
import { controls } from '@wordpress/data-controls';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import * as actions from './actions';
|
||||
import * as resolvers from './resolvers';
|
||||
import * as selectors from './selectors';
|
||||
import reducer from './reducer';
|
||||
import { STORE_KEY } from './constants';
|
||||
|
||||
export default registerStore( STORE_KEY, {
|
||||
actions,
|
||||
selectors,
|
||||
resolvers,
|
||||
controls,
|
||||
reducer,
|
||||
} );
|
|
@ -1,60 +0,0 @@
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import TYPES from './action-types';
|
||||
|
||||
const DEFAULT_STATE = {
|
||||
options: [],
|
||||
isLoading: true,
|
||||
editingOption: {
|
||||
name: null,
|
||||
content: '{}',
|
||||
},
|
||||
notice: {
|
||||
status: 'success',
|
||||
message: '',
|
||||
},
|
||||
};
|
||||
|
||||
const reducer = ( state = DEFAULT_STATE, action ) => {
|
||||
switch ( action.type ) {
|
||||
case TYPES.SET_OPTION_FOR_EDITING:
|
||||
return {
|
||||
...state,
|
||||
editingOption: {
|
||||
...state.editingOption,
|
||||
...action.editingOption,
|
||||
},
|
||||
};
|
||||
case TYPES.SET_IS_LOADING:
|
||||
return {
|
||||
...state,
|
||||
isLoading: action.isLoading,
|
||||
};
|
||||
case TYPES.SET_OPTIONS:
|
||||
return {
|
||||
...state,
|
||||
options: action.options,
|
||||
isLoading: false,
|
||||
};
|
||||
case TYPES.SET_NOTICE:
|
||||
return {
|
||||
...state,
|
||||
notice: {
|
||||
...state.notice,
|
||||
...action.notice,
|
||||
},
|
||||
};
|
||||
case TYPES.DELETE_OPTION:
|
||||
return {
|
||||
...state,
|
||||
options: state.options.filter(
|
||||
( item ) => item.option_name !== action.optionName
|
||||
),
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export default reducer;
|
|
@ -1,61 +0,0 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { apiFetch } from '@wordpress/data-controls';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { API_NAMESPACE } from './constants';
|
||||
import { setLoadingState, setOptions, setOptionForEditing } from './actions';
|
||||
|
||||
export function* getOptions( search ) {
|
||||
let path = `${ API_NAMESPACE }/options?`;
|
||||
if ( search ) {
|
||||
path += `search=${ search }`;
|
||||
}
|
||||
|
||||
yield setLoadingState( true );
|
||||
|
||||
try {
|
||||
const response = yield apiFetch( {
|
||||
path,
|
||||
} );
|
||||
yield setOptions( response );
|
||||
} catch ( error ) {
|
||||
throw new Error();
|
||||
}
|
||||
}
|
||||
|
||||
export function* getOptionForEditing( optionName ) {
|
||||
const loadingOption = {
|
||||
name: 'Loading...',
|
||||
content: '',
|
||||
saved: false,
|
||||
};
|
||||
if ( optionName === undefined ) {
|
||||
return setOptionForEditing( loadingOption );
|
||||
}
|
||||
|
||||
yield setOptionForEditing( loadingOption );
|
||||
|
||||
const path = '/wc-admin/options?options=' + optionName;
|
||||
|
||||
try {
|
||||
const response = yield apiFetch( {
|
||||
path,
|
||||
} );
|
||||
|
||||
let content = response[ optionName ];
|
||||
if ( typeof content === 'object' ) {
|
||||
content = JSON.stringify( response[ optionName ], null, 2 );
|
||||
}
|
||||
|
||||
yield setOptionForEditing( {
|
||||
name: optionName,
|
||||
content,
|
||||
} );
|
||||
} catch ( error ) {
|
||||
throw new Error( error );
|
||||
}
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
export function getOptions( state ) {
|
||||
return state.options;
|
||||
}
|
||||
|
||||
export function isLoading( state ) {
|
||||
return state.isLoading;
|
||||
}
|
||||
|
||||
export function getOptionForEditing( state ) {
|
||||
return state.editingOption;
|
||||
}
|
||||
|
||||
export function getNotice( state ) {
|
||||
return state.notice;
|
||||
}
|
|
@ -1,257 +0,0 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { withDispatch, withSelect } from '@wordpress/data';
|
||||
import { compose } from '@wordpress/compose';
|
||||
import { Modal, Notice } from '@wordpress/components';
|
||||
import { useState } from '@wordpress/element';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { STORE_KEY } from './data/constants';
|
||||
import { default as OptionEditor } from './OptionEditor';
|
||||
import './data';
|
||||
|
||||
function shorten( input ) {
|
||||
if ( input.length > 20 ) {
|
||||
return input.substring( 0, 20 ) + '...';
|
||||
}
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
function Options( {
|
||||
options,
|
||||
getOptions,
|
||||
deleteOption,
|
||||
isLoading,
|
||||
invalidateResolution,
|
||||
getOptionForEditing,
|
||||
editingOption,
|
||||
saveOption,
|
||||
notice,
|
||||
setNotice,
|
||||
} ) {
|
||||
const [ isEditModalOpen, setEditModalOpen ] = useState( false );
|
||||
|
||||
const deleteOptionByName = ( optionName ) => {
|
||||
// eslint-disable-next-line no-alert
|
||||
if ( confirm( 'Are you sure you want to delete this option?' ) ) {
|
||||
deleteOption( optionName );
|
||||
}
|
||||
};
|
||||
|
||||
const openEditModal = ( optionName ) => {
|
||||
invalidateResolution( STORE_KEY, 'getOptionForEditing', [
|
||||
optionName,
|
||||
] );
|
||||
|
||||
getOptionForEditing( optionName );
|
||||
setEditModalOpen( true );
|
||||
};
|
||||
|
||||
const handleSaveOption = ( optionName, newValue ) => {
|
||||
saveOption( optionName, newValue );
|
||||
setEditModalOpen( false );
|
||||
};
|
||||
|
||||
const renderLoading = () => {
|
||||
return (
|
||||
<tr>
|
||||
<td colSpan="6" align="center">
|
||||
Loading...
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
};
|
||||
|
||||
const renderTableData = () => {
|
||||
if ( options.length === 0 ) {
|
||||
return (
|
||||
<tr>
|
||||
<td colSpan="6" align="center">
|
||||
No Options Found
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
return options.map( ( option ) => {
|
||||
// eslint-disable-next-line camelcase
|
||||
const { option_id, option_name, option_value, autoload } = option;
|
||||
|
||||
// eslint-disable-next-line camelcase
|
||||
const optionId = option_id;
|
||||
// eslint-disable-next-line camelcase
|
||||
const optionName = option_name;
|
||||
// eslint-disable-next-line camelcase
|
||||
const optionValue = shorten( option_value );
|
||||
|
||||
return (
|
||||
<tr key={ optionId }>
|
||||
<td key={ 0 }>{ optionId }</td>
|
||||
<td key={ 1 }>{ optionName }</td>
|
||||
<td key={ 'optionValue' }>{ optionValue }</td>
|
||||
<td className="align-center" key={ 2 }>
|
||||
{ autoload }
|
||||
</td>
|
||||
<td className="align-center" key={ 3 }>
|
||||
<button
|
||||
className="button btn-danger"
|
||||
onClick={ () => deleteOptionByName( optionName ) }
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
</td>
|
||||
<td className="align-center" key={ 4 }>
|
||||
<button
|
||||
className="button btn-primary"
|
||||
onClick={ () => openEditModal( optionName ) }
|
||||
>
|
||||
Edit
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
} );
|
||||
};
|
||||
|
||||
const searchOption = ( event ) => {
|
||||
event.preventDefault();
|
||||
const keyword = event.target.search.value;
|
||||
|
||||
// Invalidate resolution of the same selector + arg
|
||||
// so that entering the same keyword always works
|
||||
invalidateResolution( STORE_KEY, 'getOptions', [ keyword ] );
|
||||
|
||||
getOptions( keyword );
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{ isEditModalOpen && (
|
||||
<Modal
|
||||
title={ editingOption.name }
|
||||
onRequestClose={ () => {
|
||||
setEditModalOpen( false );
|
||||
} }
|
||||
>
|
||||
<OptionEditor
|
||||
option={ editingOption }
|
||||
onSave={ handleSaveOption }
|
||||
></OptionEditor>
|
||||
</Modal>
|
||||
) }
|
||||
<div id="wc-admin-test-helper-options">
|
||||
{ notice.message.length > 0 && (
|
||||
<Notice
|
||||
status={ notice.status }
|
||||
onRemove={ () => {
|
||||
setNotice( { message: '' } );
|
||||
} }
|
||||
>
|
||||
{ notice.message }
|
||||
</Notice>
|
||||
) }
|
||||
<form onSubmit={ searchOption }>
|
||||
<div className="search-box">
|
||||
<label
|
||||
className="screen-reader-text"
|
||||
htmlFor="post-search-input"
|
||||
>
|
||||
Search options:
|
||||
</label>
|
||||
<input type="search" name="search" />
|
||||
<input
|
||||
type="submit"
|
||||
id="search-submit"
|
||||
className="button"
|
||||
value="Search options"
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
<div className="clear"></div>
|
||||
<table className="wp-list-table striped table-view-list widefat">
|
||||
<thead>
|
||||
<tr>
|
||||
<td
|
||||
className="manage-column column-thumb"
|
||||
key={ 0 }
|
||||
>
|
||||
I.D
|
||||
</td>
|
||||
<td
|
||||
className="manage-column column-thumb"
|
||||
key={ 1 }
|
||||
>
|
||||
Name
|
||||
</td>
|
||||
<td
|
||||
className="manage-column column-thumb"
|
||||
key={ 'optionValue' }
|
||||
>
|
||||
Value
|
||||
</td>
|
||||
<td
|
||||
className="manage-column column-thumb align-center"
|
||||
key={ 2 }
|
||||
>
|
||||
Autoload
|
||||
</td>
|
||||
<td
|
||||
className="manage-column column-thumb align-center"
|
||||
key={ 3 }
|
||||
>
|
||||
Delete
|
||||
</td>
|
||||
<td
|
||||
className="manage-column column-thumb align-center"
|
||||
key={ 4 }
|
||||
>
|
||||
Edit
|
||||
</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{ isLoading ? renderLoading() : renderTableData() }
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withSelect( ( select ) => {
|
||||
const {
|
||||
getOptions,
|
||||
getOptionForEditing,
|
||||
getNotice,
|
||||
isLoading,
|
||||
} = select( STORE_KEY );
|
||||
const options = getOptions();
|
||||
const editingOption = getOptionForEditing();
|
||||
const notice = getNotice();
|
||||
|
||||
return {
|
||||
options,
|
||||
getOptions,
|
||||
isLoading: isLoading(),
|
||||
editingOption,
|
||||
getOptionForEditing,
|
||||
notice,
|
||||
};
|
||||
} ),
|
||||
withDispatch( ( dispatch ) => {
|
||||
const { deleteOption, saveOption, setNotice } = dispatch( STORE_KEY );
|
||||
const { invalidateResolution } = dispatch( 'core/data' );
|
||||
|
||||
return {
|
||||
deleteOption,
|
||||
invalidateResolution,
|
||||
saveOption,
|
||||
setNotice,
|
||||
};
|
||||
} )
|
||||
)( Options );
|
|
@ -1,9 +0,0 @@
|
|||
const TYPES = {
|
||||
SET_FILTERS: 'SET_FILTERS',
|
||||
SET_IS_LOADING: 'SET_IS_LOADING',
|
||||
DELETE_FILTER: 'DELETE_FILTER',
|
||||
SAVE_FILTER: 'SAVE_FILTER',
|
||||
TOGGLE_FILTER: 'TOGGLE_FILTER',
|
||||
};
|
||||
|
||||
export default TYPES;
|
|
@ -1,93 +0,0 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { apiFetch } from '@wordpress/data-controls';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import TYPES from './action-types';
|
||||
import { API_NAMESPACE } from './constants';
|
||||
|
||||
/**
|
||||
* Initialize the state
|
||||
*
|
||||
* @param {Array} filter
|
||||
* @param filters
|
||||
*/
|
||||
export function setFilters( filters ) {
|
||||
return {
|
||||
type: TYPES.SET_FILTERS,
|
||||
filters,
|
||||
};
|
||||
}
|
||||
|
||||
export function setLoadingState( isLoading ) {
|
||||
return {
|
||||
type: TYPES.SET_IS_LOADING,
|
||||
isLoading,
|
||||
};
|
||||
}
|
||||
|
||||
export function* toggleFilter( index ) {
|
||||
try {
|
||||
yield apiFetch( {
|
||||
method: 'POST',
|
||||
path: `${ API_NAMESPACE }/rest-api-filters/${ index }/toggle`,
|
||||
headers: { 'content-type': 'application/json' },
|
||||
} );
|
||||
yield {
|
||||
type: TYPES.TOGGLE_FILTER,
|
||||
index,
|
||||
};
|
||||
} catch {
|
||||
throw new Error();
|
||||
}
|
||||
}
|
||||
|
||||
export function* deleteFilter( index ) {
|
||||
try {
|
||||
yield apiFetch( {
|
||||
method: 'DELETE',
|
||||
path: `${ API_NAMESPACE }/rest-api-filters/`,
|
||||
headers: { 'content-type': 'application/json' },
|
||||
body: JSON.stringify( {
|
||||
index,
|
||||
} ),
|
||||
} );
|
||||
|
||||
yield {
|
||||
type: TYPES.DELETE_FILTER,
|
||||
index,
|
||||
};
|
||||
} catch {
|
||||
throw new Error();
|
||||
}
|
||||
}
|
||||
|
||||
export function* saveFilter( endpoint, dotNotation, replacement ) {
|
||||
try {
|
||||
yield apiFetch( {
|
||||
method: 'POST',
|
||||
path: API_NAMESPACE + '/rest-api-filters',
|
||||
headers: { 'content-type': 'application/json' },
|
||||
body: JSON.stringify( {
|
||||
endpoint,
|
||||
dot_notation: dotNotation,
|
||||
replacement,
|
||||
} ),
|
||||
} );
|
||||
|
||||
yield {
|
||||
type: TYPES.SAVE_FILTER,
|
||||
filter: {
|
||||
endpoint,
|
||||
dot_notation: dotNotation,
|
||||
replacement,
|
||||
enabled: true,
|
||||
},
|
||||
};
|
||||
} catch {
|
||||
throw new Error();
|
||||
}
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
export const STORE_KEY = 'wc-admin-helper/rest-api-filters';
|
||||
export const API_NAMESPACE = '/wc-admin-test-helper';
|
||||
|
||||
// Option name where we're going to save the filters.
|
||||
export const FILTERS_OPTION_NAME = 'wc-admin-test-helper-rest-api-filters';
|
|
@ -1,22 +0,0 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { registerStore } from '@wordpress/data';
|
||||
import { controls } from '@wordpress/data-controls';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import * as actions from './actions';
|
||||
import * as resolvers from './resolvers';
|
||||
import * as selectors from './selectors';
|
||||
import reducer from './reducer';
|
||||
import { STORE_KEY } from './constants';
|
||||
|
||||
export default registerStore( STORE_KEY, {
|
||||
actions,
|
||||
selectors,
|
||||
resolvers,
|
||||
controls,
|
||||
reducer,
|
||||
} );
|
|
@ -1,55 +0,0 @@
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import TYPES from './action-types';
|
||||
|
||||
const DEFAULT_STATE = {
|
||||
filters: [],
|
||||
isLoading: true,
|
||||
notice: {
|
||||
status: 'success',
|
||||
message: '',
|
||||
},
|
||||
};
|
||||
|
||||
const reducer = ( state = DEFAULT_STATE, action ) => {
|
||||
switch ( action.type ) {
|
||||
case TYPES.TOGGLE_FILTER:
|
||||
return {
|
||||
...state,
|
||||
filters: state.filters.map( ( filter, index ) => {
|
||||
if ( index === action.index ) {
|
||||
filter.enabled = ! filter.enabled;
|
||||
}
|
||||
return filter;
|
||||
} ),
|
||||
};
|
||||
case TYPES.SET_IS_LOADING:
|
||||
return {
|
||||
...state,
|
||||
isLoading: action.isLoading,
|
||||
};
|
||||
case TYPES.SET_FILTERS:
|
||||
return {
|
||||
...state,
|
||||
filters: action.filters,
|
||||
isLoading: false,
|
||||
};
|
||||
case TYPES.DELETE_FILTER:
|
||||
return {
|
||||
...state,
|
||||
filters: state.filters.filter(
|
||||
( item, index ) => index !== action.index
|
||||
),
|
||||
};
|
||||
case TYPES.SAVE_FILTER:
|
||||
return {
|
||||
...state,
|
||||
filters: [ ...state.filters, action.filter ],
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export default reducer;
|
|
@ -1,29 +0,0 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { apiFetch } from '@wordpress/data-controls';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { FILTERS_OPTION_NAME } from './constants';
|
||||
import { setLoadingState, setFilters } from './actions';
|
||||
|
||||
export function* getFilters() {
|
||||
const path = '/wc-admin/options?options=' + FILTERS_OPTION_NAME;
|
||||
|
||||
yield setLoadingState( true );
|
||||
|
||||
try {
|
||||
const response = yield apiFetch( {
|
||||
path,
|
||||
} );
|
||||
if ( response[ FILTERS_OPTION_NAME ] === false ) {
|
||||
yield setFilters( [] );
|
||||
} else {
|
||||
yield setFilters( response[ FILTERS_OPTION_NAME ] );
|
||||
}
|
||||
} catch ( error ) {
|
||||
throw new Error();
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
export function getFilters( state ) {
|
||||
return state.filters;
|
||||
}
|
||||
|
||||
export function isLoading( state ) {
|
||||
return state.isLoading;
|
||||
}
|
|
@ -1,182 +0,0 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { withDispatch, withSelect } from '@wordpress/data';
|
||||
import { compose } from '@wordpress/compose';
|
||||
import { Modal } from '@wordpress/components';
|
||||
import { useState } from '@wordpress/element';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { STORE_KEY } from './data/constants';
|
||||
import './data';
|
||||
|
||||
function RestAPIFilters( {
|
||||
filters,
|
||||
deleteFilter,
|
||||
isLoading,
|
||||
saveFilter,
|
||||
toggleFilter,
|
||||
} ) {
|
||||
const [ isNewModalOpen, setNewModalOpen ] = useState( false );
|
||||
|
||||
const submitAddForm = ( e ) => {
|
||||
e.preventDefault();
|
||||
saveFilter(
|
||||
e.target.endpoint.value,
|
||||
e.target.dotNotation.value,
|
||||
e.target.replacement.value
|
||||
);
|
||||
setNewModalOpen( false );
|
||||
};
|
||||
|
||||
const renderLoading = () => {
|
||||
return (
|
||||
<tr>
|
||||
<td colSpan="6" align="center">
|
||||
Loading...
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
};
|
||||
|
||||
const renderTableData = () => {
|
||||
if ( filters.length === 0 ) {
|
||||
return (
|
||||
<tr>
|
||||
<td colSpan="7" align="center">
|
||||
No Filters Found
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
}
|
||||
|
||||
return filters.map( ( filter, index ) => {
|
||||
// eslint-disable-next-line camelcase
|
||||
const {
|
||||
endpoint,
|
||||
dot_notation: dotNotation,
|
||||
replacement,
|
||||
enabled,
|
||||
} = filter;
|
||||
|
||||
return (
|
||||
<tr key={ index }>
|
||||
<td>{ index + 1 }</td>
|
||||
<td>{ endpoint }</td>
|
||||
<td key={ 'optionValue' }>{ dotNotation }</td>
|
||||
<td className="align-center">{ replacement + '' }</td>
|
||||
<td className="align-center">{ enabled + '' }</td>
|
||||
<td className="align-center">
|
||||
<button
|
||||
className="button btn-primary"
|
||||
onClick={ () => toggleFilter( index ) }
|
||||
>
|
||||
Toggle
|
||||
</button>
|
||||
</td>
|
||||
<td className="align-center">
|
||||
<button
|
||||
className="button btn-danger"
|
||||
onClick={ () => deleteFilter( index ) }
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
} );
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{ isNewModalOpen && (
|
||||
<Modal
|
||||
title={ 'New Filter' }
|
||||
onRequestClose={ () => {
|
||||
setNewModalOpen( false );
|
||||
} }
|
||||
>
|
||||
<form
|
||||
className="rest-api-filter-new-form"
|
||||
onSubmit={ submitAddForm }
|
||||
>
|
||||
<div className="grid">
|
||||
<label htmlFor="endpoint">Endpoint</label>
|
||||
<input type="text" name="endpoint" autoFocus />
|
||||
<label htmlFor="jsonPath">Dot Notation</label>
|
||||
<input type="text" name="dotNotation" />
|
||||
<label htmlFor="replacement">Replacement </label>
|
||||
<input type="text" name="replacement" />
|
||||
</div>
|
||||
<input
|
||||
type="submit"
|
||||
value="Create New Filter"
|
||||
className="button btn-new"
|
||||
/>
|
||||
</form>
|
||||
</Modal>
|
||||
) }
|
||||
<div id="wc-admin-test-helper-rest-api-filters">
|
||||
<input
|
||||
type="button"
|
||||
className="button btn-primary btn-new"
|
||||
value="New Filter"
|
||||
onClick={ () => setNewModalOpen( true ) }
|
||||
/>
|
||||
<br />
|
||||
<br />
|
||||
<table className="wp-list-table striped table-view-list widefat">
|
||||
<thead>
|
||||
<tr>
|
||||
<td className="manage-column column-thumb">I.D</td>
|
||||
<td className="manage-column column-thumb">
|
||||
Endpoint
|
||||
</td>
|
||||
<td className="manage-column column-thumb">
|
||||
Dot Notation
|
||||
</td>
|
||||
<td className="manage-column column-thumb align-center">
|
||||
Replacement
|
||||
</td>
|
||||
<td className="manage-column column-thumb align-center">
|
||||
Enabled
|
||||
</td>
|
||||
<td className="manage-column column-thumb align-center">
|
||||
Toggle
|
||||
</td>
|
||||
<td className="manage-column column-thumb align-center"></td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{ isLoading ? renderLoading() : renderTableData() }
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withSelect( ( select ) => {
|
||||
const { getFilters, isLoading } = select( STORE_KEY );
|
||||
const filters = getFilters();
|
||||
|
||||
return {
|
||||
filters,
|
||||
isLoading: isLoading(),
|
||||
};
|
||||
} ),
|
||||
withDispatch( ( dispatch ) => {
|
||||
const { saveFilter, deleteFilter, toggleFilter } = dispatch(
|
||||
STORE_KEY
|
||||
);
|
||||
|
||||
return {
|
||||
saveFilter,
|
||||
deleteFilter,
|
||||
toggleFilter,
|
||||
};
|
||||
} )
|
||||
)( RestAPIFilters );
|
|
@ -1,17 +0,0 @@
|
|||
# Tools
|
||||
|
||||
## Adding a New Command
|
||||
|
||||
1. Open `commands.js` and add a new object with command, description, and action keys. Action value must be a valid function name.
|
||||
2. Open `data/actions.js` and add a function. The function name must be the value of `Action` from the first step.
|
||||
|
||||
Sample function:
|
||||
```
|
||||
export function* helloWorld() {
|
||||
yield runCommand( 'Hello World', function* () {
|
||||
console.log('Hello World');
|
||||
} );
|
||||
}
|
||||
```
|
||||
|
||||
3. Run `npm start` to compile and test.
|
|
@ -1,37 +0,0 @@
|
|||
/**
|
||||
* External dependencies.
|
||||
*/
|
||||
import { useSelect } from '@wordpress/data';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { STORE_KEY } from '../data/constants';
|
||||
|
||||
export const DisableEmail = () => {
|
||||
const { isEmailDisabled } = useSelect( ( select ) => {
|
||||
const { getIsEmailDisabled } = select( STORE_KEY );
|
||||
return {
|
||||
isEmailDisabled: getIsEmailDisabled(),
|
||||
};
|
||||
} );
|
||||
|
||||
const getEmailStatus = () => {
|
||||
switch( isEmailDisabled ) {
|
||||
case 'yes':
|
||||
return 'WooCommerce emails are turned off 🔴';
|
||||
case 'no':
|
||||
return 'WooCommerce emails are turned on 🟢';
|
||||
case 'error':
|
||||
return 'Error 🙁';
|
||||
default:
|
||||
return 'Loading ...';
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="disable-wc-email">
|
||||
{ getEmailStatus() }
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -1,67 +0,0 @@
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { TriggerCronJob, TRIGGER_CRON_ACTION_NAME } from './trigger-cron';
|
||||
import { DisableEmail } from './disable-email';
|
||||
import {
|
||||
TriggerUpdateCallbacks,
|
||||
TRIGGER_UPDATE_CALLBACKS_ACTION_NAME,
|
||||
} from './trigger-update-callbacks';
|
||||
|
||||
export default [
|
||||
{
|
||||
command: 'Trigger WCA Install',
|
||||
description: `This will trigger a WooCommerce Admin install, which usually
|
||||
happens when a new version (or new install) of WooCommerce
|
||||
Admin is installed. Triggering the install manually can
|
||||
run tasks such as removing obsolete admin notes.`,
|
||||
action: 'triggerWcaInstall',
|
||||
},
|
||||
{
|
||||
command: 'Reset Onboarding Wizard',
|
||||
description: 'Resets Onboarding Wizard progress.',
|
||||
action: 'resetOnboardingWizard',
|
||||
},
|
||||
{
|
||||
command: 'Reset Jetpack Connection',
|
||||
description: 'Resets Jepack Connection options.',
|
||||
action: 'resetJetpackConnection',
|
||||
},
|
||||
{
|
||||
command: 'Enable wc-admin Tracking',
|
||||
description:
|
||||
'Enable Tracking Debug mode. You should change your console level to verbose.',
|
||||
action: 'enableTrackingDebug',
|
||||
},
|
||||
{
|
||||
command: 'Update WC installation timestamp',
|
||||
description:
|
||||
'Updates woocommerce_admin_install_timestamp to a certain date',
|
||||
action: 'updateStoreAge',
|
||||
},
|
||||
{
|
||||
command: 'Run wc_admin_daily job',
|
||||
description: 'Run wc_admin_daily job',
|
||||
action: 'runWcAdminDailyJob',
|
||||
},
|
||||
{
|
||||
command: 'Delete all products',
|
||||
description: 'Delete all products',
|
||||
action: 'deleteAllProducts',
|
||||
},
|
||||
{
|
||||
command: 'Run a cron job',
|
||||
description: <TriggerCronJob />,
|
||||
action: TRIGGER_CRON_ACTION_NAME,
|
||||
},
|
||||
{
|
||||
command: 'Disable WC emails',
|
||||
description: <DisableEmail />,
|
||||
action: 'runDisableEmail',
|
||||
},
|
||||
{
|
||||
command: 'Run version update callbacks',
|
||||
description: <TriggerUpdateCallbacks />,
|
||||
action: TRIGGER_UPDATE_CALLBACKS_ACTION_NAME,
|
||||
},
|
||||
];
|
|
@ -1,48 +0,0 @@
|
|||
/**
|
||||
* External dependencies.
|
||||
*/
|
||||
import { SelectControl } from '@wordpress/components';
|
||||
import { useDispatch, useSelect } from '@wordpress/data';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { STORE_KEY } from '../data/constants';
|
||||
|
||||
export const TRIGGER_CRON_ACTION_NAME = 'runSelectedCronJob';
|
||||
|
||||
export const TriggerCronJob = () => {
|
||||
const { cronList } = useSelect((select) => {
|
||||
const { getCronJobs } = select(STORE_KEY);
|
||||
return {
|
||||
cronList: getCronJobs(),
|
||||
};
|
||||
});
|
||||
const { updateCommandParams } = useDispatch(STORE_KEY);
|
||||
|
||||
function onCronChange(selectedValue) {
|
||||
const { hook, signature } = cronList[selectedValue];
|
||||
updateCommandParams(TRIGGER_CRON_ACTION_NAME, { hook, signature });
|
||||
}
|
||||
|
||||
function getOptions() {
|
||||
return Object.keys(cronList).map((name) => {
|
||||
return { label: name, value: name };
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="trigger-cron-job">
|
||||
{!cronList ? (
|
||||
<p>Loading ...</p>
|
||||
) : (
|
||||
<SelectControl
|
||||
label="Select cron job to run"
|
||||
onChange={onCronChange}
|
||||
labelPosition="side"
|
||||
options={getOptions()}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -1,51 +0,0 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { SelectControl } from '@wordpress/components';
|
||||
import { useDispatch, useSelect } from '@wordpress/data';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { STORE_KEY } from '../data/constants';
|
||||
|
||||
export const TRIGGER_UPDATE_CALLBACKS_ACTION_NAME =
|
||||
'runSelectedUpdateCallbacks';
|
||||
|
||||
export const TriggerUpdateCallbacks = () => {
|
||||
const { dbUpdateVersions } = useSelect((select) => {
|
||||
const { getDBUpdateVersions } = select(STORE_KEY);
|
||||
return {
|
||||
dbUpdateVersions: getDBUpdateVersions(),
|
||||
};
|
||||
});
|
||||
|
||||
const { updateCommandParams } = useDispatch(STORE_KEY);
|
||||
|
||||
function onCronChange(version) {
|
||||
updateCommandParams(TRIGGER_UPDATE_CALLBACKS_ACTION_NAME, {
|
||||
version,
|
||||
});
|
||||
}
|
||||
|
||||
function getOptions() {
|
||||
return dbUpdateVersions.map((version) => {
|
||||
return { label: version, value: version };
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="trigger-cron-job">
|
||||
{!dbUpdateVersions ? (
|
||||
<p>Loading ...</p>
|
||||
) : (
|
||||
<SelectControl
|
||||
label="Select a version to run"
|
||||
onChange={onCronChange}
|
||||
labelPosition="side"
|
||||
options={getOptions().reverse()}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
|
@ -1,13 +0,0 @@
|
|||
const TYPES = {
|
||||
ADD_CURRENTLY_RUNNING: 'ADD_CURRENTLY_RUNNING',
|
||||
REMOVE_CURRENTLY_RUNNING: 'REMOVE_CURRENTLY_RUNNING',
|
||||
ADD_MESSAGE: 'ADD_MESSAGE',
|
||||
UPDATE_MESSAGE: 'UPDATE_MESSAGE',
|
||||
REMOVE_MESSAGE: 'REMOVE_MESSAGE',
|
||||
ADD_COMMAND_PARAMS: 'ADD_COMMAND_PARAMS',
|
||||
SET_CRON_JOBS: 'SET_CRON_JOBS',
|
||||
IS_EMAIL_DISABLED: 'IS_EMAIL_DISABLED',
|
||||
SET_DB_UPDATE_VERSIONS: 'SET_DB_UPDATE_VERSIONS',
|
||||
};
|
||||
|
||||
export default TYPES;
|
|
@ -1,211 +0,0 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { apiFetch } from '@wordpress/data-controls';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import TYPES from './action-types';
|
||||
import { API_NAMESPACE } from './constants';
|
||||
|
||||
export function addCurrentlyRunning(command) {
|
||||
return {
|
||||
type: TYPES.ADD_CURRENTLY_RUNNING,
|
||||
command,
|
||||
};
|
||||
}
|
||||
|
||||
export function removeCurrentlyRunning(command) {
|
||||
return {
|
||||
type: TYPES.REMOVE_CURRENTLY_RUNNING,
|
||||
command,
|
||||
};
|
||||
}
|
||||
|
||||
export function addMessage(source, message) {
|
||||
return {
|
||||
type: TYPES.ADD_MESSAGE,
|
||||
source,
|
||||
message,
|
||||
};
|
||||
}
|
||||
|
||||
export function updateMessage(source, message, status) {
|
||||
return {
|
||||
type: TYPES.ADD_MESSAGE,
|
||||
source,
|
||||
message,
|
||||
status,
|
||||
};
|
||||
}
|
||||
|
||||
export function removeMessage(source) {
|
||||
return {
|
||||
type: TYPES.REMOVE_MESSAGE,
|
||||
source,
|
||||
};
|
||||
}
|
||||
|
||||
export function updateCommandParams(source, params) {
|
||||
return {
|
||||
type: TYPES.ADD_COMMAND_PARAMS,
|
||||
source,
|
||||
params,
|
||||
};
|
||||
}
|
||||
|
||||
export function setCronJobs(cronJobs) {
|
||||
return {
|
||||
type: TYPES.SET_CRON_JOBS,
|
||||
cronJobs,
|
||||
};
|
||||
}
|
||||
|
||||
export function setDBUpdateVersions(versions) {
|
||||
return {
|
||||
type: TYPES.SET_DB_UPDATE_VERSIONS,
|
||||
versions,
|
||||
};
|
||||
}
|
||||
|
||||
export function setIsEmailDisabled(isEmailDisabled) {
|
||||
return {
|
||||
type: TYPES.IS_EMAIL_DISABLED,
|
||||
isEmailDisabled,
|
||||
};
|
||||
}
|
||||
|
||||
function* runCommand(commandName, func) {
|
||||
try {
|
||||
yield addCurrentlyRunning(commandName);
|
||||
yield addMessage(commandName, 'Executing...');
|
||||
yield func();
|
||||
yield removeCurrentlyRunning(commandName);
|
||||
yield updateMessage(commandName, 'Successful!');
|
||||
} catch (e) {
|
||||
yield updateMessage(commandName, e.message, 'error');
|
||||
yield removeCurrentlyRunning(commandName);
|
||||
}
|
||||
}
|
||||
|
||||
export function* triggerWcaInstall() {
|
||||
yield runCommand('Trigger WCA Install', function* () {
|
||||
yield apiFetch({
|
||||
path: API_NAMESPACE + '/tools/trigger-wca-install/v1',
|
||||
method: 'POST',
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function* resetOnboardingWizard() {
|
||||
yield runCommand('Reset Onboarding Wizard', function* () {
|
||||
const optionsToDelete = [
|
||||
'woocommerce_task_list_tracked_completed_tasks',
|
||||
'woocommerce_onboarding_profile',
|
||||
'_transient_wc_onboarding_themes',
|
||||
];
|
||||
yield apiFetch({
|
||||
method: 'DELETE',
|
||||
path: `${API_NAMESPACE}/options/${optionsToDelete.join(',')}`,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function* resetJetpackConnection() {
|
||||
yield runCommand('Reset Jetpack Connection', function* () {
|
||||
yield apiFetch({
|
||||
method: 'DELETE',
|
||||
path: `${API_NAMESPACE}/options/jetpack_options`,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function* enableTrackingDebug() {
|
||||
yield runCommand('Enable WC Admin Tracking Debug Mode', function* () {
|
||||
window.localStorage.setItem('debug', 'wc-admin:*');
|
||||
});
|
||||
}
|
||||
|
||||
export function* updateStoreAge() {
|
||||
yield runCommand('Update Installation timestamp', function* () {
|
||||
const today = new Date();
|
||||
const dd = String(today.getDate()).padStart(2, '0');
|
||||
const mm = String(today.getMonth() + 1).padStart(2, '0'); //January is 0!
|
||||
const yyyy = today.getFullYear();
|
||||
|
||||
// eslint-disable-next-line no-alert
|
||||
const numberOfDays = window.prompt(
|
||||
'Please enter a date in yyyy/mm/dd format',
|
||||
yyyy + '/' + mm + '/' + dd
|
||||
);
|
||||
|
||||
if (numberOfDays !== null) {
|
||||
const dates = numberOfDays.split('/');
|
||||
const newTimestamp = Math.round(
|
||||
new Date(dates[0], dates[1] - 1, dates[2]).getTime() / 1000
|
||||
);
|
||||
const payload = {
|
||||
woocommerce_admin_install_timestamp: JSON.parse(newTimestamp),
|
||||
};
|
||||
yield apiFetch({
|
||||
method: 'POST',
|
||||
path: '/wc-admin/options',
|
||||
headers: { 'content-type': 'application/json' },
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function* runWcAdminDailyJob() {
|
||||
yield runCommand('Run wc_admin_daily job', function* () {
|
||||
yield apiFetch({
|
||||
path: API_NAMESPACE + '/tools/run-wc-admin-daily/v1',
|
||||
method: 'POST',
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function* deleteAllProducts() {
|
||||
if (!confirm('Are you sure you want to delete all of the products?')) {
|
||||
return;
|
||||
}
|
||||
|
||||
yield runCommand('Delete all products', function* () {
|
||||
yield apiFetch({
|
||||
path: `${API_NAMESPACE}/tools/delete-all-products/v1`,
|
||||
method: 'POST',
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function* runSelectedCronJob(params) {
|
||||
yield runCommand('Run selected cron job', function* () {
|
||||
yield apiFetch({
|
||||
path: API_NAMESPACE + '/tools/run-wc-admin-daily/v1',
|
||||
method: 'POST',
|
||||
data: params,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function* runSelectedUpdateCallbacks(params) {
|
||||
yield runCommand('Run version update callbacks', function* () {
|
||||
yield apiFetch({
|
||||
path: API_NAMESPACE + '/tools/trigger-selected-update-callbacks/v1',
|
||||
method: 'POST',
|
||||
data: params,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function* runDisableEmail() {
|
||||
yield runCommand('Disable/Enable WooCommerce emails', function* () {
|
||||
const response = yield apiFetch({
|
||||
path: `${API_NAMESPACE}/tools/toggle-emails/v1`,
|
||||
method: 'POST',
|
||||
});
|
||||
yield setIsEmailDisabled( response );
|
||||
});
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
export const STORE_KEY = 'wc-admin-helper/tools';
|
||||
export const API_NAMESPACE = '/wc-admin-test-helper';
|
|
@ -1,22 +0,0 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { registerStore } from '@wordpress/data';
|
||||
import { controls } from '@wordpress/data-controls';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import * as actions from './actions';
|
||||
import * as resolvers from './resolvers';
|
||||
import * as selectors from './selectors';
|
||||
import reducer from './reducer';
|
||||
import { STORE_KEY } from './constants';
|
||||
|
||||
export default registerStore( STORE_KEY, {
|
||||
actions,
|
||||
selectors,
|
||||
resolvers,
|
||||
controls,
|
||||
reducer,
|
||||
} );
|
|
@ -1,88 +0,0 @@
|
|||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import TYPES from './action-types';
|
||||
|
||||
const DEFAULT_STATE = {
|
||||
currentlyRunning: {},
|
||||
errorMessages: [],
|
||||
cronJobs: false,
|
||||
isEmailDisabled: '',
|
||||
messages: {},
|
||||
params: [],
|
||||
status: '',
|
||||
dbUpdateVersions: [],
|
||||
};
|
||||
|
||||
const reducer = ( state = DEFAULT_STATE, action ) => {
|
||||
switch ( action.type ) {
|
||||
case TYPES.ADD_MESSAGE:
|
||||
if ( ! action.status ) {
|
||||
action.status = 'info';
|
||||
}
|
||||
return {
|
||||
...state,
|
||||
messages: {
|
||||
...state.messages,
|
||||
[ action.source ]: {
|
||||
message: action.message,
|
||||
status: action.status,
|
||||
},
|
||||
},
|
||||
};
|
||||
case TYPES.REMOVE_MESSAGE:
|
||||
const messages = { ...state.messages };
|
||||
delete messages[ action.source ];
|
||||
return {
|
||||
...state,
|
||||
messages,
|
||||
};
|
||||
case TYPES.SET_STATUS:
|
||||
return {
|
||||
...state,
|
||||
status: action.status,
|
||||
};
|
||||
case TYPES.ADD_CURRENTLY_RUNNING:
|
||||
return {
|
||||
...state,
|
||||
currentlyRunning: {
|
||||
...state,
|
||||
[ action.command ]: true,
|
||||
},
|
||||
};
|
||||
case TYPES.REMOVE_CURRENTLY_RUNNING:
|
||||
return {
|
||||
...state,
|
||||
currentlyRunning: {
|
||||
...state,
|
||||
[ action.command ]: false,
|
||||
},
|
||||
};
|
||||
case TYPES.SET_CRON_JOBS:
|
||||
return {
|
||||
...state,
|
||||
cronJobs: action.cronJobs,
|
||||
};
|
||||
case TYPES.IS_EMAIL_DISABLED:
|
||||
return {
|
||||
...state,
|
||||
isEmailDisabled: action.isEmailDisabled,
|
||||
};
|
||||
case TYPES.ADD_COMMAND_PARAMS:
|
||||
return {
|
||||
...state,
|
||||
params: {
|
||||
[ action.source ]: action.params,
|
||||
},
|
||||
};
|
||||
case TYPES.SET_DB_UPDATE_VERSIONS:
|
||||
return {
|
||||
...state,
|
||||
dbUpdateVersions: action.versions,
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export default reducer;
|
|
@ -1,57 +0,0 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { apiFetch } from '@wordpress/data-controls';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { API_NAMESPACE } from './constants';
|
||||
import {
|
||||
setCronJobs,
|
||||
setDBUpdateVersions,
|
||||
setIsEmailDisabled,
|
||||
} from './actions';
|
||||
|
||||
export function* getCronJobs() {
|
||||
const path = `${ API_NAMESPACE }/tools/get-cron-list/v1`;
|
||||
|
||||
try {
|
||||
const response = yield apiFetch( {
|
||||
path,
|
||||
method: 'GET',
|
||||
} );
|
||||
yield setCronJobs( response );
|
||||
} catch ( error ) {
|
||||
throw new Error( error );
|
||||
}
|
||||
}
|
||||
|
||||
export function* getDBUpdateVersions() {
|
||||
const path = `${API_NAMESPACE}/tools/get-update-versions/v1`;
|
||||
|
||||
try {
|
||||
const response = yield apiFetch({
|
||||
path,
|
||||
method: 'GET',
|
||||
});
|
||||
yield setDBUpdateVersions(response);
|
||||
} catch (error) {
|
||||
throw new Error(error);
|
||||
}
|
||||
}
|
||||
|
||||
export function* getIsEmailDisabled() {
|
||||
const path = `${API_NAMESPACE}/tools/get-email-status/v1`;
|
||||
|
||||
try {
|
||||
const response = yield apiFetch( {
|
||||
path,
|
||||
method: 'GET',
|
||||
} );
|
||||
yield setIsEmailDisabled( response );
|
||||
} catch ( error ) {
|
||||
yield setIsEmailDisabled( 'error' );
|
||||
throw new Error( error );
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
export function getCurrentlyRunning( state ) {
|
||||
return state.currentlyRunning;
|
||||
}
|
||||
|
||||
export function getMessages( state ) {
|
||||
return state.messages;
|
||||
}
|
||||
|
||||
export function getStatus( state ) {
|
||||
return state.status;
|
||||
}
|
||||
|
||||
export function getCommandParams( state ) {
|
||||
return state.params;
|
||||
}
|
||||
|
||||
export function getCronJobs( state ) {
|
||||
return state.cronJobs;
|
||||
}
|
||||
|
||||
export function getIsEmailDisabled( state ) {
|
||||
return state.isEmailDisabled;
|
||||
}
|
||||
|
||||
export function getDBUpdateVersions(state) {
|
||||
return state.dbUpdateVersions;
|
||||
}
|
|
@ -1,87 +0,0 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { withDispatch, withSelect } from '@wordpress/data';
|
||||
import { compose } from '@wordpress/compose';
|
||||
import { Notice, Button } from '@wordpress/components';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { default as commands } from './commands';
|
||||
import { STORE_KEY } from './data/constants';
|
||||
import './data';
|
||||
|
||||
function Tools( { actions, currentlyRunningCommands, messages, comandParams } ) {
|
||||
actions = actions();
|
||||
return (
|
||||
<div id="wc-admin-test-helper-tools">
|
||||
<h2>Tools</h2>
|
||||
<p>This section contains miscellaneous tools.</p>
|
||||
{ Object.keys( messages ).map( ( key ) => {
|
||||
return (
|
||||
<Notice
|
||||
status={ messages[ key ].status }
|
||||
key={ key }
|
||||
isDismissible={ false }
|
||||
>
|
||||
{ key }: { messages[ key ].message }
|
||||
</Notice>
|
||||
);
|
||||
} ) }
|
||||
<table className="tools wp-list-table striped table-view-list widefat">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Command</th>
|
||||
<th>Description</th>
|
||||
<th>Run</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{ commands.map( ( { action, command, description }, index ) => {
|
||||
const params = comandParams[ action ] ?? false;
|
||||
return (
|
||||
<tr key={ index }>
|
||||
<td className="command">{ command }</td>
|
||||
<td>{ description }</td>
|
||||
<td>
|
||||
<Button
|
||||
onClick={ () => actions[ action ]( params ) }
|
||||
disabled={
|
||||
currentlyRunningCommands[
|
||||
command
|
||||
]
|
||||
}
|
||||
isPrimary
|
||||
>
|
||||
Run
|
||||
</Button>
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
} ) }
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default compose(
|
||||
withSelect( ( select ) => {
|
||||
const { getCurrentlyRunning, getMessages, getCommandParams } = select( STORE_KEY );
|
||||
return {
|
||||
currentlyRunningCommands: getCurrentlyRunning(),
|
||||
messages: getMessages(),
|
||||
comandParams: getCommandParams(),
|
||||
};
|
||||
} ),
|
||||
withDispatch( ( dispatch ) => {
|
||||
const actions = function () {
|
||||
return dispatch( STORE_KEY );
|
||||
};
|
||||
|
||||
return {
|
||||
actions,
|
||||
};
|
||||
} )
|
||||
)( Tools );
|
Loading…
Reference in New Issue