refactor(v4): Base admin migration

pull/25/head
Boaz Poolman 2021-10-14 17:13:12 +02:00
parent 75711f7da4
commit 41a9bfdf6c
15 changed files with 198 additions and 233 deletions

View File

@ -2,7 +2,8 @@ import React, { useState } from 'react';
import styled from 'styled-components';
import { useDispatch } from 'react-redux';
import { isEmpty } from 'lodash';
import { Button } from '@buffetjs/core';
import { Button } from '@strapi/parts/Button';
import ConfirmModal from '../ConfirmModal';
import { exportAllConfig, importAllConfig } from '../../state/actions/Config';
@ -23,8 +24,8 @@ const ActionButtons = ({ diff }) => {
return (
<ActionButtonsStyling>
<Button disabled={isEmpty(diff.diff)} color="primary" label="Import" onClick={() => openModal('import')} />
<Button disabled={isEmpty(diff.diff)} color="primary" label="Export" onClick={() => openModal('export')} />
<Button disabled={isEmpty(diff.diff)} label="Import" onClick={() => openModal('import')} />
<Button disabled={isEmpty(diff.diff)} label="Export" onClick={() => openModal('export')} />
{!isEmpty(diff.diff) && (
<h4 style={{ display: 'inline' }}>{Object.keys(diff.diff).length} {Object.keys(diff.diff).length === 1 ? "config change" : "config changes"}</h4>
)}
@ -32,11 +33,11 @@ const ActionButtons = ({ diff }) => {
isOpen={modalIsOpen}
onClose={closeModal}
type={actionType}
onSubmit={() => actionType === 'import' ? dispatch(importAllConfig()) : dispatch(exportAllConfig())}
onSubmit={actionType === 'import' ? dispatch(importAllConfig()) : dispatch(exportAllConfig())}
/>
</ActionButtonsStyling>
);
}
};
const ActionButtonsStyling = styled.div`
padding: 10px 0 20px 0;

View File

@ -1,48 +1,41 @@
import React from 'react';
import ReactDiffViewer, { DiffMethod } from 'react-diff-viewer';
import { AttributeIcon } from '@buffetjs/core';
import {
HeaderModal,
HeaderModalTitle,
Modal,
ModalBody,
ModalFooter,
} from 'strapi-helper-plugin';
import { ModalLayout, ModalFooter, ModalBody, ModalHeader } from '@strapi/parts/ModalLayout';
import { ButtonText } from '@strapi/parts/Text';
import { Button } from '@strapi/parts/Button';
const ConfigDiff = ({ isOpen, onClose, onToggle, oldValue, newValue, configName }) => {
if (!isOpen) {
return null;
}
return (
<Modal
isOpen={isOpen}
onClosed={onClose}
onToggle={onToggle}
<ModalLayout
onClose={onClose}
labelledBy="title"
>
<HeaderModal>
<section style={{ alignItems: 'center' }}>
<AttributeIcon type='enum' />
<HeaderModalTitle style={{ marginLeft: 15 }}>Config changes for {configName}</HeaderModalTitle>
</section>
</HeaderModal>
<ModalBody style={{
paddingTop: '0.5rem',
paddingBottom: '3rem'
}}>
<div className="container-fluid">
<ModalHeader>
<ButtonText textColor="neutral800" as="h2" id="title">
Config changes for {configName}
</ButtonText>
</ModalHeader>
<ModalBody>
<section style={{ marginTop: 20 }}>
<ReactDiffViewer
oldValue={JSON.stringify(oldValue, null, 2)}
newValue={JSON.stringify(newValue, null, 2)}
splitView={true}
splitView
compareMethod={DiffMethod.WORDS}
/>
</section>
</div>
</ModalBody>
<ModalFooter>
<section style={{ alignItems: 'center' }}>
</section>
</ModalFooter>
</Modal>
</ModalLayout>
);
}

View File

@ -4,24 +4,8 @@ import styled from 'styled-components';
const CustomRow = ({ row }) => {
const { config_name, config_type, state, onClick } = row;
return (
<tr onClick={() => onClick(config_type, config_name)}>
<td>
<p>{config_name}</p>
</td>
<td>
<p>{config_type}</p>
</td>
<td>
<p style={stateStyle(state)}>{state}</p>
</td>
</tr>
);
};
const stateStyle = (state) => {
let style = {
const stateStyle = (state) => {
const style = {
display: 'inline-flex',
padding: '0 10px',
borderRadius: '12px',
@ -46,7 +30,21 @@ const stateStyle = (state) => {
}
return style;
};
return (
<tr onClick={() => onClick(config_type, config_name)}>
<td>
<p>{config_name}</p>
</td>
<td>
<p>{config_type}</p>
</td>
<td>
<p style={stateStyle(state)}>{state}</p>
</td>
</tr>
);
};
export default CustomRow
export default CustomRow;

View File

@ -1,25 +1,17 @@
import React, { useState, useEffect } from 'react';
import { Table } from '@buffetjs/core';
import { isEmpty } from 'lodash';
import { NoContent } from '@strapi/helper-plugin';
import AddIcon from '@strapi/icons/AddIcon';
import { VisuallyHidden } from '@strapi/parts/VisuallyHidden';
import { Table, Thead, Tbody, Tr, Th, TFooter } from '@strapi/parts/Table';
import { TableLabel } from '@strapi/parts/Text';
import { Button } from '@strapi/parts/Button';
import ConfigDiff from '../ConfigDiff';
import FirstExport from '../FirstExport';
import ConfigListRow from './ConfigListRow';
const headers = [
{
name: 'Config name',
value: 'config_name',
},
{
name: 'Config type',
value: 'config_type',
},
{
name: 'State',
value: 'state',
},
];
const ConfigList = ({ diff, isLoading }) => {
const [openModal, setOpenModal] = useState(false);
const [originalConfig, setOriginalConfig] = useState({});
@ -81,7 +73,7 @@ const ConfigList = ({ diff, isLoading }) => {
};
if (!isLoading && !isEmpty(diff.message)) {
return <FirstExport />
return <FirstExport />;
}
return (
@ -94,15 +86,28 @@ const ConfigList = ({ diff, isLoading }) => {
onToggle={closeModal}
configName={configName}
/>
<Table
headers={headers}
customRow={ConfigListRow}
rows={!isLoading ? rows : []}
isLoading={isLoading}
tableEmptyText="No config changes. You are up to date!"
/>
<Table colCount={4} rowCount={rows.length + 1}>
<Thead>
<Tr>
<Th>
<TableLabel>Config name</TableLabel>
</Th>
<Th>
<TableLabel>Config type</TableLabel>
</Th>
<Th>
<TableLabel>State</TableLabel>
</Th>
</Tr>
</Thead>
<Tbody>
{rows.map((row) => (
<ConfigListRow key={row.name} {...row} />
))}
</Tbody>
</Table>
</div>
);
}
};
export default ConfigList;

View File

@ -1,7 +1,12 @@
import React from 'react';
import {
ModalConfirm,
} from 'strapi-helper-plugin';
import { Dialog, DialogBody, DialogFooter } from '@strapi/parts/Dialog';
import { Row } from '@strapi/parts/Row';
import { Text } from '@strapi/parts/Text';
import { Stack } from '@strapi/parts/Stack';
import { Button } from '@strapi/parts/Button';
import DeleteIcon from '@strapi/icons/DeleteIcon';
import AlertWarningIcon from '@strapi/icons/AlertWarningIcon';
import getTrad from '../../helpers/getTrad';
@ -9,26 +14,37 @@ const ConfirmModal = ({ isOpen, onClose, onSubmit, type }) => {
if (!isOpen) return null;
return (
<ModalConfirm
confirmButtonLabel={{
id: getTrad(`popUpWarning.button.${type}`),
}}
<Dialog
onClose={onClose}
title="Confirmation"
isOpen={isOpen}
toggle={onClose}
onClosed={onClose}
onConfirm={() => {
>
<DialogBody icon={<AlertWarningIcon />}>
<Stack size={2}>
<Row justifyContent="center">
<Text id="confirm-description">{getTrad(`popUpWarning.warning.${type}`)}</Text>
</Row>
</Stack>
</DialogBody>
<DialogFooter
startAction={(
<Button
onClick={() => {
onClose();
onSubmit();
}}
type="success"
content={{
id: getTrad(`popUpWarning.warning.${type}`),
values: {
br: () => <br />,
},
}}
/>
variant="tertiary"
>
Cancel
</Button>
)}
endAction={(
<Button variant="danger-light" startIcon={<DeleteIcon />}>
{getTrad(`popUpWarning.button.${type}`)}
</Button>
)} />
</Dialog>
);
}
};
export default ConfirmModal;

View File

@ -1,24 +0,0 @@
import styled from 'styled-components';
const ContainerFluid = styled.div`
padding: 18px 30px;
> div:first-child {
max-height: 33px;
}
.buttonOutline {
height: 30px;
padding: 0 15px;
border: 1px solid #dfe0e1;
font-weight: 500;
font-size: 13px;
&:before {
margin-right: 10px;
content: '\f08e';
font-family: 'FontAwesome';
font-size: 10px;
}
}
`;
export default ContainerFluid;

View File

@ -1,6 +1,8 @@
import React, { useState } from 'react';
import { useDispatch } from 'react-redux';
import { Button } from '@buffetjs/core';
import { Button } from '@strapi/parts/Button';
import { exportAllConfig } from '../../state/actions/Config';
import ConfirmModal from '../ConfirmModal';
@ -20,14 +22,18 @@ const FirstExport = () => {
<ConfirmModal
isOpen={modalIsOpen}
onClose={() => setModalIsOpen(false)}
type={'export'}
type="export"
onSubmit={() => dispatch(exportAllConfig())}
/>
<h3>Looks like this is your first time using config-sync for this project.</h3>
<p>Make the initial export!</p>
<Button color="primary" onClick={() => setModalIsOpen(true)}>Initial export</Button>
<Button
onClick={() => setModalIsOpen(true)}
>
Initial export
</Button>
</div>
);
}
};
export default FirstExport;

View File

@ -5,21 +5,22 @@
*/
import React, { memo } from 'react';
import { Header } from '@buffetjs/custom';
import { useGlobalContext } from 'strapi-helper-plugin';
import { useIntl } from 'react-intl';
import { HeaderLayout } from '@strapi/parts/Layout';
import { Box } from '@strapi/parts/Box';
const HeaderComponent = () => {
const { formatMessage } = useGlobalContext();
const headerProps = {
title: {
label: formatMessage({ id: 'config-sync.Header.Title' }),
},
content: formatMessage({ id: 'config-sync.Header.Description' }),
};
const { formatMessage } = useIntl();
return (
<Header {...headerProps} />
<Box background="neutral100">
<HeaderLayout
title={formatMessage({ id: 'config-sync.Header.Title' })}
subtitle={formatMessage({ id: 'config-sync.Header.Description' })}
as="h2"
/>
</Box>
);
};

View File

@ -1 +1 @@
export const __DEBUG__ = strapi.env === 'development';
export const __DEBUG__ = true; // TODO: set actual env.

View File

@ -1,8 +0,0 @@
const config = {
blacklist: [
'REDUX_STORAGE_SAVE',
'REDUX_STORAGE_LOAD',
],
};
export default config;

View File

@ -7,7 +7,6 @@
import React from 'react';
import { Provider } from 'react-redux';
import ContainerFluid from '../../components/Container';
import Header from '../../components/Header';
import { store } from "../../helpers/configureStore";
@ -16,10 +15,8 @@ import ConfigPage from '../ConfigPage';
const App = () => {
return (
<Provider store={store}>
<ContainerFluid>
<Header />
<ConfigPage />
</ContainerFluid>
</Provider>
);
};

View File

@ -21,6 +21,6 @@ const ConfigPage = () => {
<ConfigList isLoading={isLoading} diff={configDiff.toJS()} />
</div>
);
}
};
export default ConfigPage;

View File

@ -1,14 +1,12 @@
import { createStore, applyMiddleware, compose } from 'redux';
import { createLogger } from 'redux-logger';
import thunkMiddleware from 'redux-thunk';
import { Map } from 'immutable';
import rootReducer from '../state/reducers';
import loggerConfig from '../config/logger';
import { __DEBUG__ } from '../config/constants';
const configureStore = () => {
let initialStoreState = Map();
const initialStoreState = Map();
const enhancers = [];
const middlewares = [
@ -27,24 +25,6 @@ const configureStore = () => {
if (devtools) {
console.info('[setup] ✓ Enabling Redux DevTools Extension');
}
console.info('[setup] ✓ Enabling state logger');
const loggerMiddleware = createLogger({
level: 'info',
collapsed: true,
stateTransformer: (state) => state.toJS(),
predicate: (getState, action) => {
const state = getState();
const showBlacklisted = state.getIn(['debug', 'logs', 'blacklisted']);
if (loggerConfig.blacklist.indexOf(action.type) !== -1 && !showBlacklisted) {
return false;
}
return state.getIn(['debug', 'logs', 'enabled']);
},
});
middlewares.push(loggerMiddleware);
}
const composedEnhancers = devtools || compose;

View File

@ -4,7 +4,7 @@
*
*/
import { request } from 'strapi-helper-plugin';
import { request } from '@strapi/helper-plugin';
import { Map } from 'immutable';
export function getAllConfigDiff() {
@ -14,11 +14,11 @@ export function getAllConfigDiff() {
const configDiff = await request('/config-sync/diff', { method: 'GET' });
dispatch(setConfigDiffInState(configDiff));
dispatch(setLoadingState(false));
} catch(err) {
} catch (err) {
strapi.notification.error('notification.error');
dispatch(setLoadingState(false));
}
}
};
}
export const SET_CONFIG_DIFF_IN_STATE = 'SET_CONFIG_DIFF_IN_STATE';
@ -38,11 +38,11 @@ export function exportAllConfig() {
strapi.notification.success(message);
dispatch(setLoadingState(false));
} catch(err) {
} catch (err) {
strapi.notification.error('notification.error');
dispatch(setLoadingState(false));
}
}
};
}
export function importAllConfig() {
@ -54,11 +54,11 @@ export function importAllConfig() {
strapi.notification.success(message);
dispatch(setLoadingState(false));
} catch(err) {
} catch (err) {
strapi.notification.error('notification.error');
dispatch(setLoadingState(false));
}
}
};
}
export const SET_LOADING_STATE = 'SET_LOADING_STATE';

View File

@ -1006,9 +1006,9 @@
integrity sha512-XmdEOrKQ8a1Y/yxQFOMbC47G/V2VDO1GvMRnl4O75M4GW/abC5tnfzadQYkqEveqRM1dEJGFFegfPNA2vvx2iw==
"@types/node@*":
version "16.10.5"
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.10.5.tgz#7fe4123b061753f1a58a6cd077ff0bb069ee752d"
integrity sha512-9iI3OOlkyOjLQQ9s+itIJNMRepDhB/96jW3fqduJ2FTPQj1dJjw6Q3QCImF9FE1wmdBs5QSun4FjDSFS8d8JLw==
version "16.10.9"
resolved "https://registry.yarnpkg.com/@types/node/-/node-16.10.9.tgz#8f1cdd517972f76a3b928298f4c0747cd6fef25a"
integrity sha512-H9ReOt+yqIJPCutkTYjFjlyK6WEMQYT9hLZMlWtOjFQY2ItppsWZ6RJf8Aw+jz5qTYceuHvFgPIaKOHtLAEWBw==
"@types/normalize-package-data@^2.4.0":
version "2.4.1"
@ -1041,9 +1041,9 @@
redux "^4.0.0"
"@types/react@*", "@types/react@16 || 17":
version "17.0.29"
resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.29.tgz#9535f3fc01a4981ce9cadcf0daa2593c0c2f2251"
integrity sha512-HSenIfBEBZ70BLrrVhtEtHpqaP79waauPtA8XKlczTxL3hXrW/ElGNLTpD1TmqkykgGlOAK55+D3SmUHEirpFw==
version "17.0.30"
resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.30.tgz#2f8e6f5ab6415c091cc5e571942ee9064b17609e"
integrity sha512-3Dt/A8gd3TCXi2aRe84y7cK1K8G+N9CZRDG8kDGguOKa0kf/ZkSwTmVIDPsm/KbQOVMaDJXwhBtuOXxqwdpWVg==
dependencies:
"@types/prop-types" "*"
"@types/scheduler" "*"
@ -1702,9 +1702,9 @@ camelize@^1.0.0:
integrity sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=
caniuse-lite@^1.0.30001265:
version "1.0.30001265"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001265.tgz#0613c9e6c922e422792e6fcefdf9a3afeee4f8c3"
integrity sha512-YzBnspggWV5hep1m9Z6sZVLOt7vrju8xWooFAgN6BA5qvy98qPAPb7vNUzypFaoh2pb3vlfzbDO8tB57UPGbtw==
version "1.0.30001267"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001267.tgz#b1cf2937175afc0570e4615fc2d2f9069fa0ed30"
integrity sha512-r1mjTzAuJ9W8cPBGbbus8E0SKcUP7gn03R14Wk8FlAlqhH9hroy9nLqmpuXlfKEw/oILW+FGz47ipXV2O7x8lg==
capture-exit@^2.0.0:
version "2.0.0"
@ -2254,9 +2254,9 @@ domexception@^2.0.1:
webidl-conversions "^5.0.0"
electron-to-chromium@^1.3.867:
version "1.3.867"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.867.tgz#7cb484db4b57c28da0b65c51e434c3a1f3f9aa0d"
integrity sha512-WbTXOv7hsLhjJyl7jBfDkioaY++iVVZomZ4dU6TMe/SzucV6mUAs2VZn/AehBwuZMiNEQDaPuTGn22YK5o+aDw==
version "1.3.868"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.868.tgz#ed835023b57ecf0ba63dfe7d50e16b53758ab1da"
integrity sha512-kZYCHqwJ1ctGrYDlOcWQH+/AftAm/KD4lEnLDNwS0kKwx1x6dU4zv+GuDjsPPOGn/2TjnKBaZjDyjXaoix0q/A==
elliptic@^6.5.3:
version "6.5.4"
@ -2463,9 +2463,9 @@ eslint-loader@2.1.1:
rimraf "^2.6.1"
eslint-module-utils@^2.7.0:
version "2.7.0"
resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.0.tgz#9e97c12688113401259b39d960e6a1f09f966435"
integrity sha512-hqSE88MmHl3ru9SYvDyGrlo0JwROlf9fiEMplEV7j/EAuq9iSlIlyCFbBT6pdULQBSnBYtYKiMLps+hKkyP7Gg==
version "2.7.1"
resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.1.tgz#b435001c9f8dd4ab7f6d0efcae4b9696d4c24b7c"
integrity sha512-fjoetBXQZq2tSTWZ9yWVl2KuFrTZZH3V+9iD1V1RfpDgxzJR+mPd/KZmMiA8gbPqdBzpNiEHOuT7IYEWxrH0zQ==
dependencies:
debug "^3.2.7"
find-up "^2.1.0"
@ -3656,9 +3656,9 @@ istanbul-lib-source-maps@^4.0.0:
source-map "^0.6.1"
istanbul-reports@^3.0.2:
version "3.0.4"
resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.0.4.tgz#5c38ce8136edf484c0fcfbf7514aafb0363ed1db"
integrity sha512-bFjUnc95rHjdCR63WMHUS7yfJJh8T9IPSWavvR02hhjVwezWALZ5axF9EqjmwZHpXqkzbgAMP8DmAtiyNxrdrQ==
version "3.0.5"
resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.0.5.tgz#a2580107e71279ea6d661ddede929ffc6d693384"
integrity sha512-5+19PlhnGabNWB7kOFnuxT8H3T/iIyQzIbQMxXsURmmvKg86P2sbkrGOT77VnHw0Qr0gc2XzRaRfMZYYbSQCJQ==
dependencies:
html-escaper "^2.0.0"
istanbul-lib-report "^3.0.0"