Commit 5665fb60 authored by Florent Chehab's avatar Florent Chehab

Cleaned module wrapper / editor

Removed hacks
Tweaks
And changes coherent with last commit

Group Module broken again !
parent 1bae06b2
...@@ -146,6 +146,7 @@ const mapStateToProps = (state) => { ...@@ -146,6 +146,7 @@ const mapStateToProps = (state) => {
return { return {
countries: state.api.countriesAll, countries: state.api.countriesAll,
currencies: state.api.currenciesAll, currencies: state.api.currenciesAll,
serverModerationStatus: state.api.serverModerationStatusSpecific,
}; };
}; };
...@@ -154,6 +155,7 @@ const mapDispatchToProps = (dispatch) => { ...@@ -154,6 +155,7 @@ const mapDispatchToProps = (dispatch) => {
api: { api: {
countries: () => dispatch(getActions("countries").readAll()), countries: () => dispatch(getActions("countries").readAll()),
currencies: () => dispatch(getActions("currencies").readAll()), currencies: () => dispatch(getActions("currencies").readAll()),
serverModerationStatus: () => dispatch(getActions("serverModerationStatus").readSpecific("")), // not needed for server moderation status
}, },
}; };
}; };
......
...@@ -17,8 +17,7 @@ import TextField from "./fields/TextField"; ...@@ -17,8 +17,7 @@ import TextField from "./fields/TextField";
import MultiSelectField from "./fields/MultiSelectField"; import MultiSelectField from "./fields/MultiSelectField";
import NumberField from "./fields/NumberField"; import NumberField from "./fields/NumberField";
import store from "../../redux/store"; import { getLatestReadDataFromStore } from "../../redux/api/utils";
import { getLatestRead } from "../../redux/api/utils";
export default { export default {
/** /**
...@@ -33,7 +32,7 @@ export default { ...@@ -33,7 +32,7 @@ export default {
renderObjModerationLevelField() { renderObjModerationLevelField() {
// hack to access directly the store and get the value we need. // hack to access directly the store and get the value we need.
const userData = getLatestRead(store.getState().api.userDataSpecific).data, const userData = getLatestReadDataFromStore("userDataSpecific"),
possibleObjModeration = getObjModerationLevel(userData.owner_level, true); possibleObjModeration = getObjModerationLevel(userData.owner_level, true);
if (possibleObjModeration.length > 1) { if (possibleObjModeration.length > 1) {
return ( return (
......
...@@ -54,6 +54,17 @@ class Editor extends Component { ...@@ -54,6 +54,17 @@ class Editor extends Component {
*/ */
extraFieldMappings = [] extraFieldMappings = []
/**
* Creates an instance of Editor. and subscribes to the module wrapper so that it can access its functions.
*
* @param {object} props
* @memberof Editor
*/
constructor(props) {
super(props);
props.subscribeToModuleWrapper(this);
}
/** /**
* Function that extracts the modelData from the raw one. * Function that extracts the modelData from the raw one.
* *
...@@ -116,7 +127,8 @@ class Editor extends Component { ...@@ -116,7 +127,8 @@ class Editor extends Component {
handleSaveEditorRequest() { handleSaveEditorRequest() {
const getFormError = this.getFormError(); const getFormError = this.getFormError();
if (!getFormError.status) { // no error, we can save if necessary if (!getFormError.status) { // no error, we can save if necessary
if (this.props.forceSave || this.formHasChanges()) {
if (this.formHasChanges()) {
// Copy the model data and copy above the data from the form // Copy the model data and copy above the data from the form
// So that we don't forget anything. // So that we don't forget anything.
this.performSave( this.performSave(
...@@ -183,7 +195,6 @@ class Editor extends Component { ...@@ -183,7 +195,6 @@ class Editor extends Component {
if (this.formHasChanges()) { if (this.formHasChanges()) {
this.alertChangesNotSaved(); this.alertChangesNotSaved();
} else { } else {
this.notifyNoChangesDetected();
this.closeEditor(false); this.closeEditor(false);
} }
} }
...@@ -196,7 +207,7 @@ class Editor extends Component { ...@@ -196,7 +207,7 @@ class Editor extends Component {
*/ */
closeEditor(somethingWasSaved) { closeEditor(somethingWasSaved) {
this.props.closeFullScreenDialog(); this.props.closeFullScreenDialog();
this.props.handleEditorWasClosed(somethingWasSaved); this.props.closeEditorPanel(somethingWasSaved);
} }
...@@ -217,16 +228,6 @@ class Editor extends Component { ...@@ -217,16 +228,6 @@ class Editor extends Component {
this.props.openFullScreenDialog(this.renderEditor()); this.props.openFullScreenDialog(this.renderEditor());
} }
// To handle moderation, we use the editor in "hacky" manner
// with the `dataToSave` props, which triggers a save.
const { dataToSave } = this.props;
if (dataToSave) {
this.props.handleEditorWasClosed();
this.performSave(dataToSave);
}
// end of hacky behavior.
// we make to notify if saving to the server has errors // we make to notify if saving to the server has errors
const { savingHasError } = this.props; const { savingHasError } = this.props;
if (savingHasError.failed && !this.state.alert.open) { if (savingHasError.failed && !this.state.alert.open) {
...@@ -364,17 +365,15 @@ class Editor extends Component { ...@@ -364,17 +365,15 @@ class Editor extends Component {
Editor.propTypes = { Editor.propTypes = {
classes: PropTypes.object.isRequired, classes: PropTypes.object.isRequired,
savingHasError: PropTypes.object.isRequired, open: PropTypes.bool.isRequired, // should the editor be opened
clearSaveError: PropTypes.func.isRequired, subscribeToModuleWrapper: PropTypes.func.isRequired,
lastUpdateTimeInModel: PropTypes.string,
lastSaveTime: PropTypes.number,
forceSave: PropTypes.bool.isRequired,
__apiAttr: PropTypes.oneOf([PropTypes.number, PropTypes.string, ""]), __apiAttr: PropTypes.oneOf([PropTypes.number, PropTypes.string, ""]),
rawModelData: PropTypes.object.isRequired, rawModelData: PropTypes.object.isRequired,
open: PropTypes.bool.isRequired, // should the editor be opened closeEditorPanel: PropTypes.func.isRequired,
handleEditorWasClosed: PropTypes.func.isRequired,
dataToSave: PropTypes.object,
// props added in subclasses but are absolutely required to handle redux // props added in subclasses but are absolutely required to handle redux
savingHasError: PropTypes.object.isRequired,
clearSaveError: PropTypes.func.isRequired,
lastUpdateTimeInModel: PropTypes.string,
openFullScreenDialog: PropTypes.func.isRequired, openFullScreenDialog: PropTypes.func.isRequired,
closeFullScreenDialog: PropTypes.func.isRequired, closeFullScreenDialog: PropTypes.func.isRequired,
saveData: PropTypes.func.isRequired, saveData: PropTypes.func.isRequired,
...@@ -385,10 +384,9 @@ Editor.propTypes = { ...@@ -385,10 +384,9 @@ Editor.propTypes = {
Editor.defaultProps = { Editor.defaultProps = {
open: false, open: false,
forceSave: false,
__apiAttr: "", __apiAttr: "",
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
handleEditorWasClosed: () => console.error("Dev forgot something...") closeEditorPanel: () => console.error("Dev forgot something...")
}; };
export default Editor; export default Editor;
...@@ -62,8 +62,7 @@ class History extends Component { ...@@ -62,8 +62,7 @@ class History extends Component {
return []; return [];
} else { } else {
return this.props.versions.readSucceeded.data return this.props.versions.readSucceeded.data
.map((el) => { return el.data; }) // get the data inside the version .map((el) => { return el.data; }); // get the data inside the version
.slice(1); // the first (ie 0) version is the one already displayed
} }
} }
...@@ -100,21 +99,28 @@ class History extends Component { ...@@ -100,21 +99,28 @@ class History extends Component {
&& lastVersion.id == id; && lastVersion.id == id;
} }
/**
* As soon as the History is added to the DOM, we make sure to update it,
* To force the call to `componentDidUpdate`.
*
* @memberof Editor
*/
componentDidMount() {
this.forceUpdate();
}
/** /**
* Extends function to open panel in redux and load Data * Extends function to open panel in redux and load Data
* *
* @memberof History * @memberof History
*/ */
componentDidUpdate() { componentDidUpdate() {
if (this.props.open) { // Load the data as necessary
// already open the panel if (!this.dataIsReady() && !this.props.versions.isLoading) {
this.props.openFullScreenDialog(this.renderPanel()); const { contentTypeId, id } = this.getContentTypeAndId();
// Load the data as necessary this.props.readVersions(contentTypeId, id);
if (!this.dataIsReady() && !this.props.versions.isLoading) {
const { contentTypeId, id } = this.getContentTypeAndId();
this.props.readVersions(contentTypeId, id);
}
} }
this.props.openFullScreenDialog(this.renderPanel());
} }
/** /**
...@@ -230,7 +236,7 @@ class History extends Component { ...@@ -230,7 +236,7 @@ class History extends Component {
variant='outlined' variant='outlined'
color="primary" color="primary"
className={this.props.classes.editButton} className={this.props.classes.editButton}
onClick={() => this.props.handleEditFromVersion(rawModelData)} onClick={() => this.props.editFromVersion(rawModelData)}
> >
Éditer à partir de cette version Éditer à partir de cette version
</Button> </Button>
...@@ -247,11 +253,10 @@ class History extends Component { ...@@ -247,11 +253,10 @@ class History extends Component {
History.propTypes = { History.propTypes = {
classes: PropTypes.object.isRequired, classes: PropTypes.object.isRequired,
handleEditFromVersion: PropTypes.func.isRequired, editFromVersion: PropTypes.func.isRequired,
readVersions: PropTypes.func.isRequired, readVersions: PropTypes.func.isRequired,
closeHistoryPanel: PropTypes.func.isRequired, closeHistoryPanel: PropTypes.func.isRequired,
resetVersions: PropTypes.func.isRequired, resetVersions: PropTypes.func.isRequired,
open: PropTypes.bool.isRequired,
versions: PropTypes.object.isRequired, versions: PropTypes.object.isRequired,
theme: PropTypes.object.isRequired, theme: PropTypes.object.isRequired,
openFullScreenDialog: PropTypes.func.isRequired, openFullScreenDialog: PropTypes.func.isRequired,
...@@ -261,7 +266,6 @@ History.propTypes = { ...@@ -261,7 +266,6 @@ History.propTypes = {
}; };
History.defaultProps = { History.defaultProps = {
open: false,
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
closeHistoryPanel: () => console.error("Dev forgot something..."), closeHistoryPanel: () => console.error("Dev forgot something..."),
}; };
......
import React from "react"; import React, { Component } from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import withStyles from "@material-ui/core/styles/withStyles"; import withStyles from "@material-ui/core/styles/withStyles";
import compose from "recompose/compose"; import compose from "recompose/compose";
import { connect } from "react-redux"; import { getLatestReadDataFromStore } from "../../../../redux/api/utils";
import Typography from "@material-ui/core/Typography"; import Typography from "@material-ui/core/Typography";
import Paper from "@material-ui/core/Paper"; import Paper from "@material-ui/core/Paper";
...@@ -14,10 +14,8 @@ import Divider from "@material-ui/core/Divider"; ...@@ -14,10 +14,8 @@ import Divider from "@material-ui/core/Divider";
import AddIcon from "@material-ui/icons/Add"; import AddIcon from "@material-ui/icons/Add";
import getActions from "../../../../redux/api/getActions";
import green from "@material-ui/core/colors/green"; import green from "@material-ui/core/colors/green";
import CustomComponentForAPI from "../../../common/CustomComponentForAPI";
const styles = theme => ({ const styles = theme => ({
root: { root: {
...@@ -47,7 +45,7 @@ const styles = theme => ({ ...@@ -47,7 +45,7 @@ const styles = theme => ({
* @extends {CustomComponentForAPI} * @extends {CustomComponentForAPI}
* @extends React.Component * @extends React.Component
*/ */
class ModuleGroupWrapper extends CustomComponentForAPI { class ModuleGroupWrapper extends Component {
state = { state = {
editorOpen: false, editorOpen: false,
}; };
...@@ -56,25 +54,24 @@ class ModuleGroupWrapper extends CustomComponentForAPI { ...@@ -56,25 +54,24 @@ class ModuleGroupWrapper extends CustomComponentForAPI {
this.setState({ editorOpen: true }); this.setState({ editorOpen: true });
} }
handleEditorWasClosed = (somethingWasSaved = false) => { closeEditorPanel = (somethingWasSaved = false) => {
this.setState({ editorOpen: false }); this.setState({ editorOpen: false });
if (somethingWasSaved) { if (somethingWasSaved) {
this.props.invalidateGroup(); this.props.invalidateGroup();
} }
}; };
customRender() { render() {
const { classes, groupTitle, endPoint } = this.props, const { classes, groupTitle, endPoint } = this.props,
userCanPostTo = this.getReadData("userData").owner_can_post_to, userCanPostTo = getLatestReadDataFromStore("userDataSpecific").owner_can_post_to,
disabled = userCanPostTo.indexOf(endPoint) < 0; disabled = userCanPostTo.indexOf(endPoint) < 0;
return <></>; // TODO NOW remove
return ( return (
<Paper className={classes.root}> <Paper className={classes.root}>
<this.props.editor <this.props.editor
{...this.props.propsForEditor} {...this.props.propsForEditor}
open={this.state.editorOpen} open={this.state.editorOpen}
handleEditorWasClosed={this.handleEditorWasClosed} closeEditorPanel={this.closeEditorPanel}
userData={this.props.userData}
/> />
<Grid container spacing={8} alignItems='center'> <Grid container spacing={8} alignItems='center'>
...@@ -114,26 +111,10 @@ ModuleGroupWrapper.propTypes = { ...@@ -114,26 +111,10 @@ ModuleGroupWrapper.propTypes = {
classes: PropTypes.object.isRequired, classes: PropTypes.object.isRequired,
propsForEditor: PropTypes.object.isRequired, propsForEditor: PropTypes.object.isRequired,
children: PropTypes.node.isRequired, children: PropTypes.node.isRequired,
userData: PropTypes.object.isRequired,
};
const mapStateToProps = (state) => {
return {
userData: state.api.userDataSpecific
};
}; };
const mapDispatchToProps = (dispatch) => {
return {
api: {
userData: () => dispatch(getActions("userData").readSpecific("")), // Id not required for user data
}
};
};
export default compose( export default compose(
withStyles(styles, { withTheme: true }), withStyles(styles, { withTheme: true }),
connect(mapStateToProps, mapDispatchToProps)
)(ModuleGroupWrapper); )(ModuleGroupWrapper);
...@@ -36,15 +36,22 @@ const styles = theme => ({ ...@@ -36,15 +36,22 @@ const styles = theme => ({
*/ */
class PendingModeration extends Component { class PendingModeration extends Component {
/**
* Force update to force call of componentDidUpdate en mount and force the rendering of the moderation panel.
*
* @memberof PendingModeration
*/
componentDidMount(){
this.forceUpdate();
}
/** /**
* Customization to handle opening the app full screen dialog when needed. * Customization to handle opening the app full screen dialog when needed.
* *
* @memberof PendingModeration * @memberof PendingModeration
*/ */
componentDidUpdate(prevProps) { componentDidUpdate() {
if (this.props.open && !prevProps.open) { // a bit of rendering optimization this.props.openFullScreenDialog(this.renderPendingModerationPanel());
this.props.openFullScreenDialog(this.renderPendingModerationPanel());
}
} }
/** /**
...@@ -75,7 +82,7 @@ class PendingModeration extends Component { ...@@ -75,7 +82,7 @@ class PendingModeration extends Component {
variant='outlined' variant='outlined'
color="primary" color="primary"
className={this.props.classes.editButton} className={this.props.classes.editButton}
onClick={() => this.props.handleEditPendingModeration(rawModelData)} onClick={() => this.props.editFromPendingModeration(rawModelData)}
> >
Éditer à partir de cette version Éditer à partir de cette version
</Button> </Button>
...@@ -87,7 +94,7 @@ class PendingModeration extends Component { ...@@ -87,7 +94,7 @@ class PendingModeration extends Component {
color="primary" color="primary"
disabled={!this.props.userCanModerate} disabled={!this.props.userCanModerate}
className={this.props.classes.editButton} className={this.props.classes.editButton}
onClick={() => this.props.handleApproveModeration(rawModelData)} onClick={() => this.props.moderatePendingModeration(rawModelData)}
> >
Valider cette version Valider cette version
</Button> </Button>
...@@ -103,8 +110,8 @@ class PendingModeration extends Component { ...@@ -103,8 +110,8 @@ class PendingModeration extends Component {
* @memberof PendingModeration * @memberof PendingModeration
*/ */
closePanel() { closePanel() {
this.props.handleClosePendingModeration();
this.props.closeFullScreenDialog(); this.props.closeFullScreenDialog();
this.props.closePendingModerationPanel();
} }
/** /**
...@@ -146,10 +153,9 @@ class PendingModeration extends Component { ...@@ -146,10 +153,9 @@ class PendingModeration extends Component {
PendingModeration.propTypes = { PendingModeration.propTypes = {
classes: PropTypes.object.isRequired, classes: PropTypes.object.isRequired,
handleEditPendingModeration: PropTypes.func.isRequired, editFromPendingModeration: PropTypes.func.isRequired,
handleClosePendingModeration: PropTypes.func.isRequired, closePendingModerationPanel: PropTypes.func.isRequired,
handleApproveModeration: PropTypes.func.isRequired, moderatePendingModeration: PropTypes.func.isRequired,
open: PropTypes.bool.isRequired,
userCanModerate: PropTypes.bool.isRequired, userCanModerate: PropTypes.bool.isRequired,
openFullScreenDialog: PropTypes.func.isRequired, openFullScreenDialog: PropTypes.func.isRequired,
closeFullScreenDialog: PropTypes.func.isRequired, closeFullScreenDialog: PropTypes.func.isRequired,
...@@ -157,9 +163,8 @@ PendingModeration.propTypes = { ...@@ -157,9 +163,8 @@ PendingModeration.propTypes = {
}; };
PendingModeration.defaultProps = { PendingModeration.defaultProps = {
open: false,
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
handleClosePendingModeration: () => console.error("Dev forgot something...") closePendingModerationPanel: () => console.error("Dev forgot something...")
}; };
const mapDispatchToProps = (dispatch) => { const mapDispatchToProps = (dispatch) => {
......
...@@ -18,7 +18,7 @@ import Tooltip from "@material-ui/core/Tooltip"; ...@@ -18,7 +18,7 @@ import Tooltip from "@material-ui/core/Tooltip";
export default function renderFirstRow(userCanModerate) { export default function renderFirstRow(userCanModerate) {
const { classes, theme, rawModelData } = this.props, const { classes, theme, rawModelData } = this.props,
nbVersions = Math.max(0, rawModelData.nb_versions - 1), nbVersions = Math.max(0, rawModelData.nb_versions),
{ pending_moderation } = rawModelData; { pending_moderation } = rawModelData;
let nbPendingModeration = 0; let nbPendingModeration = 0;
...@@ -42,7 +42,7 @@ export default function renderFirstRow(userCanModerate) { ...@@ -42,7 +42,7 @@ export default function renderFirstRow(userCanModerate) {
<Tooltip title={moderTooltip} placement="top"> <Tooltip title={moderTooltip} placement="top">
<div style={{ display: "inline-block" }}> {/* Needed to fire events for the tooltip when below is disabled! when below is disabled!! */} <div style={{ display: "inline-block" }}> {/* Needed to fire events for the tooltip when below is disabled! when below is disabled!! */}
<MyBadge className={classes.badge} badgeContent={nbPendingModeration} color="secondary"> <MyBadge className={classes.badge} badgeContent={nbPendingModeration} color="secondary">
<IconButton aria-label="Modération" disabled={moderClass == "disabled" || moderClass == "green"} onClick={this.handleOpenPendingModeration} className={classes.button}> <IconButton aria-label="Modération" disabled={moderClass == "disabled" || moderClass == "green"} onClick={() => this.handleOpenPendingModeration()} className={classes.button}>
<VerifiedUserIcon className={classes[moderClass]} /> <VerifiedUserIcon className={classes[moderClass]} />
</IconButton> </IconButton>
</MyBadge> </MyBadge>
...@@ -51,7 +51,7 @@ export default function renderFirstRow(userCanModerate) { ...@@ -51,7 +51,7 @@ export default function renderFirstRow(userCanModerate) {
<Tooltip title={editTooltip} placement="top"> <Tooltip title={editTooltip} placement="top">
<div style={{ display: "inline-block" }}> {/* Needed to fire events for the tooltip when below is disabled!! */} <div style={{ display: "inline-block" }}> {/* Needed to fire events for the tooltip when below is disabled!! */}
<IconButton aria-label="Éditer" className={classes.button} disabled={editClass == "disabled"} onClick={this.handleEditCurrent}> <IconButton aria-label="Éditer" className={classes.button} disabled={editClass == "disabled"} onClick={() => this.editCurrent()}>
<CreateIcon className={classes[editClass]} /> <CreateIcon className={classes[editClass]} />
</IconButton> </IconButton>
</div> </div>
...@@ -60,7 +60,7 @@ export default function renderFirstRow(userCanModerate) { ...@@ -60,7 +60,7 @@ export default function renderFirstRow(userCanModerate) {
<Tooltip title={versionTooltip} placement="top"> <Tooltip title={versionTooltip} placement="top">
<div style={{ display: "inline-block" }}> {/* Needed to fire events for the tooltip when below is disabled!! */} <div style={{ display: "inline-block" }}> {/* Needed to fire events for the tooltip when below is disabled!! */}
<MyBadge className={classes.badge} badgeContent={nbVersions} color="secondary"> <MyBadge className={classes.badge} badgeContent={nbVersions} color="secondary">
<IconButton aria-label="Restorer" disabled={versionClass == "disabled"} className={classes.button} onClick={this.handleOpenHistory}> <IconButton aria-label="Restorer" disabled={versionClass == "disabled"} className={classes.button} onClick={() => this.setState({ historyOpen: true })}>
<SettingsBackRestoreIcon className={classes[versionClass]} /> <SettingsBackRestoreIcon className={classes[versionClass]} />
</IconButton> </IconButton>
</MyBadge> </MyBadge>
......
...@@ -10,15 +10,6 @@ import CrudReducers from "./CrudReducers"; ...@@ -10,15 +10,6 @@ import CrudReducers from "./CrudReducers";
let apiActionsTmp = {}; let apiActionsTmp = {};
let apiReducersTmp = {}; let apiReducersTmp = {};
// Simple APIs outside rest framework
// TODO merge with the shared file
const otherAPI = [
{
name: "serverModerationStatus",
api_end_point: "serverModerationStatus"
}
];
/** /**
* Create api actions and reducers for given the info in each arr obj * Create api actions and reducers for given the info in each arr obj
* Add those to apiActions and apiReducers objects * Add those to apiActions and apiReducers objects
...@@ -48,7 +39,6 @@ function addAPIs(arr) { ...@@ -48,7 +39,6 @@ function addAPIs(arr) {
}); });
} }
addAPIs(otherAPI);
addAPIs(API_CONFIG); addAPIs(API_CONFIG);
export const apiActions = apiActionsTmp; export const apiActions = apiActionsTmp;
......
/** /**
* This file contains utilities linked to the use of the API. * This file contains utilities linked to the use of the API.
*/ */
import store from "../store";
// Stores the name of the reducers/actions that result in read data // Stores the name of the reducers/actions that result in read data
export const successActionsWithReads = ["readSucceeded", "createSucceeded", "updateSucceeded"]; export const successActionsWithReads = ["readSucceeded", "createSucceeded", "updateSucceeded"];
...@@ -19,3 +20,18 @@ export function getLatestRead(stateExtract) { ...@@ -19,3 +20,18 @@ export function getLatestRead(stateExtract) {
(prev, curr) => prev.readAt < curr.readAt ? curr : prev, (prev, curr) => prev.readAt < curr.readAt ? curr : prev,
{ readAt: 0 }); { readAt: 0 });
} }
/**
* Function that returns the latest read data directly from the store.
* To be used with parsimony.
*
* It assumes that the data has already been fetched at some point.
*
* @export
* @param {string} entry eg: userDataSpecific
* @returns
*/
export function getLatestReadDataFromStore(entry){
return getLatestRead(store.getState().api[entry]).data;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment