diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 58c4865cf2..486e647e87 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -107,6 +107,12 @@ jobs: matrix: python-version: ['3.8', '3.12'] db-backend: [mysql, postgres] + # test Python 3.13 already + # release is scheduled for 2024-10-01 (https://peps.python.org/pep-0719/) + # when released: replace "python-version: ['3.8', '3.12']" with python-version: ['3.8', '3.13'] + include: + - python-version: '3.13' + db-backend: postgres steps: - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} diff --git a/.gitignore b/.gitignore index 2af3bf59da..3d2c0d88b1 100644 --- a/.gitignore +++ b/.gitignore @@ -36,8 +36,12 @@ dist rdmo/management/static -rdmo/projects/static/projects/js/projects.js +rdmo/core/static/core/js/base.js +rdmo/core/static/core/fonts +rdmo/core/static/core/css/base.css + +rdmo/projects/static/projects/js/*.js rdmo/projects/static/projects/fonts -rdmo/projects/static/projects/css/projects.css +rdmo/projects/static/projects/css/*.css screenshots diff --git a/rdmo/core/static/core/fonts/DroidSans-Bold.ttf b/rdmo/core/assets/fonts/DroidSans-Bold.ttf similarity index 100% rename from rdmo/core/static/core/fonts/DroidSans-Bold.ttf rename to rdmo/core/assets/fonts/DroidSans-Bold.ttf diff --git a/rdmo/core/static/core/fonts/DroidSans.ttf b/rdmo/core/assets/fonts/DroidSans.ttf similarity index 100% rename from rdmo/core/static/core/fonts/DroidSans.ttf rename to rdmo/core/assets/fonts/DroidSans.ttf diff --git a/rdmo/core/static/core/fonts/DroidSansMono.ttf b/rdmo/core/assets/fonts/DroidSansMono.ttf similarity index 100% rename from rdmo/core/static/core/fonts/DroidSansMono.ttf rename to rdmo/core/assets/fonts/DroidSansMono.ttf diff --git a/rdmo/core/static/core/fonts/DroidSerif-Bold.ttf b/rdmo/core/assets/fonts/DroidSerif-Bold.ttf similarity index 100% rename from rdmo/core/static/core/fonts/DroidSerif-Bold.ttf rename to rdmo/core/assets/fonts/DroidSerif-Bold.ttf diff --git a/rdmo/core/static/core/fonts/DroidSerif-BoldItalic.ttf b/rdmo/core/assets/fonts/DroidSerif-BoldItalic.ttf similarity index 100% rename from rdmo/core/static/core/fonts/DroidSerif-BoldItalic.ttf rename to rdmo/core/assets/fonts/DroidSerif-BoldItalic.ttf diff --git a/rdmo/core/static/core/fonts/DroidSerif-Italic.ttf b/rdmo/core/assets/fonts/DroidSerif-Italic.ttf similarity index 100% rename from rdmo/core/static/core/fonts/DroidSerif-Italic.ttf rename to rdmo/core/assets/fonts/DroidSerif-Italic.ttf diff --git a/rdmo/core/static/core/fonts/DroidSerif.ttf b/rdmo/core/assets/fonts/DroidSerif.ttf similarity index 100% rename from rdmo/core/static/core/fonts/DroidSerif.ttf rename to rdmo/core/assets/fonts/DroidSerif.ttf diff --git a/rdmo/core/assets/img/favicon.png b/rdmo/core/assets/img/favicon.png new file mode 100644 index 0000000000..042bcf2bbb Binary files /dev/null and b/rdmo/core/assets/img/favicon.png differ diff --git a/rdmo/core/assets/img/rdmo-logo.svg b/rdmo/core/assets/img/rdmo-logo.svg new file mode 100644 index 0000000000..93fc2eae24 --- /dev/null +++ b/rdmo/core/assets/img/rdmo-logo.svg @@ -0,0 +1,260 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + RDMO + + diff --git a/rdmo/core/assets/js/actions/actionTypes.js b/rdmo/core/assets/js/actions/actionTypes.js new file mode 100644 index 0000000000..1f97c54f3d --- /dev/null +++ b/rdmo/core/assets/js/actions/actionTypes.js @@ -0,0 +1,17 @@ +export const UPDATE_CONFIG = 'UPDATE_CONFIG' +export const DELETE_CONFIG = 'DELETE_CONFIG' + +export const ADD_TO_PENDING = 'ADD_TO_PENDING' +export const REMOVE_FROM_PENDING = 'REMOVE_FROM_PENDING' + +export const FETCH_SETTINGS_ERROR = 'FETCH_SETTINGS_ERROR' +export const FETCH_SETTINGS_INIT = 'FETCH_SETTINGS_INIT' +export const FETCH_SETTINGS_SUCCESS = 'FETCH_SETTINGS_SUCCESS' + +export const FETCH_TEMPLATES_ERROR = 'FETCH_TEMPLATES_ERROR' +export const FETCH_TEMPLATES_INIT = 'FETCH_TEMPLATES_INIT' +export const FETCH_TEMPLATES_SUCCESS = 'FETCH_TEMPLATES_SUCCESS' + +export const FETCH_CURRENT_USER_ERROR = 'FETCH_CURRENT_USER_ERROR' +export const FETCH_CURRENT_USER_INIT = 'FETCH_CURRENT_USER_INIT' +export const FETCH_CURRENT_USER_SUCCESS = 'FETCH_CURRENT_USER_SUCCESS' diff --git a/rdmo/core/assets/js/actions/configActions.js b/rdmo/core/assets/js/actions/configActions.js new file mode 100644 index 0000000000..0aaebe47b9 --- /dev/null +++ b/rdmo/core/assets/js/actions/configActions.js @@ -0,0 +1,9 @@ +import { UPDATE_CONFIG, DELETE_CONFIG } from './actionTypes' + +export function updateConfig(path, value, ls = false) { + return {type: UPDATE_CONFIG, path, value, ls} +} + +export function deleteConfig(path, ls = false) { + return {type: DELETE_CONFIG, path, ls} +} diff --git a/rdmo/core/assets/js/actions/pendingActions.js b/rdmo/core/assets/js/actions/pendingActions.js new file mode 100644 index 0000000000..2ee4aaf514 --- /dev/null +++ b/rdmo/core/assets/js/actions/pendingActions.js @@ -0,0 +1,9 @@ +import { ADD_TO_PENDING, REMOVE_FROM_PENDING } from './actionTypes' + +export function addToPending(item) { + return {type: ADD_TO_PENDING, item} +} + +export function removeFromPending(item) { + return {type: REMOVE_FROM_PENDING, item} +} diff --git a/rdmo/core/assets/js/actions/settingsActions.js b/rdmo/core/assets/js/actions/settingsActions.js new file mode 100644 index 0000000000..d95f6be36a --- /dev/null +++ b/rdmo/core/assets/js/actions/settingsActions.js @@ -0,0 +1,25 @@ +import CoreApi from '../api/CoreApi' + +import { FETCH_SETTINGS_ERROR, FETCH_SETTINGS_INIT, FETCH_SETTINGS_SUCCESS } from './actionTypes' + +export function fetchSettings() { + return function(dispatch) { + dispatch(fetchSettingsInit()) + + return CoreApi.fetchSettings() + .then((settings) => dispatch(fetchSettingsSuccess(settings))) + .catch((errors) => dispatch(fetchSettingsError(errors))) + } +} + +export function fetchSettingsInit() { + return {type: FETCH_SETTINGS_INIT} +} + +export function fetchSettingsSuccess(settings) { + return {type: FETCH_SETTINGS_SUCCESS, settings} +} + +export function fetchSettingsError(errors) { + return {type: FETCH_SETTINGS_ERROR, errors} +} diff --git a/rdmo/core/assets/js/actions/templateActions.js b/rdmo/core/assets/js/actions/templateActions.js new file mode 100644 index 0000000000..90e598e342 --- /dev/null +++ b/rdmo/core/assets/js/actions/templateActions.js @@ -0,0 +1,25 @@ +import CoreApi from '../api/CoreApi' + +import { FETCH_TEMPLATES_ERROR, FETCH_TEMPLATES_INIT, FETCH_TEMPLATES_SUCCESS } from './actionTypes' + +export function fetchTemplates() { + return function(dispatch) { + dispatch(fetchTemplatesInit()) + + return CoreApi.fetchTemplates() + .then((templates) => dispatch(fetchTemplatesSuccess(templates))) + .catch((errors) => dispatch(fetchTemplatesError(errors))) + } +} + +export function fetchTemplatesInit() { + return {type: FETCH_TEMPLATES_INIT} +} + +export function fetchTemplatesSuccess(templates) { + return {type: FETCH_TEMPLATES_SUCCESS, templates} +} + +export function fetchTemplatesError(errors) { + return {type: FETCH_TEMPLATES_ERROR, errors} +} diff --git a/rdmo/core/assets/js/actions/userActions.js b/rdmo/core/assets/js/actions/userActions.js new file mode 100644 index 0000000000..2c2922428d --- /dev/null +++ b/rdmo/core/assets/js/actions/userActions.js @@ -0,0 +1,25 @@ +import AccountsApi from '../api/AccountsApi' + +import { FETCH_CURRENT_USER_ERROR, FETCH_CURRENT_USER_INIT, FETCH_CURRENT_USER_SUCCESS } from './actionTypes' + +export function fetchCurrentUser() { + return function(dispatch) { + dispatch(fetchCurrentUserInit()) + + return AccountsApi.fetchCurrentUser(true) + .then(currentUser => dispatch(fetchCurrentUserSuccess({ currentUser }))) + .catch(error => dispatch(fetchCurrentUserError(error))) + } +} + +export function fetchCurrentUserInit() { + return {type: FETCH_CURRENT_USER_INIT} +} + +export function fetchCurrentUserSuccess(currentUser) { + return {type: FETCH_CURRENT_USER_SUCCESS, currentUser} +} + +export function fetchCurrentUserError(error) { + return {type: FETCH_CURRENT_USER_ERROR, error} +} diff --git a/rdmo/projects/assets/js/api/AccountsApi.js b/rdmo/core/assets/js/api/AccountsApi.js similarity index 100% rename from rdmo/projects/assets/js/api/AccountsApi.js rename to rdmo/core/assets/js/api/AccountsApi.js diff --git a/rdmo/core/assets/js/api/BaseApi.js b/rdmo/core/assets/js/api/BaseApi.js index c2d81e9473..ae0281bb69 100644 --- a/rdmo/core/assets/js/api/BaseApi.js +++ b/rdmo/core/assets/js/api/BaseApi.js @@ -1,7 +1,7 @@ import Cookies from 'js-cookie' import isUndefined from 'lodash/isUndefined' -import baseUrl from '../utils/baseUrl' +import { baseUrl } from '../utils/meta' function ApiError(statusText, status) { this.status = status @@ -52,6 +52,28 @@ class BaseApi { }) } + static postFormData(url, formData) { + return fetch(baseUrl + url, { + method: 'POST', + headers: { + 'X-CSRFToken': Cookies.get('csrftoken') + }, + body: formData + }).catch(error => { + throw new ApiError(error.message) + }).then(response => { + if (response.ok) { + return response.json() + } else if (response.status == 400) { + return response.json().then(errors => { + throw new ValidationError(errors) + }) + } else { + throw new ApiError(response.statusText, response.status) + } + }) + } + static put(url, data) { return fetch(baseUrl + url, { method: 'PUT', diff --git a/rdmo/core/assets/js/api/CoreApi.js b/rdmo/core/assets/js/api/CoreApi.js index ef77eb4701..d97cfc2983 100644 --- a/rdmo/core/assets/js/api/CoreApi.js +++ b/rdmo/core/assets/js/api/CoreApi.js @@ -14,6 +14,10 @@ class CoreApi extends BaseApi { return this.get('/api/v1/core/groups/') } + static fetchTemplates() { + return this.get('/api/v1/core/templates/') + } + } export default CoreApi diff --git a/rdmo/core/assets/js/base.js b/rdmo/core/assets/js/base.js new file mode 100644 index 0000000000..3bab0caba5 --- /dev/null +++ b/rdmo/core/assets/js/base.js @@ -0,0 +1 @@ +import 'bootstrap-sass' diff --git a/rdmo/core/assets/js/components/Html.js b/rdmo/core/assets/js/components/Html.js new file mode 100644 index 0000000000..acd13bbd23 --- /dev/null +++ b/rdmo/core/assets/js/components/Html.js @@ -0,0 +1,16 @@ +import React from 'react' +import PropTypes from 'prop-types' +import { isEmpty } from 'lodash' + +const Html = ({ html = '' }) => { + return !isEmpty(html) && ( +
+ ) +} + +Html.propTypes = { + className: PropTypes.string, + html: PropTypes.string +} + +export default Html diff --git a/rdmo/core/assets/js/components/Modal.js b/rdmo/core/assets/js/components/Modal.js index a3f2cd70b8..22c07abeab 100644 --- a/rdmo/core/assets/js/components/Modal.js +++ b/rdmo/core/assets/js/components/Modal.js @@ -2,9 +2,9 @@ import React from 'react' import PropTypes from 'prop-types' import { Modal as BootstrapModal } from 'react-bootstrap' -const Modal = ({ bsSize, buttonLabel, buttonProps, title, show, onClose, onSave, children }) => { +const Modal = ({ title, show, modalProps, submitLabel, submitProps, onClose, onSubmit, children }) => { return ( - +

{title}

@@ -15,11 +15,12 @@ const Modal = ({ bsSize, buttonLabel, buttonProps, title, show, onClose, onSave, - { onSave ? - - : null + { + onSubmit && ( + + ) }
@@ -27,14 +28,14 @@ const Modal = ({ bsSize, buttonLabel, buttonProps, title, show, onClose, onSave, } Modal.propTypes = { - bsSize: PropTypes.oneOf(['lg', 'large', 'sm', 'small']), - buttonLabel: PropTypes.string, - buttonProps: PropTypes.object, - children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]).isRequired, - onClose: PropTypes.func.isRequired, - onSave: PropTypes.func, - show: PropTypes.bool.isRequired, title: PropTypes.string.isRequired, + show: PropTypes.bool.isRequired, + modalProps: PropTypes.object, + submitLabel: PropTypes.string, + submitProps: PropTypes.object, + onClose: PropTypes.func.isRequired, + onSubmit: PropTypes.func, + children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]).isRequired, } export default Modal diff --git a/rdmo/core/assets/js/containers/Pending.js b/rdmo/core/assets/js/containers/Pending.js new file mode 100644 index 0000000000..07609f8a79 --- /dev/null +++ b/rdmo/core/assets/js/containers/Pending.js @@ -0,0 +1,24 @@ +import React from 'react' +import PropTypes from 'prop-types' +import { connect } from 'react-redux' +import { isEmpty } from 'lodash' + +const Pending = ({ pending }) => { + return ( + !isEmpty(pending.items) && ( + + ) + ) +} + +Pending.propTypes = { + pending: PropTypes.object.isRequired, +} + +function mapStateToProps(state) { + return { + pending: state.pending, + } +} + +export default connect(mapStateToProps)(Pending) diff --git a/rdmo/core/assets/js/reducers/configReducer.js b/rdmo/core/assets/js/reducers/configReducer.js new file mode 100644 index 0000000000..9001d4e8c4 --- /dev/null +++ b/rdmo/core/assets/js/reducers/configReducer.js @@ -0,0 +1,22 @@ +import { updateConfig, deleteConfig, setConfigInLocalStorage, deleteConfigInLocalStorage } from '../utils/config' + +import { DELETE_CONFIG, UPDATE_CONFIG } from '../actions/actionTypes' + +const initialState = {} + +export default function configReducer(state = initialState, action) { + switch(action.type) { + case UPDATE_CONFIG: + if (action.ls) { + setConfigInLocalStorage(state.prefix, action.path, action.value) + } + return updateConfig(state, action.path, action.value) + case DELETE_CONFIG: + if (action.ls) { + deleteConfigInLocalStorage(state.prefix, action.path) + } + return deleteConfig(state, action.path) + default: + return state + } +} diff --git a/rdmo/core/assets/js/reducers/pendingReducer.js b/rdmo/core/assets/js/reducers/pendingReducer.js new file mode 100644 index 0000000000..7f5b9de1cc --- /dev/null +++ b/rdmo/core/assets/js/reducers/pendingReducer.js @@ -0,0 +1,16 @@ +import { ADD_TO_PENDING, REMOVE_FROM_PENDING } from '../actions/actionTypes' + +const initialState = { + items: [] +} + +export default function pendingReducer(state = initialState, action) { + switch(action.type) { + case ADD_TO_PENDING: + return { ...state, items: [...state.items, action.item] } + case REMOVE_FROM_PENDING: + return { ...state, items: state.items.filter((item) => (item != action.item)) } + default: + return state + } +} diff --git a/rdmo/core/assets/js/reducers/settingsReducer.js b/rdmo/core/assets/js/reducers/settingsReducer.js new file mode 100644 index 0000000000..379f1829de --- /dev/null +++ b/rdmo/core/assets/js/reducers/settingsReducer.js @@ -0,0 +1,14 @@ +import { FETCH_SETTINGS_ERROR, FETCH_SETTINGS_SUCCESS } from '../actions/actionTypes' + +const initialState = {} + +export default function settingsReducer(state = initialState, action) { + switch(action.type) { + case FETCH_SETTINGS_SUCCESS: + return { ...state, ...action.settings } + case FETCH_SETTINGS_ERROR: + return { ...state, errors: action.errors } + default: + return state + } +} diff --git a/rdmo/core/assets/js/reducers/templateReducer.js b/rdmo/core/assets/js/reducers/templateReducer.js new file mode 100644 index 0000000000..b7897f2eb8 --- /dev/null +++ b/rdmo/core/assets/js/reducers/templateReducer.js @@ -0,0 +1,14 @@ +import { FETCH_TEMPLATES_ERROR, FETCH_TEMPLATES_SUCCESS } from '../actions/actionTypes' + +const initialState = {} + +export default function templateReducer(state = initialState, action) { + switch(action.type) { + case FETCH_TEMPLATES_SUCCESS: + return { ...state, ...action.templates } + case FETCH_TEMPLATES_ERROR: + return { ...state, errors: action.errors } + default: + return state + } +} diff --git a/rdmo/core/assets/js/reducers/userReducer.js b/rdmo/core/assets/js/reducers/userReducer.js new file mode 100644 index 0000000000..8c76285f56 --- /dev/null +++ b/rdmo/core/assets/js/reducers/userReducer.js @@ -0,0 +1,18 @@ +import { FETCH_CURRENT_USER_ERROR, FETCH_CURRENT_USER_INIT, FETCH_CURRENT_USER_SUCCESS } from '../actions/actionTypes' + +const initialState = { + currentUser: {}, +} + +export default function userReducer(state = initialState, action) { + switch(action.type) { + case FETCH_CURRENT_USER_INIT: + return {...state, ...action.currentUser} + case FETCH_CURRENT_USER_SUCCESS: + return {...state, ...action.currentUser} + case FETCH_CURRENT_USER_ERROR: + return {...state, errors: action.error.errors} + default: + return state + } +} diff --git a/rdmo/core/assets/js/utils/baseUrl.js b/rdmo/core/assets/js/utils/baseUrl.js deleted file mode 100644 index 22a17df960..0000000000 --- a/rdmo/core/assets/js/utils/baseUrl.js +++ /dev/null @@ -1,2 +0,0 @@ -// take the baseurl from the of the django template -export default document.querySelector('meta[name="baseurl"]').content.replace(/\/+$/, '') diff --git a/rdmo/core/assets/js/utils/config.js b/rdmo/core/assets/js/utils/config.js new file mode 100644 index 0000000000..22b1be6460 --- /dev/null +++ b/rdmo/core/assets/js/utils/config.js @@ -0,0 +1,58 @@ +import { set, unset, toNumber, isNaN } from 'lodash' + +const updateConfig = (config, path, value) => { + const newConfig = {...config} + set(newConfig, path, value) + return newConfig +} + +const deleteConfig = (config, path) => { + const newConfig = {...config} + unset(newConfig, path) + return newConfig +} + +const getConfigFromLocalStorage = (prefix) => { + const ls = {...localStorage} + + return Object.entries(ls) + .filter(([lsPath,]) => lsPath.startsWith(prefix)) + .map(([lsPath, lsValue]) => { + if (lsPath.startsWith(prefix)) { + const path = lsPath.replace(`${prefix}.`, '') + + // check if it is literal 'true' or 'false' + if (lsValue === 'true') { + return [path, true] + } else if (lsValue === 'false') { + return [path, false] + } + + // check if the value is number or a string + const numberValue = toNumber(lsValue) + if (isNaN(numberValue)) { + return [path, lsValue] + } else { + return [path, numberValue] + } + } else { + return null + } + }) +} + +const setConfigInLocalStorage = (prefix, path, value) => { + localStorage.setItem(`${prefix}.${path}`, value) +} + +const deleteConfigInLocalStorage = (prefix, path) => { + localStorage.removeItem(`${prefix}.${path}`) +} + +export { + updateConfig, + deleteConfig, + getConfigFromLocalStorage, + setConfigInLocalStorage, + deleteConfigInLocalStorage +} diff --git a/rdmo/core/assets/js/utils/index.js b/rdmo/core/assets/js/utils/index.js index c2d32da182..30d03229a7 100644 --- a/rdmo/core/assets/js/utils/index.js +++ b/rdmo/core/assets/js/utils/index.js @@ -1,5 +1,2 @@ export * from './api' -export { default as baseUrl } from './baseUrl' -export { default as language } from './language' -export { default as siteId } from './siteId' -export { default as staticUrl } from './staticUrl' +export { baseUrl, language, siteId, staticUrl } from './meta' diff --git a/rdmo/core/assets/js/utils/language.js b/rdmo/core/assets/js/utils/language.js deleted file mode 100644 index 58dc8a369e..0000000000 --- a/rdmo/core/assets/js/utils/language.js +++ /dev/null @@ -1,2 +0,0 @@ -// take the language from the of the django template -export default document.querySelector('meta[name="language"]').content diff --git a/rdmo/core/assets/js/utils/meta.js b/rdmo/core/assets/js/utils/meta.js new file mode 100644 index 0000000000..d8186e6c2f --- /dev/null +++ b/rdmo/core/assets/js/utils/meta.js @@ -0,0 +1,9 @@ +// take information from the of the django template + +export const baseUrl = document.querySelector('meta[name="baseurl"]').content.replace(/\/+$/, '') + +export const staticUrl = document.querySelector('meta[name="staticurl"]').content.replace(/\/+$/, '') + +export const siteId = Number(document.querySelector('meta[name="site_id"]').content) + +export const language = document.querySelector('meta[name="language"]').content diff --git a/rdmo/core/assets/js/utils/siteId.js b/rdmo/core/assets/js/utils/siteId.js deleted file mode 100644 index 7b413b672e..0000000000 --- a/rdmo/core/assets/js/utils/siteId.js +++ /dev/null @@ -1,2 +0,0 @@ -// take the site_id from the of the django template -export default Number(document.querySelector('meta[name="site_id"]').content) diff --git a/rdmo/core/assets/js/utils/staticUrl.js b/rdmo/core/assets/js/utils/staticUrl.js deleted file mode 100644 index 0a1323cb10..0000000000 --- a/rdmo/core/assets/js/utils/staticUrl.js +++ /dev/null @@ -1,2 +0,0 @@ -// take the staticurl from the of the django template -export default document.querySelector('meta[name="staticurl"]').content.replace(/\/+$/, '') diff --git a/rdmo/core/assets/js/utils/store.js b/rdmo/core/assets/js/utils/store.js new file mode 100644 index 0000000000..d30203fd59 --- /dev/null +++ b/rdmo/core/assets/js/utils/store.js @@ -0,0 +1,14 @@ +import Cookies from 'js-cookie' +import isEmpty from 'lodash/isEmpty' + +const checkStoreId = () => { + const currentStoreId = Cookies.get('storeid') + const localStoreId = localStorage.getItem('rdmo.storeid') + + if (isEmpty(localStoreId) || localStoreId !== currentStoreId) { + localStorage.clear() + localStorage.setItem('rdmo.storeid', currentStoreId) + } +} + +export { checkStoreId } diff --git a/rdmo/core/assets/scss/base.scss b/rdmo/core/assets/scss/base.scss new file mode 100644 index 0000000000..dadff686a5 --- /dev/null +++ b/rdmo/core/assets/scss/base.scss @@ -0,0 +1,15 @@ +$icon-font-path: "bootstrap-sass/assets/fonts/bootstrap/"; +@import '~bootstrap-sass'; +@import '~font-awesome/css/font-awesome.css'; + +@import 'react-datepicker/dist/react-datepicker.css'; + +@import 'variables'; +@import 'style'; + +@import 'codemirror'; +@import 'fonts'; +@import 'footer'; +@import 'header'; +@import 'swagger'; +@import 'utils'; diff --git a/rdmo/core/assets/scss/codemirror.scss b/rdmo/core/assets/scss/codemirror.scss new file mode 100644 index 0000000000..be9f2c0bef --- /dev/null +++ b/rdmo/core/assets/scss/codemirror.scss @@ -0,0 +1,9 @@ +.CodeMirror { + font-family: DroidSans-Mono, mono; +} + +formgroup .CodeMirror { + border-radius: 4px; + border: 1px solid #ccc; + color: #555; +} diff --git a/rdmo/core/assets/scss/fonts.scss b/rdmo/core/assets/scss/fonts.scss new file mode 100644 index 0000000000..4d1f217320 --- /dev/null +++ b/rdmo/core/assets/scss/fonts.scss @@ -0,0 +1,44 @@ +@font-face { + font-family: "DroidSans"; + src: url('../fonts/DroidSans.ttf'); +} +@font-face { + font-family: "DroidSans"; + src: url('../fonts/DroidSans-Bold.ttf'); + font-weight: bold; +} +@font-face { + font-family: "DroidSans-Mono"; + src: url('../fonts/DroidSansMono.ttf'); +} +@font-face { + font-family: "DroidSerif"; + src: url('../fonts/DroidSerif.ttf'); +} +@font-face { + font-family: "DroidSerif"; + src: url('../fonts/DroidSerif-Bold.ttf'); + font-weight: bold; +} +@font-face { + font-family: "DroidSerif"; + src: url('../fonts/DroidSerif-Italic.ttf'); + font-style: italic; +} +@font-face { + font-family: "DroidSerif"; + src: url('../fonts/DroidSerif-BoldItalic.ttf'); + font-style: italic; + font-weight: bold; +} + +body { + font-family: DroidSans, sans; +} +h1, h2, h3, h4, h5, h6 { + font-family: DroidSerif, serif; +} + +a.fa { + text-decoration: none !important; +} diff --git a/rdmo/core/assets/scss/footer.scss b/rdmo/core/assets/scss/footer.scss new file mode 100644 index 0000000000..7b4838c053 --- /dev/null +++ b/rdmo/core/assets/scss/footer.scss @@ -0,0 +1,55 @@ +$footer-height: 280px; +$footer-height-md: 600px; +$footer-height-sm: 260px; + +/* footer layout */ + +.content { + min-height: 100%; + margin-bottom: -$footer-height; + padding-bottom: $footer-height; +} +footer { + height: $footer-height; +} +@media (max-width: $screen-sm-max) { + .content { + margin-bottom: -$footer-height-md; + padding-bottom: $footer-height-md; + } + footer { + height: $footer-height-md; + } +} +@media (max-width: $screen-xs-max) { + .content { + margin-bottom: -$footer-height-md; + padding-bottom: $footer-height-md; + } + footer { + height: $footer-height-md; + } +} + +/* footer style */ + +footer { + color: $footer-color; + background-color: $footer-background-color; + padding-top: 20px; + + a, + a:visited, + a:hover { + color: $footer-link-color; + } + h4 { + color: $footer-link-color; + } + p { + text-align: left; + } + img { + display: block; + } +} diff --git a/rdmo/core/assets/scss/header.scss b/rdmo/core/assets/scss/header.scss new file mode 100644 index 0000000000..769b65c0df --- /dev/null +++ b/rdmo/core/assets/scss/header.scss @@ -0,0 +1,89 @@ +$header-height: 400px; +$header-height-md: 300px; + +header { + position: relative; + + height: $header-height; + background-color: black; + + .header-image { + position: absolute; + left: 0; + right: 0; + + opacity: 0; + -webkit-transition: $image-transition; + -moz-transition: $image-transition; + -ms-transition: $image-transition; + -o-transition: $image-transition; + transition: $image-transition; + + &.visible { + opacity: 1; + } + img { + display: block; + width: 100%; + height: $header-height; + } + p { + position: absolute; + bottom: 0; + right: 0; + z-index: 10; + + padding-right: 5px; + margin-bottom: 5px; + font-size: 10px; + color: $footer-link-color; + + } + a, + a:visited, + a:hover { + color: $footer-link-color; + } + } + .header-text { + position: relative; + padding-top: 100px; + + h1 { + font-size: 60px; + color: white; + } + p { + font-size: 30px; + color: white; + } + } +} +@media (max-width: $screen-md-max) { + header { + height: $header-height-md; + } + header .header-image img { + height: $header-height-md; + } + header .header-text { + padding-top: 50px; + } +} +@media (max-width: $screen-xs-max) { + header { + background-color: inherit; + height: auto; + } + header .header-text { + padding-top: 0; + } + header .header-text h1 { + font-size: 40px; + color: $headline-color; + } + header .header-text p { + font-size: 20px; + color: $variant-color; + } +} diff --git a/rdmo/core/assets/scss/style.scss b/rdmo/core/assets/scss/style.scss new file mode 100644 index 0000000000..795a6f9228 --- /dev/null +++ b/rdmo/core/assets/scss/style.scss @@ -0,0 +1,497 @@ +html, body { + height: 100%; + background-color: $background-color; +} + +h1, h2, h3, h4 { + color: $headline-color; + background-color: $headline-background-color; + line-height: 40px; +} +h5, h6 { + color: $headline-color; + background-color: $headline-background-color; + font-size: medium; + line-height: 20px; +} +h1 { + font-size: 28px; +} +h2 { + font-size: 24px; +} +.sidebar h2, +.modal h2 { + font-size: 20px; +} +h3 { + font-size: 16px; +} +h4 { + font-size: 14px; +} +form { + margin-bottom: 20px; +} +.extend { + width: 100%; +} + +a { + color: $link-color; + + &:visited { + color: $link-color-visited; + } + &:hover { + color: $link-color-hover; + } + &:focus { + color: $link-color-focus; + } + + &.btn { + color: white; + + &:visited, + &:hover, + &:focus { + color: white; + } + } + &.text-warning { + &:visited, + &:hover, + &:focus { + color: #8a6d3b; + } + } + &.text-danger { + &:visited, + &:hover, + &:focus { + color: #a94442; + } + } + + &.disabled { + cursor: not-allowed; + } +} + +code { + word-wrap: break-word; + + &.code-questions { + color: rgb(16, 31, 112); + background-color: rgba(16, 31, 112, 0.1); + } + &.code-options { + color: rgb(255, 100, 0); + background-color: rgba(255, 100, 0, 0.1); + } + &.code-options-provider { + color: white; + background-color: rgba(255, 100, 0, 0.8); + } + &.code-conditions { + color: rgb(128, 0, 128); + background-color: rgba(128, 0, 128, 0.1); + } + &.code-tasks { + color: rgb(128, 0, 0); + background-color: rgba(128, 0, 0, 0.1); + } + &.code-views { + color: rgb(0, 128, 0); + background-color: rgba(0, 128, 0, 0.1); + } + &.code-order { + color: rgb(96, 96, 96); + background-color: rgba(96, 96, 96, 0.1); + } + &.code-import { + color: black; + background-color: rgba(96, 96, 96, 0.1); + } +} + +table { + p { + margin-bottom: 5px; + } + p:last-child { + margin-bottom: 0; + } +} + +.table-break-word { + td { + word-break: break-all; + } +} + +details { + margin-bottom: 10px; +} + +summary { + display: list-item; + cursor: pointer; + margin-bottom: 5px; +} + +metadata { + display: none; +} + +/* navbar */ + +.navbar-default { + background-color: $navigation-background-color; + border-bottom: none; + + .navbar-brand, + .navbar-nav > li > a, + .navbar-nav > li > a:focus { + color: $navigation-color; + background-color: transparent; + } + .navbar-brand:hover, + .navbar-nav > li > a:hover, + .navbar-nav > .open > a, + .navbar-nav > .open > a:focus, + .navbar-nav > .open > a:hover { + color: $navigation-hover-color; + background-color: $navigation-hover-background-color; + } + + .dropdown li.divider:first-child { + display: none; + } +} + +/* content */ + +.content { + padding-top: 50px; /* same height as the navbar */ +} +.sidebar { + /* make the sidebar sticky */ + position: -webkit-sticky; + position: sticky; + top: 0; +} +.page, .sidebar { + height: 100%; + margin-top: 10px; + margin-bottom: 60px; +} +.page h2:nth-child(2) { + margin-top: 0; +} +.sidebar h2:first-child, +.sidebar .import-buttons { + margin-top: 70px; +} + +/* questions overview */ + +.section-panel { + +} + +.subsection-panel { + margin-left: 40px; +} + +.group-panel { + margin-left: 80px; + + table th:first-child, + table td:first-child { + padding-left: 15px; + } + + table th:last-child, + table td:last-child { + padding-right: 15px; + } +} + +/* angular forms */ + +.input-collection { + margin-bottom: 15px; +} + +/* forms */ + +.form-label { + margin-bottom: 5px; + font-weight: 700; +} + +form .yesno label { + margin-right: 10px; +} + +.row { + .checkbox, + .radio { + margin-top: 10px; + margin-bottom: 10px; + } + + @media (min-width: $screen-xs-max) { + .checkbox-padding .checkbox, + .radio-padding .radio { + margin-top: 32px; + margin-bottom: 11px; + } + } +} + +.input-xs { + height: 24px; + padding: 5px 10px; + font-size: 11px; + line-height: 1; + border-radius: 2px; +} + +.help-block.info { + margin-top: 0; +} + +.sidebar-form { + display: flex; + gap: 5px; +} + +.upload-form { + .upload-form-field { + position: relative; + + cursor: pointer; + border-radius: 4px; + + flex-grow: 1; + overflow: hidden; + + p, + input { + height: 34px; + margin: 0px; + } + + p { + text-align: left; + cursor: pointer; + + color: $link-color; + border: 1px solid silver; + border-radius: 4px; + + width: calc(100% - 1px); + padding: 6px 14px; + + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + input { + position: absolute; + z-index: 1; + padding: 0; + opacity: 0; + } + + &:hover { + background-color: #e6e6e6; + } + } +} + +/* modals */ + +.modal-body { + > p:last-child, + formgroup:last-child .form-group { + margin-bottom: 0; + } + + .copy-block { + margin-bottom: 20px; + } + + .help-block { + font-size: small; + word-break: break-word; + } + + .nav.nav-tabs { + margin-bottom: 20px; + } +} + +/* options */ + +.options-dropdown { + display: inline-block; + + > a { + cursor: pointer; + } +} + +/* panels */ + +.panel-default { + min-height: 5px; +} + +.panel-body { + padding-top: 10px; + padding-bottom: 10px; +} + +.panel li > p:last-child { + margin-bottom: 0; +} + +/* lists */ + +ul.list-arrow li { + margin-left: 20px; + + &.active { + margin-left: 0; + } + + &.active a:before { + float: left; + width: 20px; + text-align: right; + content: '\2192\0000a0'; /* right-arrow followed by a space */ + } +} + +/* misc */ +.form-errors { + margin-bottom: 20px; +} +li > a.control-label > i { + display: none; +} +li.has-error > a.control-label > i, +li.has-warning > a.control-label > i { + display: inline; +} +.email-form label, +.connections-form label { + display: block; + margin: 0; + line-height: 40px; + border-bottom: 1px solid $modal-border-color; +} +.email-form label:first-child, +.connections-form label:first-child { + border-top: 1px solid $modal-border-color; +} +.email-form label input, +.connections-form label input { + margin-left: 5px; + margin-right: 5px; +} +.email-form .email-form-buttons, +.connections-form .connections-form-buttons { + margin-top: 10px; +} +.socialaccount_providers { + margin: 0; + padding: 0; + height: 42px; +} +.socialaccount_providers li { + float: left; + margin: 0 5px 10px 5px; + list-style: none; +} +.socialaccount_providers li.socialaccount_provider_break { + float: none; + margin-left: 0; + margin-right: 0; +} +.socialaccount_provider_name { + line-height: 29px; + font-weight: bold; +} +.logout-form { + margin: 0; +} +.logout-form .btn-link { + padding: 3px 20px; + color: $navigation-dropdown-color; + display: block; + width: 100%; + text-align: left; + border: none; + clear: both; + font-weight: 400; + line-height: 1.42857143; + white-space: nowrap; +} +.logout-form .btn-link:hover { + color: $navigation-dropdown-hover-color; + background-color: $navigation-dropdown-hover-background-color; + text-decoration: none; +} +.logout-form .btn-link:focus { + color: $navigation-dropdown-hover-color; + background-color: $navigation-dropdown-hover-background-color; + text-decoration: none; + outline: none; +} +.rdmo-logo { + width: 240px; + margin-top: 40px; +} + +// adjust background "hover" color in select2 to $link-color +.select2-results__option--highlighted{ + background-color: $link-color !important, +} + +.cc-myself { + .checkbox { + margin: 0; + } +} + +.ng-binding { + :last-child { + margin-bottom: 0; + } +} + +.inline_image { + max-width: 100%; +} + +[data-toggle="tooltip"] { + cursor: help; + text-decoration: underline; + text-decoration-style: dotted; +} + +.more, +.show-less { + display: none; +} +.show-more, +.show-less { + color: $link-color; + cursor: pointer; +} diff --git a/rdmo/core/assets/scss/swagger.scss b/rdmo/core/assets/scss/swagger.scss new file mode 100644 index 0000000000..0a0ecc6cac --- /dev/null +++ b/rdmo/core/assets/scss/swagger.scss @@ -0,0 +1,28 @@ +.topbar { + background-color: $headline-color !important; +} + +.swagger-ui .info { + margin: 30px; +} + +.swagger-ui .btn.authorize { + border-color: $footer-background-color; + color: $text-color; +} + +.swagger-ui .btn.authorize svg { + fill: $footer-background-color; +} + +.swagger-ui .btn.authorize { + color: $footer-background-color !important; +} + +.topbar img { + filter: hue-rotate(180deg) +} + +.download-url-wrapper .download-url-button { + background-color: $headline-color !important; +} diff --git a/rdmo/core/assets/scss/utils.scss b/rdmo/core/assets/scss/utils.scss new file mode 100644 index 0000000000..0e45f92470 --- /dev/null +++ b/rdmo/core/assets/scss/utils.scss @@ -0,0 +1,92 @@ +.flip { + transform: rotate(180deg) scaleX(-1); +} + +.w-100 { + width: 100%; +} +.mt-0 { + margin-top: 0; +} +.mt-5 { + margin-top: 5px; +} +.mt-10 { + margin-top: 10px; +} +.mt-20 { + margin-top: 20px; +} +.mr-0 { + margin-right: 0; +} +.mr-5 { + margin-right: 5px; +} +.mr-10 { + margin-right: 10px; +} +.mr-20 { + margin-right: 20px; +} +.mb-0 { + margin-bottom: 0; +} +.mb-5 { + margin-bottom: 5px; +} +.mb-10 { + margin-bottom: 10px; +} +.mb-20 { + margin-bottom: 20px; +} +.ml-0 { + margin-left: 0; +} +.ml-5 { + margin-left: 5px; +} +.ml-10 { + margin-left: 10px; +} +.ml-20 { + margin-left: 20px; +} + +.pt-0 { + padding-top: 0; +} +.pt-10 { + padding-top: 10px; +} +.pt-20 { + padding-top: 20px; +} +.pr-0 { + padding-right: 0; +} +.pr-10 { + padding-right: 10px; +} +.pr-20 { + padding-right: 20px; +} +.pb-0 { + padding-bottom: 0; +} +.pb-10 { + padding-bottom: 10px; +} +.pb-20 { + padding-bottom: 20px; +} +.pl-0 { + padding-left: 0; +} +.pl-10 { + padding-left: 10px; +} +.pl-20 { + padding-left: 20px; +} diff --git a/rdmo/core/settings.py b/rdmo/core/settings.py index 422e7886fc..999d5be52b 100644 --- a/rdmo/core/settings.py +++ b/rdmo/core/settings.py @@ -218,6 +218,8 @@ 'PROJECT_TABLE_PAGE_SIZE' ] +TEMPLATES_API = [] + EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' DEFAULT_FROM_EMAIL = 'info@example.com' diff --git a/rdmo/core/tests/test_viewset_templates.py b/rdmo/core/tests/test_viewset_templates.py new file mode 100644 index 0000000000..ea90ffc989 --- /dev/null +++ b/rdmo/core/tests/test_viewset_templates.py @@ -0,0 +1,32 @@ +import pytest + +from django.urls import reverse + +users = ( + ('owner', 'owner'), + ('manager', 'manager'), + ('author', 'author'), + ('guest', 'guest'), + ('api', 'api'), + ('user', 'user'), + ('anonymous', None), +) + +status_map = { + 'list': { + 'owner': 200, 'manager': 200, 'author': 200, 'guest': 200, 'api': 200, 'user': 200, 'anonymous': 401 + } +} + +urlnames = { + 'list': 'template-list', +} + + +@pytest.mark.parametrize('username,password', users) +def test_list(db, client, username, password): + client.login(username=username, password=password) + + url = reverse(urlnames['list']) + response = client.get(url) + assert response.status_code == status_map['list'][username], response.json() diff --git a/rdmo/core/urls/v1.py b/rdmo/core/urls/v1.py index 442d612de5..6e7139a9a4 100644 --- a/rdmo/core/urls/v1.py +++ b/rdmo/core/urls/v1.py @@ -2,12 +2,13 @@ from rest_framework import routers -from ..viewsets import GroupViewSet, SettingsViewSet, SitesViewSet +from ..viewsets import GroupViewSet, SettingsViewSet, SitesViewSet, TemplatesViewSet router = routers.DefaultRouter() router.register(r'settings', SettingsViewSet, basename='setting') router.register(r'sites', SitesViewSet, basename='site') router.register(r'groups', GroupViewSet, basename='group') +router.register(r'templates', TemplatesViewSet, basename='template') urlpatterns = [ path('accounts/', include('rdmo.accounts.urls.v1')), diff --git a/rdmo/core/viewsets.py b/rdmo/core/viewsets.py index eaa97a2072..ea4e41af1a 100644 --- a/rdmo/core/viewsets.py +++ b/rdmo/core/viewsets.py @@ -1,6 +1,9 @@ +from pathlib import Path + from django.conf import settings from django.contrib.auth.models import Group from django.contrib.sites.models import Site +from django.template.loader import get_template from rest_framework import viewsets from rest_framework.permissions import IsAuthenticated @@ -31,3 +34,14 @@ class GroupViewSet(viewsets.ReadOnlyModelViewSet): permission_classes = (HasModelPermission, ) queryset = Group.objects.all() serializer_class = GroupSerializer + + +class TemplatesViewSet(viewsets.GenericViewSet): + + permission_classes = (IsAuthenticated, ) + + def list(self, request, *args, **kwargs): + return Response({ + Path(template_path).stem: get_template(template_path).render(request=request).strip() + for template_path in settings.TEMPLATES_API + }) diff --git a/rdmo/management/assets/js/reducers/configReducer.js b/rdmo/management/assets/js/reducers/configReducer.js index 31029c17f0..1476203c16 100644 --- a/rdmo/management/assets/js/reducers/configReducer.js +++ b/rdmo/management/assets/js/reducers/configReducer.js @@ -1,6 +1,6 @@ import set from 'lodash/set' -import baseUrl from 'rdmo/core/assets/js/utils/baseUrl' +import { baseUrl } from 'rdmo/core/assets/js/utils/meta' const initialState = { baseUrl: baseUrl + '/management/', diff --git a/rdmo/projects/assets/js/projects.js b/rdmo/projects/assets/js/projects.js index 414a8c915f..e4c4e562e7 100644 --- a/rdmo/projects/assets/js/projects.js +++ b/rdmo/projects/assets/js/projects.js @@ -2,12 +2,12 @@ import React from 'react' import { createRoot } from 'react-dom/client' import { Provider } from 'react-redux' -import configureStore from './store/configureStore' +import configureStore from './projects/store/configureStore' import { DndProvider } from 'react-dnd' import { HTML5Backend } from 'react-dnd-html5-backend' -import Main from './containers/Main' +import Main from './projects/containers/Main' const store = configureStore() diff --git a/rdmo/projects/assets/js/actions/actionTypes.js b/rdmo/projects/assets/js/projects/actions/actionTypes.js similarity index 100% rename from rdmo/projects/assets/js/actions/actionTypes.js rename to rdmo/projects/assets/js/projects/actions/actionTypes.js diff --git a/rdmo/projects/assets/js/actions/configActions.js b/rdmo/projects/assets/js/projects/actions/configActions.js similarity index 100% rename from rdmo/projects/assets/js/actions/configActions.js rename to rdmo/projects/assets/js/projects/actions/configActions.js diff --git a/rdmo/projects/assets/js/actions/projectsActions.js b/rdmo/projects/assets/js/projects/actions/projectsActions.js similarity index 100% rename from rdmo/projects/assets/js/actions/projectsActions.js rename to rdmo/projects/assets/js/projects/actions/projectsActions.js diff --git a/rdmo/projects/assets/js/actions/userActions.js b/rdmo/projects/assets/js/projects/actions/userActions.js similarity index 100% rename from rdmo/projects/assets/js/actions/userActions.js rename to rdmo/projects/assets/js/projects/actions/userActions.js diff --git a/rdmo/projects/assets/js/projects/api/AccountsApi.js b/rdmo/projects/assets/js/projects/api/AccountsApi.js new file mode 100644 index 0000000000..94e42a94ad --- /dev/null +++ b/rdmo/projects/assets/js/projects/api/AccountsApi.js @@ -0,0 +1,9 @@ +import BaseApi from 'rdmo/core/assets/js/api/BaseApi' + +class AccountsApi extends BaseApi { + static fetchCurrentUser() { + return this.get('/api/v1/accounts/users/current/') + } +} + +export default AccountsApi diff --git a/rdmo/projects/assets/js/api/ProjectsApi.js b/rdmo/projects/assets/js/projects/api/ProjectsApi.js similarity index 98% rename from rdmo/projects/assets/js/api/ProjectsApi.js rename to rdmo/projects/assets/js/projects/api/ProjectsApi.js index 5fc35590ad..d07cb6a54d 100644 --- a/rdmo/projects/assets/js/api/ProjectsApi.js +++ b/rdmo/projects/assets/js/projects/api/ProjectsApi.js @@ -1,7 +1,7 @@ import Cookies from 'js-cookie' import BaseApi from 'rdmo/core/assets/js/api/BaseApi' import { encodeParams } from 'rdmo/core/assets/js/utils/api' -import baseUrl from 'rdmo/core/assets/js/utils/baseUrl' +import { baseUrl } from 'rdmo/core/assets/js/utils/meta' function BadRequestError(errors) { this.errors = errors diff --git a/rdmo/projects/assets/js/components/helper/PendingInvitations.js b/rdmo/projects/assets/js/projects/components/helper/PendingInvitations.js similarity index 100% rename from rdmo/projects/assets/js/components/helper/PendingInvitations.js rename to rdmo/projects/assets/js/projects/components/helper/PendingInvitations.js diff --git a/rdmo/projects/assets/js/components/helper/ProjectFilters.js b/rdmo/projects/assets/js/projects/components/helper/ProjectFilters.js similarity index 100% rename from rdmo/projects/assets/js/components/helper/ProjectFilters.js rename to rdmo/projects/assets/js/projects/components/helper/ProjectFilters.js diff --git a/rdmo/projects/assets/js/components/helper/ProjectImport.js b/rdmo/projects/assets/js/projects/components/helper/ProjectImport.js similarity index 100% rename from rdmo/projects/assets/js/components/helper/ProjectImport.js rename to rdmo/projects/assets/js/projects/components/helper/ProjectImport.js diff --git a/rdmo/projects/assets/js/components/helper/Table.js b/rdmo/projects/assets/js/projects/components/helper/Table.js similarity index 100% rename from rdmo/projects/assets/js/components/helper/Table.js rename to rdmo/projects/assets/js/projects/components/helper/Table.js diff --git a/rdmo/projects/assets/js/components/helper/index.js b/rdmo/projects/assets/js/projects/components/helper/index.js similarity index 100% rename from rdmo/projects/assets/js/components/helper/index.js rename to rdmo/projects/assets/js/projects/components/helper/index.js diff --git a/rdmo/projects/assets/js/components/main/Projects.js b/rdmo/projects/assets/js/projects/components/main/Projects.js similarity index 100% rename from rdmo/projects/assets/js/components/main/Projects.js rename to rdmo/projects/assets/js/projects/components/main/Projects.js diff --git a/rdmo/projects/assets/js/containers/Main.js b/rdmo/projects/assets/js/projects/containers/Main.js similarity index 100% rename from rdmo/projects/assets/js/containers/Main.js rename to rdmo/projects/assets/js/projects/containers/Main.js diff --git a/rdmo/projects/assets/js/hooks/useDatePicker.js b/rdmo/projects/assets/js/projects/hooks/useDatePicker.js similarity index 100% rename from rdmo/projects/assets/js/hooks/useDatePicker.js rename to rdmo/projects/assets/js/projects/hooks/useDatePicker.js diff --git a/rdmo/projects/assets/js/reducers/configReducer.js b/rdmo/projects/assets/js/projects/reducers/configReducer.js similarity index 100% rename from rdmo/projects/assets/js/reducers/configReducer.js rename to rdmo/projects/assets/js/projects/reducers/configReducer.js diff --git a/rdmo/projects/assets/js/reducers/projectsReducer.js b/rdmo/projects/assets/js/projects/reducers/projectsReducer.js similarity index 100% rename from rdmo/projects/assets/js/reducers/projectsReducer.js rename to rdmo/projects/assets/js/projects/reducers/projectsReducer.js diff --git a/rdmo/projects/assets/js/reducers/rootReducer.js b/rdmo/projects/assets/js/projects/reducers/rootReducer.js similarity index 100% rename from rdmo/projects/assets/js/reducers/rootReducer.js rename to rdmo/projects/assets/js/projects/reducers/rootReducer.js diff --git a/rdmo/projects/assets/js/reducers/userReducer.js b/rdmo/projects/assets/js/projects/reducers/userReducer.js similarity index 100% rename from rdmo/projects/assets/js/reducers/userReducer.js rename to rdmo/projects/assets/js/projects/reducers/userReducer.js diff --git a/rdmo/projects/assets/js/store/configureStore.js b/rdmo/projects/assets/js/projects/store/configureStore.js similarity index 100% rename from rdmo/projects/assets/js/store/configureStore.js rename to rdmo/projects/assets/js/projects/store/configureStore.js diff --git a/rdmo/projects/assets/js/utils/constants.js b/rdmo/projects/assets/js/projects/utils/constants.js similarity index 100% rename from rdmo/projects/assets/js/utils/constants.js rename to rdmo/projects/assets/js/projects/utils/constants.js diff --git a/rdmo/projects/assets/js/utils/getProjectTitlePath.js b/rdmo/projects/assets/js/projects/utils/getProjectTitlePath.js similarity index 100% rename from rdmo/projects/assets/js/utils/getProjectTitlePath.js rename to rdmo/projects/assets/js/projects/utils/getProjectTitlePath.js diff --git a/rdmo/projects/assets/js/utils/getUserRoles.js b/rdmo/projects/assets/js/projects/utils/getUserRoles.js similarity index 100% rename from rdmo/projects/assets/js/utils/getUserRoles.js rename to rdmo/projects/assets/js/projects/utils/getUserRoles.js diff --git a/rdmo/projects/assets/js/utils/index.js b/rdmo/projects/assets/js/projects/utils/index.js similarity index 100% rename from rdmo/projects/assets/js/utils/index.js rename to rdmo/projects/assets/js/projects/utils/index.js diff --git a/rdmo/projects/assets/js/utils/translations.js b/rdmo/projects/assets/js/projects/utils/translations.js similarity index 100% rename from rdmo/projects/assets/js/utils/translations.js rename to rdmo/projects/assets/js/projects/utils/translations.js diff --git a/rdmo/projects/assets/js/utils/userIsManager.js b/rdmo/projects/assets/js/projects/utils/userIsManager.js similarity index 82% rename from rdmo/projects/assets/js/utils/userIsManager.js rename to rdmo/projects/assets/js/projects/utils/userIsManager.js index 435d866a33..9b4e77430d 100644 --- a/rdmo/projects/assets/js/utils/userIsManager.js +++ b/rdmo/projects/assets/js/projects/utils/userIsManager.js @@ -1,4 +1,4 @@ -import siteId from 'rdmo/core/assets/js/utils/siteId' +import { siteId } from 'rdmo/core/assets/js/utils/meta' const userIsManager = (currentUser) => { if (currentUser.is_superuser || diff --git a/webpack.config.js b/webpack.config.js index cc71e803bb..d1d630dd32 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -6,6 +6,18 @@ const TerserPlugin = require('terser-webpack-plugin') // list of separate config objects for each django app and their corresponding java script applications const configList = [ + { + name: 'core', + entry: { + base: [ + './rdmo/core/assets/js/base.js', + './rdmo/core/assets/scss/base.scss' + ] + }, + output: { + path: path.resolve(__dirname, './rdmo/core/static/core/'), + } + }, { name: 'management', entry: { @@ -15,7 +27,6 @@ const configList = [ ] }, output: { - filename: 'js/management.js', path: path.resolve(__dirname, './rdmo/management/static/management/'), } }, @@ -28,7 +39,6 @@ const configList = [ ] }, output: { - filename: 'js/projects.js', path: path.resolve(__dirname, './rdmo/projects/static/projects/'), } } @@ -42,6 +52,9 @@ const baseConfig = { }, extensions: ['*', '.js', '.jsx'] }, + output: { + filename: 'js/[name].js' + }, plugins: [ new MiniCssExtractPlugin({ filename: 'css/[name].css',