diff --git a/examples/getstarted/config/locales/af.json b/examples/getstarted/config/locales/af.json new file mode 100644 index 0000000000..9e26dfeeb6 --- /dev/null +++ b/examples/getstarted/config/locales/af.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/packages/strapi-admin/admin/src/plugins.js b/packages/strapi-admin/admin/src/plugins.js index c04a4f39b7..500b485f35 100644 --- a/packages/strapi-admin/admin/src/plugins.js +++ b/packages/strapi-admin/admin/src/plugins.js @@ -22,16 +22,16 @@ window.strapi = Object.assign(window.strapi || {}, { module.exports = { 'strapi-plugin-users-permissions': require('../../../strapi-plugin-users-permissions/admin/src') .default, - 'strapi-plugin-content-manager': require('../../../strapi-plugin-content-manager/admin/src') - .default, + // 'strapi-plugin-content-manager': require('../../../strapi-plugin-content-manager/admin/src') + // .default, 'strapi-plugin-content-type-builder': require('../../../strapi-plugin-content-type-builder/admin/src') .default, - 'strapi-plugin-documentation': require('../../../strapi-plugin-documentation/admin/src') - .default, + // 'strapi-plugin-documentation': require('../../../strapi-plugin-documentation/admin/src') + // .default, 'strapi-plugin-settings-manager': require('../../../strapi-plugin-settings-manager/admin/src') .default, - 'strapi-plugin-email': require('../../../strapi-plugin-email/admin/src') - .default, - 'strapi-plugin-upload': require('../../../strapi-plugin-upload/admin/src') - .default, + // 'strapi-plugin-email': require('../../../strapi-plugin-email/admin/src') + // .default, + // 'strapi-plugin-upload': require('../../../strapi-plugin-upload/admin/src') + // .default, }; diff --git a/packages/strapi-plugin-content-type-builder/admin/src/containers/ViewContainer/StyledViewContainer.js b/packages/strapi-helper-plugin/lib/src/components/ViewContainer/index.js similarity index 94% rename from packages/strapi-plugin-content-type-builder/admin/src/containers/ViewContainer/StyledViewContainer.js rename to packages/strapi-helper-plugin/lib/src/components/ViewContainer/index.js index 0e29cf6120..fdd2d56844 100644 --- a/packages/strapi-plugin-content-type-builder/admin/src/containers/ViewContainer/StyledViewContainer.js +++ b/packages/strapi-helper-plugin/lib/src/components/ViewContainer/index.js @@ -6,7 +6,7 @@ import styled from 'styled-components'; -import { sizes } from 'strapi-helper-plugin'; +import sizes from '../../assets/styles/sizes'; const StyledViewContainer = styled.div` min-height: calc(100vh - ${sizes.header.height}); diff --git a/packages/strapi-helper-plugin/lib/src/index.js b/packages/strapi-helper-plugin/lib/src/index.js index 1cdb7dad6a..cca2deeb9c 100644 --- a/packages/strapi-helper-plugin/lib/src/index.js +++ b/packages/strapi-helper-plugin/lib/src/index.js @@ -93,6 +93,7 @@ export { default as PluginHeader } from './components/PluginHeader'; export { default as PopUpWarning } from './components/PopUpWarning'; export { default as StyledLeftMenu } from './components/StyledLeftMenu'; export { default as TrashButton } from './components/TrashButton'; +export { default as ViewContainer } from './components/ViewContainer'; // Utils export { default as auth } from './utils/auth'; diff --git a/packages/strapi-plugin-content-type-builder/admin/src/containers/ViewContainer/index.js b/packages/strapi-plugin-content-type-builder/admin/src/containers/ViewContainer/index.js index 169527caf2..710f0fbe2a 100644 --- a/packages/strapi-plugin-content-type-builder/admin/src/containers/ViewContainer/index.js +++ b/packages/strapi-plugin-content-type-builder/admin/src/containers/ViewContainer/index.js @@ -7,10 +7,14 @@ import React from 'react'; import PropTypes from 'prop-types'; -import StyledViewContainer from './StyledViewContainer'; +// import StyledViewContainer from './StyledViewContainer'; import LeftMenu from '../LeftMenu'; -import { PluginHeader, getQueryParameters } from 'strapi-helper-plugin'; +import { + PluginHeader, + getQueryParameters, + ViewContainer as StyledViewContainer, +} from 'strapi-helper-plugin'; function ViewContainer({ children, diff --git a/packages/strapi-plugin-settings-manager/admin/src/components/ListRow/index.js b/packages/strapi-plugin-settings-manager/admin/src/components/ListRow/index.js new file mode 100644 index 0000000000..0a822a9784 --- /dev/null +++ b/packages/strapi-plugin-settings-manager/admin/src/components/ListRow/index.js @@ -0,0 +1,67 @@ +/** + * + * ListRow + * + */ + +import styled from 'styled-components'; + +import { colors } from 'strapi-helper-plugin'; + +const ListRow = styled.tr` + background-color: transparent; + p { + margin-bottom: 0; + } + img { + width: 35px; + } + button { + cursor: pointer; + } + td:first-of-type { + padding-left: 3rem; + position: relative; + img { + width: 35px; + height: 20px; + position: absolute; + top: calc(50% - 10px); + left: 3rem; + } + img + p { + width: 237px; + padding-left: calc(3rem + 35px); + } + } + td:nth-child(2) { + width: 25rem; + p { + font-weight: 500; + text-transform: capitalize; + } + } + td:last-child { + text-align: right; + } + &.relation-row { + background: linear-gradient( + 135deg, + rgba(28, 93, 231, 0.05), + rgba(239, 243, 253, 0) + ); + } + &.clickable { + &:hover { + cursor: pointer; + background-color: ${colors.grey}; + & + tr { + &::before { + background-color: transparent; + } + } + } + } +`; + +export default ListRow; diff --git a/packages/strapi-plugin-settings-manager/admin/src/components/MenuSection/index.js b/packages/strapi-plugin-settings-manager/admin/src/components/MenuSection/index.js new file mode 100644 index 0000000000..f69d1692cd --- /dev/null +++ b/packages/strapi-plugin-settings-manager/admin/src/components/MenuSection/index.js @@ -0,0 +1,36 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { NavLink } from 'react-router-dom'; +import { FormattedMessage } from 'react-intl'; +import pluginId from '../../pluginId'; + +const MenuSection = ({ name, items }) => { + return ( + + + + + + {items.map(link => { + return ( + + + + + + + + + ); + })} + + + ); +}; + +MenuSection.propTypes = { + items: PropTypes.array.isRequired, + name: PropTypes.string.isRequired, +}; + +export default MenuSection; diff --git a/packages/strapi-plugin-settings-manager/admin/src/containers/LanguagePage/Action.js b/packages/strapi-plugin-settings-manager/admin/src/containers/LanguagePage/Action.js new file mode 100644 index 0000000000..f77b38f88c --- /dev/null +++ b/packages/strapi-plugin-settings-manager/admin/src/containers/LanguagePage/Action.js @@ -0,0 +1,23 @@ +import React from 'react'; +import { FormattedMessage } from 'react-intl'; +import PropTypes from 'prop-types'; +import pluginId from '../../pluginId'; + +const Action = ({ isActive }) => { + const trad = isActive ? 'default' : 'set'; + const tradId = `${pluginId}.list.languages.${trad}.languages`; + const color = isActive ? '#49515a' : '#1c5de7'; + const fontStyle = isActive ? 'italic' : ''; + + return ( + + {msg => {msg}} + + ); +}; + +Action.propTypes = { + isActive: PropTypes.bool.isRequired, +}; + +export default Action; diff --git a/packages/strapi-plugin-settings-manager/admin/src/containers/LanguagePage/Flags.js b/packages/strapi-plugin-settings-manager/admin/src/containers/LanguagePage/Flags.js new file mode 100644 index 0000000000..133e602491 --- /dev/null +++ b/packages/strapi-plugin-settings-manager/admin/src/containers/LanguagePage/Flags.js @@ -0,0 +1,7 @@ +import { createGlobalStyle } from 'styled-components'; + +const Flags = createGlobalStyle` + @import url('https://cdnjs.cloudflare.com/ajax/libs/flag-icon-css/3.3.0/css/flag-icon.css'); +`; + +export default Flags; diff --git a/packages/strapi-plugin-settings-manager/admin/src/containers/LanguagePage/index.js b/packages/strapi-plugin-settings-manager/admin/src/containers/LanguagePage/index.js new file mode 100644 index 0000000000..add9a1fd59 --- /dev/null +++ b/packages/strapi-plugin-settings-manager/admin/src/containers/LanguagePage/index.js @@ -0,0 +1,155 @@ +import React, { useCallback, useEffect, useReducer } from 'react'; +// import PropTypes from 'prop-types'; +import { FormattedMessage } from 'react-intl'; +import { get } from 'lodash'; +import { + LoadingIndicatorPage, + PluginHeader, + List, + ListHeader, + ListTitle, + ListWrapper, +} from 'strapi-helper-plugin'; +import pluginId from '../../pluginId'; +import useFetch from '../../hooks/useFetch'; +import ListRow from '../../components/ListRow'; +import getFlag, { formatLanguageLocale } from '../../utils/getFlag'; +import filterLanguages from './utils/filterLanguages'; +import reducer, { initialState } from './reducer'; +import Action from './Action'; +import Flags from './Flags'; + +const getTrad = key => `${pluginId}.${key}`; + +const LanguagePage = () => { + const [reducerState, dispatch] = useReducer(reducer, initialState); + const { modifiedData, allLanguages } = reducerState.toJS(); + const { data, isLoading } = useFetch(['languages', 'i18n']); + const findLangTrad = useCallback( + lang => { + const trad = get(allLanguages, [ + 'sections', + '0', + 'items', + '0', + 'items', + ]).find(obj => obj.value === formatLanguageLocale(lang).join('_')); + + return trad; + }, + [allLanguages] + ); + + useEffect(() => { + if (!isLoading) { + const [{ languages }, result] = data; + + dispatch({ + type: 'GET_DATA_SUCCEEDED', + languages, + allLanguages: result, + availableLanguages: filterLanguages(languages, result), + }); + } + + return () => {}; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isLoading]); + + if (isLoading) { + return ; + } + + // return null; + const buttonProps = { + kind: 'secondaryHotlineAdd', + label: getTrad('list.languages.button.label'), + }; + const availableLanguagesLength = modifiedData.length; + const listTitleSuffix = availableLanguagesLength > 1 ? 'plural' : 'singular'; + + return ( + <> + + + + + + + + {title => ( + + {availableLanguagesLength} {title} + + )} + + + + + + + {modifiedData.map(lang => { + const langArray = formatLanguageLocale(lang.name); + const flag = getFlag(langArray); + + return ( + + + + {msg => ( + + + {msg} + + )} + + + {lang.name} + + + + + + + + + + ); + })} + + + + + > + ); +}; + +export default LanguagePage; diff --git a/packages/strapi-plugin-settings-manager/admin/src/containers/LanguagePage/reducer.js b/packages/strapi-plugin-settings-manager/admin/src/containers/LanguagePage/reducer.js new file mode 100644 index 0000000000..2ea24ba13c --- /dev/null +++ b/packages/strapi-plugin-settings-manager/admin/src/containers/LanguagePage/reducer.js @@ -0,0 +1,34 @@ +import { fromJS } from 'immutable'; +import { get } from 'lodash'; + +const initialState = fromJS({ + availableLanguages: {}, + allLanguages: {}, + initialData: [], + modifiedData: [], + selectOptions: [], +}); + +const reducer = (state, action) => { + switch (action.type) { + case 'GET_DATA_SUCCEEDED': + return state + .update('allLanguages', () => fromJS(action.allLanguages)) + .update('modifiedData', () => fromJS(action.languages)) + .update('initialData', () => fromJS(action.languages)) + .update('selectOptions', () => + fromJS( + get( + action.availableLanguages, + ['sections', '0', 'items', '0', 'items'], + [] + ) + ) + ); + default: + return state; + } +}; + +export default reducer; +export { initialState }; diff --git a/packages/strapi-plugin-settings-manager/admin/src/containers/LanguagePage/utils/filterLanguages.js b/packages/strapi-plugin-settings-manager/admin/src/containers/LanguagePage/utils/filterLanguages.js new file mode 100644 index 0000000000..b631790c15 --- /dev/null +++ b/packages/strapi-plugin-settings-manager/admin/src/containers/LanguagePage/utils/filterLanguages.js @@ -0,0 +1,22 @@ +import { cloneDeep, get, set } from 'lodash'; + +const filterLanguages = (appLanguages, availableLanguages) => { + const ret = cloneDeep(availableLanguages); + const filteredLanguages = get( + ret, + ['sections', '0', 'items', '0', 'items'], + [] + ).filter(lang => { + const i = appLanguages.findIndex(obj => { + return obj.name === lang.value.toLowerCase(); + }); + + return i === -1; + }); + + set(ret, ['sections', '0', 'items', '0', 'items'], filteredLanguages); + + return ret; +}; + +export default filterLanguages; diff --git a/packages/strapi-plugin-settings-manager/admin/src/containers/Main/index.js b/packages/strapi-plugin-settings-manager/admin/src/containers/Main/index.js new file mode 100644 index 0000000000..f6a46f3748 --- /dev/null +++ b/packages/strapi-plugin-settings-manager/admin/src/containers/Main/index.js @@ -0,0 +1,110 @@ +import React, { useEffect, useState } from 'react'; +import { get } from 'lodash'; +import { Switch, Route } from 'react-router-dom'; +import PropTypes from 'prop-types'; +import { + LoadingIndicatorPage, + request, + StyledLeftMenu, + ViewContainer, +} from 'strapi-helper-plugin'; +import pluginId from '../../pluginId'; +import MenuSection from '../../components/MenuSection'; +import LanguagePage from '../LanguagePage'; +/* eslint-disable */ +const Main = ({ + global: { currentEnvironment }, + history: { push }, + location: { pathname }, +}) => { + const [{ menuSections, environments, isLoading }, setState] = useState({ + menuSections: [], + environments: [], + isLoading: true, + }); + const abortController = new AbortController(); + const { signal } = abortController; + + useEffect(() => { + const getData = async () => { + try { + const endPoints = ['menu', 'configurations/environments']; + const [ + { sections: menuSections }, + { environments }, + ] = await Promise.all( + endPoints.map(endPoint => + request(`/${pluginId}/${endPoint}`, { method: 'GET', signal }) + ) + ); + + // Redirect to first link item + if (pathname.split('/').length === 3) { + const firstLink = get( + menuSections, + [0, 'items', 0, 'slug'], + 'application' + ); + + push(`/plugins/${pluginId}/${firstLink}`); + } + + setState({ menuSections, environments, isLoading: false }); + } catch (err) { + strapi.notification.error(`${pluginId}.strapi.notification.error`); + } + }; + + getData(); + + return () => { + abortController.abort(); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + if (isLoading) { + return ; + } + + // console.log({ menuSections }); + + return ( + + + + + {menuSections.map(section => ( + + ))} + + + + + + + + + + + + ); +}; + +Main.propTypes = { + global: PropTypes.shape({ + currentEnvironment: PropTypes.string.isRequired, + }), + history: PropTypes.shape({ + push: PropTypes.func.isRequired, + }), + location: PropTypes.shape({ + pathname: PropTypes.string.isRequired, + }), +}; + +export default Main; diff --git a/packages/strapi-plugin-settings-manager/admin/src/hooks/useFetch.js b/packages/strapi-plugin-settings-manager/admin/src/hooks/useFetch.js new file mode 100644 index 0000000000..0ef94d5350 --- /dev/null +++ b/packages/strapi-plugin-settings-manager/admin/src/hooks/useFetch.js @@ -0,0 +1,38 @@ +import { useEffect, useState } from 'react'; +import { request } from 'strapi-helper-plugin'; +import pluginId from '../pluginId'; + +const useFetch = endPoints => { + const abortController = new AbortController(); + const { signal } = abortController; + const [state, setState] = useState({ data: {}, isLoading: true }); + + useEffect(() => { + const getData = async () => { + try { + const data = await Promise.all( + endPoints.map(endPoint => + request(`/${pluginId}/configurations/${endPoint}`, { + method: 'GET', + signal, + }) + ) + ); + setState({ data, isLoading: false }); + } catch (err) { + strapi.notification.error(`${pluginId}.strapi.notification.error`); + } + }; + + getData(); + + return () => { + abortController.abort(); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + return { data: state.data, isLoading: state.isLoading }; +}; + +export default useFetch; diff --git a/packages/strapi-plugin-settings-manager/admin/src/index.js b/packages/strapi-plugin-settings-manager/admin/src/index.js index 24396be235..e4bd8bdff0 100644 --- a/packages/strapi-plugin-settings-manager/admin/src/index.js +++ b/packages/strapi-plugin-settings-manager/admin/src/index.js @@ -1,7 +1,8 @@ import React from 'react'; import pluginPkg from '../../package.json'; import pluginId from './pluginId'; -import App from './containers/App'; +// import App from './containers/App'; +import App from './containers/Main'; import Initializer from './containers/Initializer'; import lifecycles from './lifecycles'; import trads from './translations';
+ + +
+ + {msg} +