Remove duplicated src folder/

This commit is contained in:
Paul Sealock 2022-06-08 13:31:16 +12:00
parent 979fa45627
commit 2dce7b5bba
55 changed files with 0 additions and 2836 deletions

View File

@ -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>
</>
);
};

View File

@ -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/>
</>
);
};

View File

@ -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>
</>
);
};

View File

@ -1 +0,0 @@
export { AdminNotes } from './admin-notes.js';

View File

@ -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>
);
}

View File

@ -1 +0,0 @@
export { App } from './app';

View File

@ -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&apos;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 );

View File

@ -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;

View File

@ -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,
};
}

View File

@ -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';

View File

@ -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,
} );

View File

@ -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;

View File

@ -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();
}
}

View File

@ -1,3 +0,0 @@
export function getExperiments( state ) {
return state.experiments;
}

View File

@ -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&nbsp;
<a
target="_blank"
href={
wcSettings.adminUrl +
'/admin.php?page=wc-settings&tab=advanced&section=woocommerce_com'
}
rel="noreferrer"
>
WooCommerce &#8594; Settings &#8594; Advanced &#8594;
Woocommerce.com
</a>
&nbsp;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 );

View File

@ -1,7 +0,0 @@
const TYPES = {
TOGGLE_FEATURE: 'TOGGLE_FEATURE',
SET_FEATURES: 'SET_FEATURES',
SET_MODIFIED_FEATURES: 'SET_MODIFIED_FEATURES',
};
export default TYPES;

View File

@ -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,
};
}

View File

@ -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';

View File

@ -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,
} );

View File

@ -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;

View File

@ -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();
}
}

View File

@ -1,7 +0,0 @@
export function getFeatures( state ) {
return state.features;
}
export function getModifiedFeatures( state ) {
return state.modifiedFeatures;
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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;

View File

@ -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();
}
}

View File

@ -1,2 +0,0 @@
export const STORE_KEY = 'wc-admin-helper/options';
export const API_NAMESPACE = '/wc-admin-test-helper';

View File

@ -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,
} );

View File

@ -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;

View File

@ -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 );
}
}

View File

@ -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;
}

View File

@ -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 );

View File

@ -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;

View File

@ -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();
}
}

View File

@ -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';

View File

@ -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,
} );

View File

@ -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;

View File

@ -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();
}
}

View File

@ -1,7 +0,0 @@
export function getFilters( state ) {
return state.filters;
}
export function isLoading( state ) {
return state.isLoading;
}

View File

@ -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 );

View File

@ -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.

View File

@ -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>
);
};

View File

@ -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,
},
];

View File

@ -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>
);
};

View File

@ -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>
);
};

View File

@ -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;

View File

@ -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 );
});
}

View File

@ -1,2 +0,0 @@
export const STORE_KEY = 'wc-admin-helper/tools';
export const API_NAMESPACE = '/wc-admin-test-helper';

View File

@ -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,
} );

View File

@ -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;

View File

@ -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 );
}
}

View File

@ -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;
}

View File

@ -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 );