Change build folder
6
.gitignore
vendored
@ -119,3 +119,9 @@ dist
|
||||
############################
|
||||
|
||||
packages/strapi-generate-new/files/public/
|
||||
|
||||
############################
|
||||
# Example app
|
||||
############################
|
||||
|
||||
# *.cache
|
||||
|
||||
12
examples/getstarted/.cache/admin/.gitignore
vendored
@ -1,12 +0,0 @@
|
||||
# Don't check auto-generated stuff into git
|
||||
coverage
|
||||
node_modules
|
||||
manifest.json
|
||||
plugins.json
|
||||
stats.json
|
||||
package-lock.json
|
||||
|
||||
# Cruft
|
||||
.DS_Store
|
||||
npm-debug.log
|
||||
.idea
|
||||
@ -1,101 +0,0 @@
|
||||
############################
|
||||
# OS X
|
||||
############################
|
||||
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
Icon
|
||||
.Spotlight-V100
|
||||
.Trashes
|
||||
._*
|
||||
|
||||
|
||||
############################
|
||||
# Linux
|
||||
############################
|
||||
|
||||
*~
|
||||
|
||||
|
||||
############################
|
||||
# Windows
|
||||
############################
|
||||
|
||||
Thumbs.db
|
||||
ehthumbs.db
|
||||
Desktop.ini
|
||||
$RECYCLE.BIN/
|
||||
*.cab
|
||||
*.msi
|
||||
*.msm
|
||||
*.msp
|
||||
|
||||
|
||||
############################
|
||||
# Packages
|
||||
############################
|
||||
|
||||
*.7z
|
||||
*.csv
|
||||
*.dat
|
||||
*.dmg
|
||||
*.gz
|
||||
*.iso
|
||||
*.jar
|
||||
*.rar
|
||||
*.tar
|
||||
*.zip
|
||||
*.com
|
||||
*.class
|
||||
*.dll
|
||||
*.exe
|
||||
*.o
|
||||
*.seed
|
||||
*.so
|
||||
*.swo
|
||||
*.swp
|
||||
*.swn
|
||||
*.swm
|
||||
*.out
|
||||
*.pid
|
||||
|
||||
|
||||
############################
|
||||
# Logs and databases
|
||||
############################
|
||||
|
||||
*.log
|
||||
*.sql
|
||||
|
||||
|
||||
############################
|
||||
# Misc.
|
||||
############################
|
||||
|
||||
*#
|
||||
ssl
|
||||
.idea
|
||||
nbproject
|
||||
|
||||
|
||||
############################
|
||||
# Node.js
|
||||
############################
|
||||
|
||||
lib-cov
|
||||
lcov.info
|
||||
pids
|
||||
logs
|
||||
results
|
||||
node_modules
|
||||
.node_history
|
||||
|
||||
|
||||
############################
|
||||
# Tests
|
||||
############################
|
||||
|
||||
test
|
||||
testApp
|
||||
coverage
|
||||
@ -1,213 +0,0 @@
|
||||
// /**
|
||||
// *
|
||||
// * app.js
|
||||
// *
|
||||
// * Entry point of the application
|
||||
// */
|
||||
|
||||
import '@babel/polyfill';
|
||||
import 'sanitize.css/sanitize.css';
|
||||
|
||||
// Third party css library needed
|
||||
// Currently unable to bundle them.
|
||||
import 'codemirror/lib/codemirror.css';
|
||||
import 'codemirror/theme/liquibyte.css';
|
||||
import 'codemirror/theme/xq-dark.css';
|
||||
import 'codemirror/theme/3024-day.css';
|
||||
import 'codemirror/theme/3024-night.css';
|
||||
import 'codemirror/theme/blackboard.css';
|
||||
import 'codemirror/theme/monokai.css';
|
||||
import 'codemirror/theme/cobalt.css';
|
||||
|
||||
import 'react-select/dist/react-select.css';
|
||||
import 'react-datetime/css/react-datetime.css';
|
||||
|
||||
import './styles/main.scss';
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { Provider } from 'react-redux';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
|
||||
import { merge } from 'lodash';
|
||||
import {
|
||||
freezeApp,
|
||||
pluginLoaded,
|
||||
unfreezeApp,
|
||||
updatePlugin,
|
||||
getAppPluginsSucceeded,
|
||||
} from './containers/App/actions';
|
||||
import { showNotification } from './containers/NotificationProvider/actions';
|
||||
|
||||
import basename from './utils/basename';
|
||||
import injectReducer from './utils/injectReducer';
|
||||
import injectSaga from './utils/injectSaga';
|
||||
|
||||
// Import root component
|
||||
import App from './containers/App';
|
||||
// Import Language provider
|
||||
import LanguageProvider from './containers/LanguageProvider';
|
||||
|
||||
import configureStore from './configureStore';
|
||||
|
||||
// Import i18n messages
|
||||
import { translationMessages, languages } from './i18n';
|
||||
|
||||
// Create redux store with history
|
||||
import history from './utils/history';
|
||||
|
||||
import plugins from './plugins';
|
||||
|
||||
const initialState = {};
|
||||
const store = configureStore(initialState, history);
|
||||
const { dispatch } = store;
|
||||
const MOUNT_NODE =
|
||||
document.getElementById('app') || document.createElement('div');
|
||||
|
||||
dispatch(getAppPluginsSucceeded(Object.keys(plugins)));
|
||||
|
||||
Object.keys(plugins).forEach(plugin => {
|
||||
const currentPlugin = plugins[plugin];
|
||||
|
||||
const pluginTradsPrefixed = languages.reduce((acc, lang) => {
|
||||
const currentLocale = currentPlugin.trads[lang];
|
||||
|
||||
if (currentLocale) {
|
||||
const localeprefixedWithPluginId = Object.keys(currentLocale).reduce(
|
||||
(acc2, current) => {
|
||||
acc2[`${plugins[plugin].id}.${current}`] = currentLocale[current];
|
||||
|
||||
return acc2;
|
||||
},
|
||||
{},
|
||||
);
|
||||
|
||||
acc[lang] = localeprefixedWithPluginId;
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
try {
|
||||
merge(translationMessages, pluginTradsPrefixed);
|
||||
dispatch(pluginLoaded(currentPlugin));
|
||||
} catch (err) {
|
||||
console.log({ err });
|
||||
}
|
||||
});
|
||||
|
||||
// TODO
|
||||
const remoteURL = (() => {
|
||||
if (window.location.port === '4000') {
|
||||
return 'http://localhost:4000/admin';
|
||||
}
|
||||
|
||||
// Relative URL (ex: /dashboard)
|
||||
if (process.env.REMOTE_URL[0] === '/') {
|
||||
return (window.location.origin + process.env.REMOTE_URL).replace(/\/$/, '');
|
||||
}
|
||||
|
||||
return process.env.REMOTE_URL.replace(/\/$/, '');
|
||||
})();
|
||||
|
||||
const displayNotification = (message, status) => {
|
||||
dispatch(showNotification(message, status));
|
||||
};
|
||||
const lockApp = data => {
|
||||
dispatch(freezeApp(data));
|
||||
};
|
||||
const unlockApp = () => {
|
||||
dispatch(unfreezeApp());
|
||||
};
|
||||
|
||||
window.strapi = Object.assign(window.strapi || {}, {
|
||||
node: MODE || 'host',
|
||||
remoteURL,
|
||||
backendURL: BACKEND_URL,
|
||||
notification: {
|
||||
success: message => {
|
||||
displayNotification(message, 'success');
|
||||
},
|
||||
warning: message => {
|
||||
displayNotification(message, 'warning');
|
||||
},
|
||||
error: message => {
|
||||
displayNotification(message, 'error');
|
||||
},
|
||||
info: message => {
|
||||
displayNotification(message, 'info');
|
||||
},
|
||||
},
|
||||
refresh: pluginId => ({
|
||||
translationMessages: translationMessagesUpdated => {
|
||||
render(merge({}, translationMessages, translationMessagesUpdated));
|
||||
},
|
||||
leftMenuSections: leftMenuSectionsUpdated => {
|
||||
store.dispatch(
|
||||
updatePlugin(pluginId, 'leftMenuSections', leftMenuSectionsUpdated),
|
||||
);
|
||||
},
|
||||
}),
|
||||
router: history,
|
||||
languages,
|
||||
currentLanguage:
|
||||
window.localStorage.getItem('strapi-admin-language') ||
|
||||
window.navigator.language ||
|
||||
window.navigator.userLanguage ||
|
||||
'en',
|
||||
lockApp,
|
||||
unlockApp,
|
||||
injectReducer,
|
||||
injectSaga,
|
||||
store,
|
||||
});
|
||||
|
||||
const render = messages => {
|
||||
ReactDOM.render(
|
||||
<Provider store={store}>
|
||||
<LanguageProvider messages={messages}>
|
||||
<BrowserRouter basename={basename}>
|
||||
<App store={store} />
|
||||
</BrowserRouter>
|
||||
</LanguageProvider>
|
||||
</Provider>,
|
||||
MOUNT_NODE,
|
||||
);
|
||||
};
|
||||
|
||||
if (module.hot) {
|
||||
module.hot.accept(['./i18n', './containers/App'], () => {
|
||||
ReactDOM.unmountComponentAtNode(MOUNT_NODE);
|
||||
|
||||
render(translationMessages);
|
||||
});
|
||||
}
|
||||
|
||||
if (NODE_ENV !== 'test') {
|
||||
// Chunked polyfill for browsers without Intl support
|
||||
if (!window.Intl) {
|
||||
new Promise(resolve => {
|
||||
resolve(import('intl'));
|
||||
})
|
||||
.then(() =>
|
||||
Promise.all([
|
||||
import('intl/locale-data/jsonp/en.js'),
|
||||
import('intl/locale-data/jsonp/de.js'),
|
||||
]),
|
||||
) // eslint-disable-line prettier/prettier
|
||||
.then(() => render(translationMessages))
|
||||
.catch(err => {
|
||||
throw err;
|
||||
});
|
||||
} else {
|
||||
render(translationMessages);
|
||||
}
|
||||
}
|
||||
|
||||
// @Pierre Burgy exporting dispatch for the notifications...
|
||||
export { dispatch };
|
||||
|
||||
// TODO remove this for the new Cypress tests
|
||||
if (window.Cypress) {
|
||||
window.__store__ = Object.assign(window.__store__ || {}, { store });
|
||||
}
|
||||
@ -1 +0,0 @@
|
||||
<svg width="28" height="26" xmlns="http://www.w3.org/2000/svg"><g fill-rule="nonzero" fill="none"><path d="M8.453 12.772c-1.54.047-2.799.656-3.778 1.824h-1.91c-.779 0-1.435-.192-1.967-.577C.266 13.634 0 13.071 0 12.33c0-3.354.59-5.032 1.768-5.032.057 0 .263.1.62.3.356.2.82.401 1.39.605.57.205 1.135.307 1.696.307.636 0 1.268-.11 1.896-.328a7.106 7.106 0 0 0-.072.94c0 1.322.385 2.538 1.155 3.65zm15.266 9.08c0 1.14-.347 2.04-1.04 2.701-.694.66-1.616.99-2.766.99H7.455c-1.15 0-2.072-.33-2.765-.99-.694-.66-1.04-1.56-1.04-2.701 0-.504.016-.995.049-1.475.033-.48.1-.998.2-1.554s.225-1.072.377-1.547c.152-.475.357-.938.613-1.39.257-.45.551-.836.884-1.154a3.718 3.718 0 0 1 1.219-.763 4.28 4.28 0 0 1 1.59-.285c.094 0 .298.102.612.307.314.204.66.432 1.04.684.38.252.89.48 1.526.684a6.264 6.264 0 0 0 1.924.307c.646 0 1.288-.103 1.925-.307.636-.204 1.145-.432 1.525-.684.38-.252.727-.48 1.04-.684.314-.205.518-.307.613-.307.58 0 1.11.095 1.59.285.48.19.886.445 1.218.763.333.318.628.703.884 1.155.257.45.461.914.613 1.39.152.474.278.99.378 1.546.1.556.166 1.074.2 1.554.033.48.05.971.05 1.475zm3.65-9.522c0 .741-.267 1.304-.799 1.69-.532.384-1.188.576-1.967.576h-1.91c-.979-1.168-2.238-1.777-3.777-1.824.77-1.112 1.154-2.328 1.154-3.65 0-.275-.024-.588-.071-.94.627.219 1.259.328 1.896.328.56 0 1.126-.102 1.696-.307a9.374 9.374 0 0 0 1.39-.605c.356-.2.563-.3.62-.3 1.178 0 1.767 1.678 1.767 5.032z" fill="#553D38"/><path d="M9.123 3.65a3.516 3.516 0 0 1-1.07 2.58 3.516 3.516 0 0 1-2.58 1.068 3.516 3.516 0 0 1-2.58-1.069 3.516 3.516 0 0 1-1.068-2.58c0-1.007.356-1.867 1.069-2.58A3.516 3.516 0 0 1 5.474 0C6.48 0 7.34.356 8.054 1.07a3.516 3.516 0 0 1 1.069 2.58zm10.035 5.473c0 1.51-.535 2.8-1.604 3.87-1.069 1.069-2.359 1.603-3.87 1.603-1.51 0-2.8-.534-3.87-1.603-1.069-1.07-1.603-2.36-1.603-3.87 0-1.511.534-2.801 1.603-3.87 1.07-1.07 2.36-1.604 3.87-1.604 1.511 0 2.801.535 3.87 1.604 1.07 1.069 1.604 2.359 1.604 3.87zm6.386-5.474a3.516 3.516 0 0 1-1.07 2.58 3.516 3.516 0 0 1-2.58 1.07 3.516 3.516 0 0 1-2.58-1.07 3.516 3.516 0 0 1-1.068-2.58c0-1.007.356-1.867 1.069-2.58A3.516 3.516 0 0 1 21.895 0c1.007 0 1.867.356 2.58 1.07a3.516 3.516 0 0 1 1.069 2.58z" fill="#EBBF8E"/><path d="M12.496 19.991h2.393v-.897c0-.33-.117-.612-.35-.846a1.153 1.153 0 0 0-.847-.35c-.33 0-.612.116-.846.35-.233.234-.35.516-.35.846v.897zm3.889.45v2.691c0 .125-.044.231-.131.318a.433.433 0 0 1-.318.131h-4.487a.433.433 0 0 1-.318-.13.433.433 0 0 1-.131-.319V20.44c0-.124.044-.23.13-.318a.433.433 0 0 1 .319-.13h.15v-.898c0-.573.205-1.066.616-1.477A2.015 2.015 0 0 1 13.692 17c.574 0 1.066.206 1.477.617.412.411.617.904.617 1.477v.897h.15c.125 0 .23.044.318.131.087.088.13.194.13.318z" fill="#FFF"/></g></svg>
|
||||
|
Before Width: | Height: | Size: 2.6 KiB |
@ -1 +0,0 @@
|
||||
<svg width="35" height="24" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path d="M1.148 20.03c.567-7.141 2.974-7.675 7.222-1.604 6.372 9.107 7.533-.667 9.19-2.017" stroke="#FFD2B6" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/><g fill-rule="nonzero"><path d="M21.313 16.48l1.081-1.082-2.792-2.792-1.081 1.081v1.271h1.52v1.521h1.272zm6.214-11.027c0-.174-.087-.26-.262-.26a.275.275 0 0 0-.202.082l-6.44 6.44a.275.275 0 0 0-.082.202c0 .174.087.261.261.261.08 0 .147-.028.202-.083l6.44-6.44a.275.275 0 0 0 .083-.202zm-.642-2.28l4.943 4.942L21.943 18H17v-4.943l9.885-9.885zM35 4.312c0 .42-.147.776-.44 1.07l-1.972 1.971-4.942-4.942 1.972-1.96A1.412 1.412 0 0 1 30.688 0c.419 0 .78.15 1.08.451l2.792 2.78c.293.31.44.67.44 1.082z" fill="#181D29"/><path d="M35 4.313c0 .42-.147.776-.44 1.07l-1.972 1.971-4.942-4.942 1.972-1.96A1.412 1.412 0 0 1 30.688 0c.419 0 .78.15 1.08.451l2.792 2.78c.293.31.44.67.44 1.082z" fill="#FF84B3"/></g></g></svg>
|
||||
|
Before Width: | Height: | Size: 980 B |
@ -1 +0,0 @@
|
||||
<svg width="22" height="22" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><path d="M8.604.607c.317.623.75 1.155 1.298 1.597.549.443 1.16.754 1.835.934l.012.873c.032 1.745-.494 3.166-1.579 4.264-1.084 1.098-2.5 1.647-4.247 1.647-1 0-1.885-.19-2.657-.571a4.852 4.852 0 0 1-1.858-1.567 7.325 7.325 0 0 1-1.055-2.25A9.927 9.927 0 0 1 0 2.832l.5.369c.276.205.528.387.755.547.228.16.468.309.72.448.251.14.438.21.56.21.333 0 .557-.152.67-.455a6.33 6.33 0 0 1 .701-1.383c.264-.381.547-.692.847-.934.3-.242.658-.436 1.073-.584A6.547 6.547 0 0 1 7.08.736c.422-.062.93-.105 1.523-.13z" id="a"/></defs><g fill="none" fill-rule="evenodd"><g transform="translate(0 12)"><mask id="b" fill="#fff"><use xlink:href="#a"/></mask><use fill="#EABE8D" fill-rule="nonzero" xlink:href="#a"/><path d="M10.18.264c4.086 6.335 2.315 9.584-5.314 9.747-7.629.163-6.185-1.797 4.331-5.88l.982-3.867z" fill="#DB1818" mask="url(#b)"/><path d="M10.564.16C11.422 6.26 8.9 9.507 2.998 9.904c-5.902.397-4.484-1.392 4.255-5.367L10.563.16z" fill="#F1D520" mask="url(#b)"/><path d="M10.196-1.556C9.168 5.281 6.272 8.728 1.507 8.786c-4.764.059-3.209-1.83 4.668-5.663l4.021-4.679z" fill="#32B167" mask="url(#b)"/><path d="M9.506-4.516C9.417 2.713 6.99 6.357 2.226 6.414-2.539 6.474-.784 4.262 7.49-.22l2.016-4.295z" fill="#1279EA" mask="url(#b)"/><path d="M9.449-4.392C8.72 1.871 5.974 5.032 1.209 5.09c-4.764.058-3.398-1.712 4.099-5.311l4.14-4.17z" fill="#4C2DCE" mask="url(#b)"/></g><path d="M19.88 0c.564 0 1.058.186 1.482.56.424.372.635.84.635 1.401 0 .505-.181 1.111-.544 1.817-2.679 5.045-4.555 8.06-5.628 9.047-.783.73-1.662 1.095-2.638 1.095-1.017 0-1.89-.37-2.62-1.113-.73-.742-1.096-1.622-1.096-2.64 0-1.027.371-1.877 1.113-2.551L18.306.65c.476-.433 1-.65 1.573-.65z" fill-rule="nonzero" fill="#553D38"/></g></svg>
|
||||
|
Before Width: | Height: | Size: 1.8 KiB |
@ -1 +0,0 @@
|
||||
<svg width="54" height="34" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><g fill-rule="nonzero"><path d="M11.392 30.3a1.797 1.797 0 0 0-.22-1.409 1.797 1.797 0 0 0-1.141-.857 1.797 1.797 0 0 0-1.41.22c-.449.27-.734.65-.857 1.142-.122.492-.049.962.22 1.41.27.449.65.734 1.142.857.491.122.961.049 1.41-.22.448-.27.734-.65.856-1.142zm21.224-7.353L8.464 37.459c-.874.525-1.812.663-2.813.413-.983-.245-1.756-.809-2.318-1.692L1.09 32.37c-.548-.86-.695-1.8-.44-2.82.249-1.002.822-1.772 1.72-2.311l24.117-14.492a14.885 14.885 0 0 0 2.02 5.728 14.885 14.885 0 0 0 4.108 4.472z" fill="#181D29"/><path d="M53.663 15.097c-.184.737-.65 1.684-1.401 2.842-1.52 2.31-3.587 3.978-6.2 5.003-2.615 1.024-5.254 1.204-7.918.54-3.497-.872-6.177-2.86-8.043-5.965-1.865-3.105-2.362-6.405-1.49-9.901.871-3.496 2.86-6.177 5.964-8.043 3.105-1.865 6.405-2.362 9.901-1.49 1.096.273 2.205.715 3.328 1.326 1.122.611 2.028 1.304 2.718 2.078.251.283.336.586.256.907-.08.321-.297.548-.651.68l-9.5 2.72-1.584 6.35 4.715 4.397c.109-.033.97-.305 2.582-.816 1.613-.511 3.084-.957 4.414-1.339 1.33-.38 2.08-.55 2.25-.508.283.071.481.22.595.45.113.229.135.485.064.769z" fill="#A1A1A1"/></g><path stroke="#C6C6C6" fill="#D8D8D8" d="M46.521 5.425l3.657 3.41-1.125 4.872-4.781 1.461-3.657-3.41 1.125-4.871z"/></g></svg>
|
||||
|
Before Width: | Height: | Size: 1.3 KiB |
@ -1,19 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="20px" height="20px" viewBox="0 0 20 20" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<!-- Generator: Sketch 44.1 (41455) - http://www.bohemiancoding.com/sketch -->
|
||||
<title>Check</title>
|
||||
<desc>Created with Sketch.</desc>
|
||||
<defs></defs>
|
||||
<g id="Pages" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||
<g id="Home-view" transform="translate(-1086.000000, -34.000000)">
|
||||
<g id="Notification-2---Success" transform="translate(1066.000000, 14.000000)">
|
||||
<g id="Check" transform="translate(20.000000, 20.000000)">
|
||||
<g id="check" transform="translate(5.500000, 7.000000)" fill-rule="nonzero" fill="#27B70F">
|
||||
<path d="M9.34239514,1.3983456 C9.34239514,1.55907497 9.28613986,1.69569495 9.1736293,1.80820551 L4.80982666,6.17200815 L3.99010683,6.99172798 C3.87759626,7.10423854 3.74097629,7.16049383 3.58024691,7.16049383 C3.41951753,7.16049383 3.28289756,7.10423854 3.170387,6.99172798 L2.35066717,6.17200815 L0.168765848,3.99010683 C0.0562552826,3.87759626 0,3.74097629 0,3.58024691 C0,3.41951753 0.0562552826,3.28289756 0.168765848,3.170387 L0.98848568,2.35066717 C1.10099625,2.2381566 1.23761622,2.18190132 1.3983456,2.18190132 C1.55907497,2.18190132 1.69569495,2.2381566 1.80820551,2.35066717 L3.58024691,4.12873592 L7.53418963,0.168765848 C7.6467002,0.0562552826 7.78332017,0 7.94404955,0 C8.10477893,0 8.2413989,0.0562552826 8.35390947,0.168765848 L9.1736293,0.98848568 C9.28613986,1.10099625 9.34239514,1.23761622 9.34239514,1.3983456 Z" id="Shape"></path>
|
||||
</g>
|
||||
<rect id="Rectangle-2" stroke="#27B70F" x="0.5" y="0.5" width="19" height="19" rx="9.5"></rect>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 77 KiB |
|
Before Width: | Height: | Size: 381 KiB |
|
Before Width: | Height: | Size: 345 KiB |
|
Before Width: | Height: | Size: 2.4 KiB |
@ -1 +0,0 @@
|
||||
<svg width="24" height="17" xmlns="http://www.w3.org/2000/svg"><text transform="translate(-23 -9)" fill="#4B515A" fill-rule="evenodd" font-size="24" font-family="AppleColorEmoji, Apple Color Emoji"><tspan x="23" y="28">👕</tspan></text></svg>
|
||||
|
Before Width: | Height: | Size: 245 B |
|
Before Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 5.1 KiB |
|
Before Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 4.8 KiB |
|
Before Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 2.6 KiB |
@ -1,25 +0,0 @@
|
||||
/**
|
||||
* CTAWrapper
|
||||
*
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
function CTAWrapper({ children }) {
|
||||
return <div style={style}>{children}</div>;
|
||||
}
|
||||
|
||||
const style = {
|
||||
position: 'fixed',
|
||||
top: '0',
|
||||
right: '0',
|
||||
display: 'flex',
|
||||
zIndex: '1050',
|
||||
};
|
||||
|
||||
CTAWrapper.propTypes = {
|
||||
children: PropTypes.node.isRequired,
|
||||
};
|
||||
|
||||
export default CTAWrapper;
|
||||
@ -1,27 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* DownloadInfo
|
||||
*
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import Icon from '../../assets/icons/icon_success.svg';
|
||||
import styles from './styles.scss';
|
||||
|
||||
function DownloadInfo() {
|
||||
return (
|
||||
<div className={styles.wrapper}>
|
||||
<div className={styles.content}>
|
||||
<img src={Icon} alt="info" />
|
||||
<div>
|
||||
<FormattedMessage id="app.components.DownloadInfo.download" />
|
||||
<br />
|
||||
<FormattedMessage id="app.components.DownloadInfo.text" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default DownloadInfo;
|
||||
@ -1,34 +0,0 @@
|
||||
.wrapper {
|
||||
width: 372px;
|
||||
height: 159px;
|
||||
border-radius: 2px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding-top: 3rem;
|
||||
// color: #F64D0A;
|
||||
text-align: center;
|
||||
font-family: Lato;
|
||||
font-size: 1.3rem;
|
||||
> img {
|
||||
width: 2.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
> div {
|
||||
padding-top: 9px;
|
||||
line-height: 18px;
|
||||
> span:first-child {
|
||||
color: #333740;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
> span {
|
||||
color: #787E8F;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,95 +0,0 @@
|
||||
/*
|
||||
* Copyright@React-FullStory (https://github.com/cereallarceny/react-fullstory)
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const canUseDOM = !!(
|
||||
typeof window !== 'undefined' &&
|
||||
window.document &&
|
||||
window.document.createElement
|
||||
);
|
||||
|
||||
export const getWindowFullStory = () => window[window['_fs_namespace']];
|
||||
|
||||
class FullStory extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
window['_fs_debug'] = false;
|
||||
window['_fs_host'] = 'fullstory.com';
|
||||
window['_fs_org'] = props.org;
|
||||
window['_fs_namespace'] = 'FS';
|
||||
(function(m,n,e,t,l,o,g,y) {
|
||||
if (e in m) {
|
||||
if(m.console && m.console.log) {
|
||||
m.console.log('FullStory namespace conflict. Please set window["_fs_namespace"].');
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
g = m[e]= function(a,b,s) {
|
||||
g.q ? g.q.push([a,b,s]) : g._api(a,b,s);
|
||||
};
|
||||
g.q=[];
|
||||
o = n.createElement(t);
|
||||
o.async = 1;
|
||||
o.src = `https://${window._fs_host}/s/fs.js`;
|
||||
y = n.getElementsByTagName(t)[0];
|
||||
y.parentNode.insertBefore(o,y);
|
||||
g.identify = function(i,v,s) {
|
||||
g(l,{ uid:i },s);
|
||||
|
||||
if (v) {
|
||||
g(l,v,s);
|
||||
}
|
||||
};
|
||||
g.setUserVars = function(v,s) {
|
||||
g(l,v,s);
|
||||
};
|
||||
g.event = function(i,v,s) {
|
||||
g('event',{ n:i,p:v },s);
|
||||
};
|
||||
g.shutdown = function() {
|
||||
g("rec",!1);
|
||||
};
|
||||
g.restart = function() {
|
||||
g("rec",!0);
|
||||
};
|
||||
g.consent = function(a) {
|
||||
g("consent",!arguments.length||a);
|
||||
};
|
||||
g.identifyAccount = function(i,v) {
|
||||
o = 'account';
|
||||
v = v||{};
|
||||
v.acctId = i;
|
||||
g(o,v);
|
||||
};
|
||||
g.clearUserCookie = function() {};
|
||||
})(window, document, window['_fs_namespace'], 'script', 'user');
|
||||
}
|
||||
|
||||
shouldComponentUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (!canUseDOM || !getWindowFullStory()) return false;
|
||||
|
||||
getWindowFullStory().shutdown();
|
||||
|
||||
delete getWindowFullStory();
|
||||
}
|
||||
|
||||
render() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
FullStory.propTypes = {
|
||||
org: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export default FullStory;
|
||||
@ -1,12 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* Header
|
||||
*
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import styles from './styles.scss';
|
||||
|
||||
export default function Header() {
|
||||
return <div className={styles.header} />;
|
||||
}
|
||||
@ -1,15 +0,0 @@
|
||||
// Import
|
||||
@import "../../styles/variables/variables";
|
||||
|
||||
.header { /* stylelint-ignore */
|
||||
width: 100%;
|
||||
height: $header-height;
|
||||
position: fixed;
|
||||
z-index: 1040;
|
||||
left: $left-menu-width;
|
||||
|
||||
box-shadow: 0 1px 2px 0 rgba(40, 42, 49, 0.16);
|
||||
background-color: $white;
|
||||
|
||||
line-height: $header-height;
|
||||
}
|
||||
@ -1,8 +0,0 @@
|
||||
import Loadable from 'react-loadable';
|
||||
|
||||
import LoadingIndicator from 'components/LoadingIndicator';
|
||||
|
||||
export default Loadable({
|
||||
loader: () => import('./index'),
|
||||
loading: LoadingIndicator,
|
||||
});
|
||||
@ -1,36 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* HomePageBlock
|
||||
*
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import cn from 'classnames';
|
||||
|
||||
import styles from './styles.scss';
|
||||
|
||||
function HomePageBlock({ children, className }) {
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
className,
|
||||
styles.homePageBlock,
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
HomePageBlock.defaultProps = {
|
||||
children: '',
|
||||
className: '',
|
||||
};
|
||||
|
||||
HomePageBlock.propTypes = {
|
||||
children: PropTypes.node,
|
||||
className: PropTypes.string,
|
||||
};
|
||||
|
||||
export default HomePageBlock;
|
||||
@ -1,8 +0,0 @@
|
||||
.homePageBlock {
|
||||
width: 100%;
|
||||
margin-bottom: 34px;
|
||||
background: #ffffff;
|
||||
padding: 20px 30px 30px 30px;
|
||||
box-shadow: 0 2px 4px 0 #E3E9F3;
|
||||
border-radius: 3px;
|
||||
}
|
||||
@ -1,161 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* InstallPluginPopup
|
||||
*
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { Modal, ModalHeader, ModalBody } from 'reactstrap';
|
||||
import { map } from 'lodash';
|
||||
import cn from 'classnames';
|
||||
|
||||
import Official from '../Official';
|
||||
// import StarsContainer from 'components/StarsContainer';
|
||||
|
||||
import styles from './styles.scss';
|
||||
|
||||
class InstallPluginPopup extends React.Component {
|
||||
handleClick = () => {
|
||||
this.props.history.push({ pathname: this.props.history.location.pathname });
|
||||
|
||||
if (!this.props.isAlreadyInstalled) {
|
||||
this.context.downloadPlugin(this.props.plugin.id);
|
||||
}
|
||||
}
|
||||
|
||||
toggle = () => {
|
||||
this.props.history.push({
|
||||
pathname: this.props.history.location.pathname,
|
||||
});
|
||||
}
|
||||
|
||||
navLinks = [
|
||||
{
|
||||
content: 'app.components.InstallPluginPopup.navLink.description',
|
||||
name: 'description',
|
||||
},
|
||||
{
|
||||
content: 'app.components.InstallPluginPopup.navLink.screenshots',
|
||||
name: 'screenshots',
|
||||
},
|
||||
{
|
||||
content: 'app.components.InstallPluginPopup.navLink.avis',
|
||||
name: 'avis',
|
||||
},
|
||||
{
|
||||
content: 'app.components.InstallPluginPopup.navLink.faq',
|
||||
name: 'faq',
|
||||
},
|
||||
{
|
||||
content: 'app.components.InstallPluginPopup.navLink.changelog',
|
||||
name: 'changelog',
|
||||
},
|
||||
];
|
||||
|
||||
render() {
|
||||
const descriptions = {
|
||||
short: this.props.plugin.id === 'support-us' ? <FormattedMessage id={this.props.plugin.description.short} /> : this.props.plugin.description.short,
|
||||
long: this.props.plugin.id === 'support-us' ? <FormattedMessage id={this.props.plugin.description.long || this.props.plugin.description.short} /> : this.props.plugin.description.long || this.props.plugin.description.short,
|
||||
};
|
||||
const buttonName = this.props.isAlreadyInstalled ? 'app.components.PluginCard.Button.label.install' : 'app.components.InstallPluginPopup.downloads';
|
||||
|
||||
return (
|
||||
<Modal isOpen={this.props.isOpen} toggle={this.toggle} className={styles.modalPosition}>
|
||||
<ModalHeader toggle={this.toggle} className={styles.modalHeader} />
|
||||
<ModalBody className={styles.modalBody}>
|
||||
<div className={styles.wrapper}>
|
||||
|
||||
<div className={styles.headerWrapper}>
|
||||
<div className={styles.logo}><img src={`${this.props.plugin.logo}`} alt="icon" /></div>
|
||||
<div className={styles.headerInfo}>
|
||||
<div className={styles.name}>{this.props.plugin.name}</div>
|
||||
<div className={styles.ratings}>
|
||||
{/*}
|
||||
<StarsContainer ratings={this.props.plugin.ratings} />
|
||||
<div>
|
||||
<span style={{ fontWeight: '600', color: '#333740', fontSize: '12px'}}>{this.props.plugin.ratings}</span>
|
||||
<span style={{ fontWeight: '500', color: '#666666', fontSize: '11px' }}>/5</span>
|
||||
</div>
|
||||
*/}
|
||||
<Official style={{ marginTop: '0' }} />
|
||||
</div>
|
||||
<div className={styles.headerDescription}>
|
||||
{descriptions.short}
|
||||
</div>
|
||||
<div className={styles.headerButtonContainer}>
|
||||
<div>
|
||||
<i className={`fa fa-${this.props.plugin.isCompatible ? 'check' : 'times'}`} />
|
||||
<FormattedMessage id={`app.components.PluginCard.compatible${this.props.plugin.id === 'support-us' ? 'Community' : ''}`} />
|
||||
</div>
|
||||
<div>
|
||||
<div>
|
||||
{/*}
|
||||
<span style={{ fontWeight: '600' }}>+{this.props.plugin.downloads_nb}k </span><FormattedMessage id="app.components.InstallPluginPopup.downloads" />
|
||||
*/}
|
||||
</div>
|
||||
<div className={styles.buttonWrapper} onClick={this.handleClick}>
|
||||
<div>
|
||||
<FormattedMessage id={buttonName} />
|
||||
</div>
|
||||
{/* Uncomment whebn prices are running}
|
||||
<div>{this.props.plugin.price} €</div>
|
||||
*/}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.navContainer}>
|
||||
{map(this.navLinks, link => {
|
||||
const isActive = this.props.history.location.hash.split('::')[1] === link.name;
|
||||
|
||||
return (
|
||||
<div
|
||||
key={link.name}
|
||||
className={cn(isActive ? styles.navLink : '', link.name !== 'description' ? styles.notAllowed : '')}
|
||||
onClick={() => {
|
||||
if (link.name === 'description') {
|
||||
this.props.history.push({ pathname: this.props.history.location.pathname, hash: `${this.props.plugin.id}::${link.name}` });
|
||||
}
|
||||
}}
|
||||
style={isActive ? { paddingTop: '4px'} : { paddingTop: '6px' }}
|
||||
>
|
||||
<FormattedMessage id={link.content} />
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div className={styles.pluginDescription}>
|
||||
{descriptions.long}
|
||||
</div>
|
||||
</ModalBody>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
InstallPluginPopup.contextTypes = {
|
||||
downloadPlugin: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
InstallPluginPopup.defaultProps = {
|
||||
description: {
|
||||
short: 'app.Components.InstallPluginPopup.noDescription',
|
||||
},
|
||||
};
|
||||
|
||||
InstallPluginPopup.propTypes = {
|
||||
description: PropTypes.shape({
|
||||
long: PropTypes.string,
|
||||
short: PropTypes.string,
|
||||
}),
|
||||
history: PropTypes.object.isRequired,
|
||||
isAlreadyInstalled: PropTypes.bool.isRequired,
|
||||
isOpen: PropTypes.bool.isRequired,
|
||||
plugin: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
export default InstallPluginPopup;
|
||||
@ -1,199 +0,0 @@
|
||||
.buttonWrapper {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
min-width: 136px;
|
||||
height: 30px;
|
||||
margin-left: 20px;
|
||||
border-radius: 0.3rem;
|
||||
background: linear-gradient(315deg, #0097F6 0%, #005EEA 100%);
|
||||
|
||||
color: white;
|
||||
font-size: 13px;
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
|
||||
cursor: pointer;
|
||||
&:focus {
|
||||
outline: 0;
|
||||
}
|
||||
> div {
|
||||
text-align: center;
|
||||
text-transform: capitalize;
|
||||
&:first-child {
|
||||
flex-grow: 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.headerButtonContainer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding-top: 13px;
|
||||
line-height: 29px;
|
||||
|
||||
> div {
|
||||
font-size: 13px;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
> div:first-child {
|
||||
> i {
|
||||
margin-right: 10px;
|
||||
}
|
||||
color: #5A9E06;
|
||||
}
|
||||
|
||||
> div:nth-child(2) {
|
||||
display: flex;
|
||||
> span:nth-child(2) {
|
||||
font-style: normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.headerDescription {
|
||||
height: 48px;
|
||||
padding-top: 12px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.headerInfo {
|
||||
flex-grow: 1;
|
||||
padding-top: 7px;
|
||||
padding-left: 30px;
|
||||
}
|
||||
|
||||
.headerWrapper {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.logo {
|
||||
flex-shrink: 0;
|
||||
width: 144px;
|
||||
height: 144px;
|
||||
background: #FAFAFB;
|
||||
border: 1px solid #F3F3F7;
|
||||
border-radius: 3px;
|
||||
background-size: contain;
|
||||
line-height: 144px;
|
||||
text-align: center;
|
||||
> img {
|
||||
min-width: 72px;
|
||||
// max-height: 72px;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.modalHeader {
|
||||
margin: 0 1.4rem !important;
|
||||
padding: 1.4rem 0 0 0 !important;
|
||||
border-bottom: 0 !important;
|
||||
> button {
|
||||
margin-right: -1rem !important;
|
||||
color: #C3C5C8;
|
||||
opacity: 1;
|
||||
font-size: 1.8rem;
|
||||
font-weight: 100;
|
||||
z-index: 999;
|
||||
&:hover, &:focus {
|
||||
color: #C3C5C8;
|
||||
opacity: 1;
|
||||
outline: 0!important;
|
||||
cursor: pointer;
|
||||
}
|
||||
> span {
|
||||
display: none;
|
||||
}
|
||||
&:before {
|
||||
-webkit-font-smoothing: antialiased;
|
||||
content: '\F00d';
|
||||
font-family: 'FontAwesome';
|
||||
font-weight: 400;
|
||||
font-size: 1.2rem;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.modalBody {
|
||||
padding: 5px 0px !important;
|
||||
}
|
||||
|
||||
.modalPosition {
|
||||
> div {
|
||||
border:none;
|
||||
border-radius: 2px;
|
||||
width: 74.5rem;
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.name {
|
||||
padding-bottom: 1px;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.navContainer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
height: 36px;
|
||||
|
||||
margin-top: 38px;
|
||||
padding: 0 30px;
|
||||
|
||||
background-color: #FAFAFB;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
text-transform: capitalize;
|
||||
|
||||
> div {
|
||||
flex-grow: 2;
|
||||
text-align: center;
|
||||
line-height: 23px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.navLink {
|
||||
border-top: 2px solid #1C5DE7;
|
||||
}
|
||||
|
||||
.notAllowed {
|
||||
opacity: .3;
|
||||
cursor: not-allowed !important;
|
||||
}
|
||||
.pluginDescription {
|
||||
padding: 24px 30px;
|
||||
font-size: 13px;
|
||||
line-height: 18px;
|
||||
}
|
||||
|
||||
.ratings {
|
||||
display: flex;
|
||||
line-height: 18px;
|
||||
|
||||
> div:last-child {
|
||||
padding-top: 1px;
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
||||
|
||||
.starsContainer {
|
||||
display: flex;
|
||||
> div {
|
||||
color: #EED348;
|
||||
> i {
|
||||
margin-right: 2px;
|
||||
}
|
||||
}
|
||||
> div:last-child {
|
||||
color: #B3B5B9;
|
||||
}
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
padding: 0 30px;
|
||||
}
|
||||
@ -1,65 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* LeftMenuFooter
|
||||
*
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { defineMessages, FormattedMessage } from 'react-intl';
|
||||
import { PropTypes } from 'prop-types';
|
||||
|
||||
import LeftMenuLink from '../LeftMenuLink';
|
||||
|
||||
import styles from './styles.scss';
|
||||
import messages from './messages.json';
|
||||
defineMessages(messages);
|
||||
|
||||
function LeftMenuFooter({ version, ...rest }) {
|
||||
const staticLinks = [
|
||||
{
|
||||
icon: 'book',
|
||||
label: 'documentation',
|
||||
destination: 'https://strapi.io/documentation',
|
||||
},
|
||||
{
|
||||
icon: 'question-circle',
|
||||
label: 'help',
|
||||
destination: 'https://strapi.io/help',
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className={styles.leftMenuFooter}>
|
||||
<ul className={styles.list}>
|
||||
{staticLinks.map(link => (
|
||||
<LeftMenuLink
|
||||
{...rest}
|
||||
{...link}
|
||||
destination={messages[link.label].id}
|
||||
key={link.label}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
<div className={styles.poweredBy}>
|
||||
<FormattedMessage {...messages.poweredBy} key="poweredBy" />
|
||||
<a key="website" href="https://strapi.io" target="_blank">
|
||||
Strapi
|
||||
</a>
|
||||
|
||||
<a
|
||||
href={`https://github.com/strapi/strapi/releases/tag/v${version}`}
|
||||
key="github"
|
||||
target="_blank"
|
||||
>
|
||||
v{version}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
LeftMenuFooter.propTypes = {
|
||||
version: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export default LeftMenuFooter;
|
||||
@ -1,14 +0,0 @@
|
||||
{
|
||||
"documentation": {
|
||||
"id": "app.components.LeftMenuFooter.documentation",
|
||||
"defaultMessage": "Documentation"
|
||||
},
|
||||
"help": {
|
||||
"id": "app.components.LeftMenuFooter.help",
|
||||
"defaultMessage": "Help"
|
||||
},
|
||||
"poweredBy": {
|
||||
"id": "app.components.LeftMenuFooter.poweredBy",
|
||||
"defaultMessage": "Proudly powered by "
|
||||
}
|
||||
}
|
||||
@ -1,30 +0,0 @@
|
||||
// Import
|
||||
@import "../../styles/variables/variables";
|
||||
|
||||
.leftMenuFooter { /* stylelint-disable */
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
background: $left-menu-bg;
|
||||
bottom: 0;
|
||||
}
|
||||
|
||||
.list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.poweredBy {
|
||||
width: 100%;
|
||||
bottom: 0;
|
||||
height: 3rem;
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
line-height: 3rem;
|
||||
background-color: rgba(255, 255, 255, .02);
|
||||
font-size: 1rem;
|
||||
font-weight: 400;
|
||||
letter-spacing: 0.05rem;
|
||||
vertical-align: middle;
|
||||
color: $strapi-gray-light;
|
||||
}
|
||||
@ -1,22 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* LeftMenuHeader
|
||||
*
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import styles from './styles.scss';
|
||||
|
||||
function LeftMenuHeader() {
|
||||
return (
|
||||
<div className={styles.leftMenuHeader}>
|
||||
<Link to="/" className={styles.leftMenuHeaderLink}>
|
||||
<span className={styles.projectName} />
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default LeftMenuHeader;
|
||||
@ -1,31 +0,0 @@
|
||||
// Import
|
||||
@import "../../styles/variables/variables";
|
||||
|
||||
.leftMenuHeader { /* stylelint-ignore */
|
||||
background: $left-menu-link-hover;
|
||||
height: $header-height;
|
||||
background: linear-gradient(100deg , #1C5DE7, #1C91E7);
|
||||
}
|
||||
|
||||
.projectName {
|
||||
display: block;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
line-height: $header-height;
|
||||
vertical-align: middle;
|
||||
font-size: 2rem;
|
||||
letter-spacing: 0.2rem;
|
||||
color: $white;
|
||||
|
||||
background-image: url('../../assets/images/logo-strapi.png');
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
background-size: auto 3rem;
|
||||
}
|
||||
|
||||
.leftMenuHeaderLink {
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
@ -1,94 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* LeftMenuLink
|
||||
*
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { startsWith, upperFirst } from 'lodash';
|
||||
import PropTypes from 'prop-types';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import en from '../../translations/en.json';
|
||||
|
||||
import styles from './styles.scss';
|
||||
|
||||
function LeftMenuLink(props) {
|
||||
const isLinkActive = startsWith(
|
||||
props.location.pathname.replace('/admin', '').concat('/'),
|
||||
props.destination.concat('/'),
|
||||
);
|
||||
|
||||
const plugin =
|
||||
props.source !== 'content-manager' && props.source !== '' ? (
|
||||
<div className={styles.plugin}>
|
||||
<span>{upperFirst(props.source.split('-').join(' '))}</span>
|
||||
</div>
|
||||
) : (
|
||||
''
|
||||
);
|
||||
|
||||
// Check if messageId exists in en locale to prevent warning messages
|
||||
const content = en[props.label] ? (
|
||||
<FormattedMessage
|
||||
id={props.label}
|
||||
defaultMessage="{label}"
|
||||
values={{
|
||||
label: `${props.label}`,
|
||||
}}
|
||||
className={styles.linkLabel}
|
||||
/>
|
||||
) : (
|
||||
<span className={styles.linkLabel}>{props.label}</span>
|
||||
);
|
||||
|
||||
// Icon.
|
||||
const icon = <i className={`${styles.linkIcon} fa-${props.icon} fa`} />;
|
||||
|
||||
// Create external or internal link.
|
||||
const link = props.destination.includes('http') ? (
|
||||
<a
|
||||
className={`${styles.link} ${isLinkActive ? styles.linkActive : ''}`}
|
||||
href={props.destination}
|
||||
target="_blank"
|
||||
>
|
||||
{icon}
|
||||
{content}
|
||||
</a>
|
||||
) : (
|
||||
<Link
|
||||
className={`${styles.link} ${isLinkActive ? styles.linkActive : ''}`}
|
||||
to={{
|
||||
pathname: props.destination,
|
||||
search: props.source ? `?source=${props.source}` : '',
|
||||
}}
|
||||
>
|
||||
{icon}
|
||||
{content}
|
||||
</Link>
|
||||
);
|
||||
|
||||
return (
|
||||
<li className={styles.item}>
|
||||
{link}
|
||||
{plugin}
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
LeftMenuLink.propTypes = {
|
||||
destination: PropTypes.string.isRequired,
|
||||
icon: PropTypes.string.isRequired,
|
||||
label: PropTypes.string.isRequired,
|
||||
location: PropTypes.shape({
|
||||
pathname: PropTypes.string,
|
||||
}).isRequired,
|
||||
source: PropTypes.string,
|
||||
};
|
||||
|
||||
LeftMenuLink.defaultProps = {
|
||||
source: '',
|
||||
};
|
||||
|
||||
export default LeftMenuLink;
|
||||
@ -1,89 +0,0 @@
|
||||
// Import
|
||||
@import "../../styles/variables/variables";
|
||||
|
||||
.item {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
&:not(:first-child) {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.plugin {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
top: 10px; left: calc(100% - 4px);
|
||||
display: inline-block;
|
||||
width: auto;
|
||||
height: 20px;
|
||||
transition: right 1s ease-in-out;
|
||||
|
||||
span{
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
width: auto;
|
||||
height: 20px;
|
||||
padding: 0 14px 0 10px;
|
||||
color: #ffffff;
|
||||
font-size: 12px;
|
||||
line-height: 20px;
|
||||
background: #0097f7;
|
||||
border-radius: 3px;
|
||||
transition: transform .3s ease-in-out;
|
||||
white-space: pre;
|
||||
|
||||
&:hover{
|
||||
transform: translateX(calc(-100% + 9px));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.link {
|
||||
padding-top: .9rem;
|
||||
padding-bottom: 0.2rem;
|
||||
padding-left: 1.6rem;
|
||||
min-height: 3.6rem;
|
||||
border-left: 0.3rem solid transparent;
|
||||
cursor: pointer;
|
||||
color: $left-menu-link-color;
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
|
||||
&:hover {
|
||||
color: $white;
|
||||
background: $left-menu-link-hover;
|
||||
border-left: 0.3rem solid $strapi-blue;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&:focus {
|
||||
color: $white;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&:visited {
|
||||
color: $left-menu-link-color;
|
||||
}
|
||||
}
|
||||
|
||||
.linkActive {
|
||||
color: $white !important;
|
||||
border-left: 0.3rem solid $strapi-blue;
|
||||
}
|
||||
|
||||
.linkIcon {
|
||||
position: relative;
|
||||
margin-right: 1.2rem;
|
||||
font-size: 1.2rem;
|
||||
width: 1.4rem;
|
||||
height: 1.2rem;
|
||||
padding-bottom: 0.2rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.linkLabel {
|
||||
display: inline-block;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
@ -1,140 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* LeftMenuLinkContainer
|
||||
*
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { get, snakeCase, isEmpty, map, sortBy } from 'lodash';
|
||||
|
||||
import LeftMenuLink from '../LeftMenuLink';
|
||||
|
||||
import styles from './styles.scss';
|
||||
import messages from './messages.json';
|
||||
|
||||
function LeftMenuLinkContainer({ plugins, ...rest }) {
|
||||
// Generate the list of sections
|
||||
const pluginsSections = Object.keys(plugins).reduce((acc, current) => {
|
||||
plugins[current].leftMenuSections.forEach((section = {}) => {
|
||||
if (!isEmpty(section.links)) {
|
||||
acc[snakeCase(section.name)] = {
|
||||
name: section.name,
|
||||
links: get(acc[snakeCase(section.name)], 'links', []).concat(
|
||||
section.links.map(link => {
|
||||
link.source = current;
|
||||
link.plugin = !isEmpty(plugins[link.plugin])
|
||||
? link.plugin
|
||||
: plugins[current].id;
|
||||
|
||||
return link;
|
||||
}),
|
||||
),
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
return acc;
|
||||
}, {});
|
||||
|
||||
const linkSections = Object.keys(pluginsSections).map((current, j) => {
|
||||
const contentTypes = pluginsSections[current].links;
|
||||
|
||||
return (
|
||||
<div key={j}>
|
||||
<p className={styles.title}>{pluginsSections[current].name}</p>
|
||||
<ul className={styles.list}>
|
||||
{sortBy(contentTypes, 'label').map((link, i) => (
|
||||
<LeftMenuLink
|
||||
{...rest}
|
||||
key={`${i}-${link.label}`}
|
||||
icon={link.icon || 'caret-right'}
|
||||
label={link.label}
|
||||
destination={`/plugins/${link.plugin}/${link.destination}`}
|
||||
source={link.source}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
// Check if the plugins list is empty or not and display plugins by name
|
||||
const pluginsLinks = !isEmpty(plugins) ? (
|
||||
map(sortBy(plugins, 'name'), plugin => {
|
||||
if (plugin.id !== 'email' && plugin.id !== 'settings-manager') {
|
||||
const basePath = `/plugins/${get(plugin, 'id')}`;
|
||||
// NOTE: this should be dynamic
|
||||
const destination =
|
||||
plugin.id === 'content-manager'
|
||||
? `${basePath}/ctm-configurations`
|
||||
: basePath;
|
||||
|
||||
return (
|
||||
<LeftMenuLink
|
||||
{...rest}
|
||||
key={get(plugin, 'id')}
|
||||
icon={get(plugin, 'icon') || 'plug'}
|
||||
label={get(plugin, 'name')}
|
||||
destination={destination}
|
||||
/>
|
||||
);
|
||||
}
|
||||
})
|
||||
) : (
|
||||
<li key="emptyList" className={styles.noPluginsInstalled}>
|
||||
<FormattedMessage {...messages.noPluginsInstalled} key="noPlugins" />.
|
||||
</li>
|
||||
);
|
||||
|
||||
const hasSettingsManager = get(plugins, 'settings-manager', null);
|
||||
const staticLinks = [
|
||||
{
|
||||
icon: 'list',
|
||||
label: messages.listPlugins.id,
|
||||
destination: '/list-plugins',
|
||||
},
|
||||
{
|
||||
icon: 'shopping-basket',
|
||||
label: messages.installNewPlugin.id,
|
||||
destination: '/marketplace',
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className={styles.leftMenuLinkContainer}>
|
||||
{linkSections}
|
||||
<div>
|
||||
<p className={styles.title}>
|
||||
<FormattedMessage {...messages.plugins} />
|
||||
</p>
|
||||
<ul className={styles.list}>{pluginsLinks}</ul>
|
||||
</div>
|
||||
<div>
|
||||
<p className={styles.title}>
|
||||
<FormattedMessage {...messages.general} />
|
||||
</p>
|
||||
<ul className={styles.list}>
|
||||
{staticLinks.map(link => (
|
||||
<LeftMenuLink {...rest} key={link.destination} {...link} />
|
||||
))}
|
||||
{hasSettingsManager && (
|
||||
<LeftMenuLink
|
||||
{...rest}
|
||||
icon="gear"
|
||||
label={messages.configuration.id}
|
||||
destination="/plugins/settings-manager"
|
||||
/>
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
LeftMenuLinkContainer.propTypes = {
|
||||
plugins: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
export default LeftMenuLinkContainer;
|
||||
@ -1,26 +0,0 @@
|
||||
{
|
||||
"listPlugins": {
|
||||
"id": "app.components.LeftMenuLinkContainer.listPlugins",
|
||||
"defaultMessage": "Plugins"
|
||||
},
|
||||
"installNewPlugin": {
|
||||
"id": "app.components.LeftMenuLinkContainer.installNewPlugin",
|
||||
"defaultMessage": "Marketplace"
|
||||
},
|
||||
"configuration": {
|
||||
"id": "app.components.LeftMenuLinkContainer.configuration",
|
||||
"defaultMessage": "Configurations"
|
||||
},
|
||||
"plugins": {
|
||||
"id": "app.components.LeftMenuLinkContainer.plugins",
|
||||
"defaultMessage": "Plugins"
|
||||
},
|
||||
"general": {
|
||||
"id": "app.components.LeftMenuLinkContainer.general",
|
||||
"defaultMessage": "General"
|
||||
},
|
||||
"noPluginsInstalled": {
|
||||
"id": "app.components.LeftMenuLinkContainer.noPluginsInstalled",
|
||||
"defaultMessage": "No plugins installed yet"
|
||||
}
|
||||
}
|
||||
@ -1,40 +0,0 @@
|
||||
// Import
|
||||
@import "../../styles/variables/variables";
|
||||
|
||||
.leftMenuLinkContainer { /* stylelint-ignore */
|
||||
padding-top: .6rem;
|
||||
padding-bottom: 10.2rem; // LeftMenuFooter height
|
||||
position: absolute;
|
||||
top: 60px;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.title {
|
||||
padding-left: 2rem;
|
||||
padding-right: 1.6rem;
|
||||
padding-top: 1rem;
|
||||
margin-bottom: .8rem;
|
||||
color: $left-menu-title-color;
|
||||
text-transform: uppercase;
|
||||
font-size: 1.1rem;
|
||||
letter-spacing: .1rem;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.noPluginsInstalled {
|
||||
color: $white;
|
||||
padding-left: 1.6rem;
|
||||
padding-right: 1.6rem;
|
||||
font-weight: 300;
|
||||
min-height: 3.6rem;
|
||||
padding-top: .9rem;
|
||||
}
|
||||
@ -1,77 +0,0 @@
|
||||
/*
|
||||
*
|
||||
*
|
||||
* ListPlugins
|
||||
*
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { map, size } from 'lodash';
|
||||
|
||||
// Design
|
||||
import { Button } from 'strapi-helper-plugin';
|
||||
import Row from '../Row';
|
||||
|
||||
import styles from './styles.scss';
|
||||
|
||||
class ListPlugins extends React.Component {
|
||||
render() {
|
||||
const listSize = size(this.props.plugins);
|
||||
let titleType = listSize === 1 ? 'singular' : 'plural';
|
||||
|
||||
if (listSize === 0) {
|
||||
titleType = 'none';
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<div className={styles.titleContainer}>
|
||||
<div>
|
||||
<FormattedMessage
|
||||
id={`app.components.listPlugins.title.${titleType}`}
|
||||
values={{ number: listSize }}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Button
|
||||
label="app.components.listPlugins.button"
|
||||
onClick={() => this.props.history.push('/install-plugin')}
|
||||
secondaryHotlineAdd
|
||||
style={{ display: 'none' }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="container-fluid">
|
||||
<div className="row">
|
||||
<div className={styles.ulContainer}>
|
||||
<ul>
|
||||
{map(this.props.plugins, (plugin, key) => (
|
||||
<Row
|
||||
name={key}
|
||||
key={plugin.name}
|
||||
plugin={plugin}
|
||||
onDeleteClick={this.props.onDeleteClick}
|
||||
pluginActionSucceeded={this.props.pluginActionSucceeded}
|
||||
onDeleteConfirm={this.props.onDeleteConfirm}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ListPlugins.propTypes = {
|
||||
history: PropTypes.object.isRequired,
|
||||
onDeleteClick: PropTypes.func.isRequired,
|
||||
onDeleteConfirm: PropTypes.func.isRequired,
|
||||
pluginActionSucceeded: PropTypes.bool.isRequired,
|
||||
plugins: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
export default ListPlugins;
|
||||
@ -1,76 +0,0 @@
|
||||
.container {
|
||||
padding-top: 1.6rem;
|
||||
background-color: #FFFFFF;
|
||||
box-shadow: 0 2px 4px #E3E9F3;
|
||||
}
|
||||
|
||||
.titleContainer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding-right: 1.8rem;
|
||||
padding-left: 1.8rem;
|
||||
font-size: 1.8rem;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.ulContainer {
|
||||
width: 100%;
|
||||
padding-top: 1.5rem;
|
||||
> ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
> li {
|
||||
height: 5.4rem !important;
|
||||
line-height: 5.4rem !important;
|
||||
padding-right: 3.2rem;
|
||||
padding-left: 1.5rem;
|
||||
> div:first-child {
|
||||
margin: 0;
|
||||
> div:first-child {
|
||||
padding-left: 0!important;
|
||||
// margin-right: 1.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
> li:last-child {
|
||||
> div {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.pluginContent {
|
||||
text-align: left !important;
|
||||
> span:first-child {
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.7px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
> span:last-child {
|
||||
font-size: 13px;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.icoContainer {
|
||||
width: 70px;
|
||||
height: 36px;
|
||||
margin: auto 0;
|
||||
line-height: 36px;
|
||||
text-align: center;
|
||||
border: 1px solid rgba(28,93,231,0.1);
|
||||
border-radius: 3px;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.actionContainer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.nameWrapper {
|
||||
display: flex;
|
||||
}
|
||||
@ -1,78 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* Logout
|
||||
*
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import { get } from 'lodash';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
ButtonDropdown,
|
||||
DropdownItem,
|
||||
DropdownMenu,
|
||||
DropdownToggle,
|
||||
} from 'reactstrap';
|
||||
import { auth } from 'strapi-helper-plugin';
|
||||
|
||||
import styles from './styles.scss';
|
||||
|
||||
class Logout extends React.Component {
|
||||
// eslint-disable-line react/prefer-stateless-function
|
||||
state = { isOpen: false };
|
||||
|
||||
handleGoTo = () => {
|
||||
const id = get(auth.getUserInfo(), 'id') || get(auth.getUserInfo(), '_id');
|
||||
this.props.history.push({
|
||||
pathname: `/plugins/content-manager/administrator/${id}`,
|
||||
search:
|
||||
'?redirectUrl=/plugins/content-manager/administrator/?page=0&limit=0&sort=id&source=admin',
|
||||
});
|
||||
};
|
||||
|
||||
handleGoToAdministrator = () => {
|
||||
this.props.history.push({
|
||||
pathname: '/plugins/content-manager/administrator',
|
||||
search: '?source=admin',
|
||||
});
|
||||
};
|
||||
|
||||
handleLogout = () => {
|
||||
auth.clearAppStorage();
|
||||
this.props.history.push('/plugins/users-permissions/auth/login');
|
||||
};
|
||||
|
||||
toggle = () => this.setState({ isOpen: !this.state.isOpen });
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className={styles.logout}>
|
||||
<ButtonDropdown isOpen={this.state.isOpen} toggle={this.toggle}>
|
||||
<DropdownToggle>
|
||||
{get(auth.getUserInfo(), 'username')}
|
||||
<i className="fa fa-caret-down" alt={`${this.state.isOpen}`} />
|
||||
</DropdownToggle>
|
||||
<DropdownMenu className={styles.dropDownContent}>
|
||||
<DropdownItem onClick={this.handleGoTo} className={styles.item}>
|
||||
<FormattedMessage id="app.components.Logout.profile" />
|
||||
</DropdownItem>
|
||||
<DropdownItem
|
||||
onClick={this.handleGoToAdministrator}
|
||||
className={styles.item}
|
||||
>
|
||||
<FormattedMessage id="app.components.Logout.admin" />
|
||||
</DropdownItem>
|
||||
<DropdownItem onClick={this.handleLogout}>
|
||||
<FormattedMessage id="app.components.Logout.logout" />
|
||||
<i className="fa fa-sign-out" />
|
||||
</DropdownItem>
|
||||
</DropdownMenu>
|
||||
</ButtonDropdown>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withRouter(Logout);
|
||||
@ -1,113 +0,0 @@
|
||||
.logout {
|
||||
position: relative;
|
||||
min-width: 19rem;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
> div {
|
||||
height: 6rem;
|
||||
width: 100%;
|
||||
line-height: 5.8rem;
|
||||
z-index: 999;
|
||||
> button {
|
||||
width: 100%;
|
||||
padding-right: 20px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
color: #333740;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
text-align: right;
|
||||
cursor: pointer;
|
||||
transition: background .2s ease-out;
|
||||
|
||||
&:hover, &:focus, &:active {
|
||||
color: #333740;
|
||||
background-color: #FAFAFB !important;
|
||||
}
|
||||
|
||||
> i {
|
||||
margin-left: 10px;
|
||||
transition: transform .3s ease-out;
|
||||
|
||||
&[alt="true"] {
|
||||
transform: rotateX(180deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:after {
|
||||
position: absolute;
|
||||
right: -1px;
|
||||
top: calc(50% - 10px);
|
||||
content: '';
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
height: 20px;
|
||||
border-left: 1px solid #F3F4F4;
|
||||
transition: opacity .2s ease-out;
|
||||
}
|
||||
|
||||
&:hover:after {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
}
|
||||
.dropDownContent {
|
||||
left: auto !important;
|
||||
min-width: 100%!important;
|
||||
margin: 0 !important;
|
||||
padding: 0;
|
||||
line-height: 1.8rem;
|
||||
border: none!important;
|
||||
border-top-left-radius: 0!important;
|
||||
border-top-right-radius: 0!important;
|
||||
font-size: 14px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 1px 4px 0px rgba(40, 42, 49, 0.05);
|
||||
|
||||
&:before{
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -3px;
|
||||
left: -1px;
|
||||
width: calc(100% + 1px);
|
||||
height: 3px;
|
||||
box-shadow: 0 1px 2px 0 rgba(40, 42, 49, 0.16);
|
||||
}
|
||||
|
||||
> button {
|
||||
height: 40px;
|
||||
padding: 0px 15px;
|
||||
line-height: 40px;
|
||||
&:hover, &:focus, &:active {
|
||||
background-color: #FAFAFB!important;
|
||||
border-radius: 0px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
> button {
|
||||
height: 44px;
|
||||
line-height: 48px;
|
||||
&:hover, &:active {
|
||||
color: #333740;
|
||||
}
|
||||
}
|
||||
|
||||
> button:last-child {
|
||||
color: #F75B1D;
|
||||
> i {
|
||||
margin-left: 10px
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.item {
|
||||
&:active {
|
||||
color: black;
|
||||
}
|
||||
&:hover {
|
||||
background-color: #FAFAFB !important;
|
||||
}
|
||||
}
|
||||
@ -1,86 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* Notification
|
||||
*
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { isObject } from 'lodash';
|
||||
|
||||
import styles from './styles.scss';
|
||||
|
||||
class Notification extends React.Component { // eslint-disable-line react/prefer-stateless-function
|
||||
handleCloseClicked = () => {
|
||||
this.props.onHideNotification(this.props.notification.id);
|
||||
};
|
||||
|
||||
options = {
|
||||
success: {
|
||||
icon: 'fa-check',
|
||||
title: 'Success',
|
||||
class: 'notificationSuccess',
|
||||
},
|
||||
warning: {
|
||||
icon: 'fa-exclamation',
|
||||
title: 'Warning',
|
||||
class: 'notificationWarning',
|
||||
},
|
||||
error: {
|
||||
icon: 'fa-exclamation',
|
||||
title: 'Error',
|
||||
class: 'notificationError',
|
||||
},
|
||||
info: {
|
||||
icon: 'fa-info',
|
||||
title: 'Info',
|
||||
class: 'notificationInfo',
|
||||
},
|
||||
};
|
||||
|
||||
render() {
|
||||
const options = this.options[this.props.notification.status] || this.options.info;
|
||||
const { notification: { message } } = this.props;
|
||||
const content = isObject(message) && message.id ?
|
||||
<FormattedMessage id={message.id} defaultMessage={message.id} values={message.values} />
|
||||
: <FormattedMessage id={message} defaultMessage={message} />;
|
||||
|
||||
return (
|
||||
<li key={this.props.notification.id} className={`${styles.notification} ${styles[options.class]}`} onClick={this.handleCloseClicked}>
|
||||
<i className={`fa ${options.icon} ${styles.notificationIcon}`} />
|
||||
<div className={styles.notificationContent}>
|
||||
<p className={styles.notificationTitle}>
|
||||
{content}
|
||||
</p>
|
||||
</div>
|
||||
<i className={`fa fa-close ${styles.notificationClose}`} onClick={this.handleCloseClicked} />
|
||||
</li>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Notification.defaultProps = {
|
||||
notification: {
|
||||
id: 1,
|
||||
message: 'app.utils.defaultMessage',
|
||||
status: 'success',
|
||||
},
|
||||
};
|
||||
|
||||
Notification.propTypes = {
|
||||
notification: PropTypes.shape({
|
||||
id: PropTypes.number,
|
||||
message: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.shape({
|
||||
id: PropTypes.string,
|
||||
values: PropTypes.object,
|
||||
}),
|
||||
]),
|
||||
status: PropTypes.string,
|
||||
}),
|
||||
onHideNotification: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default Notification;
|
||||
@ -1,125 +0,0 @@
|
||||
/* Import */
|
||||
@import '../../styles/variables/variables';
|
||||
|
||||
.notification {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: stretch;
|
||||
width: 300px;
|
||||
min-height: 60px;
|
||||
margin-bottom: 14px;
|
||||
background: $white;
|
||||
border-radius: 2px;
|
||||
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.15);
|
||||
color: #333740;
|
||||
transition: all 0.15s ease;
|
||||
overflow: hidden;
|
||||
z-index: 10;
|
||||
|
||||
// The last notification must appear from
|
||||
// the background of the previous one.
|
||||
&:last-child {
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.notification:hover {
|
||||
cursor: pointer;
|
||||
box-shadow: 0 1px 5px 0 rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.notificationIcon {
|
||||
position: relative;
|
||||
display: block;
|
||||
width: 60px;
|
||||
text-align: center;
|
||||
font-size: 2.4rem;
|
||||
|
||||
&:before {
|
||||
position: absolute;
|
||||
top: calc(50% - 10px); left: calc(50% - 10px);
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
padding-top: 4px;
|
||||
border-radius: 100%;
|
||||
border: 1px solid $brand-success;
|
||||
color: $brand-success;
|
||||
font-size: 1.2rem;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.notificationContent {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 220px;
|
||||
margin: 0;
|
||||
padding-right: 10px;
|
||||
border-right: 1px solid rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
|
||||
.notificationTitle {
|
||||
margin-bottom: 0;
|
||||
font-size: 1.4rem;
|
||||
font-weight: 400;
|
||||
line-height: 1.8rem;
|
||||
}
|
||||
|
||||
|
||||
.notificationClose {
|
||||
cursor: pointer;
|
||||
opacity: 0.6;
|
||||
position: relative;
|
||||
display: block;
|
||||
|
||||
width: 20px;
|
||||
font-size: 1.4rem;
|
||||
color: #BBC2BF;
|
||||
transition: opacity 0.1s ease;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&:before {
|
||||
position: absolute;
|
||||
top: calc(50% - 6px);
|
||||
height: 100%;
|
||||
font-size: 1.3rem;
|
||||
font-weight: 100!important;
|
||||
}
|
||||
}
|
||||
|
||||
.notificationSuccess{
|
||||
background: linear-gradient(100deg , #FFFFFF 50%, rgba(39, 183, 15, .05)), $white;
|
||||
}
|
||||
|
||||
.notificationWarning {
|
||||
background: linear-gradient(100deg , #FFFFFF 50%, rgba(250, 156, 0, .05)), $white;
|
||||
|
||||
.notificationIcon:before {
|
||||
padding-top: 4px;
|
||||
border-color: $brand-warning;
|
||||
color: $brand-warning;
|
||||
}
|
||||
}
|
||||
|
||||
.notificationError {
|
||||
background: linear-gradient(100deg , #FFFFFF 50%, rgba(255, 93, 0, .05)), $white;
|
||||
|
||||
.notificationIcon:before {
|
||||
padding-top: 4px;
|
||||
border-color: $brand-danger;
|
||||
color: $brand-danger;
|
||||
}
|
||||
}
|
||||
|
||||
.notificationInfo {
|
||||
background: linear-gradient(100deg , #FFFFFF 50%, rgba(28, 93, 231, .05)), $white;
|
||||
|
||||
.notificationIcon:before {
|
||||
border-color: $brand-primary;
|
||||
color: $brand-primary;
|
||||
}
|
||||
}
|
||||
@ -1,64 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* NotificationsContainer
|
||||
*
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { CSSTransition, TransitionGroup } from 'react-transition-group';
|
||||
|
||||
import Notification from '../Notification';
|
||||
|
||||
import styles from './styles.scss';
|
||||
|
||||
class NotificationsContainer extends React.Component { // eslint-disable-line react/prefer-stateless-function
|
||||
render() {
|
||||
if (this.props.notifications.length === 0) {
|
||||
return (false);
|
||||
}
|
||||
|
||||
const notifications = this.props.notifications.map((notification, i) => (
|
||||
<CSSTransition
|
||||
key={i}
|
||||
classNames="notification"
|
||||
timeout={{
|
||||
enter: 500,
|
||||
exit: 300,
|
||||
}}
|
||||
>
|
||||
<Notification
|
||||
key={notification.id}
|
||||
onHideNotification={this.props.onHideNotification}
|
||||
notification={notification}
|
||||
/>
|
||||
</CSSTransition>
|
||||
));
|
||||
|
||||
return (
|
||||
<TransitionGroup className={styles.notificationsContainer}>
|
||||
{notifications}
|
||||
</TransitionGroup>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
NotificationsContainer.defaultProps = {
|
||||
notifications: [
|
||||
{
|
||||
id: 1,
|
||||
message: 'app.utils.defaultMessage',
|
||||
status: 'success',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
NotificationsContainer.propTypes = {
|
||||
notifications: PropTypes.oneOfType([
|
||||
PropTypes.object,
|
||||
PropTypes.array,
|
||||
]),
|
||||
onHideNotification: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default NotificationsContainer;
|
||||
@ -1,14 +0,0 @@
|
||||
/* Import */
|
||||
@import '../../styles/variables/variables';
|
||||
|
||||
.notificationsContainer { /* stylelint-disable */
|
||||
position: fixed;
|
||||
top: 72px;
|
||||
left: 240px;
|
||||
right: 0;
|
||||
z-index: 1000;
|
||||
list-style: none;
|
||||
width: 300px;
|
||||
margin: 0 auto;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
@ -1,29 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* Official
|
||||
*
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import PropTypes from 'prop-types';
|
||||
import styles from './styles.scss';
|
||||
|
||||
function Official(props) {
|
||||
return (
|
||||
<button className={styles.wrapper} style={props.style}>
|
||||
<i className="fa fa-star" />
|
||||
<FormattedMessage id="app.components.Official" />
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
Official.defaultProps = {
|
||||
style: {},
|
||||
};
|
||||
|
||||
Official.propTypes = {
|
||||
style: PropTypes.object,
|
||||
};
|
||||
|
||||
export default Official;
|
||||
@ -1,29 +0,0 @@
|
||||
.wrapper {
|
||||
display: flex;
|
||||
// justify-content: space-between;
|
||||
height: 20px !important;
|
||||
width: 88px;
|
||||
padding: 0 10px;
|
||||
border-radius: 2px;
|
||||
background-color: #EE8948;
|
||||
line-height: 20px;
|
||||
text-align: center;
|
||||
text-transform: uppercase;
|
||||
> span {
|
||||
height: 20px;
|
||||
padding: 0!important;
|
||||
color: #fff;
|
||||
letter-spacing: 0.5px;
|
||||
font-weight: 600;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
> i {
|
||||
margin-top: 1px;
|
||||
margin-right: 6px;
|
||||
vertical-align: -webkit-baseline-middle;
|
||||
|
||||
color: #FFDC00;
|
||||
font-size: 10px;
|
||||
}
|
||||
}
|
||||
@ -1,176 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* OnboardingList
|
||||
*
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import cn from 'classnames';
|
||||
|
||||
import { Modal, ModalHeader, ModalBody } from 'reactstrap';
|
||||
import { Player } from 'video-react';
|
||||
import '../../../../node_modules/video-react/dist/video-react.css';
|
||||
|
||||
import styles from './styles.scss';
|
||||
|
||||
class OnboardingVideo extends React.Component {
|
||||
componentDidMount() {
|
||||
this.hiddenPlayer.current.subscribeToStateChange(this.handleChangeState);
|
||||
}
|
||||
|
||||
hiddenPlayer = React.createRef();
|
||||
|
||||
player = React.createRef();
|
||||
|
||||
handleChangeState = (state, prevState) => {
|
||||
const { duration } = state;
|
||||
const { id } = this.props;
|
||||
|
||||
if (duration !== prevState.duration) {
|
||||
this.props.setVideoDuration(id, duration);
|
||||
}
|
||||
};
|
||||
|
||||
handleChangeIsPlayingState = (state, prevState) => {
|
||||
const { isActive } = state;
|
||||
const { id } = this.props;
|
||||
|
||||
if (isActive !== prevState.isActive && isActive) {
|
||||
this.props.didPlayVideo(id, this.props.video.startTime);
|
||||
}
|
||||
};
|
||||
|
||||
handleCurrentTimeChange = curr => {
|
||||
this.props.getVideoCurrentTime(
|
||||
this.props.id,
|
||||
curr,
|
||||
this.props.video.duration,
|
||||
);
|
||||
};
|
||||
|
||||
handleModalOpen = () => {
|
||||
this.player.current.subscribeToStateChange(this.handleChangeIsPlayingState);
|
||||
|
||||
this.player.current.play();
|
||||
|
||||
if (this.props.video.startTime === 0) {
|
||||
const { player } = this.player.current.getState();
|
||||
player.isActive = true;
|
||||
|
||||
this.props.didPlayVideo(this.props.id, this.props.video.startTime);
|
||||
} else {
|
||||
this.player.current.pause();
|
||||
}
|
||||
};
|
||||
|
||||
handleVideoPause = () => {
|
||||
const { player } = this.player.current.getState();
|
||||
const currTime = player.currentTime;
|
||||
|
||||
this.handleCurrentTimeChange(currTime);
|
||||
this.props.didStopVideo(this.props.id, currTime);
|
||||
};
|
||||
|
||||
handleModalClose = () => {
|
||||
const { player } = this.player.current.getState();
|
||||
const paused = player.paused;
|
||||
|
||||
if (!paused) {
|
||||
this.handleVideoPause();
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { video } = this.props;
|
||||
|
||||
return (
|
||||
<li
|
||||
key={this.props.id}
|
||||
onClick={this.props.onClick}
|
||||
id={this.props.id}
|
||||
className={cn(styles.listItem, video.end && styles.finished)}
|
||||
>
|
||||
<div className={styles.thumbWrapper}>
|
||||
<img src={video.preview} alt="preview" />
|
||||
<div className={styles.overlay} />
|
||||
<div className={styles.play} />
|
||||
</div>
|
||||
<div className={styles.txtWrapper}>
|
||||
<p className={styles.title}>{video.title}</p>
|
||||
<p className={styles.time}>
|
||||
{isNaN(video.duration)
|
||||
? '\xA0'
|
||||
: `${Math.floor(video.duration / 60)}:${Math.floor(
|
||||
video.duration,
|
||||
) % 60}`}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<Modal
|
||||
isOpen={video.isOpen}
|
||||
toggle={this.props.onClick} // eslint-disable-line react/jsx-handler-names
|
||||
className={styles.videoModal}
|
||||
onOpened={this.handleModalOpen}
|
||||
onClosed={this.handleModalClose}
|
||||
>
|
||||
<ModalHeader
|
||||
toggle={this.props.onClick} // eslint-disable-line react/jsx-handler-names
|
||||
className={styles.videoModalHeader}
|
||||
>
|
||||
{video.title}
|
||||
</ModalHeader>
|
||||
<ModalBody className={styles.modalBodyHelper}>
|
||||
<div>
|
||||
<Player
|
||||
ref={this.player}
|
||||
className={styles.videoPlayer}
|
||||
// poster="/assets/poster.png"
|
||||
src={video.video}
|
||||
startTime={video.startTime}
|
||||
preload="auto"
|
||||
onPause={this.handleVideoPause}
|
||||
onplay={this.videoStart}
|
||||
subscribeToStateChange={this.subscribeToStateChange}
|
||||
/>
|
||||
</div>
|
||||
</ModalBody>
|
||||
</Modal>
|
||||
|
||||
{!this.props.video.duration && (
|
||||
<div className={cn(styles.hiddenPlayerWrapper)}>
|
||||
<Player
|
||||
ref={this.hiddenPlayer}
|
||||
// poster="/assets/poster.png"
|
||||
src={video.video}
|
||||
preload="auto"
|
||||
subscribeToStateChange={this.subscribeToStateChange}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</li>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
OnboardingVideo.defaultProps = {
|
||||
didPlayVideo: () => {},
|
||||
didStopVideo: () => {},
|
||||
getVideoCurrentTime: () => {},
|
||||
id: 0,
|
||||
onClick: () => {},
|
||||
setVideoDuration: () => {},
|
||||
video: {},
|
||||
};
|
||||
|
||||
OnboardingVideo.propTypes = {
|
||||
didPlayVideo: PropTypes.func,
|
||||
didStopVideo: PropTypes.func,
|
||||
getVideoCurrentTime: PropTypes.func,
|
||||
id: PropTypes.number,
|
||||
onClick: PropTypes.func,
|
||||
setVideoDuration: PropTypes.func,
|
||||
video: PropTypes.object,
|
||||
};
|
||||
|
||||
export default OnboardingVideo;
|
||||
@ -1,144 +0,0 @@
|
||||
li.listItem {
|
||||
display: block;
|
||||
padding: 17px 15px;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background-color: #f7f8f8;
|
||||
.title {
|
||||
color: #0e7de7;
|
||||
}
|
||||
}
|
||||
.txtWrapper,
|
||||
.thumbWrapper {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.thumbWrapper {
|
||||
position: relative;
|
||||
width: 55px;
|
||||
height: 38px;
|
||||
background-color: #d8d8d8;
|
||||
border-radius: 2px;
|
||||
overflow: hidden;
|
||||
.overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(#0E7DE7, .8);
|
||||
}
|
||||
img {
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.play {
|
||||
position: absolute;
|
||||
top: calc(50% - 10px);
|
||||
left: calc(50% - 10px);
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
background-color: #0e7de7;
|
||||
border: 1px solid white;
|
||||
text-align: center;
|
||||
line-height: 20px;
|
||||
border-radius: 50%;
|
||||
z-index: 2;
|
||||
&::before {
|
||||
content: '\f04b';
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
height: 100%;
|
||||
font-family: 'FontAwesome';
|
||||
color: white;
|
||||
font-size: 10px;
|
||||
margin-left: 3px;
|
||||
line-height: 18px;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.finished {
|
||||
.title {
|
||||
color: #919bae;
|
||||
}
|
||||
.thumbWrapper {
|
||||
.overlay {
|
||||
background-color: transparent;
|
||||
}
|
||||
img {
|
||||
opacity: 0.6;
|
||||
}
|
||||
.play {
|
||||
background-color: #5a9e06;
|
||||
border-color: #5a9e06;
|
||||
&::before {
|
||||
content: '\f00c';
|
||||
margin-left: 0;
|
||||
font-size: 11px;
|
||||
line-height: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.txtWrapper {
|
||||
padding: 0 15px;
|
||||
p {
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
font-family: Lato;
|
||||
font-weight: 600;
|
||||
}
|
||||
.time {
|
||||
color: #919bae;
|
||||
font-family: Lato;
|
||||
font-weight: bold;
|
||||
font-size: 11px;
|
||||
line-height: 11px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.hiddenPlayerWrapper {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.videoModal {
|
||||
margin-right: auto !important;
|
||||
margin-left: auto !important;
|
||||
.videoModalHeader {
|
||||
padding-bottom: 0;
|
||||
border-bottom: 0;
|
||||
> h5 {
|
||||
font-family: Lato;
|
||||
font-weight: bold!important;
|
||||
font-size: 1.8rem!important;
|
||||
line-height: 3.1rem;
|
||||
color: #333740;
|
||||
}
|
||||
> button {
|
||||
display: flex;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
margin-top: 0;
|
||||
margin-right: 0;
|
||||
padding: 10px;
|
||||
cursor: pointer;
|
||||
span {
|
||||
line-height: 0.6em;
|
||||
}
|
||||
}
|
||||
}
|
||||
.videoPlayer {
|
||||
> button {
|
||||
top: 50%;
|
||||
margin-top: -0.75em;
|
||||
left: 50%;
|
||||
margin-left: -1.5em;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,227 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* PluginCard
|
||||
*
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import cn from 'classnames';
|
||||
import { isEmpty, replace } from 'lodash';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { Button } from 'strapi-helper-plugin';
|
||||
import InstallPluginPopup from '../InstallPluginPopup';
|
||||
|
||||
import styles from './styles.scss';
|
||||
|
||||
const PLUGINS_WITH_CONFIG = ['content-manager', 'email', 'upload'];
|
||||
|
||||
/* eslint-disable react/no-unused-state */
|
||||
class PluginCard extends React.Component {
|
||||
state = {
|
||||
boostrapCol: 'col-lg-4',
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
// Listen window resize.
|
||||
window.addEventListener('resize', this.setBoostrapCol);
|
||||
this.setBoostrapCol();
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener('resize', this.setBoostrapCol);
|
||||
}
|
||||
|
||||
setBoostrapCol = () => {
|
||||
let boostrapCol = 'col-lg-4';
|
||||
|
||||
if (window.innerWidth > 1680) {
|
||||
boostrapCol = 'col-lg-3';
|
||||
}
|
||||
|
||||
if (window.innerWidth > 2300) {
|
||||
boostrapCol = 'col-lg-2';
|
||||
}
|
||||
|
||||
this.setState({ boostrapCol });
|
||||
};
|
||||
|
||||
handleClick = () => {
|
||||
if (this.props.plugin.id !== 'support-us') {
|
||||
this.props.history.push({
|
||||
pathname: this.props.history.location.pathname,
|
||||
hash: `${this.props.plugin.id}::description`,
|
||||
});
|
||||
} else {
|
||||
this.aTag.click();
|
||||
}
|
||||
};
|
||||
|
||||
handleClickSettings = e => {
|
||||
const settingsPath =
|
||||
this.props.plugin.id === 'content-manager'
|
||||
? '/plugins/content-manager/ctm-configurations'
|
||||
: `/plugins/${this.props.plugin.id}/configurations/${
|
||||
this.props.currentEnvironment
|
||||
}`;
|
||||
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
this.props.history.push(settingsPath);
|
||||
};
|
||||
|
||||
handleDownloadPlugin = e => {
|
||||
if (
|
||||
!this.props.isAlreadyInstalled &&
|
||||
this.props.plugin.id !== 'support-us'
|
||||
) {
|
||||
this.props.downloadPlugin(e);
|
||||
} else if (this.props.plugin.id === 'support-us') {
|
||||
this.aTag.click();
|
||||
} else {
|
||||
this.props.history.push('/list-plugins');
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const buttonClass = !this.props.isAlreadyInstalled
|
||||
? styles.primary
|
||||
: styles.secondary;
|
||||
const buttonLabel = this.props.isAlreadyInstalled
|
||||
? 'app.components.PluginCard.Button.label.install'
|
||||
: 'app.components.PluginCard.Button.label.download';
|
||||
|
||||
// Display settings link for a selection of plugins.
|
||||
const settingsComponent = PLUGINS_WITH_CONFIG.includes(
|
||||
this.props.plugin.id,
|
||||
) && (
|
||||
<div className={styles.settings} onClick={this.handleClickSettings}>
|
||||
<i className="fa fa-cog" />
|
||||
<FormattedMessage id="app.components.PluginCard.settings" />
|
||||
</div>
|
||||
);
|
||||
|
||||
const descriptions = {
|
||||
short:
|
||||
this.props.plugin.id === 'support-us' ? (
|
||||
<FormattedMessage id={this.props.plugin.description.short} />
|
||||
) : (
|
||||
this.props.plugin.description.short
|
||||
),
|
||||
long:
|
||||
this.props.plugin.id === 'support-us' ? (
|
||||
<FormattedMessage
|
||||
id={
|
||||
this.props.plugin.description.long ||
|
||||
this.props.plugin.description.short
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
this.props.plugin.description.long ||
|
||||
this.props.plugin.description.short
|
||||
),
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={cn(this.state.boostrapCol, styles.pluginCard)}>
|
||||
<div className={styles.wrapper}>
|
||||
<div className={styles.cardTitle}>
|
||||
<div className={styles.frame}>
|
||||
<span className={styles.helper} />
|
||||
<img src={this.props.plugin.logo} alt="icon" />
|
||||
</div>
|
||||
<div>
|
||||
{this.props.plugin.name}{' '}
|
||||
<i
|
||||
className="fa fa-external-link"
|
||||
onClick={() =>
|
||||
window.open(
|
||||
`https://github.com/strapi/strapi/tree/master/packages/strapi-plugin-${
|
||||
this.props.plugin.id
|
||||
}`,
|
||||
'_blank',
|
||||
)
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.cardDescription}>
|
||||
{descriptions.long}
|
||||
{/* <FormattedMessage id="app.components.PluginCard.more-details" /> */}
|
||||
</div>
|
||||
<div className={styles.cardFooter} onClick={e => e.stopPropagation()}>
|
||||
<div className={styles.cardFooterButton}>
|
||||
<Button
|
||||
className={cn(buttonClass, styles.button)}
|
||||
label={buttonLabel}
|
||||
onClick={this.handleDownloadPlugin}
|
||||
/>
|
||||
<a
|
||||
href="https://strapi.io/shop"
|
||||
style={{ display: 'none' }}
|
||||
ref={a => {
|
||||
this.aTag = a;
|
||||
}}
|
||||
target="_blank"
|
||||
>
|
||||
|
||||
</a>
|
||||
</div>
|
||||
{this.props.isAlreadyInstalled ? (
|
||||
settingsComponent
|
||||
) : (
|
||||
<div className={styles.compatible}>
|
||||
<i
|
||||
className={`fa fa-${
|
||||
this.props.plugin.isCompatible ? 'check' : 'times'
|
||||
}`}
|
||||
/>
|
||||
<FormattedMessage
|
||||
id={`app.components.PluginCard.compatible${
|
||||
this.props.plugin.id === 'support-us' ? 'Community' : ''
|
||||
}`}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<InstallPluginPopup
|
||||
history={this.props.history}
|
||||
isAlreadyInstalled={this.props.isAlreadyInstalled}
|
||||
isOpen={
|
||||
!isEmpty(this.props.history.location.hash) &&
|
||||
replace(
|
||||
this.props.history.location.hash.split('::')[0],
|
||||
'#',
|
||||
'',
|
||||
) === this.props.plugin.id
|
||||
}
|
||||
plugin={this.props.plugin}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
PluginCard.defaultProps = {
|
||||
isAlreadyInstalled: false,
|
||||
plugin: {
|
||||
description: '',
|
||||
id: '',
|
||||
name: '',
|
||||
price: 0,
|
||||
ratings: 5,
|
||||
},
|
||||
};
|
||||
|
||||
PluginCard.propTypes = {
|
||||
currentEnvironment: PropTypes.string.isRequired,
|
||||
downloadPlugin: PropTypes.func.isRequired,
|
||||
history: PropTypes.object.isRequired,
|
||||
isAlreadyInstalled: PropTypes.bool,
|
||||
plugin: PropTypes.object,
|
||||
};
|
||||
|
||||
export default PluginCard;
|
||||
@ -1,146 +0,0 @@
|
||||
.wrapper {
|
||||
position: relative;
|
||||
min-height: 216px;
|
||||
margin-bottom: 3.6rem;
|
||||
padding: 1.2rem 1.5rem;
|
||||
padding-bottom: 0;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 2px 4px #E3E9F3;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
.cardTitle {
|
||||
display: flex;
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
|
||||
> div:first-child {
|
||||
margin-right: 14px;
|
||||
}
|
||||
|
||||
> div:last-child {
|
||||
height: 36px;
|
||||
line-height: 36px;
|
||||
}
|
||||
|
||||
i {
|
||||
margin-left: 7px;
|
||||
color: #B3B5B9;
|
||||
font-size: 1rem;
|
||||
vertical-align: baseline;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.cardDescription {
|
||||
height: 54px;
|
||||
margin-top: 27px;
|
||||
margin-bottom: 9px;
|
||||
font-size: 13px;
|
||||
font-weight: 400;
|
||||
|
||||
> span:last-child {
|
||||
color: #1C5DE7;
|
||||
}
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
.cardFooter {
|
||||
position: absolute;
|
||||
bottom: 0; left: 0;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 45px;
|
||||
padding: 0.9rem 1.5rem 1rem;
|
||||
background-color: #FAFAFB;
|
||||
justify-content: space-between;
|
||||
flex-direction: row-reverse;
|
||||
cursor: initial;
|
||||
}
|
||||
|
||||
.compatible {
|
||||
margin-top: 3px;
|
||||
color: #5A9E06;
|
||||
font-size: 1.3rem;
|
||||
font-style: italic;
|
||||
|
||||
> i {
|
||||
margin-right: 7px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.settings{
|
||||
margin-top: 3px;
|
||||
color: #323740;
|
||||
font-size: 1.3rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
|
||||
|
||||
> i {
|
||||
margin-right: 7px;
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
.button {
|
||||
height: 26px;
|
||||
min-width: 89px !important;
|
||||
padding: 0 15px;
|
||||
margin: 0;
|
||||
border-radius: 2px !important;
|
||||
line-height: 24px;
|
||||
font-size: 13px;
|
||||
font-weight: 500 !important;
|
||||
cursor: pointer;
|
||||
|
||||
span {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
line-height: 24px;
|
||||
}
|
||||
}
|
||||
|
||||
.frame {
|
||||
width: 70px;
|
||||
height: 36px;
|
||||
margin: auto 0;
|
||||
text-align: center;
|
||||
border: 1px solid #F3F3F7;
|
||||
border-radius: 3px;
|
||||
white-space: nowrap;
|
||||
> img {
|
||||
max-height: 36px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
.helper {
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.pluginCard {
|
||||
|
||||
}
|
||||
|
||||
.primary {
|
||||
background: linear-gradient(315deg, #0097F6 0%, #005EEA 100%);
|
||||
color: white;
|
||||
font-weight: 500;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
|
||||
&:active {
|
||||
box-shadow: inset 1px 1px 3px rgba(0,0,0,.15);
|
||||
}
|
||||
}
|
||||
|
||||
.secondary {
|
||||
border: 1px solid #DFE0E1;
|
||||
font-weight: 600;
|
||||
}
|
||||
@ -1,120 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* Row
|
||||
*
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import cn from 'classnames';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { includes, isEmpty } from 'lodash';
|
||||
|
||||
// Design
|
||||
import { IcoContainer, ListRow, PopUpWarning } from 'strapi-helper-plugin';
|
||||
|
||||
import styles from './styles.scss';
|
||||
|
||||
const PLUGINS_WITH_CONFIG = ['content-manager', 'email', 'upload'];
|
||||
|
||||
class Row extends React.Component {
|
||||
state = { showModal: false };
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.pluginActionSucceeded !== this.props.pluginActionSucceeded) {
|
||||
this.setState({ showModal: false });
|
||||
}
|
||||
}
|
||||
|
||||
handleClick = e => {
|
||||
this.setState({ showModal: !this.state.showModal });
|
||||
this.props.onDeleteClick(e);
|
||||
};
|
||||
|
||||
render() {
|
||||
// const uploadPath = `/plugins/upload/configurations/${this.context.currentEnvironment}`;
|
||||
// Make sure to match the ctm config URI instead of content-type view URI
|
||||
const settingsPath =
|
||||
this.props.name === 'content-manager'
|
||||
? '/plugins/content-manager/ctm-configurations'
|
||||
: `/plugins/${this.props.name}/configurations/${
|
||||
this.context.currentEnvironment
|
||||
}`;
|
||||
// const icons = this.props.name === 'upload' || this.props.name === 'email' ? [
|
||||
const icons = includes(PLUGINS_WITH_CONFIG, this.props.name)
|
||||
? [
|
||||
{
|
||||
icoType: 'cog',
|
||||
onClick: e => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.props.history.push(settingsPath);
|
||||
},
|
||||
},
|
||||
{
|
||||
icoType: 'trash',
|
||||
id: this.props.name,
|
||||
onClick: this.handleClick,
|
||||
},
|
||||
]
|
||||
: [
|
||||
{
|
||||
icoType: 'trash',
|
||||
id: this.props.name,
|
||||
onClick: this.handleClick,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<ListRow>
|
||||
<div className={cn('col-md-11', styles.nameWrapper)}>
|
||||
<div className={styles.icoContainer} style={{ marginRight: '14px' }}>
|
||||
{!isEmpty(this.props.plugin.logo) && (
|
||||
<img src={`${this.props.plugin.logo}`} alt="icon" />
|
||||
)}
|
||||
{isEmpty(this.props.plugin.logo) && (
|
||||
<div className={styles.icoWrapper}>
|
||||
<i className={`fa fa-${this.props.plugin.icon}`} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className={styles.pluginContent}>
|
||||
<span>{this.props.plugin.name} — </span>
|
||||
<FormattedMessage
|
||||
id={`${this.props.plugin.description}.short`}
|
||||
defaultMessage={this.props.plugin.description}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-md-1">
|
||||
<div className={styles.actionContainer}>
|
||||
<IcoContainer icons={icons} />
|
||||
</div>
|
||||
</div>
|
||||
<PopUpWarning
|
||||
isOpen={this.state.showModal}
|
||||
toggleModal={() =>
|
||||
this.setState({ showModal: !this.state.showModal })
|
||||
}
|
||||
popUpWarningType="danger"
|
||||
onConfirm={this.props.onDeleteConfirm}
|
||||
/>
|
||||
</ListRow>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Row.contextTypes = {
|
||||
currentEnvironment: PropTypes.string,
|
||||
};
|
||||
|
||||
Row.propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
onDeleteClick: PropTypes.func.isRequired,
|
||||
onDeleteConfirm: PropTypes.func.isRequired,
|
||||
plugin: PropTypes.object.isRequired,
|
||||
pluginActionSucceeded: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
export default withRouter(Row);
|
||||
@ -1,74 +0,0 @@
|
||||
.nameWrapper {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.icoContainer {
|
||||
width: 70px;
|
||||
height: 36px;
|
||||
position: relative;
|
||||
margin: auto 0;
|
||||
text-align: center;
|
||||
background: #FAFAFB;
|
||||
border: 1px solid #F3F3F7;
|
||||
border-radius: 3px;
|
||||
font-size: 20px;
|
||||
|
||||
> img {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
width: auto;
|
||||
height: auto;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.pluginContent {
|
||||
text-align: left !important;
|
||||
> span:first-child {
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.7px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
> span:last-child {
|
||||
font-size: 13px;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.actionContainer {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.frame {
|
||||
width: 70px;
|
||||
height: 36px;
|
||||
margin: auto 0;
|
||||
text-align: center;
|
||||
border: 1px solid rgba(28,93,231,0.1);
|
||||
border-radius: 3px;
|
||||
white-space: nowrap;
|
||||
> img {
|
||||
max-height: 36px;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
}
|
||||
|
||||
.helper {
|
||||
display: inline-block;
|
||||
height: 100%;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.icoWrapper {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
}
|
||||
@ -1,38 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* StarsContainer
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { map, times } from 'lodash';
|
||||
import styles from './styles.scss';
|
||||
|
||||
function StarsContainer({ ratings }) {
|
||||
const stars = Math.round(ratings);
|
||||
const coloredStars = times(stars, String);
|
||||
const emptyStars = times(5 - stars, String);
|
||||
|
||||
return (
|
||||
<div className={styles.starsContainer}>
|
||||
<div>
|
||||
{map(coloredStars, star => <i key={star} className="fa fa-star" />)}
|
||||
</div>
|
||||
<div>
|
||||
{map(emptyStars, s => <i key={s} className="fa fa-star" />)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
StarsContainer.defaultProps = {
|
||||
ratings: 5,
|
||||
};
|
||||
|
||||
StarsContainer.propTypes = {
|
||||
ratings: PropTypes.number,
|
||||
};
|
||||
|
||||
export default StarsContainer;
|
||||
@ -1,13 +0,0 @@
|
||||
.starsContainer {
|
||||
display: flex;
|
||||
margin-right: 10px;
|
||||
> div {
|
||||
color: #EEA348;
|
||||
> i {
|
||||
margin-right: 2px;
|
||||
}
|
||||
}
|
||||
> div:last-child {
|
||||
color: #B3B5B9;
|
||||
}
|
||||
}
|
||||
@ -1,8 +0,0 @@
|
||||
import Loadable from 'react-loadable';
|
||||
|
||||
import LoadingIndicator from 'components/LoadingIndicator';
|
||||
|
||||
export default Loadable({
|
||||
loader: () => import('./index'),
|
||||
loading: LoadingIndicator,
|
||||
});
|
||||
@ -1,75 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* Sub
|
||||
*
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import { isFunction, isObject } from 'lodash';
|
||||
import cn from 'classnames';
|
||||
import { LoadingBar } from 'strapi-helper-plugin';
|
||||
|
||||
import styles from './styles.scss';
|
||||
|
||||
function Sub({ bordered, content, link, name, style, title, underline }) {
|
||||
if (isObject(title)) {
|
||||
return (
|
||||
<div className={cn(styles.subWrapper, bordered && styles.subBordered)}>
|
||||
<FormattedMessage {...title}>
|
||||
{message => (
|
||||
<span className={cn(underline && styles.underlinedTitle)}>
|
||||
{message}
|
||||
{name}
|
||||
</span>
|
||||
)}
|
||||
</FormattedMessage>
|
||||
{content()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<a
|
||||
className={cn(
|
||||
styles.subWrapper,
|
||||
bordered && styles.subBordered,
|
||||
styles.link,
|
||||
)}
|
||||
href={`https://blog.strapi.io/${link}`}
|
||||
target="_blank"
|
||||
>
|
||||
<span>{title}</span>
|
||||
{title === '' && <LoadingBar />}
|
||||
{content === '' && <LoadingBar style={{ width: '40%' }} />}
|
||||
<p style={style}>{isFunction(content) ? content() : content}</p>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
|
||||
Sub.defaultProps = {
|
||||
bordered: false,
|
||||
content: () => '',
|
||||
link: '',
|
||||
name: '',
|
||||
style: {},
|
||||
title: {
|
||||
id: 'app.utils.defaultMessage',
|
||||
defaultMessage: 'app.utils.defaultMessage',
|
||||
values: {},
|
||||
},
|
||||
underline: false,
|
||||
};
|
||||
|
||||
Sub.propTypes = {
|
||||
bordered: PropTypes.bool,
|
||||
content: PropTypes.oneOfType([PropTypes.func, PropTypes.string]),
|
||||
link: PropTypes.string,
|
||||
name: PropTypes.string,
|
||||
style: PropTypes.object,
|
||||
title: PropTypes.oneOfType([PropTypes.object, PropTypes.string]),
|
||||
underline: PropTypes.bool,
|
||||
};
|
||||
|
||||
export default Sub;
|
||||
@ -1,46 +0,0 @@
|
||||
.subBordered {
|
||||
margin-bottom: 18px;
|
||||
border-bottom: 1px solid #F7F8F8;
|
||||
}
|
||||
|
||||
.subWrapper {
|
||||
position: relative;
|
||||
line-height: 18px;
|
||||
text-decoration: none;
|
||||
|
||||
> span {
|
||||
text-decoration: none;
|
||||
font-family: Lato-Bold;
|
||||
font-size: 20px;
|
||||
color: #333740;
|
||||
letter-spacing: 0;
|
||||
transition: color .2s ease;
|
||||
}
|
||||
|
||||
p {
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
max-width: calc(100% - 150px);
|
||||
margin-top: 18px;
|
||||
color: #333740;
|
||||
font-size: 14px;
|
||||
transition: color .2s ease;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.underlinedTitle {
|
||||
border-bottom: 3px solid #F0B41E;
|
||||
}
|
||||
|
||||
.link{
|
||||
&:hover, &:focus, &:active{
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
&:hover{
|
||||
> span, p {
|
||||
color: lighten(#333740, 20%);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,8 +0,0 @@
|
||||
import Loadable from 'react-loadable';
|
||||
|
||||
import { LoadingIndicator } from 'strapi-helper-plugin';
|
||||
|
||||
export default Loadable({
|
||||
loader: () => import('./index'),
|
||||
loading: LoadingIndicator,
|
||||
});
|
||||
@ -1,23 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* SupportUsCta
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import styles from './styles.scss';
|
||||
|
||||
function SupportUsCta() {
|
||||
return (
|
||||
<FormattedMessage id="app.components.HomePage.support.link">
|
||||
{message => (
|
||||
<a href="https://strapi.io/shop" target="_blank" className={styles.supportUsCta}>
|
||||
{message}
|
||||
</a>
|
||||
)}
|
||||
</FormattedMessage>
|
||||
);
|
||||
}
|
||||
|
||||
export default SupportUsCta;
|
||||
@ -1,18 +0,0 @@
|
||||
.supportUsCta {
|
||||
padding: 7px 12px 7px 20px;
|
||||
border: 1px solid #FFFFFF;
|
||||
border-radius: 3px;
|
||||
color: #FFFFFF;
|
||||
font-size: 13px;
|
||||
font-weight: 800;
|
||||
letter-spacing: 0.5px;
|
||||
&:after {
|
||||
content: '\f178';
|
||||
font-family: 'FontAwesome';
|
||||
margin-left: 10px;
|
||||
}
|
||||
&:hover {
|
||||
color: #FFFFFF;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
@ -1,8 +0,0 @@
|
||||
import Loadable from 'react-loadable';
|
||||
|
||||
import { LoadingIndicator } from 'strapi-helper-plugin';
|
||||
|
||||
export default Loadable({
|
||||
loader: () => import('./index'),
|
||||
loading: LoadingIndicator,
|
||||
});
|
||||
@ -1,19 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* SupportUsTitle
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import styles from './styles.scss';
|
||||
|
||||
function SupportUsTitle() {
|
||||
return (
|
||||
<FormattedMessage id="app.components.HomePage.support">
|
||||
{message => <span className={styles.supportUsTitle}>{message}</span>}
|
||||
</FormattedMessage>
|
||||
);
|
||||
}
|
||||
|
||||
export default SupportUsTitle;
|
||||
@ -1,11 +0,0 @@
|
||||
.supportUsTitle {
|
||||
color: #FFFFFF;
|
||||
font-size: 18px;
|
||||
font-weight: 800;
|
||||
letter-spacing: 0.5px;
|
||||
&:after {
|
||||
content: '❤️';
|
||||
margin-left: 7px;
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
@ -1,3 +0,0 @@
|
||||
{
|
||||
"languages": ["en", "ar", "es", "fr", "de", "it", "ko", "nl", "pl", "pt", "pt-BR", "ru", "tr", "zh", "zh-Hans", "ja"]
|
||||
}
|
||||
@ -1,56 +0,0 @@
|
||||
/**
|
||||
* Create the store with dynamic reducers
|
||||
*/
|
||||
|
||||
import { createStore, applyMiddleware, compose } from 'redux';
|
||||
import { fromJS } from 'immutable';
|
||||
// import { routerMiddleware } from 'react-router-redux';
|
||||
import createSagaMiddleware from 'redux-saga';
|
||||
import createReducer from './reducers';
|
||||
|
||||
const sagaMiddleware = createSagaMiddleware();
|
||||
|
||||
export default function configureStore(initialState = {}) {
|
||||
// Create the store with two middlewares
|
||||
// 1. sagaMiddleware: Makes redux-sagas work
|
||||
// 2. routerMiddleware: Syncs the location/URL path to the state
|
||||
const middlewares = [sagaMiddleware];
|
||||
|
||||
const enhancers = [applyMiddleware(...middlewares)];
|
||||
|
||||
// If Redux DevTools Extension is installed use it, otherwise use Redux compose
|
||||
/* eslint-disable no-underscore-dangle */
|
||||
const composeEnhancers =
|
||||
process.env.NODE_ENV !== 'production' &&
|
||||
typeof window === 'object' &&
|
||||
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
|
||||
? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
|
||||
// TODO Try to remove when `react-router-redux` is out of beta, LOCATION_CHANGE should not be fired more than once after hot reloading
|
||||
// Prevent recomputing reducers for `replaceReducer`
|
||||
shouldHotReload: false,
|
||||
name: 'Strapi - Dashboard',
|
||||
})
|
||||
: compose;
|
||||
/* eslint-enable */
|
||||
|
||||
const store = createStore(
|
||||
createReducer(),
|
||||
fromJS(initialState),
|
||||
composeEnhancers(...enhancers),
|
||||
);
|
||||
|
||||
// Extensions
|
||||
store.runSaga = sagaMiddleware.run;
|
||||
store.injectedReducers = {}; // Reducer registry
|
||||
store.injectedSagas = {}; // Saga registry
|
||||
|
||||
// Make reducers hot reloadable, see http://mxs.is/googmo
|
||||
/* istanbul ignore next */
|
||||
if (module.hot) {
|
||||
module.hot.accept('./reducers', () => {
|
||||
store.replaceReducer(createReducer(store.injectedReducers));
|
||||
});
|
||||
}
|
||||
|
||||
return store;
|
||||
}
|
||||
@ -1,24 +0,0 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
const style = {
|
||||
position: 'fixed',
|
||||
top: '0',
|
||||
right: '0',
|
||||
display: 'flex',
|
||||
zIndex: '1050',
|
||||
};
|
||||
|
||||
function NavWrapper({ children }) {
|
||||
return <div style={style}>{children}</div>;
|
||||
}
|
||||
|
||||
NavWrapper.propTypes = {
|
||||
children: PropTypes.node,
|
||||
};
|
||||
|
||||
NavWrapper.defaultProps = {
|
||||
children: null,
|
||||
};
|
||||
|
||||
export default NavWrapper;
|
||||
@ -1,96 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* Admin actions
|
||||
*
|
||||
*/
|
||||
|
||||
import {
|
||||
EMIT_EVENT,
|
||||
GET_INIT_DATA,
|
||||
GET_INIT_DATA_SUCCEEDED,
|
||||
GET_SECURED_DATA,
|
||||
GET_SECURED_DATA_SUCCEEDED,
|
||||
HIDE_LEFT_MENU,
|
||||
HIDE_LOGOUT,
|
||||
SET_APP_ERROR,
|
||||
SET_APP_SECURED,
|
||||
SHOW_LEFT_MENU,
|
||||
SHOW_LOGOUT,
|
||||
UNSET_APP_SECURED,
|
||||
} from './constants';
|
||||
|
||||
export function emitEvent(event, properties) {
|
||||
return {
|
||||
type: EMIT_EVENT,
|
||||
event,
|
||||
properties,
|
||||
};
|
||||
}
|
||||
|
||||
export function getInitData() {
|
||||
return {
|
||||
type: GET_INIT_DATA,
|
||||
};
|
||||
}
|
||||
|
||||
export function getInitDataSucceeded(data) {
|
||||
return {
|
||||
type: GET_INIT_DATA_SUCCEEDED,
|
||||
data,
|
||||
};
|
||||
}
|
||||
|
||||
export function getSecuredData() {
|
||||
return {
|
||||
type: GET_SECURED_DATA,
|
||||
};
|
||||
}
|
||||
|
||||
export function getSecuredDataSucceeded(data) {
|
||||
return {
|
||||
type: GET_SECURED_DATA_SUCCEEDED,
|
||||
data,
|
||||
};
|
||||
}
|
||||
|
||||
export function hideLeftMenu() {
|
||||
return {
|
||||
type: HIDE_LEFT_MENU,
|
||||
};
|
||||
}
|
||||
|
||||
export function hideLogout() {
|
||||
return {
|
||||
type: HIDE_LOGOUT,
|
||||
};
|
||||
}
|
||||
|
||||
export function setAppError() {
|
||||
return {
|
||||
type: SET_APP_ERROR,
|
||||
};
|
||||
}
|
||||
|
||||
export function setAppSecured() {
|
||||
return {
|
||||
type: SET_APP_SECURED,
|
||||
};
|
||||
}
|
||||
|
||||
export function showLeftMenu() {
|
||||
return {
|
||||
type: SHOW_LEFT_MENU,
|
||||
};
|
||||
}
|
||||
|
||||
export function showLogout() {
|
||||
return {
|
||||
type: SHOW_LOGOUT,
|
||||
};
|
||||
}
|
||||
|
||||
export function unsetAppSecured() {
|
||||
return {
|
||||
type: UNSET_APP_SECURED,
|
||||
};
|
||||
}
|
||||
@ -1,20 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* Admin constants
|
||||
*
|
||||
*/
|
||||
|
||||
export const EMIT_EVENT = 'app/Admin/EMIT_EVENT';
|
||||
export const GET_INIT_DATA = 'StrapiAdmin/Admin/GET_INIT_DATA';
|
||||
export const GET_INIT_DATA_SUCCEEDED =
|
||||
'StrapiAdmin/Admin/GET_INIT_DATA_SUCCEEDED';
|
||||
export const GET_SECURED_DATA = 'StrapiAdmin/Admin/GET_SECURED_DATA';
|
||||
export const GET_SECURED_DATA_SUCCEEDED =
|
||||
'StrapiAdmin/Admin/GET_SECURED_DATA_SUCCEEDED';
|
||||
export const HIDE_LEFT_MENU = 'StrapiAdmin/Admin/HIDE_LEFT_MENU';
|
||||
export const HIDE_LOGOUT = 'StrapiAdmin/Admin/HIDE_LOGOUT';
|
||||
export const SET_APP_ERROR = 'StrapiAdmin/Admin/SET_APP_ERROR';
|
||||
export const SET_APP_SECURED = 'StrapiAdmin/Admin/SET_APP_SECURED';
|
||||
export const SHOW_LEFT_MENU = 'StrapiAdmin/Admin/SHOW_LEFT_MENU';
|
||||
export const SHOW_LOGOUT = 'StrapiAdmin/Admin/SHOW_LOGOUT';
|
||||
export const UNSET_APP_SECURED = 'StrapiAdmin/Admin/UNSET_APP_SECURED';
|
||||
@ -1,385 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* Admin
|
||||
*
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import ReactGA from 'react-ga';
|
||||
import PropTypes from 'prop-types';
|
||||
import { connect } from 'react-redux';
|
||||
import { createStructuredSelector } from 'reselect';
|
||||
import { bindActionCreators, compose } from 'redux';
|
||||
import { Switch, Route } from 'react-router-dom';
|
||||
|
||||
// Components from strapi-helper-plugin
|
||||
import {
|
||||
LoadingIndicatorPage,
|
||||
OverlayBlocker,
|
||||
injectHooks,
|
||||
} from 'strapi-helper-plugin';
|
||||
// import OverlayBlocker from 'components/OverlayBlocker';
|
||||
|
||||
// import injectHooks from 'utils/injectHooks';
|
||||
|
||||
import Header from '../../components/Header/index';
|
||||
import Logout from '../../components/Logout';
|
||||
|
||||
import ComingSoonPage from '../ComingSoonPage';
|
||||
import LeftMenu from '../LeftMenu';
|
||||
import ListPluginsPage from '../ListPluginsPage';
|
||||
import LocaleToggle from '../LocaleToggle';
|
||||
import HomePage from '../HomePage';
|
||||
import Marketplace from '../Marketplace';
|
||||
import NotFoundPage from '../NotFoundPage';
|
||||
import Onboarding from '../Onboarding';
|
||||
import PluginDispatcher from '../PluginDispatcher';
|
||||
|
||||
import {
|
||||
disableGlobalOverlayBlocker,
|
||||
enableGlobalOverlayBlocker,
|
||||
updatePlugin,
|
||||
} from '../App/actions';
|
||||
import makeSelecApp from '../App/selectors';
|
||||
|
||||
import injectSaga from '../../utils/injectSaga';
|
||||
import injectReducer from '../../utils/injectReducer';
|
||||
|
||||
import localeToggleReducer from '../LocaleToggle/reducer';
|
||||
import {
|
||||
resetLocaleDefaultClassName,
|
||||
setLocaleCustomClassName,
|
||||
} from '../LocaleToggle/actions';
|
||||
|
||||
import {
|
||||
emitEvent,
|
||||
getInitData,
|
||||
getSecuredData,
|
||||
hideLeftMenu,
|
||||
hideLogout,
|
||||
setAppError,
|
||||
setAppSecured,
|
||||
showLeftMenu,
|
||||
showLogout,
|
||||
unsetAppSecured,
|
||||
} from './actions';
|
||||
import makeSelectAdmin from './selectors';
|
||||
import reducer from './reducer';
|
||||
import saga from './saga';
|
||||
|
||||
import NavTopRightWrapper from './NavTopRightWrapper';
|
||||
|
||||
import styles from './styles.scss';
|
||||
|
||||
export class Admin extends React.Component {
|
||||
// eslint-disable-line react/prefer-stateless-function
|
||||
state = { shouldSecureAfterAllPluginsAreMounted: true };
|
||||
|
||||
getChildContext = () => ({
|
||||
emitEvent: this.props.emitEvent,
|
||||
currentEnvironment: this.props.admin.currentEnvironment,
|
||||
disableGlobalOverlayBlocker: this.props.disableGlobalOverlayBlocker,
|
||||
enableGlobalOverlayBlocker: this.props.enableGlobalOverlayBlocker,
|
||||
plugins: this.props.global.plugins,
|
||||
updatePlugin: this.props.updatePlugin,
|
||||
});
|
||||
|
||||
componentDidMount() {
|
||||
// Initialize Google Analytics
|
||||
// Refer to ../../../doc/disable-tracking.md for more informations
|
||||
/* istanbul ignore next */
|
||||
ReactGA.initialize('UA-54313258-9', {
|
||||
testMode: process.env.NODE_ENV === 'test',
|
||||
});
|
||||
// Retrieve the main settings of the application
|
||||
this.props.getInitData();
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
componentDidUpdate(prevProps) {
|
||||
const {
|
||||
admin: { didGetSecuredData, isLoading, isSecured },
|
||||
getHook,
|
||||
getSecuredData,
|
||||
location: { pathname },
|
||||
} = this.props;
|
||||
|
||||
if (!isLoading && this.state.shouldSecureAfterAllPluginsAreMounted) {
|
||||
if (!this.hasApluginNotReady(this.props)) {
|
||||
getHook('willSecure');
|
||||
}
|
||||
}
|
||||
|
||||
if (prevProps.location.pathname !== pathname) {
|
||||
getHook('willSecure');
|
||||
|
||||
/* istanbul ignore if */
|
||||
if (this.isAcceptingTracking()) {
|
||||
ReactGA.pageview(pathname, {
|
||||
testMode: process.env.NODE_ENV === 'test',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (prevProps.admin.isSecured !== isSecured && isSecured) {
|
||||
getSecuredData();
|
||||
}
|
||||
|
||||
if (prevProps.admin.didGetSecuredData !== didGetSecuredData) {
|
||||
getHook('didGetSecuredData');
|
||||
}
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
componentDidCatch(error, info) {
|
||||
/* eslint-disable */
|
||||
console.log('An error has occured');
|
||||
console.log('--------------------');
|
||||
console.log(error);
|
||||
console.log('Here is some infos');
|
||||
console.log(info);
|
||||
/* eslint-enable */
|
||||
|
||||
// Display the error log component which is not designed yet
|
||||
this.props.setAppError();
|
||||
}
|
||||
|
||||
getContentWrapperStyle = () => {
|
||||
const {
|
||||
admin: { showMenu },
|
||||
} = this.props;
|
||||
|
||||
return showMenu
|
||||
? { main: {}, sub: styles.content }
|
||||
: { main: { width: '100%' }, sub: styles.wrapper };
|
||||
};
|
||||
|
||||
hasApluginNotReady = props => {
|
||||
const {
|
||||
global: { plugins },
|
||||
} = props;
|
||||
|
||||
return !Object.keys(plugins).every(
|
||||
plugin => plugins[plugin].isReady === true,
|
||||
);
|
||||
};
|
||||
|
||||
helpers = {
|
||||
hideLeftMenu: this.props.hideLeftMenu,
|
||||
hideLogout: this.props.hideLogout,
|
||||
setAppSecured: this.props.setAppSecured,
|
||||
showLeftMenu: this.props.showLeftMenu,
|
||||
showLogout: this.props.showLogout,
|
||||
unsetAppSecured: this.props.unsetAppSecured,
|
||||
updatePlugin: this.props.updatePlugin,
|
||||
};
|
||||
|
||||
isAcceptingTracking = () => {
|
||||
const {
|
||||
admin: { uuid },
|
||||
} = this.props;
|
||||
|
||||
return !!uuid;
|
||||
};
|
||||
|
||||
/**
|
||||
* Display the app loader until the app is ready
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
showLoader = () => {
|
||||
const {
|
||||
global: { isAppLoading },
|
||||
} = this.props;
|
||||
|
||||
if (isAppLoading) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return this.hasApluginNotReady(this.props);
|
||||
};
|
||||
|
||||
renderInitializers = () => {
|
||||
const {
|
||||
global: { plugins },
|
||||
} = this.props;
|
||||
|
||||
return Object.keys(plugins).reduce((acc, current) => {
|
||||
const InitializerComponent = plugins[current].initializer;
|
||||
const key = plugins[current].id;
|
||||
|
||||
acc.push(
|
||||
<InitializerComponent key={key} {...this.props} {...this.helpers} />,
|
||||
);
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
};
|
||||
|
||||
renderMarketPlace = props => <Marketplace {...props} {...this.props} />;
|
||||
|
||||
renderPluginDispatcher = props => {
|
||||
// NOTE: Send the needed props instead of everything...
|
||||
|
||||
return <PluginDispatcher {...this.props} {...props} {...this.helpers} />;
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
admin: { isLoading, showLogoutComponent, showMenu, strapiVersion },
|
||||
global: { blockApp, overlayBlockerData, plugins, showGlobalAppBlocker },
|
||||
} = this.props;
|
||||
|
||||
if (isLoading) {
|
||||
return <LoadingIndicatorPage />;
|
||||
}
|
||||
|
||||
// We need the admin data in order to make the initializers work
|
||||
if (this.showLoader()) {
|
||||
return (
|
||||
<React.Fragment>
|
||||
{this.renderInitializers()}
|
||||
<LoadingIndicatorPage />
|
||||
</React.Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.adminPage}>
|
||||
{showMenu && <LeftMenu version={strapiVersion} plugins={plugins} />}
|
||||
<NavTopRightWrapper>
|
||||
{/* Injection zone not ready yet */}
|
||||
{showLogoutComponent && <Logout />}
|
||||
<LocaleToggle isLogged />
|
||||
</NavTopRightWrapper>
|
||||
<div
|
||||
className={styles.adminPageRightWrapper}
|
||||
style={this.getContentWrapperStyle().main}
|
||||
>
|
||||
{showMenu ? <Header /> : ''}
|
||||
<div className={this.getContentWrapperStyle().sub}>
|
||||
<Switch>
|
||||
<Route path="/" component={HomePage} exact />
|
||||
<Route
|
||||
path="/plugins/:pluginId"
|
||||
render={this.renderPluginDispatcher}
|
||||
/>
|
||||
<Route path="/plugins" component={ComingSoonPage} />
|
||||
<Route path="/list-plugins" component={ListPluginsPage} exact />
|
||||
<Route
|
||||
path="/marketplace"
|
||||
render={this.renderMarketPlace}
|
||||
exact
|
||||
/>
|
||||
<Route path="/configuration" component={ComingSoonPage} exact />
|
||||
<Route key="7" path="" component={NotFoundPage} />
|
||||
<Route key="8" path="404" component={NotFoundPage} />
|
||||
</Switch>
|
||||
</div>
|
||||
</div>
|
||||
<OverlayBlocker
|
||||
key="overlayBlocker"
|
||||
isOpen={blockApp && showGlobalAppBlocker}
|
||||
{...overlayBlockerData}
|
||||
/>
|
||||
{showLogoutComponent && <Onboarding />}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Admin.childContextTypes = {
|
||||
emitEvent: PropTypes.func,
|
||||
currentEnvironment: PropTypes.string,
|
||||
disableGlobalOverlayBlocker: PropTypes.func,
|
||||
enableGlobalOverlayBlocker: PropTypes.func,
|
||||
plugins: PropTypes.object,
|
||||
updatePlugin: PropTypes.func,
|
||||
};
|
||||
|
||||
Admin.propTypes = {
|
||||
admin: PropTypes.shape({
|
||||
autoReload: PropTypes.bool,
|
||||
appError: PropTypes.bool,
|
||||
currentEnvironment: PropTypes.string,
|
||||
didGetSecuredData: PropTypes.bool,
|
||||
isLoading: PropTypes.bool,
|
||||
isSecured: PropTypes.bool,
|
||||
layout: PropTypes.object,
|
||||
showLogoutComponent: PropTypes.bool,
|
||||
showMenu: PropTypes.bool,
|
||||
strapiVersion: PropTypes.string,
|
||||
uuid: PropTypes.oneOfType([PropTypes.bool, PropTypes.string]),
|
||||
}).isRequired,
|
||||
disableGlobalOverlayBlocker: PropTypes.func.isRequired,
|
||||
emitEvent: PropTypes.func.isRequired,
|
||||
enableGlobalOverlayBlocker: PropTypes.func.isRequired,
|
||||
getHook: PropTypes.func.isRequired,
|
||||
getInitData: PropTypes.func.isRequired,
|
||||
getSecuredData: PropTypes.func.isRequired,
|
||||
global: PropTypes.shape({
|
||||
appPlugins: PropTypes.array,
|
||||
blockApp: PropTypes.bool,
|
||||
overlayBlockerData: PropTypes.oneOfType([PropTypes.bool, PropTypes.object]),
|
||||
isAppLoading: PropTypes.bool,
|
||||
plugins: PropTypes.object,
|
||||
showGlobalAppBlocker: PropTypes.bool,
|
||||
}).isRequired,
|
||||
hideLeftMenu: PropTypes.func.isRequired,
|
||||
hideLogout: PropTypes.func.isRequired,
|
||||
location: PropTypes.object.isRequired,
|
||||
resetLocaleDefaultClassName: PropTypes.func.isRequired,
|
||||
setAppError: PropTypes.func.isRequired,
|
||||
setAppSecured: PropTypes.func.isRequired,
|
||||
showLeftMenu: PropTypes.func.isRequired,
|
||||
showLogout: PropTypes.func.isRequired,
|
||||
unsetAppSecured: PropTypes.func.isRequired,
|
||||
updatePlugin: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
admin: makeSelectAdmin(),
|
||||
global: makeSelecApp(),
|
||||
});
|
||||
|
||||
export function mapDispatchToProps(dispatch) {
|
||||
return bindActionCreators(
|
||||
{
|
||||
disableGlobalOverlayBlocker,
|
||||
emitEvent,
|
||||
enableGlobalOverlayBlocker,
|
||||
getInitData,
|
||||
getSecuredData,
|
||||
hideLeftMenu,
|
||||
hideLogout,
|
||||
resetLocaleDefaultClassName,
|
||||
setAppError,
|
||||
setAppSecured,
|
||||
setLocaleCustomClassName,
|
||||
showLeftMenu,
|
||||
showLogout,
|
||||
unsetAppSecured,
|
||||
updatePlugin,
|
||||
},
|
||||
dispatch,
|
||||
);
|
||||
}
|
||||
|
||||
const withConnect = connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps,
|
||||
);
|
||||
const withReducer = injectReducer({ key: 'admin', reducer });
|
||||
const withSaga = injectSaga({ key: 'admin', saga });
|
||||
const withLocaleToggleReducer = injectReducer({
|
||||
key: 'localeToggle',
|
||||
reducer: localeToggleReducer,
|
||||
});
|
||||
const withHooks = injectHooks({ key: 'admin' });
|
||||
|
||||
export default compose(
|
||||
withReducer,
|
||||
withLocaleToggleReducer,
|
||||
withSaga,
|
||||
withConnect,
|
||||
withHooks,
|
||||
)(Admin);
|
||||
@ -1,75 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* Admin reducer
|
||||
*
|
||||
*/
|
||||
|
||||
import { fromJS, Map } from 'immutable';
|
||||
import {
|
||||
GET_INIT_DATA_SUCCEEDED,
|
||||
GET_SECURED_DATA_SUCCEEDED,
|
||||
HIDE_LEFT_MENU,
|
||||
HIDE_LOGOUT,
|
||||
SET_APP_ERROR,
|
||||
SET_APP_SECURED,
|
||||
SHOW_LEFT_MENU,
|
||||
SHOW_LOGOUT,
|
||||
UNSET_APP_SECURED,
|
||||
} from './constants';
|
||||
|
||||
const initialState = fromJS({
|
||||
autoReload: false,
|
||||
appError: false,
|
||||
currentEnvironment: 'development',
|
||||
didGetSecuredData: false,
|
||||
isLoading: true,
|
||||
isSecured: false,
|
||||
layout: Map({}),
|
||||
// NOTE: This should be the models and our stuffs
|
||||
// Since this api is not implemented yet I just set this vague key ATM
|
||||
securedData: {},
|
||||
showMenu: true,
|
||||
showLogoutComponent: false,
|
||||
strapiVersion: '3',
|
||||
uuid: false,
|
||||
});
|
||||
|
||||
function adminReducer(state = initialState, action) {
|
||||
switch (action.type) {
|
||||
case GET_INIT_DATA_SUCCEEDED: {
|
||||
const {
|
||||
data: { autoReload, currentEnvironment, layout, strapiVersion, uuid },
|
||||
} = action;
|
||||
|
||||
return state
|
||||
.update('autoReload', () => autoReload)
|
||||
.update('currentEnvironment', () => currentEnvironment)
|
||||
.update('isLoading', () => false)
|
||||
.update('layout', () => Map(layout))
|
||||
.update('strapiVersion', () => strapiVersion)
|
||||
.update('uuid', () => uuid);
|
||||
}
|
||||
case GET_SECURED_DATA_SUCCEEDED:
|
||||
return state
|
||||
.update('didGetSecuredData', v => !v)
|
||||
.update('securedData', () => action.data);
|
||||
case HIDE_LEFT_MENU:
|
||||
return state.update('showMenu', () => false);
|
||||
case HIDE_LOGOUT:
|
||||
return state.update('showLogoutComponent', () => false);
|
||||
case SET_APP_ERROR:
|
||||
return state.update('appError', () => true);
|
||||
case SET_APP_SECURED:
|
||||
return state.update('isSecured', () => true);
|
||||
case SHOW_LEFT_MENU:
|
||||
return state.update('showMenu', () => true);
|
||||
case SHOW_LOGOUT:
|
||||
return state.update('showLogoutComponent', () => true);
|
||||
case UNSET_APP_SECURED:
|
||||
return state.update('isSecured', () => false);
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
export default adminReducer;
|
||||
@ -1,83 +0,0 @@
|
||||
import { all, fork, call, put, select, takeLatest } from 'redux-saga/effects';
|
||||
import { request } from 'strapi-helper-plugin';
|
||||
|
||||
import { getInitDataSucceeded, getSecuredDataSucceeded } from './actions';
|
||||
import { EMIT_EVENT, GET_INIT_DATA, GET_SECURED_DATA } from './constants';
|
||||
import { makeSelectUuid } from './selectors';
|
||||
|
||||
export function* emitter(action) {
|
||||
try {
|
||||
const requestURL = 'https://analytics.strapi.io/track';
|
||||
const uuid = yield select(makeSelectUuid());
|
||||
const { event, properties } = action;
|
||||
|
||||
if (uuid) {
|
||||
yield call(
|
||||
fetch, // eslint-disable-line no-undef
|
||||
requestURL,
|
||||
{
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ event, uuid, properties }),
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err); // eslint-disable-line no-console
|
||||
}
|
||||
}
|
||||
|
||||
export function* getData() {
|
||||
try {
|
||||
const endPoints = [
|
||||
'gaConfig',
|
||||
'strapiVersion',
|
||||
'currentEnvironment',
|
||||
'layout',
|
||||
];
|
||||
const [
|
||||
{ uuid },
|
||||
{ strapiVersion },
|
||||
{ autoReload, currentEnvironment },
|
||||
{ layout },
|
||||
] = yield all(
|
||||
endPoints.map(endPoint =>
|
||||
call(request, `/admin/${endPoint}`, { method: 'GET' }),
|
||||
),
|
||||
);
|
||||
|
||||
yield put(
|
||||
getInitDataSucceeded({
|
||||
autoReload,
|
||||
uuid,
|
||||
strapiVersion,
|
||||
currentEnvironment,
|
||||
layout,
|
||||
}),
|
||||
);
|
||||
} catch (err) {
|
||||
strapi.notification.error('notification.error');
|
||||
}
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
export function* getSecuredData() {
|
||||
try {
|
||||
const data = {};
|
||||
|
||||
yield put(getSecuredDataSucceeded(data));
|
||||
} catch (err) {
|
||||
console.log(err); // eslint-disable-line no-console
|
||||
}
|
||||
}
|
||||
|
||||
// Individual exports for testing
|
||||
export default function* defaultSaga() {
|
||||
yield all([
|
||||
fork(takeLatest, EMIT_EVENT, emitter),
|
||||
fork(takeLatest, GET_INIT_DATA, getData),
|
||||
fork(takeLatest, GET_SECURED_DATA, getSecuredData),
|
||||
]);
|
||||
}
|
||||
@ -1,29 +0,0 @@
|
||||
import { createSelector } from 'reselect';
|
||||
|
||||
/**
|
||||
* Direct selector to the admin state domain
|
||||
*/
|
||||
const selectAdminDomain = () => state => state.get('admin');
|
||||
|
||||
/**
|
||||
* Other specific selectors
|
||||
*/
|
||||
|
||||
/**
|
||||
* Default selector used by Admin
|
||||
*/
|
||||
|
||||
const makeSelectAdmin = () =>
|
||||
createSelector(
|
||||
selectAdminDomain(),
|
||||
substate => substate.toJS(),
|
||||
);
|
||||
|
||||
const makeSelectUuid = () =>
|
||||
createSelector(
|
||||
selectAdminDomain(),
|
||||
substate => substate.get('uuid'),
|
||||
);
|
||||
|
||||
export default makeSelectAdmin;
|
||||
export { makeSelectUuid, selectAdminDomain };
|
||||
@ -1,52 +0,0 @@
|
||||
/* Import */
|
||||
@import '../../styles/variables/variables';
|
||||
|
||||
.adminPage { /* stylelint-disable */
|
||||
display: flex;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.adminPageRightWrapper {
|
||||
width: calc(100% - #{$left-menu-width});
|
||||
}
|
||||
|
||||
.adminPageSeparator {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
height: 6rem;
|
||||
}
|
||||
|
||||
.content { /* stylelint-ignore */
|
||||
min-height: calc(100vh - #{$header-height});
|
||||
width: calc(100vw - #{$left-menu-width});
|
||||
margin-top: $header-height;
|
||||
margin-left: $left-menu-width;
|
||||
background: $content-background;
|
||||
}
|
||||
|
||||
.wrapper { /* stylelint-ignore */
|
||||
width: 100%;
|
||||
min-height: 100%;
|
||||
background: $content-background;
|
||||
}
|
||||
|
||||
|
||||
.localeDropdownMenuNotLogged{
|
||||
background: transparent !important;
|
||||
box-shadow: none !important;
|
||||
border: 1px solid #e3e9f3 !important;
|
||||
border-top: 0px !important;
|
||||
|
||||
button {
|
||||
padding-left: 17px;
|
||||
|
||||
&:hover {
|
||||
background-color: #F7F8F8 !important;
|
||||
}
|
||||
}
|
||||
|
||||
&:before{
|
||||
box-shadow: none !important;
|
||||
}
|
||||
}
|
||||
@ -1,121 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`getData Saga should call the strapi.notification action if the response errors 1`] = `
|
||||
Object {
|
||||
"@@redux-saga/IO": true,
|
||||
"ALL": Array [
|
||||
Object {
|
||||
"@@redux-saga/IO": true,
|
||||
"CALL": Object {
|
||||
"args": Array [
|
||||
"/admin/gaConfig",
|
||||
Object {
|
||||
"method": "GET",
|
||||
},
|
||||
],
|
||||
"context": null,
|
||||
"fn": [Function],
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"@@redux-saga/IO": true,
|
||||
"CALL": Object {
|
||||
"args": Array [
|
||||
"/admin/strapiVersion",
|
||||
Object {
|
||||
"method": "GET",
|
||||
},
|
||||
],
|
||||
"context": null,
|
||||
"fn": [Function],
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"@@redux-saga/IO": true,
|
||||
"CALL": Object {
|
||||
"args": Array [
|
||||
"/admin/currentEnvironment",
|
||||
Object {
|
||||
"method": "GET",
|
||||
},
|
||||
],
|
||||
"context": null,
|
||||
"fn": [Function],
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"@@redux-saga/IO": true,
|
||||
"CALL": Object {
|
||||
"args": Array [
|
||||
"/admin/layout",
|
||||
Object {
|
||||
"method": "GET",
|
||||
},
|
||||
],
|
||||
"context": null,
|
||||
"fn": [Function],
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`getData Saga should dispatch the getInitDataSucceeded action if it requests the data successfully 1`] = `
|
||||
Object {
|
||||
"@@redux-saga/IO": true,
|
||||
"ALL": Array [
|
||||
Object {
|
||||
"@@redux-saga/IO": true,
|
||||
"CALL": Object {
|
||||
"args": Array [
|
||||
"/admin/gaConfig",
|
||||
Object {
|
||||
"method": "GET",
|
||||
},
|
||||
],
|
||||
"context": null,
|
||||
"fn": [Function],
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"@@redux-saga/IO": true,
|
||||
"CALL": Object {
|
||||
"args": Array [
|
||||
"/admin/strapiVersion",
|
||||
Object {
|
||||
"method": "GET",
|
||||
},
|
||||
],
|
||||
"context": null,
|
||||
"fn": [Function],
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"@@redux-saga/IO": true,
|
||||
"CALL": Object {
|
||||
"args": Array [
|
||||
"/admin/currentEnvironment",
|
||||
Object {
|
||||
"method": "GET",
|
||||
},
|
||||
],
|
||||
"context": null,
|
||||
"fn": [Function],
|
||||
},
|
||||
},
|
||||
Object {
|
||||
"@@redux-saga/IO": true,
|
||||
"CALL": Object {
|
||||
"args": Array [
|
||||
"/admin/layout",
|
||||
Object {
|
||||
"method": "GET",
|
||||
},
|
||||
],
|
||||
"context": null,
|
||||
"fn": [Function],
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
`;
|
||||
@ -1,106 +0,0 @@
|
||||
import {
|
||||
emitEvent,
|
||||
getInitData,
|
||||
getInitDataSucceeded,
|
||||
hideLeftMenu,
|
||||
setAppError,
|
||||
setAppSecured,
|
||||
showLeftMenu,
|
||||
unsetAppSecured,
|
||||
} from '../actions';
|
||||
import {
|
||||
EMIT_EVENT,
|
||||
GET_INIT_DATA,
|
||||
GET_INIT_DATA_SUCCEEDED,
|
||||
HIDE_LEFT_MENU,
|
||||
SET_APP_ERROR,
|
||||
SET_APP_SECURED,
|
||||
SHOW_LEFT_MENU,
|
||||
UNSET_APP_SECURED,
|
||||
} from '../constants';
|
||||
|
||||
describe('<Admin /> actions', () => {
|
||||
describe('EmitEvent', () => {
|
||||
it('has a type EMIT_EVENT and returns the correct data', () => {
|
||||
const expected = {
|
||||
type: EMIT_EVENT,
|
||||
event: 'test',
|
||||
properties: {},
|
||||
};
|
||||
|
||||
expect(emitEvent('test', {})).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('GetInitData Action', () => {
|
||||
it('has a type of GET_INIT_DATA', () => {
|
||||
const expected = {
|
||||
type: GET_INIT_DATA,
|
||||
};
|
||||
|
||||
expect(getInitData()).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('GetInitDataSucceeded Action', () => {
|
||||
it('should return the correct type and the passed data', () => {
|
||||
const data = { autoReload: true };
|
||||
const expected = {
|
||||
type: GET_INIT_DATA_SUCCEEDED,
|
||||
data,
|
||||
};
|
||||
|
||||
expect(getInitDataSucceeded(data)).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('HideLeftMenu Action', () => {
|
||||
it('has a type of HIDE_LEFT_MENU', () => {
|
||||
const expected = {
|
||||
type: HIDE_LEFT_MENU,
|
||||
};
|
||||
|
||||
expect(hideLeftMenu()).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('SetAppError Action', () => {
|
||||
it('has a type of SET_APP_ERROR', () => {
|
||||
const expected = {
|
||||
type: SET_APP_ERROR,
|
||||
};
|
||||
|
||||
expect(setAppError()).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('SetAppSecured Action', () => {
|
||||
it('has a type of SET_APP_SECURED', () => {
|
||||
const expected = {
|
||||
type: SET_APP_SECURED,
|
||||
};
|
||||
|
||||
expect(setAppSecured()).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ShowLeftMenu Action', () => {
|
||||
it('has a type of SHOW_LEFT_MENU', () => {
|
||||
const expected = {
|
||||
type: SHOW_LEFT_MENU,
|
||||
};
|
||||
|
||||
expect(showLeftMenu()).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('UnsetAppSecured Action', () => {
|
||||
it('has a type of UNSET_APP_SECURED', () => {
|
||||
const expected = {
|
||||
type: UNSET_APP_SECURED,
|
||||
};
|
||||
|
||||
expect(unsetAppSecured()).toEqual(expected);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,538 +0,0 @@
|
||||
import React from 'react';
|
||||
import { mount, shallow } from 'enzyme';
|
||||
|
||||
import { BrowserRouter as Router } from 'react-router-dom';
|
||||
import { Provider } from 'react-redux';
|
||||
import { IntlProvider } from 'react-intl';
|
||||
import { compose } from 'redux';
|
||||
|
||||
import {
|
||||
disableGlobalOverlayBlocker,
|
||||
enableGlobalOverlayBlocker,
|
||||
updatePlugin,
|
||||
} from '../../App/actions';
|
||||
|
||||
import { LoadingIndicatorPage, OverlayBlocker } from 'strapi-helper-plugin';
|
||||
|
||||
import injectReducer from '../../../utils/injectReducer';
|
||||
import history from '../../../utils/history';
|
||||
import configureStore from '../../../configureStore';
|
||||
import messages from 'testUtils/commonTrads.json';
|
||||
|
||||
import Header from '../../../components/Header/index';
|
||||
import Onboarding from '../../Onboarding';
|
||||
import Logout from '../../../components/Logout';
|
||||
|
||||
import localeToggleReducer from '../../LocaleToggle/reducer';
|
||||
|
||||
import {
|
||||
resetLocaleDefaultClassName,
|
||||
setLocaleCustomClassName,
|
||||
} from '../../LocaleToggle/actions';
|
||||
|
||||
import { Admin, mapDispatchToProps } from '../index';
|
||||
import {
|
||||
getInitData,
|
||||
hideLeftMenu,
|
||||
setAppError,
|
||||
showLeftMenu,
|
||||
} from '../actions';
|
||||
|
||||
import styles from '../styles.scss';
|
||||
|
||||
const initialState = {};
|
||||
const store = configureStore(initialState, history);
|
||||
|
||||
const withLocaleToggleReducer = injectReducer({
|
||||
key: 'localeToggle',
|
||||
reducer: localeToggleReducer,
|
||||
});
|
||||
const WithAdmin = compose(withLocaleToggleReducer)(Admin);
|
||||
|
||||
const renderComponent = properties =>
|
||||
mount(
|
||||
React.createElement(
|
||||
props => (
|
||||
<Provider store={store}>
|
||||
<IntlProvider locale="en" defaultLocale="en" messages={messages}>
|
||||
<Router>
|
||||
<WithAdmin store={store} {...props} />
|
||||
</Router>
|
||||
</IntlProvider>
|
||||
</Provider>
|
||||
),
|
||||
properties,
|
||||
),
|
||||
);
|
||||
|
||||
describe('<Admin />, (with Redux), React lifecycle', () => {
|
||||
let props;
|
||||
|
||||
beforeEach(() => {
|
||||
props = {
|
||||
admin: {
|
||||
autoReload: false,
|
||||
appError: false,
|
||||
currentEnvironment: 'development',
|
||||
didGetSecuredData: false,
|
||||
isLoading: true,
|
||||
isSecured: false,
|
||||
layout: {},
|
||||
showLogoutComponent: false,
|
||||
showMenu: true,
|
||||
securedData: {},
|
||||
strapiVersion: '3',
|
||||
uuid: false,
|
||||
},
|
||||
disableGlobalOverlayBlocker: jest.fn(),
|
||||
emitEvent: jest.fn(),
|
||||
enableGlobalOverlayBlocker: jest.fn(),
|
||||
getInitData: jest.fn(),
|
||||
getSecuredData: jest.fn(),
|
||||
getHook: jest.fn(),
|
||||
global: {
|
||||
appPlugins: [],
|
||||
blockApp: false,
|
||||
overlayBlockerData: null,
|
||||
hasUserPlugin: true,
|
||||
isAppLoading: true,
|
||||
plugins: {},
|
||||
showGlobalAppBlocker: true,
|
||||
},
|
||||
localeToggle: {},
|
||||
hideLeftMenu: jest.fn(),
|
||||
hideLogout: jest.fn(),
|
||||
location: {},
|
||||
resetLocaleDefaultClassName: jest.fn(),
|
||||
setAppError: jest.fn(),
|
||||
setAppSecured: jest.fn(),
|
||||
showLeftMenu: jest.fn(),
|
||||
showLogout: jest.fn(),
|
||||
showGlobalAppBlocker: jest.fn(),
|
||||
unsetAppSecured: jest.fn(),
|
||||
updatePlugin: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
it('should not crash when mounted', () => {
|
||||
renderComponent(props);
|
||||
});
|
||||
|
||||
describe('ComponentDidUpdate', () => {
|
||||
it('should call the getSecuredData if the app is secured', () => {
|
||||
const wrapper = renderComponent(props);
|
||||
|
||||
expect(props.getSecuredData).not.toHaveBeenCalled();
|
||||
|
||||
wrapper.setProps({
|
||||
admin: { isSecured: true },
|
||||
});
|
||||
|
||||
expect(props.getSecuredData).toHaveBeenCalled();
|
||||
|
||||
wrapper.unmount();
|
||||
});
|
||||
|
||||
it('should call the getHook props with the willSecure arg if the pathname has changed', () => {
|
||||
props.admin.isLoading = false;
|
||||
props.global.isAppLoading = false;
|
||||
|
||||
const renderedComponent = renderComponent(props);
|
||||
renderedComponent.setProps({
|
||||
admin: { isLoading: false },
|
||||
location: { pathname: '/admin/marketPlace' },
|
||||
});
|
||||
|
||||
expect(props.getHook).toHaveBeenCalledWith('willSecure');
|
||||
|
||||
renderedComponent.unmount();
|
||||
});
|
||||
|
||||
it('should emit the didGetSecured hook event when the secured data has been retrieved', () => {
|
||||
const wrapper = renderComponent(props);
|
||||
|
||||
expect(props.getHook).not.toHaveBeenCalled();
|
||||
|
||||
wrapper.setProps({
|
||||
admin: { didGetSecuredData: true },
|
||||
});
|
||||
|
||||
expect(props.getHook).toHaveBeenCalledWith('didGetSecuredData');
|
||||
|
||||
wrapper.unmount();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('<Admin />', () => {
|
||||
let props;
|
||||
|
||||
beforeEach(() => {
|
||||
props = {
|
||||
admin: {
|
||||
autoReload: false,
|
||||
appError: false,
|
||||
currentEnvironment: 'development',
|
||||
didGetSecuredData: false,
|
||||
isLoading: true,
|
||||
isSecured: false,
|
||||
layout: {},
|
||||
securedData: {},
|
||||
showLogoutComponent: false,
|
||||
showMenu: true,
|
||||
strapiVersion: '3',
|
||||
uuid: false,
|
||||
},
|
||||
disableGlobalOverlayBlocker: jest.fn(),
|
||||
emitEvent: jest.fn(),
|
||||
enableGlobalOverlayBlocker: jest.fn(),
|
||||
getInitData: jest.fn(),
|
||||
getHook: jest.fn(),
|
||||
getSecuredData: jest.fn(),
|
||||
global: {
|
||||
appPlugins: [],
|
||||
blockApp: false,
|
||||
overlayBlockerData: null,
|
||||
hasUserPlugin: true,
|
||||
isAppLoading: true,
|
||||
plugins: {},
|
||||
showGlobalAppBlocker: true,
|
||||
},
|
||||
localeToggle: {},
|
||||
hideLeftMenu: jest.fn(),
|
||||
hideLogout: jest.fn(),
|
||||
location: {},
|
||||
resetLocaleDefaultClassName: jest.fn(),
|
||||
setAppError: jest.fn(),
|
||||
setAppSecured: jest.fn(),
|
||||
showLeftMenu: jest.fn(),
|
||||
showLogout: jest.fn(),
|
||||
showGlobalAppBlocker: jest.fn(),
|
||||
unsetAppSecured: jest.fn(),
|
||||
updatePlugin: jest.fn(),
|
||||
};
|
||||
});
|
||||
|
||||
it('should not crash', () => {
|
||||
shallow(<Admin {...props} />);
|
||||
});
|
||||
|
||||
describe('render', () => {
|
||||
it('should not display the header if the showMenu prop is false', () => {
|
||||
const adminProps = Object.assign(props.admin, {
|
||||
isLoading: false,
|
||||
showMenu: false,
|
||||
});
|
||||
const renderedComponent = shallow(<Admin {...props} {...adminProps} />);
|
||||
|
||||
expect(renderedComponent.find(Header)).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('should not display the Logout if the showLogoutComponent prop is false', () => {
|
||||
props.admin.isLoading = false;
|
||||
props.global.isAppLoading = false;
|
||||
const wrapper = shallow(<Admin {...props} />);
|
||||
|
||||
expect(wrapper.find(Logout)).toHaveLength(0);
|
||||
expect(wrapper.find(Onboarding)).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('should display the Logout if the showLogoutComponent prop is true', () => {
|
||||
props.admin.showLogoutComponent = true;
|
||||
props.admin.isLoading = false;
|
||||
props.global.isAppLoading = false;
|
||||
|
||||
const wrapper = shallow(<Admin {...props} />);
|
||||
|
||||
expect(wrapper.find(Logout)).toHaveLength(1);
|
||||
expect(wrapper.find(Onboarding)).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('should display the OverlayBlocker if blockApp and showGlobalOverlayBlocker are true', () => {
|
||||
const globalProps = Object.assign(props.global, {
|
||||
blockApp: true,
|
||||
isAppLoading: false,
|
||||
});
|
||||
props.admin.isLoading = false;
|
||||
const renderedComponent = shallow(<Admin {...props} {...globalProps} />);
|
||||
|
||||
expect(renderedComponent.find(OverlayBlocker)).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('should display the LoadingIndicatorPage if the isLoading prop is true', () => {
|
||||
const renderedComponent = shallow(<Admin {...props} />);
|
||||
|
||||
expect(renderedComponent.find(LoadingIndicatorPage)).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('HasApluginNotReady instance', () => {
|
||||
it('should return true if a plugin is not ready', () => {
|
||||
props.global.plugins = {
|
||||
test: { isReady: true },
|
||||
other: { isReady: false },
|
||||
};
|
||||
|
||||
const wrapper = shallow(<Admin {...props} />);
|
||||
const { hasApluginNotReady } = wrapper.instance();
|
||||
|
||||
expect(hasApluginNotReady(props)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should return false if all plugins are ready', () => {
|
||||
props.global.plugins = {
|
||||
test: { isReady: true },
|
||||
other: { isReady: true },
|
||||
};
|
||||
|
||||
const wrapper = shallow(<Admin {...props} />);
|
||||
const { hasApluginNotReady } = wrapper.instance();
|
||||
|
||||
expect(hasApluginNotReady(props)).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getContentWrapperStyle instance', () => {
|
||||
it('should return an empty object for the main key if showMenu prop is true', () => {
|
||||
const renderedComponent = shallow(<Admin {...props} />);
|
||||
const { getContentWrapperStyle } = renderedComponent.instance();
|
||||
const expected = { main: {}, sub: styles.content };
|
||||
|
||||
expect(getContentWrapperStyle()).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should not return an empty object for the main key if showMenu prop is true', () => {
|
||||
const adminProps = Object.assign(props.admin, { showMenu: false });
|
||||
const renderedComponent = shallow(<Admin {...props} {...adminProps} />);
|
||||
const { getContentWrapperStyle } = renderedComponent.instance();
|
||||
const expected = { main: { width: '100%' }, sub: styles.wrapper };
|
||||
|
||||
expect(getContentWrapperStyle()).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isAcceptingTracking instance', () => {
|
||||
it('should return false if the uuid prop is false', () => {
|
||||
const renderedComponent = shallow(<Admin {...props} />);
|
||||
const { isAcceptingTracking } = renderedComponent.instance();
|
||||
|
||||
expect(isAcceptingTracking()).toEqual(false);
|
||||
});
|
||||
|
||||
it('should return false if the uuid prop is null', () => {
|
||||
const adminProps = Object.assign(props.admin, { uuid: null });
|
||||
const renderedComponent = shallow(<Admin {...props} {...adminProps} />);
|
||||
const { isAcceptingTracking } = renderedComponent.instance();
|
||||
|
||||
expect(isAcceptingTracking()).toEqual(false);
|
||||
});
|
||||
|
||||
it('should return true if the uuid prop is true', () => {
|
||||
const adminProps = Object.assign(props.admin, { uuid: true });
|
||||
const renderedComponent = shallow(<Admin {...props} {...adminProps} />);
|
||||
const { isAcceptingTracking } = renderedComponent.instance();
|
||||
|
||||
expect(isAcceptingTracking()).toEqual(true);
|
||||
});
|
||||
|
||||
it('should return true if the uuid prop is a string', () => {
|
||||
const adminProps = Object.assign(props.admin, { uuid: 'uuid' });
|
||||
const renderedComponent = shallow(<Admin {...props} {...adminProps} />);
|
||||
const { isAcceptingTracking } = renderedComponent.instance();
|
||||
|
||||
expect(isAcceptingTracking()).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('renderMarketPlace instance', () => {
|
||||
it('should return the MarketPlace container', () => {
|
||||
const renderedComponent = shallow(<Admin {...props} />);
|
||||
const { renderMarketPlace } = renderedComponent.instance();
|
||||
|
||||
expect(renderMarketPlace()).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('renderInitializers', () => {
|
||||
it('should render the plugins initializer components', () => {
|
||||
const Initializer = () => <div>Initializer</div>;
|
||||
|
||||
props.admin.isLoading = false;
|
||||
props.global.plugins = {
|
||||
test: {
|
||||
initializer: Initializer,
|
||||
isReady: false,
|
||||
id: 'test',
|
||||
},
|
||||
};
|
||||
|
||||
const wrapper = shallow(<Admin {...props} />);
|
||||
|
||||
expect(wrapper.find(Initializer)).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('renderPluginDispatcher instance', () => {
|
||||
it('should return the pluginDispatcher component', () => {
|
||||
const renderedComponent = shallow(<Admin {...props} />);
|
||||
const { renderPluginDispatcher } = renderedComponent.instance();
|
||||
|
||||
expect(renderPluginDispatcher()).not.toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('<Admin />, mapDispatchToProps', () => {
|
||||
describe('disableGlobalOverlayBlocker', () => {
|
||||
it('should be injected', () => {
|
||||
const dispatch = jest.fn();
|
||||
const result = mapDispatchToProps(dispatch);
|
||||
|
||||
expect(result.disableGlobalOverlayBlocker).toBeDefined();
|
||||
});
|
||||
|
||||
it('should dispatch the disableGlobalOverlayBlocker action when called', () => {
|
||||
const dispatch = jest.fn();
|
||||
const result = mapDispatchToProps(dispatch);
|
||||
result.disableGlobalOverlayBlocker();
|
||||
|
||||
expect(dispatch).toHaveBeenCalledWith(disableGlobalOverlayBlocker());
|
||||
});
|
||||
});
|
||||
|
||||
describe('enableGlobalOverlayBlocker', () => {
|
||||
it('should be injected', () => {
|
||||
const dispatch = jest.fn();
|
||||
const result = mapDispatchToProps(dispatch);
|
||||
|
||||
expect(result.enableGlobalOverlayBlocker).toBeDefined();
|
||||
});
|
||||
|
||||
it('should dispatch the enableGlobalOverlayBlocker action when called', () => {
|
||||
const dispatch = jest.fn();
|
||||
const result = mapDispatchToProps(dispatch);
|
||||
result.enableGlobalOverlayBlocker();
|
||||
|
||||
expect(dispatch).toHaveBeenCalledWith(enableGlobalOverlayBlocker());
|
||||
});
|
||||
});
|
||||
|
||||
describe('getInitData', () => {
|
||||
it('should be injected', () => {
|
||||
const dispatch = jest.fn();
|
||||
const result = mapDispatchToProps(dispatch);
|
||||
|
||||
expect(result.getInitData).toBeDefined();
|
||||
});
|
||||
|
||||
it('should dispatch the getInitData action when called', () => {
|
||||
const dispatch = jest.fn();
|
||||
const result = mapDispatchToProps(dispatch);
|
||||
result.getInitData();
|
||||
|
||||
expect(dispatch).toHaveBeenCalledWith(getInitData());
|
||||
});
|
||||
});
|
||||
|
||||
describe('hideLeftMenu', () => {
|
||||
it('should be injected', () => {
|
||||
const dispatch = jest.fn();
|
||||
const result = mapDispatchToProps(dispatch);
|
||||
|
||||
expect(result.hideLeftMenu).toBeDefined();
|
||||
});
|
||||
|
||||
it('should dispatch the hideLeftMenu action when called', () => {
|
||||
const dispatch = jest.fn();
|
||||
const result = mapDispatchToProps(dispatch);
|
||||
result.hideLeftMenu();
|
||||
|
||||
expect(dispatch).toHaveBeenCalledWith(hideLeftMenu());
|
||||
});
|
||||
});
|
||||
|
||||
describe('setAppError', () => {
|
||||
it('should be injected', () => {
|
||||
const dispatch = jest.fn();
|
||||
const result = mapDispatchToProps(dispatch);
|
||||
|
||||
expect(result.setAppError).toBeDefined();
|
||||
});
|
||||
|
||||
it('should dispatch the setAppError action when called', () => {
|
||||
const dispatch = jest.fn();
|
||||
const result = mapDispatchToProps(dispatch);
|
||||
result.setAppError();
|
||||
|
||||
expect(dispatch).toHaveBeenCalledWith(setAppError());
|
||||
});
|
||||
});
|
||||
|
||||
describe('showLeftMenu', () => {
|
||||
it('should be injected', () => {
|
||||
const dispatch = jest.fn();
|
||||
const result = mapDispatchToProps(dispatch);
|
||||
|
||||
expect(result.showLeftMenu).toBeDefined();
|
||||
});
|
||||
|
||||
it('should dispatch the showLeftMenu action when called', () => {
|
||||
const dispatch = jest.fn();
|
||||
const result = mapDispatchToProps(dispatch);
|
||||
result.showLeftMenu();
|
||||
|
||||
expect(dispatch).toHaveBeenCalledWith(showLeftMenu());
|
||||
});
|
||||
});
|
||||
|
||||
describe('resetLocaleDefaultClassName', () => {
|
||||
it('should be injected', () => {
|
||||
const dispatch = jest.fn();
|
||||
const result = mapDispatchToProps(dispatch);
|
||||
|
||||
expect(result.resetLocaleDefaultClassName).toBeDefined();
|
||||
});
|
||||
|
||||
it('should dispatch the resetLocaleDefaultClassName action when called', () => {
|
||||
const dispatch = jest.fn();
|
||||
const result = mapDispatchToProps(dispatch);
|
||||
result.resetLocaleDefaultClassName();
|
||||
|
||||
expect(dispatch).toHaveBeenCalledWith(resetLocaleDefaultClassName());
|
||||
});
|
||||
});
|
||||
|
||||
describe('setLocaleCustomClassName', () => {
|
||||
it('should be injected', () => {
|
||||
const dispatch = jest.fn();
|
||||
const result = mapDispatchToProps(dispatch);
|
||||
|
||||
expect(result.setLocaleCustomClassName).toBeDefined();
|
||||
});
|
||||
|
||||
it('should dispatch the setLocaleCustomClassName action when called', () => {
|
||||
const dispatch = jest.fn();
|
||||
const result = mapDispatchToProps(dispatch);
|
||||
result.setLocaleCustomClassName();
|
||||
|
||||
expect(dispatch).toHaveBeenCalledWith(setLocaleCustomClassName());
|
||||
});
|
||||
});
|
||||
|
||||
describe('updatePlugin', () => {
|
||||
it('should be injected', () => {
|
||||
const dispatch = jest.fn();
|
||||
const result = mapDispatchToProps(dispatch);
|
||||
|
||||
expect(result.updatePlugin).toBeDefined();
|
||||
});
|
||||
|
||||
it('should dispatch the updatePlugin action when called', () => {
|
||||
const dispatch = jest.fn();
|
||||
const result = mapDispatchToProps(dispatch);
|
||||
result.updatePlugin();
|
||||
|
||||
expect(dispatch).toHaveBeenCalledWith(updatePlugin());
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,21 +0,0 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
|
||||
import NavTopRightWrapper from '../NavTopRightWrapper';
|
||||
|
||||
describe('<NavTopRightWrapper />', () => {
|
||||
it('should not crash', () => {
|
||||
shallow(<NavTopRightWrapper />);
|
||||
});
|
||||
|
||||
it('should render some child if given', () => {
|
||||
const Child = () => <div>Child</div>;
|
||||
const renderedComponent = shallow(
|
||||
<NavTopRightWrapper>
|
||||
<Child />
|
||||
</NavTopRightWrapper>,
|
||||
);
|
||||
|
||||
expect(renderedComponent.find(Child).exists()).toBe(true);
|
||||
});
|
||||
});
|
||||
@ -1,114 +0,0 @@
|
||||
import { fromJS, Map } from 'immutable';
|
||||
|
||||
import {
|
||||
getSecuredDataSucceeded,
|
||||
getInitDataSucceeded,
|
||||
hideLeftMenu,
|
||||
hideLogout,
|
||||
setAppError,
|
||||
setAppSecured,
|
||||
showLeftMenu,
|
||||
showLogout,
|
||||
unsetAppSecured,
|
||||
} from '../actions';
|
||||
import adminReducer from '../reducer';
|
||||
|
||||
describe('adminReducer', () => {
|
||||
let state;
|
||||
|
||||
beforeEach(() => {
|
||||
state = fromJS({
|
||||
autoReload: false,
|
||||
appError: false,
|
||||
currentEnvironment: 'development',
|
||||
didGetSecuredData: false,
|
||||
isLoading: true,
|
||||
isSecured: false,
|
||||
layout: Map({}),
|
||||
securedData: {},
|
||||
showLogoutComponent: false,
|
||||
showMenu: true,
|
||||
strapiVersion: '3',
|
||||
uuid: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('returns the initial state', () => {
|
||||
const expected = state;
|
||||
|
||||
expect(adminReducer(undefined, {})).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should handle the getSecuredDataSucceeded action correctly', () => {
|
||||
const expected = state
|
||||
.set('didGetSecuredData', true)
|
||||
.set('securedData', undefined);
|
||||
|
||||
expect(adminReducer(state, getSecuredDataSucceeded())).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should handle the getAdminDataSucceeded action correctly', () => {
|
||||
const data = {
|
||||
autoReload: true,
|
||||
currentEnvironment: 'production',
|
||||
isLoading: false,
|
||||
layout: {},
|
||||
strapiVersion: '3.0.0-beta',
|
||||
uuid: 'uuid',
|
||||
};
|
||||
const expected = state
|
||||
.set('autoReload', true)
|
||||
.set('currentEnvironment', 'production')
|
||||
.set('isLoading', false)
|
||||
.set('layout', Map({}))
|
||||
.set('strapiVersion', '3.0.0-beta')
|
||||
.set('uuid', 'uuid');
|
||||
|
||||
expect(adminReducer(state, getInitDataSucceeded(data))).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should handle the hideLeftMenu action correctly', () => {
|
||||
const expected = state.set('showMenu', false);
|
||||
|
||||
expect(adminReducer(state, hideLeftMenu())).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should handle the hideLogout action correctly', () => {
|
||||
const expected = state.set('showLogoutComponent', false);
|
||||
|
||||
expect(adminReducer(state, hideLogout())).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should handle the setaAppError action correctly', () => {
|
||||
const expected = state.set('appError', true);
|
||||
|
||||
expect(adminReducer(state, setAppError())).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should handle the setAppSecured action correctly', () => {
|
||||
const expected = state.set('isSecured', true);
|
||||
|
||||
expect(adminReducer(state, setAppSecured())).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should handle the showLeftMenu action correctly', () => {
|
||||
const expected = state.set('showMenu', true);
|
||||
state.set('showMenu', false);
|
||||
|
||||
expect(adminReducer(state, showLeftMenu())).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should handle the showLogout action correctly', () => {
|
||||
const expected = state.set('showLogoutComponent', true);
|
||||
state.set('showLogoutComponent', false);
|
||||
|
||||
expect(adminReducer(state, showLogout())).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should handle the unsetAppSecured action correctly', () => {
|
||||
state.set('isSecured', true);
|
||||
const expected = state.set('isSecured', false);
|
||||
|
||||
expect(adminReducer(state, unsetAppSecured())).toEqual(expected);
|
||||
});
|
||||
});
|
||||
@ -1,78 +0,0 @@
|
||||
/**
|
||||
* Test sagas
|
||||
*/
|
||||
|
||||
/* eslint-disable redux-saga/yield-effects */
|
||||
import { all, fork, put, takeLatest } from 'redux-saga/effects';
|
||||
import defaultSaga, { emitter, getData, getSecuredData } from '../saga';
|
||||
|
||||
import { getInitDataSucceeded } from '../actions';
|
||||
import { EMIT_EVENT, GET_INIT_DATA, GET_SECURED_DATA } from '../constants';
|
||||
|
||||
describe('getData Saga', () => {
|
||||
let getDataGenerator;
|
||||
|
||||
beforeEach(() => {
|
||||
getDataGenerator = getData();
|
||||
const response = [
|
||||
{ uuid: 'uuid' },
|
||||
{ strapiVersion: 'beta' },
|
||||
{ autoReload: true, currentEnvironment: 'test' },
|
||||
{ layout: {} },
|
||||
];
|
||||
const callDescriptor = getDataGenerator.next(response).value;
|
||||
|
||||
expect(callDescriptor).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should dispatch the getInitDataSucceeded action if it requests the data successfully', () => {
|
||||
const response = [
|
||||
{ uuid: 'uuid' },
|
||||
{ strapiVersion: 'beta' },
|
||||
{ autoReload: true, currentEnvironment: 'test' },
|
||||
{ layout: {} },
|
||||
];
|
||||
const putDescriptor = getDataGenerator.next(response).value;
|
||||
const [
|
||||
{ uuid },
|
||||
{ strapiVersion },
|
||||
{ autoReload, currentEnvironment },
|
||||
{ layout },
|
||||
] = response;
|
||||
|
||||
expect(putDescriptor).toEqual(
|
||||
put(
|
||||
getInitDataSucceeded({
|
||||
autoReload,
|
||||
uuid,
|
||||
strapiVersion,
|
||||
currentEnvironment,
|
||||
layout,
|
||||
}),
|
||||
),
|
||||
);
|
||||
});
|
||||
|
||||
it('should call the strapi.notification action if the response errors', () => {
|
||||
const response = new Error('Some error');
|
||||
getDataGenerator.throw(response).value;
|
||||
|
||||
expect(strapi.notification.error).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('defaultSaga Saga', () => {
|
||||
const defaultSagaSaga = defaultSaga();
|
||||
|
||||
it('should start task to watch for GET_INIT_DATA and GET_SECURED_DATA actions', () => {
|
||||
const forkDescriptor = defaultSagaSaga.next().value;
|
||||
|
||||
expect(forkDescriptor).toEqual(
|
||||
all([
|
||||
fork(takeLatest, EMIT_EVENT, emitter),
|
||||
fork(takeLatest, GET_INIT_DATA, getData),
|
||||
fork(takeLatest, GET_SECURED_DATA, getSecuredData),
|
||||
]),
|
||||
);
|
||||
});
|
||||
});
|
||||
@ -1,45 +0,0 @@
|
||||
import { fromJS, Map } from 'immutable';
|
||||
|
||||
import makeSelectAdminDomain, { selectAdminDomain } from '../selectors';
|
||||
|
||||
describe('<Admin /> selectors', () => {
|
||||
describe('selectAdminDomain selector', () => {
|
||||
it('should select the global state', () => {
|
||||
const state = fromJS({
|
||||
autoReload: false,
|
||||
appError: false,
|
||||
currentEnvironment: 'development',
|
||||
isLoading: true,
|
||||
layout: Map({}),
|
||||
showLeftMenu: true,
|
||||
strapiVersion: '3',
|
||||
uuid: false,
|
||||
});
|
||||
const mockedState = fromJS({
|
||||
admin: state,
|
||||
});
|
||||
|
||||
expect(selectAdminDomain()(mockedState)).toEqual(state);
|
||||
});
|
||||
});
|
||||
|
||||
describe('makeSelectAdminDomain', () => {
|
||||
it('should select the global state (.toJS())', () => {
|
||||
const state = fromJS({
|
||||
autoReload: false,
|
||||
appError: false,
|
||||
currentEnvironment: 'development',
|
||||
isLoading: true,
|
||||
layout: Map({}),
|
||||
showLeftMenu: true,
|
||||
strapiVersion: '3',
|
||||
uuid: false,
|
||||
});
|
||||
const mockedState = fromJS({
|
||||
admin: state,
|
||||
});
|
||||
|
||||
expect(makeSelectAdminDomain()(mockedState)).toEqual(state.toJS());
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,86 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* LanguageProvider actions
|
||||
*
|
||||
*/
|
||||
|
||||
import {
|
||||
DISABLE_GLOBAL_OVERLAY_BLOCKER,
|
||||
ENABLE_GLOBAL_OVERLAY_BLOCKER,
|
||||
FREEZE_APP,
|
||||
GET_APP_PLUGINS_SUCCEEDED,
|
||||
LOAD_PLUGIN,
|
||||
PLUGIN_DELETED,
|
||||
PLUGIN_LOADED,
|
||||
UNFREEZE_APP,
|
||||
UNSET_HAS_USERS_PLUGIN,
|
||||
UPDATE_PLUGIN,
|
||||
} from './constants';
|
||||
|
||||
export function disableGlobalOverlayBlocker() {
|
||||
return {
|
||||
type: DISABLE_GLOBAL_OVERLAY_BLOCKER,
|
||||
};
|
||||
}
|
||||
|
||||
export function enableGlobalOverlayBlocker() {
|
||||
return {
|
||||
type: ENABLE_GLOBAL_OVERLAY_BLOCKER,
|
||||
};
|
||||
}
|
||||
|
||||
export function freezeApp(data) {
|
||||
return {
|
||||
type: FREEZE_APP,
|
||||
data,
|
||||
};
|
||||
}
|
||||
|
||||
export function getAppPluginsSucceeded(plugins) {
|
||||
return {
|
||||
type: GET_APP_PLUGINS_SUCCEEDED,
|
||||
appPlugins: plugins.map(plugin => plugin.id),
|
||||
};
|
||||
}
|
||||
|
||||
export function loadPlugin(newPlugin) {
|
||||
return {
|
||||
type: LOAD_PLUGIN,
|
||||
plugin: newPlugin,
|
||||
};
|
||||
}
|
||||
|
||||
export function pluginDeleted(plugin) {
|
||||
return {
|
||||
type: PLUGIN_DELETED,
|
||||
plugin,
|
||||
};
|
||||
}
|
||||
|
||||
export function pluginLoaded(newPlugin) {
|
||||
return {
|
||||
type: PLUGIN_LOADED,
|
||||
plugin: newPlugin,
|
||||
};
|
||||
}
|
||||
|
||||
export function unfreezeApp() {
|
||||
return {
|
||||
type: UNFREEZE_APP,
|
||||
};
|
||||
}
|
||||
|
||||
export function unsetHasUserPlugin() {
|
||||
return {
|
||||
type: UNSET_HAS_USERS_PLUGIN,
|
||||
};
|
||||
}
|
||||
|
||||
export function updatePlugin(pluginId, updatedKey, updatedValue) {
|
||||
return {
|
||||
type: UPDATE_PLUGIN,
|
||||
pluginId,
|
||||
updatedKey,
|
||||
updatedValue,
|
||||
};
|
||||
}
|
||||
@ -1,18 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* App constants
|
||||
*
|
||||
*/
|
||||
|
||||
export const FREEZE_APP = 'app/App/FREEZE_APP';
|
||||
export const GET_APP_PLUGINS_SUCCEEDED = 'app/App/GET_APP_PLUGINS_SUCCEEDED';
|
||||
export const LOAD_PLUGIN = 'app/App/LOAD_PLUGIN';
|
||||
export const PLUGIN_LOADED = 'app/App/PLUGIN_LOADED';
|
||||
export const PLUGIN_DELETED = 'app/App/PLUGIN_DELETED';
|
||||
export const UNFREEZE_APP = 'app/App/UNFREEZE_APP';
|
||||
export const UNSET_HAS_USERS_PLUGIN = 'app/App/UNSET_HAS_USERS_PLUGIN';
|
||||
export const UPDATE_PLUGIN = 'app/App/UPDATE_PLUGIN';
|
||||
export const DISABLE_GLOBAL_OVERLAY_BLOCKER =
|
||||
'app/App/OverlayBlocker/DISABLE_GLOBAL_OVERLAY_BLOCKER';
|
||||
export const ENABLE_GLOBAL_OVERLAY_BLOCKER =
|
||||
'app/App/OverlayBlocker/ENABLE_GLOBAL_OVERLAY_BLOCKER';
|
||||
@ -1,53 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* App.js
|
||||
*
|
||||
* This component is the skeleton around the actual pages, and should only
|
||||
* contain code that should be seen on all pages. (e.g. navigation bar)
|
||||
*
|
||||
* 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 { Switch, Route } from 'react-router-dom';
|
||||
import { LoadingIndicatorPage } from 'strapi-helper-plugin';
|
||||
|
||||
import Admin from '../Admin';
|
||||
import NotFoundPage from '../NotFoundPage';
|
||||
import NotificationProvider from '../NotificationProvider';
|
||||
import AppLoader from '../AppLoader';
|
||||
import styles from './styles.scss';
|
||||
|
||||
function App(props) {
|
||||
return (
|
||||
<div>
|
||||
<NotificationProvider />
|
||||
<AppLoader>
|
||||
{({ shouldLoad }) => {
|
||||
if (shouldLoad) {
|
||||
return <LoadingIndicatorPage />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<Switch>
|
||||
<Route
|
||||
path="/"
|
||||
render={router => <Admin {...props} {...router} />}
|
||||
/>
|
||||
<Route path="" component={NotFoundPage} />
|
||||
</Switch>
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
</AppLoader>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
App.propTypes = {};
|
||||
|
||||
export default App;
|
||||
@ -1,62 +0,0 @@
|
||||
// Shared constants
|
||||
import { fromJS, List } from 'immutable';
|
||||
|
||||
import {
|
||||
DISABLE_GLOBAL_OVERLAY_BLOCKER,
|
||||
ENABLE_GLOBAL_OVERLAY_BLOCKER,
|
||||
FREEZE_APP,
|
||||
GET_APP_PLUGINS_SUCCEEDED,
|
||||
PLUGIN_DELETED,
|
||||
PLUGIN_LOADED,
|
||||
UNFREEZE_APP,
|
||||
UNSET_HAS_USERS_PLUGIN,
|
||||
UPDATE_PLUGIN,
|
||||
} from './constants';
|
||||
|
||||
const initialState = fromJS({
|
||||
appPlugins: List([]),
|
||||
blockApp: false,
|
||||
overlayBlockerData: null,
|
||||
hasUserPlugin: true,
|
||||
isAppLoading: true,
|
||||
plugins: {},
|
||||
showGlobalAppBlocker: true,
|
||||
});
|
||||
|
||||
function appReducer(state = initialState, action) {
|
||||
switch (action.type) {
|
||||
case DISABLE_GLOBAL_OVERLAY_BLOCKER:
|
||||
return state.set('showGlobalAppBlocker', false);
|
||||
case ENABLE_GLOBAL_OVERLAY_BLOCKER:
|
||||
return state.set('showGlobalAppBlocker', true);
|
||||
case FREEZE_APP:
|
||||
return state.set('blockApp', true).update('overlayBlockerData', () => {
|
||||
if (action.data) {
|
||||
return action.data;
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
case GET_APP_PLUGINS_SUCCEEDED:
|
||||
return state
|
||||
.update('appPlugins', () => List(action.appPlugins))
|
||||
.update('isAppLoading', () => false);
|
||||
case PLUGIN_LOADED:
|
||||
return state.setIn(['plugins', action.plugin.id], fromJS(action.plugin));
|
||||
case UPDATE_PLUGIN:
|
||||
return state.setIn(
|
||||
['plugins', action.pluginId, action.updatedKey],
|
||||
fromJS(action.updatedValue),
|
||||
);
|
||||
case PLUGIN_DELETED:
|
||||
return state.deleteIn(['plugins', action.plugin]);
|
||||
case UNFREEZE_APP:
|
||||
return state.set('blockApp', false).set('overlayBlockerData', null);
|
||||
case UNSET_HAS_USERS_PLUGIN:
|
||||
return state.set('hasUserPlugin', false);
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
export default appReducer;
|
||||
@ -1,61 +0,0 @@
|
||||
import { createSelector } from 'reselect';
|
||||
|
||||
/**
|
||||
* Direct selector to the languageToggle state domain
|
||||
*/
|
||||
const selectApp = () => (state) => state.get('app');
|
||||
|
||||
/**
|
||||
* Select the language locale
|
||||
*/
|
||||
|
||||
const selectPlugins = () => createSelector(
|
||||
selectApp(),
|
||||
(appState) => appState.get('plugins')
|
||||
);
|
||||
|
||||
const makeSelectApp = () => createSelector(
|
||||
selectApp(),
|
||||
appState => appState.toJS(),
|
||||
);
|
||||
|
||||
const selectHasUserPlugin = () => createSelector(
|
||||
selectApp(),
|
||||
(appState) => appState.get('hasUserPlugin'),
|
||||
);
|
||||
|
||||
const makeSelectShowGlobalAppBlocker = () => createSelector(
|
||||
selectApp(),
|
||||
(appState) => appState.get('showGlobalAppBlocker'),
|
||||
);
|
||||
|
||||
const makeSelectBlockApp = () => createSelector(
|
||||
selectApp(),
|
||||
(appState) => appState.get('blockApp'),
|
||||
);
|
||||
|
||||
const makeSelectOverlayBlockerProps = () => createSelector(
|
||||
selectApp(),
|
||||
(appState) => appState.get('overlayBlockerData'),
|
||||
);
|
||||
|
||||
const makeSelectIsAppLoading = () => createSelector(
|
||||
selectApp(),
|
||||
appState => appState.get('isAppLoading'),
|
||||
);
|
||||
|
||||
const makeSelectAppPlugins = () => createSelector(
|
||||
selectApp(),
|
||||
appState => appState.get('appPlugins').toJS(),
|
||||
);
|
||||
export default makeSelectApp;
|
||||
export {
|
||||
selectApp,
|
||||
selectHasUserPlugin,
|
||||
selectPlugins,
|
||||
makeSelectAppPlugins,
|
||||
makeSelectBlockApp,
|
||||
makeSelectOverlayBlockerProps,
|
||||
makeSelectIsAppLoading,
|
||||
makeSelectShowGlobalAppBlocker,
|
||||
};
|
||||
@ -1,11 +0,0 @@
|
||||
/**
|
||||
* styles.css
|
||||
*
|
||||
* App container styles
|
||||
*/
|
||||
|
||||
@import "../../styles/variables/variables";
|
||||
|
||||
.container {
|
||||
display: block;
|
||||
}
|
||||
@ -1,25 +0,0 @@
|
||||
li.header{
|
||||
color: #101622 !important;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.07rem;
|
||||
-webkit-font-smoothing: subpixel-antialiased;
|
||||
}
|
||||
|
||||
li.chapter > a{
|
||||
transition: background-color 0.15s ease;
|
||||
-webkit-transition: background-color 0.15s ease;
|
||||
}
|
||||
|
||||
li.chapter:hover > a{
|
||||
background: #eee !important;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
.versions-select select{
|
||||
height: 36px;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.book-summary{
|
||||
background-color: #f7f7f7 !important;
|
||||
}
|
||||
@ -1,143 +0,0 @@
|
||||
import {
|
||||
FREEZE_APP,
|
||||
GET_APP_PLUGINS_SUCCEEDED,
|
||||
LOAD_PLUGIN,
|
||||
PLUGIN_DELETED,
|
||||
PLUGIN_LOADED,
|
||||
UNFREEZE_APP,
|
||||
UNSET_HAS_USERS_PLUGIN,
|
||||
UPDATE_PLUGIN,
|
||||
} from '../constants';
|
||||
import {
|
||||
freezeApp,
|
||||
getAppPluginsSucceeded,
|
||||
loadPlugin,
|
||||
pluginDeleted,
|
||||
pluginLoaded,
|
||||
unfreezeApp,
|
||||
unsetHasUserPlugin,
|
||||
updatePlugin,
|
||||
} from '../actions';
|
||||
|
||||
describe('<App /> actions', () => {
|
||||
describe('freezeApp', () => {
|
||||
it('should return the correct type and the passed data', () => {
|
||||
const data = { strapi: 'isCool' };
|
||||
const expected = {
|
||||
type: FREEZE_APP,
|
||||
data,
|
||||
};
|
||||
|
||||
expect(freezeApp(data)).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('unfreezeApp', () => {
|
||||
it('should return the correct type', () => {
|
||||
const expected = {
|
||||
type: UNFREEZE_APP,
|
||||
};
|
||||
|
||||
expect(unfreezeApp()).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getAppPluginsSucceeded', () => {
|
||||
it('should return the correct type and an array containing the id of the plugins', () => {
|
||||
const plugins = [
|
||||
{
|
||||
id: 'content-manager',
|
||||
source: {
|
||||
development: '/content-manager/main.js',
|
||||
production: '/content-manager/main.js',
|
||||
staging: '/content-manager/main.js',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'content-type-builder',
|
||||
source: {
|
||||
development: '/content-type-builder/main.js',
|
||||
production: '/content-type-builder/main.js',
|
||||
staging: '/content-type-builder/main.js',
|
||||
},
|
||||
},
|
||||
];
|
||||
const expected = {
|
||||
type: GET_APP_PLUGINS_SUCCEEDED,
|
||||
appPlugins: ['content-manager', 'content-type-builder'],
|
||||
};
|
||||
|
||||
expect(getAppPluginsSucceeded(plugins)).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('loadPlugin', () => {
|
||||
it('should return the correct type and the passed data', () => {
|
||||
const plugin = {
|
||||
id: 'content-manager',
|
||||
description: 'Manage your content',
|
||||
};
|
||||
const expected = {
|
||||
type: LOAD_PLUGIN,
|
||||
plugin,
|
||||
};
|
||||
|
||||
expect(loadPlugin(plugin)).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('pluginLoaded', () => {
|
||||
it('should return the correct type and the passed data', () => {
|
||||
const plugin = {
|
||||
id: 'content-manager',
|
||||
description: 'Manage your content',
|
||||
};
|
||||
const expected = {
|
||||
type: PLUGIN_LOADED,
|
||||
plugin,
|
||||
};
|
||||
|
||||
expect(pluginLoaded(plugin)).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('pluginDeleted', () => {
|
||||
it('should return the correct type and the passed data', () => {
|
||||
const plugin = 'content-manager';
|
||||
const expected = {
|
||||
type: PLUGIN_DELETED,
|
||||
plugin,
|
||||
};
|
||||
|
||||
expect(pluginDeleted(plugin)).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('unsetHasUserPlugin', () => {
|
||||
it('should return the correct type', () => {
|
||||
const expected = {
|
||||
type: UNSET_HAS_USERS_PLUGIN,
|
||||
};
|
||||
|
||||
expect(unsetHasUserPlugin()).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('updatePlugin', () => {
|
||||
it('should return the correct type and the passed data', () => {
|
||||
const pluginId = 'content-manager';
|
||||
const updatedKey = 'isReady';
|
||||
const updatedValue = true;
|
||||
const expected = {
|
||||
type: UPDATE_PLUGIN,
|
||||
pluginId,
|
||||
updatedKey,
|
||||
updatedValue,
|
||||
};
|
||||
|
||||
expect(updatePlugin(pluginId, updatedKey, updatedValue)).toEqual(
|
||||
expected,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,31 +0,0 @@
|
||||
import React from 'react';
|
||||
import { shallow } from 'enzyme';
|
||||
import { Route } from 'react-router-dom';
|
||||
|
||||
import AppLoader from '../../AppLoader';
|
||||
import App from '../../App';
|
||||
|
||||
describe('<App />', () => {
|
||||
it('should render the <AppLoader />', () => {
|
||||
const renderedComponent = shallow(<App />);
|
||||
expect(renderedComponent.find(AppLoader)).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('Should render the <Switch /> if the app is loading', () => {
|
||||
const topComp = shallow(<App />);
|
||||
const insideAppLoaderNotLoading = shallow(
|
||||
topComp.find(AppLoader).prop('children')({ shouldLoad: false }),
|
||||
);
|
||||
|
||||
expect(insideAppLoaderNotLoading.find(Route).length).toBe(2);
|
||||
});
|
||||
|
||||
it('should not render the <Switch /> if the app is loading', () => {
|
||||
const topComp = shallow(<App />);
|
||||
|
||||
const insideAppLoaderLoading = shallow(
|
||||
topComp.find(AppLoader).prop('children')({ shouldLoad: true }),
|
||||
);
|
||||
expect(insideAppLoaderLoading.find(Route).length).toBe(0);
|
||||
});
|
||||
});
|
||||
@ -1,129 +0,0 @@
|
||||
import { fromJS, List } from 'immutable';
|
||||
import {
|
||||
disableGlobalOverlayBlocker,
|
||||
enableGlobalOverlayBlocker,
|
||||
freezeApp,
|
||||
getAppPluginsSucceeded,
|
||||
pluginDeleted,
|
||||
pluginLoaded,
|
||||
unfreezeApp,
|
||||
unsetHasUserPlugin,
|
||||
updatePlugin,
|
||||
} from '../actions';
|
||||
import appReducer from '../reducer';
|
||||
|
||||
describe('<App /> reducer', () => {
|
||||
let state;
|
||||
|
||||
beforeEach(() => {
|
||||
state = fromJS({
|
||||
appPlugins: List([]),
|
||||
blockApp: false,
|
||||
overlayBlockerData: null,
|
||||
hasUserPlugin: true,
|
||||
isAppLoading: true,
|
||||
plugins: {},
|
||||
showGlobalAppBlocker: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('should return the initial state', () => {
|
||||
const expectedResult = state;
|
||||
expect(appReducer(undefined, {})).toEqual(expectedResult);
|
||||
});
|
||||
|
||||
it('should handle the disableGlobalOverlayBlocker action correctly', () => {
|
||||
const expectedResult = state.set('showGlobalAppBlocker', false);
|
||||
expect(appReducer(state, disableGlobalOverlayBlocker())).toEqual(
|
||||
expectedResult,
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle the enableGlobalOverlayBlocker action correctly', () => {
|
||||
state = state.set('showGlobalAppBlocker', false);
|
||||
const expectedResult = state.set('showGlobalAppBlocker', true);
|
||||
expect(appReducer(state, enableGlobalOverlayBlocker())).toEqual(
|
||||
expectedResult,
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle the freezeApp action correctly and set the overlayBlockerData key if passed data', () => {
|
||||
const expectedResult = state
|
||||
.set('blockApp', true)
|
||||
.set('overlayBlockerData', { title: 'A title' });
|
||||
expect(appReducer(state, freezeApp({ title: 'A title' }))).toEqual(
|
||||
expectedResult,
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle the freezeApp action correctly and NOT set the overlayBlockerData key if no passed data', () => {
|
||||
const expectedResult = state.set('blockApp', true);
|
||||
|
||||
expect(appReducer(state, freezeApp())).toEqual(expectedResult);
|
||||
});
|
||||
|
||||
it('should handle the getAppPluginsSucceeded action correclty', () => {
|
||||
const plugins = [{ id: 'content-manager' }];
|
||||
const expectedResult = state
|
||||
.set('appPlugins', List(['content-manager']))
|
||||
.set('isAppLoading', false);
|
||||
|
||||
expect(appReducer(state, getAppPluginsSucceeded(plugins))).toEqual(
|
||||
expectedResult,
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle the pluginLoaded action correclty', () => {
|
||||
const plugin = {
|
||||
id: 'content-manager',
|
||||
description: 'Manage your content',
|
||||
};
|
||||
const expectedResult = state.setIn(
|
||||
['plugins', 'content-manager'],
|
||||
fromJS(plugin),
|
||||
);
|
||||
|
||||
expect(appReducer(state, pluginLoaded(plugin))).toEqual(expectedResult);
|
||||
});
|
||||
|
||||
it('should handle the updatePlugin action correclty', () => {
|
||||
const plugin = { id: 'content-manager', isReady: false };
|
||||
state = state.setIn(['plugins', 'content-manager'], fromJS(plugin));
|
||||
|
||||
const expectedResult = state.setIn(
|
||||
['plugins', 'content-manager', 'isReady'],
|
||||
true,
|
||||
);
|
||||
|
||||
expect(
|
||||
appReducer(state, updatePlugin('content-manager', 'isReady', true)),
|
||||
).toEqual(expectedResult);
|
||||
});
|
||||
|
||||
it('should handle the pluginDeleted action correclty', () => {
|
||||
const plugin = { id: 'content-manager', isReady: false };
|
||||
state = state.setIn(['plugins', 'content-manager'], fromJS(plugin));
|
||||
const expectedResult = state.deleteIn(['plugins', 'content-manager']);
|
||||
|
||||
expect(appReducer(state, pluginDeleted('content-manager'))).toEqual(
|
||||
expectedResult,
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle the unfreezeApp action correclty', () => {
|
||||
state = state
|
||||
.set('blockApp', true)
|
||||
.set('overlayBlockerData', { foo: 'bar' });
|
||||
const expectedResult = state
|
||||
.set('blockApp', false)
|
||||
.set('overlayBlockerData', null);
|
||||
|
||||
expect(appReducer(state, unfreezeApp())).toEqual(expectedResult);
|
||||
});
|
||||
|
||||
it('should handle the unsetHasUserPlugin action correclty', () => {
|
||||
const expectedResult = state.set('hasUserPlugin', false);
|
||||
|
||||
expect(appReducer(state, unsetHasUserPlugin())).toEqual(expectedResult);
|
||||
});
|
||||
});
|
||||
@ -1,126 +0,0 @@
|
||||
import { fromJS } from 'immutable';
|
||||
|
||||
import makeSelectApp, {
|
||||
selectApp,
|
||||
selectHasUserPlugin,
|
||||
selectPlugins,
|
||||
makeSelectAppPlugins,
|
||||
makeSelectBlockApp,
|
||||
makeSelectOverlayBlockerProps,
|
||||
makeSelectIsAppLoading,
|
||||
makeSelectShowGlobalAppBlocker,
|
||||
} from '../selectors';
|
||||
|
||||
describe('<App /> selectors', () => {
|
||||
describe('selectApp', () => {
|
||||
it('should select the global state', () => {
|
||||
const appState = fromJS({});
|
||||
const mockedState = fromJS({
|
||||
app: appState,
|
||||
});
|
||||
|
||||
expect(selectApp()(mockedState)).toEqual(appState);
|
||||
});
|
||||
});
|
||||
|
||||
describe('makeSelectApp', () => {
|
||||
it('should select the appState (.toJS())', () => {
|
||||
const appState = fromJS({});
|
||||
const mockedState = fromJS({
|
||||
app: appState,
|
||||
});
|
||||
|
||||
expect(makeSelectApp()(mockedState)).toEqual(appState.toJS());
|
||||
});
|
||||
});
|
||||
|
||||
describe('selectHasUserPlugin', () => {
|
||||
it('should select the hasUserPlugin', () => {
|
||||
const appState = fromJS({
|
||||
hasUserPlugin: true,
|
||||
});
|
||||
const mockedState = fromJS({
|
||||
app: appState,
|
||||
});
|
||||
|
||||
expect(selectHasUserPlugin()(mockedState)).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('selectPlugins', () => {
|
||||
it('should select the plugins', () => {
|
||||
const plugins = fromJS({ email: { isReady: true } });
|
||||
const mockedState = fromJS({
|
||||
app: {
|
||||
plugins,
|
||||
},
|
||||
});
|
||||
|
||||
expect(selectPlugins()(mockedState)).toEqual(plugins);
|
||||
});
|
||||
});
|
||||
|
||||
describe('makeSelectAppPlugins', () => {
|
||||
it('should select the appPlugins', () => {
|
||||
const plugins = ['email'];
|
||||
const mockedState = fromJS({
|
||||
app: {
|
||||
appPlugins: plugins,
|
||||
},
|
||||
});
|
||||
|
||||
expect(makeSelectAppPlugins()(mockedState)).toEqual(plugins);
|
||||
});
|
||||
});
|
||||
|
||||
describe('makeSelectBlockApp', () => {
|
||||
it('should select the blockApp', () => {
|
||||
const mockedState = fromJS({
|
||||
app: {
|
||||
blockApp: true,
|
||||
},
|
||||
});
|
||||
|
||||
expect(makeSelectBlockApp()(mockedState)).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('makeSelectOverlayBlockerProps', () => {
|
||||
it('should select the overlayBlockerData', () => {
|
||||
const overlayBlockerData = fromJS({ title: 'title' });
|
||||
const mockedState = fromJS({
|
||||
app: {
|
||||
overlayBlockerData,
|
||||
},
|
||||
});
|
||||
|
||||
expect(makeSelectOverlayBlockerProps()(mockedState)).toEqual(
|
||||
overlayBlockerData,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('makeSelectIsAppLoading', () => {
|
||||
it('should select the isAppLoading', () => {
|
||||
const mockedState = fromJS({
|
||||
app: {
|
||||
isAppLoading: true,
|
||||
},
|
||||
});
|
||||
|
||||
expect(makeSelectIsAppLoading()(mockedState)).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('makeSelectShowGlobalAppBlocker', () => {
|
||||
it('should select the showGlobalAppBlocker', () => {
|
||||
const mockedState = fromJS({
|
||||
app: {
|
||||
showGlobalAppBlocker: true,
|
||||
},
|
||||
});
|
||||
|
||||
expect(makeSelectShowGlobalAppBlocker()(mockedState)).toEqual(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,37 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* AppLoader
|
||||
*
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import PropTypes from 'prop-types';
|
||||
import makeSelectApp from '../App/selectors';
|
||||
|
||||
export class AppLoader extends React.Component {
|
||||
shouldLoad = () => {
|
||||
const { appPlugins, plugins: mountedPlugins } = this.props;
|
||||
|
||||
return appPlugins.length !== Object.keys(mountedPlugins).length;
|
||||
};
|
||||
|
||||
render() {
|
||||
const { children } = this.props;
|
||||
|
||||
return children({ shouldLoad: this.shouldLoad() });
|
||||
}
|
||||
}
|
||||
|
||||
AppLoader.propTypes = {
|
||||
appPlugins: PropTypes.array.isRequired,
|
||||
children: PropTypes.func.isRequired,
|
||||
plugins: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
const mapStateToProps = makeSelectApp();
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
null,
|
||||
)(AppLoader);
|
||||