This commit is contained in:
soupette 2019-04-16 18:16:44 +02:00
parent d6e8ff0807
commit 6d31833a58
11 changed files with 568 additions and 297 deletions

View File

@ -22,6 +22,8 @@ module.exports = {
'content-type-builder': require('../../../strapi-plugin-content-type-builder/admin/src')
.default,
email: require('../../../strapi-plugin-email/admin/src').default,
'settings-manager': require('../../../strapi-plugin-settings-manager/admin/src')
.default,
'users-permissions': require('../../../strapi-plugin-users-permissions/admin/src')
.default,
};

View File

@ -1,4 +1,5 @@
.listContainer { /* stylelint-disable */
.listContainer {
/* stylelint-disable */
// margin-right: 15px;
}
@ -6,13 +7,13 @@
margin: 0 3.3rem;
padding: 1rem 2.8rem 0rem 2.8rem;
border-radius: 0.2rem;
background-color: #FFFFFF;
background-color: #ffffff;
box-shadow: 0 0.2rem 0.4rem 0 #E3E9F3;
box-shadow: 0 0.2rem 0.4rem 0 #e3e9f3;
}
.paddedTopList {
margin-top: 2.7rem!important;
margin-top: 2.7rem !important;
}
.flex {
@ -32,16 +33,15 @@
button {
&:focus {
outline: 0!important;
outline: 0 !important;
}
}
.ulContainer {
padding-top: 1.3rem;
margin: 0 3.3rem;
background-color: #FFFFFF;
box-shadow: 0 0.2rem 0.4rem 0 #E3E9F3;
background-color: #ffffff;
box-shadow: 0 0.2rem 0.4rem 0 #e3e9f3;
> ul {
list-style: none;
padding: 0;
@ -51,10 +51,10 @@ button {
font-size: 1.3rem;
position: relative;
&:first-child .language {
margin-top: .3rem;
margin-top: 0.3rem;
}
.language {
margin-top: .1rem;
margin-top: 0.1rem;
}
&:last-child .borderBottom {
border-bottom: none;
@ -62,14 +62,13 @@ button {
&:hover {
.hoveredLanguage {
position: absolute;
top: -.2rem;
top: -0.2rem;
min-height: 5.4rem;
width: 100%;
background-color: rgba(14,22,34,0.03);
background-color: rgba(14, 22, 34, 0.03);
}
}
}
}
}
@ -77,12 +76,11 @@ button {
margin-top: 11rem;
}
.liSpacer {
height: .2rem!important;
height: 0.2rem !important;
}
.borderBottom {
border-bottom: 1px solid rgba(14,22,34,0.04);
border-bottom: 1px solid rgba(14, 22, 34, 0.04);
}
.flexLi {
@ -126,7 +124,6 @@ button {
margin-left: 5rem;
color: #333740;
font-weight: 600;
}
.capitalized {
@ -147,7 +144,7 @@ button {
.italicText {
font-family: Lato;
color: #49515A;
color: #49515a;
font-style: italic;
height: 100%;
height: 5.2rem;
@ -157,49 +154,51 @@ button {
.normal {
font-family: Lato;
color: #1C5DE7;
color: #1c5de7;
cursor: pointer;
}
.primary {
height: 3rem;
font-family: Lato!important;
margin-left: 1.9rem!important;
font-family: Lato !important;
margin-left: 1.9rem !important;
cursor: pointer;
font-family: Lato;
&:focus {
outline: 0;
}
border: none!important;
border: none !important;
width: 15rem;
line-height: 1.6rem;
font-weight: 600;
border-radius: 3px;
background: linear-gradient(315deg, #0097F6 0%, #005EEA 100%);
background: linear-gradient(315deg, #0097f6 0%, #005eea 100%);
-webkit-font-smoothing: antialiased;
color: white!important;
&:active, &:focus, &:hover {
box-shadow: inset 1px 1px 3px rgba(0,0,0,.15);
background: linear-gradient(315deg, #0097F6 0%, #005EEA 100%);
color: white !important;
&:active,
&:focus,
&:hover {
box-shadow: inset 1px 1px 3px rgba(0, 0, 0, 0.15);
background: linear-gradient(315deg, #0097f6 0%, #005eea 100%);
background-color: transparent;
border: none;
}
}
}
.secondary {
font-family: Lato;
color: #F64D0A;
border: 0.1rem solid #F64D0A;
color: #f64d0a;
border: 0.1rem solid #f64d0a;
background-color: transparent;
cursor: pointer;
&:hover {
color: #F64D0A;
color: #f64d0a;
background-color: white;
border: 0.1rem solid #F64D0A;
border: 0.1rem solid #f64d0a;
}
height: 3rem;
color: #F64D0A;
border: 0.1rem solid #F64D0A;
color: #f64d0a;
border: 0.1rem solid #f64d0a;
position: relative;
border-radius: 3px;
}
@ -207,7 +206,7 @@ button {
.bordered {
margin-left: 3rem;
margin-right: 3rem;
border-bottom: 1px solid #F6F6F6;
border-bottom: 1px solid #f6f6f6;
}
.dbHost {
@ -220,20 +219,20 @@ button {
}
.noBorder {
border: none!important;
border: none !important;
}
.flexStart {
justify-content: flex-start!important;
justify-content: flex-start !important;
}
.italicText {
color: #49515A;
color: #49515a;
font-style: italic;
}
.normal {
color: #1C5DE7;
color: #1c5de7;
padding-top: 0rem;
}
@ -241,11 +240,10 @@ button {
color: #333740;
}
.modalPosition {
margin-top: 22rem ;
margin-top: 22rem;
margin-left: 25%;
width: 50%!important;
width: 50% !important;
max-width: none;
position: relative;
}
@ -253,14 +251,13 @@ button {
.modalBody {
padding: 0;
padding-top: 2.1rem;
}
.modalFooter {
margin-top: 1.5rem;
margin-bottom: 1.1rem;
> input {
margin-top: 1.3rem!important;
margin-top: 1.3rem !important;
}
> button {
&:focus {
@ -274,20 +271,20 @@ button {
padding-bottom: 1rem;
> h4 {
font-family: Lato;
font-weight: bold!important;
font-size: 1.8rem!important;
font-weight: bold !important;
font-size: 1.8rem !important;
}
> button {
z-index: 999;
margin-top: -2rem;
margin-right: -1.5rem;
color: #C3C5C8;
color: #c3c5c8;
opacity: 1;
font-size: 1.8rem;
font-weight: 100;
&:hover, &:focus {
color: #C3C5C8;
&:hover,
&:focus {
color: #c3c5c8;
opacity: 1;
outline: 0;
}
@ -305,23 +302,21 @@ button {
}
}
.squared {
margin-top: 1.6rem;
height: 2rem;
width: 3.5rem;
padding-top: .2rem;
padding-top: 0.2rem;
border-radius: 2px;
color: #FFFFFF;
color: #ffffff;
font-size: 1.3rem;
font-weight: 400;
line-height: 1.6rem;
text-align: center;
}
.orange {
background-color: #FFB500;
background-color: #ffb500;
}
.leftSpaced {
@ -343,28 +338,25 @@ button {
.spacer {
height: 1.6rem;
margin-bottom: 1.6rem;
border-bottom: 1px solid rgba(14,22,34,0.04);
border-bottom: 1px solid rgba(14, 22, 34, 0.04);
}
.spacerSmall {
margin-bottom: 0rem;
}
.ico {
color: #0E1622;
color: #0e1622;
> i {
font-size: 1.1rem;
}
}
.databaseFont {
font-size: 1.3rem;
&:hover {
height: 5.2rem;
background-color: rgba(14,22,34,0.03);
background-color: rgba(14, 22, 34, 0.03);
}
}
@ -375,27 +367,26 @@ button {
line-height: 5.2rem;
> div:first-of-type {
background-image: url('assets/images/unknow_flag.png');
background-image: url('../../assets/images/unknow_flag.png');
background-size: 1.3333em auto;
background-position: left center;
}
}
.bottomSpacer {
height: 1.2rem;
background-color: #FFFFFF;
background-color: #ffffff;
}
@keyframes blink {
0% {
opacity: .2;
opacity: 0.2;
}
20% {
opacity: 1;
}
100% {
opacity: .2;
opacity: 0.2;
}
}
@ -403,7 +394,6 @@ button {
margin-top: -1.2rem;
}
.saving span {
animation-name: blink;
animation-duration: 1.4s;
@ -413,9 +403,9 @@ button {
}
.saving span:nth-child(2) {
animation-delay: .2s;
animation-delay: 0.2s;
}
.saving span:nth-child(3) {
animation-delay: .4s;
animation-delay: 0.4s;
}

View File

@ -1,8 +1,8 @@
/**
*
* RowDatabase
*
*/
*
* RowDatabase
*
*/
import React from 'react';
import PropTypes from 'prop-types';
@ -10,13 +10,14 @@ import { FormattedMessage } from 'react-intl';
// modal
import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap';
import PopUpWarning from 'components/PopUpWarning';
import { PopUpWarning } from 'strapi-helper-plugin';
import PopUpForm from '../PopUpForm';
import styles from '../List/styles.scss';
/* eslint-disable react/require-default-props */
class RowDatabase extends React.Component { // eslint-disable-line react/prefer-stateless-function
class RowDatabase extends React.Component {
// eslint-disable-line react/prefer-stateless-function
constructor(props) {
super(props);
this.state = {
@ -29,69 +30,114 @@ class RowDatabase extends React.Component { // eslint-disable-line react/prefer-
deleteDatabase = () => {
this.setState({ warning: !this.state.warning });
this.props.onDeleteDatabase(this.props.data.name);
}
};
handleShowDatabaseModal = (e) => {
handleShowDatabaseModal = e => {
if (e.target.id !== 'trash') {
this.setState({ modal: !this.state.modal });
this.props.getDatabase(this.props.data.name);
}
}
};
handleSubmit = (e) => {
handleSubmit = e => {
e.preventDefault();
this.setState({ modal: false });
this.props.onSubmit(this.props.data.name);
}
};
handleToggle = () => {
this.setState({ modal: !this.state.modal });
}
};
handleToggleWarning = () => this.setState({ warning: !this.state.warning });
toggle = () => {
this.setState({ modal: !this.state.modal });
}
};
toggleWarning = () => this.setState({ warning: !this.state.warning });
render() {
const content = {
message: this.props.data.isUsed ? 'settings-manager.popUpWarning.databases.danger.message' : 'settings-manager.popUpWarning.databases.delete.message',
confirm: this.props.data.isUsed ? 'settings-manager.popUpWarning.danger.ok.message' : '',
message: this.props.data.isUsed
? 'settings-manager.popUpWarning.databases.danger.message'
: 'settings-manager.popUpWarning.databases.delete.message',
confirm: this.props.data.isUsed
? 'settings-manager.popUpWarning.danger.ok.message'
: '',
};
const loader = this.state.loader
? <Button onClick={this.handleSubmit} className={styles.primary} disabled={this.state.loader}><p className={styles.saving}><span>.</span><span>.</span><span>.</span></p></Button>
: (
<FormattedMessage id="settings-manager.form.button.save">
{(message) => (
<Button onClick={this.handleSubmit} className={styles.primary}>{message}</Button>
)}
</FormattedMessage>
);
const loader = this.state.loader ? (
<Button
onClick={this.handleSubmit}
className={styles.primary}
disabled={this.state.loader}
>
<p className={styles.saving}>
<span>.</span>
<span>.</span>
<span>.</span>
</p>
</Button>
) : (
<FormattedMessage id="settings-manager.form.button.save">
{message => (
<Button onClick={this.handleSubmit} className={styles.primary}>
{message}
</Button>
)}
</FormattedMessage>
);
return (
<li className={`${styles.databaseFont}`} style={{ cursor: 'pointer'}} onClick={this.handleShowDatabaseModal}>
<li
className={`${styles.databaseFont}`}
style={{ cursor: 'pointer' }}
onClick={this.handleShowDatabaseModal}
>
<div className={styles.flexLi}>
<div className={styles.flexed}>
<div className={styles.squared} style={{ backgroundColor: this.props.data.color }}>
<div
className={styles.squared}
style={{ backgroundColor: this.props.data.color }}
>
{this.props.data.letter}
</div>
<div className={styles.label} style={{ fontWeight: '500'}}>{this.props.data.name}</div>
<div className={styles.label} style={{ fontWeight: '500' }}>
{this.props.data.name}
</div>
</div>
<div className={styles.dbHost}>
{this.props.data.host}
<div className={styles.dbHost}>{this.props.data.host}</div>
<div className={styles.centered} style={{ width: '15rem' }}>
{this.props.data.database}
</div>
<div className={styles.centered} style={{ width: '15rem'}}>{this.props.data.database}</div>
<div className={styles.flexed} style={{ minWidth: '3rem', justifyContent: 'space-between'}}>
<div className={styles.ico}><i className="fa fa-pencil" id={this.props.data.name} /></div>
<div className={`${styles.leftSpaced} ${styles.ico}`}><i id="trash" className="fa fa-trash" onClick={this.handleToggleWarning} /></div>
<div
className={styles.flexed}
style={{ minWidth: '3rem', justifyContent: 'space-between' }}
>
<div className={styles.ico}>
<i className="fa fa-pencil" id={this.props.data.name} />
</div>
<div className={`${styles.leftSpaced} ${styles.ico}`}>
<i
id="trash"
className="fa fa-trash"
onClick={this.handleToggleWarning}
/>
</div>
</div>
</div>
<div>
<Modal isOpen={this.state.modal} toggle={this.toggle} className={styles.modalPosition}>
<ModalHeader toggle={this.toggle} className={`${styles.noBorder} ${styles.padded} ${styles.mHeader}`}>
<Modal
isOpen={this.state.modal}
toggle={this.toggle}
className={styles.modalPosition}
>
<ModalHeader
toggle={this.toggle}
className={`${styles.noBorder} ${styles.padded} ${
styles.mHeader
}`}
>
Databases
</ModalHeader>
<div className={styles.bordered} />
@ -100,10 +146,17 @@ class RowDatabase extends React.Component { // eslint-disable-line react/prefer-
<div className={styles.spacerSmall} />
<PopUpForm {...this.props} />
</ModalBody>
<ModalFooter className={`${styles.noBorder} ${styles.modalFooter}`}>
<ModalFooter
className={`${styles.noBorder} ${styles.modalFooter}`}
>
<FormattedMessage id="settings-manager.form.button.cancel">
{(message) => (
<Button onClick={this.handleToggle} className={styles.secondary}>{message}</Button>
{message => (
<Button
onClick={this.handleToggle}
className={styles.secondary}
>
{message}
</Button>
)}
</FormattedMessage>
{loader}
@ -115,7 +168,9 @@ class RowDatabase extends React.Component { // eslint-disable-line react/prefer-
<PopUpWarning
isOpen={this.state.warning}
toggleModal={this.toggleWarning}
onConfirm={this.props.data.isUsed ? this.toggleWarning : this.deleteDatabase}
onConfirm={
this.props.data.isUsed ? this.toggleWarning : this.deleteDatabase
}
content={content}
popUpWarningType={this.props.data.isUsed ? 'danger' : 'warning'}
onlyConfirmButton={this.props.data.isUsed}

View File

@ -1,8 +1,8 @@
/**
*
* RowLanguage
*
*/
*
* RowLanguage
*
*/
import React from 'react';
import PropTypes from 'prop-types';
@ -12,10 +12,11 @@ import { FormattedMessage } from 'react-intl';
// utils
import getFlag, { formatLanguageLocale } from '../../utils/getFlag';
import PopUpWarning from 'components/PopUpWarning';
import { PopUpWarning } from 'strapi-helper-plugin';
/* eslint-disable react/require-default-props */
class RowLanguage extends React.Component { // eslint-disable-line react/prefer-stateless-function
class RowLanguage extends React.Component {
// eslint-disable-line react/prefer-stateless-function
constructor(props) {
super(props);
this.state = {
@ -26,58 +27,100 @@ class RowLanguage extends React.Component { // eslint-disable-line react/prefer-
handleDeleteLanguage = () => {
this.setState({ showWarning: !this.state.showWarning });
this.props.onDeleteLanguage(this.props.name);
}
};
handleToggleWarning = () => this.setState({ showWarning: !this.state.showWarning });
handleToggleWarning = () =>
this.setState({ showWarning: !this.state.showWarning });
toggleWarning = () => this.setState({ showWarning: !this.state.showWarning });
render() {
// assign the target id the language name to prepare for delete
const deleteIcon = this.props.active ? '' : <i className="fa fa-trash" style={{ fontSize: '1.1rem', color: 'rgba(14,22,34,0.75)'}} onClick={this.handleToggleWarning} id={this.props.name} />; // eslint-disable-line jsx-a11y/no-static-element-interactions
const deleteIcon = this.props.active ? (
''
) : (
<i
className="fa fa-trash"
style={{ fontSize: '1.1rem', color: 'rgba(14,22,34,0.75)' }}
onClick={this.handleToggleWarning}
id={this.props.name}
/>
); // eslint-disable-line jsx-a11y/no-static-element-interactions
// format the locale to
const defaultLanguageArray = formatLanguageLocale(this.props.name);
const flag = getFlag(defaultLanguageArray);
// retrieve language name from i18n translation
const languageObject = find(get(this.props.listLanguages, ['sections', '0', 'items', '0', 'items']), ['value', join(defaultLanguageArray, '_')]);
const languageObject = find(
get(this.props.listLanguages, ['sections', '0', 'items', '0', 'items']),
['value', join(defaultLanguageArray, '_')],
);
// apply i18n
const languageDisplay = isObject(languageObject) ? <FormattedMessage {...{ id: `settings-manager.${languageObject.name}` }} /> : '';
const languageDisplay = isObject(languageObject) ? (
<FormattedMessage
{...{ id: `settings-manager.${languageObject.name}` }}
/>
) : (
''
);
const languageLabel = this.props.active
? (
<FormattedMessage id="settings-manager.list.languages.default.languages">
{(message) => (
<div className={this.props.liStyles.italicText} >
{message}
</div>
)}
</FormattedMessage>
)
: (
// set the span's id with the language name to retrieve it
<FormattedMessage id="settings-manager.list.languages.set.languages">
{(message) => (
<button className={this.props.liStyles.normal} onClick={this.props.onDefaultLanguageChange} id={this.props.name}>
{message}
</button>
)}
</FormattedMessage>
);
const languageLabel = this.props.active ? (
<FormattedMessage id="settings-manager.list.languages.default.languages">
{message => (
<div className={this.props.liStyles.italicText}>{message}</div>
)}
</FormattedMessage>
) : (
// set the span's id with the language name to retrieve it
<FormattedMessage id="settings-manager.list.languages.set.languages">
{message => (
<button
className={this.props.liStyles.normal}
onClick={this.props.onDefaultLanguageChange}
id={this.props.name}
>
{message}
</button>
)}
</FormattedMessage>
);
return (
<li style={{marginTop: '0'}}>
<li style={{ marginTop: '0' }}>
<div className={this.props.liStyles.hoveredLanguage} />
<div className={this.props.liStyles.language} />
<div className={`${this.props.liStyles.borderBottom} ${this.props.liStyles.flexLiLanguage}`}>
<div className={`${this.props.liStyles.flexed} ${this.props.liStyles.flagContainer}`}>
<div><span className={`${this.props.liStyles.flag} flag-icon flag-icon-${flag}`} /></div>
<div className={`${this.props.liStyles.label} ${this.props.liStyles.capitalized}`}>{languageDisplay}</div>
<div
className={`${this.props.liStyles.borderBottom} ${
this.props.liStyles.flexLiLanguage
}`}
>
<div
className={`${this.props.liStyles.flexed} ${
this.props.liStyles.flagContainer
}`}
>
<div>
<span
className={`${
this.props.liStyles.flag
} flag-icon flag-icon-${flag}`}
/>
</div>
<div
className={`${this.props.liStyles.label} ${
this.props.liStyles.capitalized
}`}
>
{languageDisplay}
</div>
</div>
<div className="text-center" style={{ width: '33%'}}>{this.props.name}</div>
<div style={{display:'flex', width: '33%'}}>
<div className="text-center" style={{ width: '33%' }}>
{this.props.name}
</div>
<div style={{ display: 'flex', width: '33%' }}>
<div className={this.props.liStyles.centered}>{languageLabel}</div>
<div className={this.props.liStyles.trashContainer}>{deleteIcon}</div>
<div className={this.props.liStyles.trashContainer}>
{deleteIcon}
</div>
</div>
</div>
<div>
@ -85,7 +128,9 @@ class RowLanguage extends React.Component { // eslint-disable-line react/prefer-
isOpen={this.state.showWarning}
toggleModal={this.toggleWarning}
onConfirm={this.handleDeleteLanguage}
content={{ message: 'settings-manager.popUpWarning.languages.delete.message' }}
content={{
message: 'settings-manager.popUpWarning.languages.delete.message',
}}
popUpWarningType="danger"
/>
</div>

View File

@ -33,7 +33,10 @@ class App extends React.Component {
}
componentWillUpdate(nextProps) {
if (!isEmpty(nextProps.sections) && nextProps.location.pathname !== '/plugins/settings-manager') {
if (
!isEmpty(nextProps.sections) &&
nextProps.location.pathname !== '/plugins/settings-manager'
) {
const allowedPaths = nextProps.sections.reduce((acc, current) => {
const slugs = current.items.reduce((acc, current) => {
acc.push(current.slug);
@ -44,7 +47,8 @@ class App extends React.Component {
}, []);
const slug = nextProps.location.pathname.split('/')[3];
const shouldRedirect = allowedPaths.filter(el => el === slug).length === 0;
const shouldRedirect =
allowedPaths.filter(el => el === slug).length === 0;
if (shouldRedirect) {
this.props.history.push('/404');
@ -56,7 +60,10 @@ class App extends React.Component {
return (
<div className={`${pluginId} ${styles.app}`}>
<Switch>
<Route path="/plugins/settings-manager/:slug/:env" component={HomePage} />
<Route
path="/plugins/settings-manager/:slug/:env"
component={HomePage}
/>
<Route path="/plugins/settings-manager/:slug" component={HomePage} />
<Route path="/plugins/settings-manager" component={HomePage} />
</Switch>
@ -65,10 +72,6 @@ class App extends React.Component {
}
}
App.contextTypes = {
router: PropTypes.object.isRequired,
};
App.propTypes = {
environmentsFetch: PropTypes.func.isRequired,
history: PropTypes.object.isRequired,

View File

@ -1,9 +1,14 @@
import { takeLatest, call, put, fork, take, cancel } from 'redux-saga/effects';
import request from 'utils/request';
import { request } from 'strapi-helper-plugin';
import { fetchMenuSucceeded, environmentsFetchSucceeded } from './actions';
import { MENU_FETCH, MENU_FETCH_SUCCEEDED, ENVIRONMENTS_FETCH, ENVIRONMENTS_FETCH_SUCCEEDED } from './constants';
import {
MENU_FETCH,
MENU_FETCH_SUCCEEDED,
ENVIRONMENTS_FETCH,
ENVIRONMENTS_FETCH_SUCCEEDED,
} from './constants';
export function* fetchMenu() {
try {
@ -15,8 +20,7 @@ export function* fetchMenu() {
const data = yield call(request, requestUrl, opts);
yield put(fetchMenuSucceeded(data));
} catch(err) {
} catch (err) {
strapi.notification.error('settings-manager.strapi.notification.error');
}
}
@ -28,19 +32,21 @@ export function* fetchEnvironments() {
};
const requestUrl = '/settings-manager/configurations/environments';
const data = yield call(request, requestUrl, opts);
const data = yield call(request, requestUrl, opts);
yield put(environmentsFetchSucceeded(data));
} catch(error) {
} catch (error) {
strapi.notification.error('settings-manager.strapi.notification.error');
}
}
function* defaultSaga() {
const loadMenu = yield fork(takeLatest, MENU_FETCH, fetchMenu);
const loadEnvironments = yield fork(takeLatest, ENVIRONMENTS_FETCH, fetchEnvironments);
const loadEnvironments = yield fork(
takeLatest,
ENVIRONMENTS_FETCH,
fetchEnvironments,
);
yield take(MENU_FETCH_SUCCEEDED);
yield cancel(loadMenu);
yield take(ENVIRONMENTS_FETCH_SUCCEEDED);

View File

@ -26,9 +26,8 @@ import {
} from 'lodash';
import { FormattedMessage } from 'react-intl';
import Helmet from 'react-helmet';
import { router } from 'app';
import InputSelect from 'strapi-helper-plugin/lib/src/components/InputSelect';
import { InputSelect } from 'strapi-helper-plugin';
import pluginId from '../../pluginId';
// design
@ -40,7 +39,10 @@ import RowDatabase from '../../components/RowDatabase';
import RowLanguage from '../../components/RowLanguage';
import PluginLeftMenu from '../../components/PluginLeftMenu';
import { checkFormValidity, getRequiredInputsDb } from '../../utils/inputValidations';
import {
checkFormValidity,
getRequiredInputsDb,
} from '../../utils/inputValidations';
import { formatLanguageLocale } from '../../utils/getFlag';
import sendUpdatedParams from '../../utils/sendUpdatedParams';
// App selectors
@ -96,22 +98,36 @@ export class HomePage extends React.Component {
if (this.props.match.params.slug) {
this.handleFetch(this.props);
} else {
router.push(
`/plugins/settings-manager/${get(this.props.menuSections, ['0', 'items', '0', 'slug']) ||
'application'}`,
this.props.history.push(
`/plugins/settings-manager/${get(this.props.menuSections, [
'0',
'items',
'0',
'slug',
]) || 'application'}`,
);
}
}
componentWillReceiveProps(nextProps) {
// check if params slug updated
if (this.props.match.params.slug !== nextProps.match.params.slug && nextProps.match.params.slug) {
if (
this.props.match.params.slug !== nextProps.match.params.slug &&
nextProps.match.params.slug
) {
if (nextProps.match.params.slug) {
// get data from api if params slug updated
this.handleFetch(nextProps);
} else {
// redirect user if no params slug provided
router.push(`/plugins/settings-manager/${get(this.props.menuSections, ['0', 'items', '0', 'slug'])}`);
this.props.history.push(
`/plugins/settings-manager/${get(this.props.menuSections, [
'0',
'items',
'0',
'slug',
])}`,
);
}
} else if (
this.props.match.params.env !== nextProps.match.params.env &&
@ -124,7 +140,10 @@ export class HomePage extends React.Component {
}
componentDidUpdate(prevProps) {
if (prevProps.home.didCreatedNewLanguage !== this.props.home.didCreatedNewLanguage) {
if (
prevProps.home.didCreatedNewLanguage !==
this.props.home.didCreatedNewLanguage
) {
this.handleFetch(this.props);
}
@ -139,7 +158,10 @@ export class HomePage extends React.Component {
e.preventDefault();
const newData = {};
/* eslint-disable no-template-curly-in-string */
const dbName = get(this.props.home.modifiedData, 'database.connections.${name}.name');
const dbName = get(
this.props.home.modifiedData,
'database.connections.${name}.name',
);
map(this.props.home.modifiedData, (data, key) => {
const k = replace(key, '${name}', dbName);
@ -148,11 +170,18 @@ export class HomePage extends React.Component {
}
});
const formErrors = getRequiredInputsDb(this.props.home.modifiedData, this.props.home.formErrors);
const formErrors = getRequiredInputsDb(
this.props.home.modifiedData,
this.props.home.formErrors,
);
if (isEmpty(formErrors)) {
// this.props.setErrors([]);
this.props.newDatabasePost(this.props.match.params.env, newData, this.context);
this.props.newDatabasePost(
this.props.match.params.env,
newData,
this.context,
);
} else {
this.props.setErrors(formErrors);
}
@ -178,7 +207,10 @@ export class HomePage extends React.Component {
};
// Find the index of the new setted language
const activeLanguageIndex = findIndex(this.props.home.configsDisplay.sections, ['name', target.id]);
const activeLanguageIndex = findIndex(
this.props.home.configsDisplay.sections,
['name', target.id],
);
forEach(this.props.home.configsDisplay.sections, (section, key) => {
// set all Language active state to false
@ -220,7 +252,10 @@ export class HomePage extends React.Component {
}
handleChange = ({ target }) => {
let value = target.type === 'number' && target.value !== '' ? toNumber(target.value) : target.value;
let value =
target.type === 'number' && target.value !== ''
? toNumber(target.value)
: target.value;
let name = target.name;
if (this.props.match.params.slug === 'security') {
@ -234,13 +269,27 @@ export class HomePage extends React.Component {
if (this.props.match.params.slug === 'databases') {
if (name === this.props.home.dbNameTarget) {
const formErrors =
value === this.props.home.addDatabaseSection.sections[1].items[0].value
? [{ target: name, errors: [{ id: 'settings-manager.request.error.database.exist' }] }]
value ===
this.props.home.addDatabaseSection.sections[1].items[0].value
? [
{
target: name,
errors: [
{ id: 'settings-manager.request.error.database.exist' },
],
},
]
: [];
this.props.setErrors(formErrors);
} else if (endsWith(name, '.settings.client')) {
const item = find(this.props.home.addDatabaseSection.sections[0].items[1].items, { value });
this.props.changeInput('database.connections.${name}.settings.port', item.port);
const item = find(
this.props.home.addDatabaseSection.sections[0].items[1].items,
{ value },
);
this.props.changeInput(
'database.connections.${name}.settings.port',
item.port,
);
this.props.changeInput(
`database.connections.${
this.props.home.addDatabaseSection.sections[1].items[0].value
@ -266,7 +315,9 @@ export class HomePage extends React.Component {
: this.props.home.modifiedData[this.props.home.dbNameTarget];
const target = { name: 'database.defaultConnection', value };
this.handleChange({ target });
this.setState({ toggleDefaultConnection: !this.state.toggleDefaultConnection });
this.setState({
toggleDefaultConnection: !this.state.toggleDefaultConnection,
});
};
handleSubmit = e => {
@ -282,7 +333,9 @@ export class HomePage extends React.Component {
const formErrors = checkFormValidity(body, this.props.home.formValidations);
if (isEmpty(body))
return strapi.notification.info('settings-manager.strapi.notification.info.settingsEqual');
return strapi.notification.info(
'settings-manager.strapi.notification.info.settingsEqual',
);
if (isEmpty(formErrors)) {
this.props.editSettings(body, apiUrl, this.context);
} else {
@ -294,11 +347,17 @@ export class HomePage extends React.Component {
// eslint-disable-line consistent-return
const body = this.sendUpdatedParams();
const apiUrl = `${databaseName}/${this.props.match.params.env}`;
const formErrors = checkFormValidity(body, this.props.home.formValidations, this.props.home.formErrors);
const formErrors = checkFormValidity(
body,
this.props.home.formValidations,
this.props.home.formErrors,
);
if (isEmpty(body)) {
this.props.closeModal();
return strapi.notification.info('settings-manager.strapi.notification.info.settingsEqual');
return strapi.notification.info(
'settings-manager.strapi.notification.info.settingsEqual',
);
}
if (isEmpty(formErrors)) {
@ -309,12 +368,19 @@ export class HomePage extends React.Component {
};
// retrieve the language to delete using the target id
handleLanguageDelete = languaToDelete => this.props.languageDelete(languaToDelete);
handleLanguageDelete = languaToDelete =>
this.props.languageDelete(languaToDelete);
handleDatabaseDelete = dbName => {
this.context.enableGlobalOverlayBlocker();
strapi.notification.success('settings-manager.strapi.notification.success.databaseDelete');
this.props.databaseDelete(dbName, this.props.match.params.env, this.context);
strapi.notification.success(
'settings-manager.strapi.notification.success.databaseDelete',
);
this.props.databaseDelete(
dbName,
this.props.match.params.env,
this.context,
);
};
// custom Row rendering for the component List with params slug === languages
@ -330,12 +396,18 @@ export class HomePage extends React.Component {
);
renderListTitle = () => {
const availableContentNumber = size(this.props.home.configsDisplay.sections);
const availableContentNumber = size(
this.props.home.configsDisplay.sections,
);
const title =
availableContentNumber > 1
? `list.${this.props.match.params.slug}.title.plural`
: `list.${this.props.match.params.slug}.title.singular`;
const titleDisplay = title ? <FormattedMessage id={`settings-manager.${title}`} /> : '';
const titleDisplay = title ? (
<FormattedMessage id={`settings-manager.${title}`} />
) : (
''
);
return (
<span>
@ -344,19 +416,20 @@ export class HomePage extends React.Component {
);
};
renderListButtonLabel = () => `list.${this.props.match.params.slug}.button.label`;
renderListButtonLabel = () =>
`list.${this.props.match.params.slug}.button.label`;
renderPopUpFormDatabase = (section, props, popUpStyles) =>
map(section.items, (item, key) => {
const isActive =
props.values[this.props.home.dbNameTarget] ===
this.props.home.modifiedData['database.defaultConnection'] ? (
<div className={popUpStyles.rounded}>
<i className="fa fa-check" />
</div>
) : (
''
);
<div className={popUpStyles.rounded}>
<i className="fa fa-check" />
</div>
) : (
''
);
if (item.name === 'form.database.item.default') {
return (
@ -377,10 +450,11 @@ export class HomePage extends React.Component {
renderPopUpFormLanguage = section =>
map(section.items, item => {
const value =
this.props.home.modifiedData[item.target] || this.props.home.selectOptions.options[0].value;
this.props.home.modifiedData[item.target] ||
this.props.home.selectOptions.options[0].value;
return (
<div className={`col-md-6`} key={item.name}>
<div className={'col-md-6'} key={item.name}>
<div className={styles.modalLanguageLabel}>
<FormattedMessage id={`settings-manager.${item.name}`} />
</div>
@ -429,10 +503,14 @@ export class HomePage extends React.Component {
// if custom view display render specificComponent
const Component = this.components[specificComponent];
const addRequiredInputDesign = this.props.match.params.slug === 'databases';
const listTitle = ['languages', 'databases'].includes(this.props.match.params.slug)
const listTitle = ['languages', 'databases'].includes(
this.props.match.params.slug,
)
? this.renderListTitle()
: '';
const listButtonLabel = ['languages', 'databases'].includes(this.props.match.params.slug)
const listButtonLabel = ['languages', 'databases'].includes(
this.props.match.params.slug,
)
? this.renderListButtonLabel()
: '';
@ -469,7 +547,10 @@ export class HomePage extends React.Component {
}
// Custom selectOptions for languages
const selectOptions = this.props.match.params.slug === 'languages' ? this.props.home.listLanguages : [];
const selectOptions =
this.props.match.params.slug === 'languages'
? this.props.home.listLanguages
: [];
return (
<Component
sections={sections}
@ -501,7 +582,8 @@ export class HomePage extends React.Component {
};
// Set the toggleDefaultConnection to false
resetToggleDefaultConnection = () => this.setState({ toggleDefaultConnection: false });
resetToggleDefaultConnection = () =>
this.setState({ toggleDefaultConnection: false });
// Hide database modal
toggle = () => this.setState({ modal: !this.state.modal });
@ -518,7 +600,12 @@ export class HomePage extends React.Component {
<div className={`${styles.home} col-md-9`}>
<Helmet
title="Settings Manager"
meta={[{ name: 'Settings Manager Plugin', content: 'Modify your app settings' }]}
meta={[
{
name: 'Settings Manager Plugin',
content: 'Modify your app settings',
},
]}
/>
<ContentHeader
name={this.props.home.configsDisplay.name}
@ -596,7 +683,11 @@ const withConnect = connect(
mapDispatchToProps,
);
const withReducer = strapi.injectReducer({ key: 'homePage', reducer, pluginId });
const withReducer = strapi.injectReducer({
key: 'homePage',
reducer,
pluginId,
});
const withSaga = strapi.injectSaga({ key: 'homePage', saga, pluginId });
export default compose(

View File

@ -1,16 +1,6 @@
// import { LOCATION_CHANGE } from 'react-router-redux';
import { forEach, set, map, replace } from 'lodash';
import {
all,
call,
// take,
put,
fork,
// cancel,
select,
takeLatest,
} from 'redux-saga/effects';
import request from 'utils/request';
import { all, call, put, fork, select, takeLatest } from 'redux-saga/effects';
import { request } from 'strapi-helper-plugin';
// selectors
import { makeSelectModifiedData } from './selectors';
import {
@ -53,21 +43,30 @@ export function* editDatabase(action) {
method: 'PUT',
body,
};
const requestUrl = `/settings-manager/configurations/databases/${action.apiUrl}`;
const requestUrl = `/settings-manager/configurations/databases/${
action.apiUrl
}`;
action.context.emitEvent('willEditDatabaseSettings');
const resp = yield call(request, requestUrl, opts, true);
if (resp.ok) {
action.context.emitEvent('didEditDatabaseSettings');
strapi.notification.success('settings-manager.strapi.notification.success.databaseEdit');
strapi.notification.success(
'settings-manager.strapi.notification.success.databaseEdit',
);
yield put(databaseActionSucceeded());
}
} catch(error) {
} catch (error) {
action.context.emitEvent('didNotEditDatabaseSettings');
const formErrors = map(error.response.payload.message, err => ({ target: err.target, errors: map(err.messages, mess => ({ id: `settings-manager.${mess.id}`})) }));
const formErrors = map(error.response.payload.message, err => ({
target: err.target,
errors: map(err.messages, mess => ({
id: `settings-manager.${mess.id}`,
})),
}));
yield put(databaseActionError(formErrors));
strapi.notification.error('settings-manager.strapi.notification.error');
@ -77,15 +76,19 @@ export function* editDatabase(action) {
export function* deleteDatabase(action) {
try {
const opts = { method: 'DELETE' };
const requestUrl = `/settings-manager/configurations/databases/${action.databaseToDelete}/${action.endPoint}`;
const requestUrl = `/settings-manager/configurations/databases/${
action.databaseToDelete
}/${action.endPoint}`;
const resp = yield call(request, requestUrl, opts, true);
if (resp.ok) {
yield call(action.context.disableGlobalOverlayBlocker);
strapi.notification.success('settings-manager.strapi.notification.success.databaseDeleted');
strapi.notification.success(
'settings-manager.strapi.notification.success.databaseDeleted',
);
}
} catch(error) {
} catch (error) {
yield call(action.context.disableGlobalOverlayBlocker);
yield put(databaseActionError([]));
strapi.notification.error('settings-manager.strapi.notification.error');
@ -97,13 +100,17 @@ export function* deleteLanguage(action) {
const opts = {
method: 'DELETE',
};
const requestUrl = `/settings-manager/configurations/languages/${action.languageToDelete}`;
const requestUrl = `/settings-manager/configurations/languages/${
action.languageToDelete
}`;
const resp = yield call(request, requestUrl, opts, true);
if (resp.ok) {
strapi.notification.success('settings-manager.strapi.notification.success.languageDelete');
strapi.notification.success(
'settings-manager.strapi.notification.success.languageDelete',
);
}
} catch(error) {
} catch (error) {
yield put(languageActionError());
strapi.notification.error('settings-manager.strapi.notification.error');
}
@ -118,26 +125,28 @@ export function* fetchConfig(action) {
const data = yield call(request, requestUrl, opts);
yield put(configFetchSucceded(data));
} catch(error) {
} catch (error) {
strapi.notification.error('settings-manager.strapi.notification.error');
}
}
export function* fetchDatabases(action) {
try {
const opts = {
method: 'GET',
};
const requestUrlListDatabases = `/settings-manager/configurations/databases/${action.environment}`;
const requestUrlAppDatabases = '/settings-manager/configurations/database/model';
const requestUrlListDatabases = `/settings-manager/configurations/databases/${
action.environment
}`;
const requestUrlAppDatabases =
'/settings-manager/configurations/database/model';
const [listDatabasesData, appDatabaseData] = yield all([
call(request, requestUrlListDatabases, opts),
call(request, requestUrlAppDatabases, opts),
]);
yield put(databasesFetchSucceeded(listDatabasesData, appDatabaseData));
} catch(error) {
} catch (error) {
strapi.notification.error('settings-manager.strapi.notification.error');
}
}
@ -155,7 +164,7 @@ export function* fetchLanguages() {
call(request, requestUrlListLanguages, opts),
]);
yield put(languagesFetchSucceeded(appLanguagesData, listLanguagesData));
} catch(error) {
} catch (error) {
strapi.notification.error('settings-manager.strapi.notification.error');
}
}
@ -175,9 +184,11 @@ export function* postLanguage() {
if (resp.ok) {
yield put(languageActionSucceeded());
strapi.notification.success('settings-manager.strapi.notification.success.languageAdd');
strapi.notification.success(
'settings-manager.strapi.notification.success.languageAdd',
);
}
} catch(error) {
} catch (error) {
yield put(languageActionError());
strapi.notification.error('settings-manager.strapi.notification.error');
}
@ -196,21 +207,30 @@ export function* postDatabase(action) {
body,
};
action.context.emitEvent('willAddDatabaseSettings');
const requestUrl = `/settings-manager/configurations/databases/${action.endPoint}`;
const requestUrl = `/settings-manager/configurations/databases/${
action.endPoint
}`;
const resp = yield call(request, requestUrl, opts, true);
if (resp.ok) {
action.context.emitEvent('didAddDatabaseSettings');
yield put(databaseActionSucceeded());
strapi.notification.success('settings-manager.strapi.notification.success.databaseAdd');
}
} catch(error) {
action.context.emitEvent('didNotAddDatabaseSettings')
const formErrors = map(error.response.payload.message, (err) => {
const target = err.target ? replace(err.target, err.target.split('.')[2], '${name}') : 'database.connections.${name}.name';
return (
{ target, errors: map(err.messages, mess => ({ id: `settings-manager.${mess.id}`})) }
strapi.notification.success(
'settings-manager.strapi.notification.success.databaseAdd',
);
}
} catch (error) {
action.context.emitEvent('didNotAddDatabaseSettings');
const formErrors = map(error.response.payload.message, err => {
const target = err.target
? replace(err.target, err.target.split('.')[2], '${name}')
: 'database.connections.${name}.name';
return {
target,
errors: map(err.messages, mess => ({
id: `settings-manager.${mess.id}`,
})),
};
});
yield put(databaseActionError(formErrors));
@ -227,16 +247,20 @@ export function* settingsEdit(action) {
body: action.newSettings,
method: 'PUT',
};
action.context.emitEvent('willEditSettings', { category : action.endPoint });
action.context.emitEvent('willEditSettings', { category: action.endPoint });
const requestUrl = `/settings-manager/configurations/${action.endPoint}`;
const resp = yield call(request, requestUrl, opts, true);
const resp = yield call(request, requestUrl, opts, true);
if (resp.ok) {
action.context.emitEvent('didEditSettings', { category : action.endPoint });
action.context.emitEvent('didEditSettings', {
category: action.endPoint,
});
yield put(editSettingsSucceeded());
strapi.notification.success('settings-manager.strapi.notification.success.settingsEdit');
strapi.notification.success(
'settings-manager.strapi.notification.success.settingsEdit',
);
}
} catch (error) {
action.context.emitEvent('didNotEditSettings', { error });
@ -251,11 +275,13 @@ export function* fetchSpecificDatabase(action) {
const opts = {
method: 'GET',
};
const requestUrl = `/settings-manager/configurations/databases/${action.databaseName}/${action.endPoint}`;
const requestUrl = `/settings-manager/configurations/databases/${
action.databaseName
}/${action.endPoint}`;
const data = yield call(request, requestUrl, opts);
yield put(specificDatabaseFetchSucceeded(data));
} catch(error) {
} catch (error) {
strapi.notification.error('settings-manager.strapi.notification.error');
}
}

View File

@ -1,27 +0,0 @@
/**
* NotFoundPage
*
* This is the page we show when the user visits a url that doesn't have a route
*
* NOTE: while this component should technically be a stateless functional
* component (SFC), hot reloading does not currently support SFCs. If hot
* reloading is not a neccessity for you then you can refactor it and remove
* the linting exception.
*/
import React from 'react';
import { FormattedMessage } from 'react-intl';
export default class NotFound extends React.Component {
render() {
return (
<div>
<div className="container">
<h1>
<FormattedMessage id="settings-manager.pageNotFound" />.
</h1>
</div>
</div>
);
}
}

View File

@ -1,13 +0,0 @@
/*
* NotFoundPage Messages
*
* This contains all the text for the NotFoundPage component.
*/
import { defineMessages } from 'react-intl';
export default defineMessages({
pageNotFound: {
id: 'app.components.NotFoundPage.pageNotFound',
defaultMessage: 'Page not found.',
},
});

View File

@ -0,0 +1,93 @@
import React from 'react';
import { reduce } from 'lodash';
import pluginPkg from '../../package.json';
import pluginId from './pluginId';
import App from './containers/App';
const pluginDescription = pluginPkg.strapi.description || pluginPkg.description;
const formatMessages = messages =>
reduce(
messages,
(result, value, key) => {
result[`${pluginId}.${key}`] = value;
return result;
},
{},
);
const requireTranslations = language => {
try {
return require(`./translations/${language}.json`); // eslint-disable-line global-require
} catch (error) {
console.error(
`Unable to load "${language}" translation for the plugin ${pluginId}. Please make sure "${language}.json" file exists in "pluginPath/admin/src/translations" folder.`,
);
return;
}
};
const translationMessages = reduce(
strapi.languages,
(result, language) => {
result[language] = formatMessages(requireTranslations(language));
return result;
},
{},
);
// const layout = (() => {
// try {
// return require('../../config/layout.js'); // eslint-disable-line import/no-unresolved
// } catch (err) {
// return null;
// }
// })();
const injectedComponents = (() => {
try {
return require('./injectedComponents').default; // eslint-disable-line import/no-unresolved
} catch (err) {
return [];
}
})();
const initializer = (() => {
try {
return require('./initializer');
} catch (err) {
return null;
}
})();
const lifecycles = (() => {
try {
return require('./lifecycles');
} catch (err) {
return null;
}
})();
function Comp(props) {
return <App {...props} />;
}
const plugin = {
blockerComponent: null,
blockerComponentProps: {},
description: pluginDescription,
icon: pluginPkg.strapi.icon,
id: pluginId,
initializer,
injectedComponents,
layout: null,
lifecycles,
leftMenuLinks: [],
leftMenuSections: [],
mainComponent: Comp,
name: pluginPkg.strapi.name,
preventComponentRendering: false,
translationMessages,
};
export default plugin;