Commit fa557fb3 authored by Florent Chehab's avatar Florent Chehab

feat(front): hookified modules & tweaks

parent a21ede50
/** General app JS entry
*/
import React, { useEffect } from "react";
import React, { useMemo } from "react";
import { compose } from "recompose";
import { Route, Switch } from "react-router-dom";
import FullScreenDialogServiceComponent from "../services/FullScreenDialogServiceComponent";
......@@ -48,7 +48,9 @@ const SERVICES_TO_INITIALIZE = [
* Main entry
*/
function App() {
useEffect(() => {
// Not using useEffect as it might not be called on first render exactly,
// Here we really need to have this stuff initialzed.
useMemo(() => {
SERVICES_TO_INITIALIZE.forEach(service => service.initialize());
}, []);
......
......@@ -8,9 +8,7 @@ export default function LinkToUser(props) {
return (
<CustomLink to={APP_ROUTES.forUser(props.userId)}>
<em>
{props.pseudo}
&nbsp; (#
{props.userId})
{props.pseudo}&nbsp;(#{props.userId})
</em>
</CustomLink>
);
......
import React from "react";
import React, { useCallback, useEffect } from "react";
import PropTypes from "prop-types";
import withStyles from "@material-ui/core/styles/withStyles";
import compose from "recompose/compose";
import { connect } from "react-redux";
import { useDispatch } from "react-redux";
import Markdown from "../../common/markdown/Markdown";
import Module from "./common/Module";
import ModuleWrapper from "./common/ModuleWrapper";
import ModuleGroupWrapper from "./common/ModuleGroupWrapper";
......@@ -16,9 +13,10 @@ import getActions from "../../../redux/api/getActions";
import withUnivInfo from "../common/withUnivInfo";
import RequestParams from "../../../redux/api/RequestParams";
import CountryService from "../../../services/data/CountryService";
// eslint-disable-next-line no-unused-vars
const styles = theme => ({});
import withNetworkWrapper, {
getApiPropTypes,
NetWrapParam
} from "../../../hoc/withNetworkWrapper";
// eslint-disable-next-line no-unused-vars
function renderCore(rawModelData, classes, outsideData) {
......@@ -26,74 +24,67 @@ function renderCore(rawModelData, classes, outsideData) {
return <Markdown source={comment} />;
}
class CountryDri extends Module {
apiParams = {
countryDri: ({ props }) =>
RequestParams.Builder.withQueryParam("countries", props.countryId).build()
};
const buildParams = countryId =>
RequestParams.Builder.withQueryParam("countries", countryId).build();
function CountryDri({ countryId, countryDriItems, api }) {
const dispatch = useDispatch();
customRender() {
const countryDriItems = this.getOnlyReadData("countryDri");
const { classes } = this.props;
const outsideData = { countries: CountryService.getCountries() };
useEffect(() => {
api.countryDriItems.setParams(buildParams(countryId));
}, [countryId]);
return (
<ModuleGroupWrapper
groupTitle="Informations émanant de la DRI liées au pays"
endPoint="countryDri"
editor={CountryDriEditor}
invalidateGroup={this.props.invalidateData}
defaultModelData={{
countries: [this.props.countryId],
importance_level: "-"
}}
propsForEditor={{
outsideData
}}
>
{countryDriItems.map((rawModelData, idx) => (
<ModuleWrapper
// eslint-disable-next-line react/no-array-index-key
key={idx}
buildTitle={modelData => modelData.title}
rawModelData={rawModelData}
editor={CountryDriEditor}
renderCore={renderCore}
coreClasses={classes}
outsideData={outsideData}
moduleInGroupInfos={{
isInGroup: true,
invalidateGroup: () => this.props.invalidateData()
}}
/>
))}
</ModuleGroupWrapper>
);
}
const invalidateData = useCallback(() => {
dispatch(getActions("countryDri").invalidateAll());
}, [dispatch]);
const outsideData = { countries: CountryService.getCountries() };
return (
<ModuleGroupWrapper
groupTitle="Informations émanant de la DRI liées au pays"
endPoint="countryDri"
editor={CountryDriEditor}
invalidateGroup={invalidateData}
defaultModelData={{
countries: [countryId],
importance_level: "-"
}}
propsForEditor={{
outsideData
}}
>
{countryDriItems.map((rawModelData, idx) => (
<ModuleWrapper
// eslint-disable-next-line react/no-array-index-key
key={idx}
buildTitle={modelData => modelData.title}
rawModelData={rawModelData}
editor={CountryDriEditor}
renderCore={renderCore}
coreClasses={{}}
outsideData={outsideData}
moduleInGroupInfos={{
isInGroup: true,
invalidateGroup: () => this.props.invalidateData()
}}
/>
))}
</ModuleGroupWrapper>
);
}
CountryDri.propTypes = {
classes: PropTypes.object.isRequired,
theme: PropTypes.object.isRequired,
countryId: PropTypes.string.isRequired
countryId: PropTypes.string.isRequired,
countryDriItems: PropTypes.array.isRequired,
api: getApiPropTypes("countryDriItems").isRequired
};
const mapStateToProps = state => ({
countryDri: state.api.countryDriAll
});
const mapDispatchToProps = dispatch => ({
api: {
countryDri: params => dispatch(getActions("countryDri").readAll(params))
},
invalidateData: () => dispatch(getActions("countryDri").invalidateAll())
});
export default compose(
withUnivInfo(["countryId"]),
withStyles(styles, { withTheme: true }),
connect(
mapStateToProps,
mapDispatchToProps
)
withNetworkWrapper([
new NetWrapParam("countryDri", "all", "countryDriItems", props =>
buildParams(props.countryId)
)
])
)(CountryDri);
import React from "react";
import React, { useCallback, useEffect } from "react";
import PropTypes from "prop-types";
import withStyles from "@material-ui/core/styles/withStyles";
import compose from "recompose/compose";
import { connect } from "react-redux";
import { useDispatch } from "react-redux";
import Module from "./common/Module";
import { makeStyles } from "@material-ui/styles";
import ModuleWrapper from "./common/ModuleWrapper";
import ModuleGroupWrapper from "./common/ModuleGroupWrapper";
......@@ -18,13 +17,17 @@ import withUnivInfo from "../common/withUnivInfo";
import RequestParams from "../../../redux/api/RequestParams";
import CountryService from "../../../services/data/CountryService";
import CurrencyService from "../../../services/data/CurrencyService";
import withNetworkWrapper, {
getApiPropTypes,
NetWrapParam
} from "../../../hoc/withNetworkWrapper";
const styles = theme => ({
const useStyles = makeStyles(theme => ({
item: {
marginBottom: theme.spacing(2),
marginTop: theme.spacing(2)
}
});
}));
// eslint-disable-next-line no-unused-vars
function renderCore(rawModelData, classes, outsideData) {
......@@ -46,84 +49,82 @@ function renderCore(rawModelData, classes, outsideData) {
);
}
class CountryScholarships extends Module {
apiParams = {
countryScholarships: ({ props }) =>
RequestParams.Builder.withQueryParam("countries", props.countryId).build()
const buildParams = countryId =>
RequestParams.Builder.withQueryParam("countries", countryId).build();
function CountryScholarships({
countryScholarshipsItems,
countryId,
country,
api
}) {
const classes = useStyles();
const dispatch = useDispatch();
useEffect(() => {
api.countryScholarshipsItems.setParams(buildParams(countryId));
}, [countryId]);
const invalidateData = useCallback(() => {
dispatch(getActions("countryScholarships").invalidateAll());
}, [dispatch]);
const outsideData = {
countries: CountryService.getCountries(),
currencies: CurrencyService.getCurrencies()
};
customRender() {
const countryScholarshipsItems = this.getOnlyReadData(
"countryScholarships"
);
const { classes } = this.props;
const outsideData = {
countries: CountryService.getCountries(),
currencies: CurrencyService.getCurrencies()
};
return (
<ModuleGroupWrapper
groupTitle={`Bourses liées au pays (${this.props.country.name})`}
endPoint="countryScholarships"
editor={CountryScholarshipEditor}
invalidateGroup={this.props.invalidateData}
defaultModelData={{
countries: [this.props.countryId],
importance_level: "-",
currency: "EUR",
obj_moderation_level: 0
}}
propsForEditor={{
outsideData
}}
>
{countryScholarshipsItems.map(rawModelData => (
<div key={rawModelData.id} className={classes.item}>
<ModuleWrapper
buildTitle={modelData => modelData.title}
rawModelData={rawModelData}
editor={CountryScholarshipEditor}
renderCore={renderCore}
coreClasses={classes}
outsideData={outsideData}
moduleInGroupInfos={{
isInGroup: true,
invalidateGroup: () => this.props.invalidateData()
}}
/>
</div>
))}
</ModuleGroupWrapper>
);
}
return (
<ModuleGroupWrapper
groupTitle={`Bourses liées au pays (${country.name})`}
endPoint="countryScholarships"
editor={CountryScholarshipEditor}
invalidateGroup={invalidateData}
defaultModelData={{
countries: [countryId],
importance_level: "-",
currency: "EUR",
obj_moderation_level: 0
}}
propsForEditor={{
outsideData
}}
>
{countryScholarshipsItems.map(rawModelData => (
<div key={rawModelData.id} className={classes.item}>
<ModuleWrapper
buildTitle={modelData => modelData.title}
rawModelData={rawModelData}
editor={CountryScholarshipEditor}
renderCore={renderCore}
coreClasses={classes}
outsideData={outsideData}
moduleInGroupInfos={{
isInGroup: true,
invalidateGroup: invalidateData
}}
/>
</div>
))}
</ModuleGroupWrapper>
);
}
CountryScholarships.propTypes = {
classes: PropTypes.object.isRequired,
theme: PropTypes.object.isRequired,
countryId: PropTypes.string.isRequired,
country: PropTypes.object.isRequired
country: PropTypes.object.isRequired,
countryScholarshipsItems: PropTypes.array.isRequired,
api: getApiPropTypes("countryScholarshipsItems").isRequired
};
const mapStateToProps = state => ({
countryScholarships: state.api.countryScholarshipsAll
});
const mapDispatchToProps = dispatch => ({
api: {
countryScholarships: params =>
dispatch(getActions("countryScholarships").readAll(params))
},
invalidateData: () =>
dispatch(getActions("countryScholarships").invalidateAll())
});
export default compose(
withUnivInfo(["countryId", "country"]),
withStyles(styles, { withTheme: true }),
connect(
mapStateToProps,
mapDispatchToProps
)
withNetworkWrapper([
new NetWrapParam(
"countryScholarships",
"all",
"countryScholarshipsItems",
props => buildParams(props.countryId)
)
])
)(CountryScholarships);
import React from "react";
import React, { useEffect } from "react";
import PropTypes from "prop-types";
import withStyles from "@material-ui/core/styles/withStyles";
import compose from "recompose/compose";
import { connect } from "react-redux";
import { useDispatch } from "react-redux";
import { makeStyles } from "@material-ui/styles";
import ModuleWrapper from "./common/ModuleWrapper";
import Module from "./common/Module";
import SharedUnivFeedbackEditor from "../editors/SharedUnivFeedbackEditor";
import getActions from "../../../redux/api/getActions";
import withUnivInfo from "../common/withUnivInfo";
import RequestParams from "../../../redux/api/RequestParams";
import TruncatedMarkdown from "../../common/markdown/TruncatedMarkdown";
import withNetworkWrapper, {
getApiPropTypes,
NetWrapParam
} from "../../../hoc/withNetworkWrapper";
const styles = theme => ({
const useStyles = makeStyles(theme => ({
root: {
width: "100%",
overflowX: "auto"
......@@ -30,7 +32,7 @@ const styles = theme => ({
icon: {
paddingRight: theme.spacing(1)
}
});
}));
// eslint-disable-next-line no-unused-vars
function renderCore(rawModelData, classes) {
......@@ -38,57 +40,46 @@ function renderCore(rawModelData, classes) {
return <TruncatedMarkdown truncateFromLength={1000} source={comment} />;
}
class SharedUnivFeedback extends Module {
apiParams = {
univSharedFeedback: ({ props }) =>
RequestParams.Builder.withQueryParam("university", props.univId).build()
};
const buildParams = univId =>
RequestParams.Builder.withQueryParam("university", univId).build();
customRender() {
const univSharedFeedback = this.getLatestReadData("univSharedFeedback")[0];
const { classes } = this.props;
return (
<ModuleWrapper
buildTitle={() =>
"Commentaire partagé par celles et ceux qui sont partis"
}
rawModelData={univSharedFeedback}
editor={SharedUnivFeedbackEditor}
renderCore={renderCore}
coreClasses={classes}
moduleInGroupInfos={{
isInGroup: true,
invalidateGroup: () => this.props.invalidateData()
}}
/>
);
}
function SharedUnivFeedback({ univId, api, feedback }) {
const classes = useStyles();
const dispatch = useDispatch();
useEffect(() => {
api.feedback.setParams(buildParams(univId));
}, [univId]);
return (
<ModuleWrapper
buildTitle={() =>
"Commentaire partagé par celles et ceux qui sont partis"
}
rawModelData={feedback[0]}
editor={SharedUnivFeedbackEditor}
renderCore={renderCore}
coreClasses={classes}
moduleInGroupInfos={{
isInGroup: true,
invalidateGroup: () =>
dispatch(getActions("sharedUnivFeedbacks").invalidateAll())
}}
/>
);
}
SharedUnivFeedback.propTypes = {
classes: PropTypes.object.isRequired,
theme: PropTypes.object.isRequired,
univId: PropTypes.number.isRequired
univId: PropTypes.number.isRequired,
feedback: PropTypes.array.isRequired,
api: getApiPropTypes("feedback").isRequired
};
const mapStateToProps = state => ({
univSharedFeedback: state.api.sharedUnivFeedbacksAll
});
const mapDispatchToProps = dispatch => ({
api: {
univSharedFeedback: params =>
dispatch(getActions("sharedUnivFeedbacks").readAll(params))
},
invalidateData: () =>
dispatch(getActions("sharedUnivFeedbacks").invalidateAll())
});
export default compose(
withUnivInfo(),
withStyles(styles, { withTheme: true }),
connect(
mapStateToProps,
mapDispatchToProps
)
withNetworkWrapper([
new NetWrapParam("sharedUnivFeedbacks", "all", "feedback", props =>
buildParams(props.univId)
)
])
)(SharedUnivFeedback);
import React from "react";
import React, { useCallback, useEffect } from "react";
import PropTypes from "prop-types";
import withStyles from "@material-ui/core/styles/withStyles";
import compose from "recompose/compose";
import { connect } from "react-redux";
import { useDispatch } from "react-redux";
import Markdown from "../../common/markdown/Markdown";
import Module from "./common/Module";
import ModuleWrapper from "./common/ModuleWrapper";
import ModuleGroupWrapper from "./common/ModuleGroupWrapper";
......@@ -16,9 +13,11 @@ import UniversityDriEditor from "../editors/UniversityDriEditor";
import getActions from "../../../redux/api/getActions";
import RequestParams from "../../../redux/api/RequestParams";
import UniversityService from "../../../services/data/UniversityService";
// eslint-disable-next-line no-unused-vars
const styles = theme => ({});
import withNetworkWrapper, {
getApiPropTypes,
NetWrapParam
} from "../../../hoc/withNetworkWrapper";
import withUnivInfo from "../common/withUnivInfo";
// eslint-disable-next-line no-unused-vars
function renderCore(rawModelData, classes, outsideData) {
......@@ -27,73 +26,66 @@ function renderCore(rawModelData, classes, outsideData) {
return <Markdown source={comment} />;
}
class UniversityDri extends Module {
apiParams = {
universityDri: ({ props }) =>
RequestParams.Builder.withQueryParam("universities", props.univId).build()
};
const buildParams = univId =>
RequestParams.Builder.withQueryParam("universities", univId).build();
function UniversityDri({ univId, univDriItems, api }) {
const dispatch = useDispatch();
customRender() {
const { classes } = this.props;
const outsideData = { universities: UniversityService.getUniversities() };
const univDriItems = this.getLatestReadData("universityDri");
useEffect(() => {
api.univDriItems.setParams(buildParams(univId));
}, [univId]);
return (
<ModuleGroupWrapper
groupTitle={"Informations émanant de la DRI liées à l'université"}
endPoint="universityDri"
editor={UniversityDriEditor}
invalidateGroup={this.props.invalidateData}
defaultModelData={{
universities: [this.props.univId],
importance_level: "-"
}}
propsForEditor={{
outsideData
}}
>
{univDriItems.map(rawModelData => (
<ModuleWrapper
key={rawModelData.id} // use the id of the model to prevent useless unmount
buildTitle={modelData => modelData.title}
rawModelData={rawModelData}
editor={UniversityDriEditor}
renderCore={renderCore}
coreClasses={classes}
outsideData={outsideData}
moduleInGroupInfos={{
isInGroup: true,
invalidateGroup: () => this.props.invalidateData()
}}
/>
))}
</ModuleGroupWrapper>
);
}
const invalidateData = useCallback(() => {
dispatch(getActions("universityDri").invalidateAll());
});
const outsideData = { universities: UniversityService.getUniversities() };
return (
<ModuleGroupWrapper
groupTitle={"Informations émanant de la DRI liées à l'université"}
endPoint="universityDri"
editor={UniversityDriEditor}
invalidateGroup={invalidateData}
defaultModelData={{
universities: [univId],
importance_level: "-"
}}
propsForEditor={{
outsideData
}}
>
{univDriItems.map(rawModelData => (
<ModuleWrapper
key={rawModelData.id} // use the id of the model to prevent useless unmount
buildTitle={modelData => modelData.title}
rawModelData={rawModelData}
editor={UniversityDriEditor}
renderCore={renderCore}
coreClasses={{}}
outsideData={outsideData}
moduleInGroupInfos={{
isInGroup: true,
invalidateGroup: invalidateData
}}
/>
))}
</ModuleGroupWrapper>
);
}
UniversityDri.propTypes = {
classes: PropTypes.object.isRequired,
theme: PropTypes.object.isRequired,
univId: PropTypes.number.isRequired
univId: PropTypes.string.isRequired,
univDriItems: PropTypes.array.isRequired,
api: getApiPropTypes("univDriItems").isRequired
};
const mapStateToProps = state => ({
universityDri: state.api.universityDriAll
});
const mapDispatchToProps = dispatch => ({
api: {
universityDri: params =>
dispatch(getActions("universityDri").readAll(params))
},
invalidateData: () => dispatch(getActions("universityDri").invalidateAll())
});
export default compose(
withStyles(styles, { withTheme: true }),
connect(
mapStateToProps,
mapDispatchToProps
)
withUnivInfo(["countryId"]),
withNetworkWrapper([
new NetWrapParam("universityDri", "all", "univDriItems", props =>
buildParams(props.univId)
)
])
)(UniversityDri);
import React from "react";
import React, { useEffect } from "react";
import PropTypes from "prop-types";
import withStyles from "@material-ui/core/styles/withStyles";
import compose from "recompose/compose";
import { connect } from "react-redux";
import Typography from "@material-ui/core/Typography";
......@@ -12,16 +10,13 @@ import PhotoSizeSelectActualIcon from "@material-ui/icons/PhotoSizeSelectActual"
import TextLink from "../../common/TextLink";
import ModuleWrapper from "./common/ModuleWrapper";
import Module from "./common/Module";
import UniversityGeneralEditor from "../editors/UniversityGeneralEditor";
import getActions from "../../../redux/api/getActions";
import withUnivInfo from "../common/withUnivInfo";
import RequestParams from "../../../redux/api/RequestParams";