Commit ee66c6a0 authored by Florent Chehab's avatar Florent Chehab

Removed(custom component for api)

parent 4f713bd5
import React from "react";
import PropTypes from "prop-types";
import withStyles from "@material-ui/core/styles/withStyles";
import Paper from "@material-ui/core/Paper";
import { compose } from "recompose";
import { connect } from "react-redux";
import { Typography } from "@material-ui/core";
import { withErrorBoundary } from "../common/ErrorBoundary";
import CustomComponentForAPI from "../common/CustomComponentForAPI";
import { makeStyles } from "@material-ui/styles";
import RequestParams from "../../redux/api/RequestParams";
import getActions from "../../redux/api/getActions";
import Pictures from "../user/Pictures";
import useInvalidateAll from "../../hooks/useInvalidateAll";
import withNetworkWrapper, { NetWrapParam } from "../../hoc/withNetworkWrapper";
const useStyles = makeStyles(theme => ({
paper: theme.myPaper
}));
function getUserIdFromUrl(match) {
return match.params.userId;
}
const buildPictureParams = match =>
RequestParams.Builder.withQueryParam(
"owner",
getUserIdFromUrl(match)
).build();
const buildFilesParams = match =>
RequestParams.Builder.withQueryParam(
"owner",
getUserIdFromUrl(match)
).build();
/**
* WARNING BETA files & padding
* Page that lists the files available
* @class PageFiles
* @extends {React.Component}
*/
class PageFiles extends CustomComponentForAPI {
apiParams = {
files: ({ props }) =>
RequestParams.Builder.withQueryParam(
"owner",
this.getUserIdFromUrl(props)
).build(),
pictures: ({ props }) =>
RequestParams.Builder.withQueryParam(
"owner",
this.getUserIdFromUrl(props)
).build()
};
getUserIdFromUrl(props = this.props) {
return props.match.params.userId;
}
// eslint-disable-next-line no-unused-vars
function PageFiles({ pictures, files }) {
const classes = useStyles();
const invalidate = useInvalidateAll("pictures", "files");
customRender() {
// WARNING BETA files & padding
const { theme } = this.props;
const { pictures } = this.getLatestReadDataFor(["pictures", "files"]);
return (
<>
<Paper style={theme.myPaper}>
<Typography variant="h3">Photos</Typography>
<Pictures
pictures={pictures}
onSomethingWasSaved={() => this.props.invalidatePictures()}
/>
</Paper>
{/* <Paper style={theme.myPaper}> */}
{/* <Typography variant={"h3"}>Fichiers</Typography> */}
{/* </Paper> */}
</>
);
}
return (
<>
<Paper className={classes.paper}>
<Typography variant="h3">Photos</Typography>
<Pictures pictures={pictures} onSomethingWasSaved={invalidate} />
</Paper>
{/* <Paper style={theme.myPaper}> */}
{/* <Typography variant={"h3"}>Fichiers</Typography> */}
{/* </Paper> */}
</>
);
}
PageFiles.propTypes = {
theme: PropTypes.object.isRequired,
match: PropTypes.shape({
params: PropTypes.shape({
userId: PropTypes.string.isRequired
})
}).isRequired,
classes: PropTypes.object.isRequired
pictures: PropTypes.array.isRequired,
files: PropTypes.array.isRequired
};
const mapStateToProps = state => ({
pictures: state.api.picturesAll,
files: state.api.filesAll
});
const mapDispatchToProps = dispatch => ({
api: {
pictures: params => dispatch(getActions("pictures").readAll(params)),
files: params => dispatch(getActions("files").readAll(params))
},
invalidatePictures: () => dispatch(getActions("pictures").invalidateAll())
});
// eslint-disable-next-line no-unused-vars
const styles = theme => ({});
export default compose(
withStyles(styles, { withTheme: true }),
connect(
mapStateToProps,
mapDispatchToProps
),
withErrorBoundary()
withNetworkWrapper([
new NetWrapParam("pictures", "all", "pictures", props =>
buildPictureParams(props.match)
),
new NetWrapParam("files", "all", "files", props =>
buildFilesParams(props.match)
)
])
)(PageFiles);
......@@ -12,7 +12,6 @@ import { rgbToHex } from "@material-ui/core/styles/colorManipulator";
import FormControlLabel from "@material-ui/core/FormControlLabel";
import Switch from "@material-ui/core/Switch";
import MuiThemeProvider from "@material-ui/core/styles/MuiThemeProvider";
import { compose } from "recompose";
import Input from "@material-ui/core/Input";
import ExpansionPanel from "@material-ui/core/ExpansionPanel";
......@@ -23,9 +22,7 @@ import isEqual from "lodash/isEqual";
import Divider from "@material-ui/core/Divider";
import defaultSiteTheme from "../../../config/defaultTheme.json";
import SaveButton from "../../common/SaveButton";
import { getLatestRead } from "../../../redux/api/utils";
import getActions from "../../../redux/api/getActions";
import CustomComponentForAPI from "../../common/CustomComponentForAPI";
import ColorDemo from "./ColorDemo";
import TextLink from "../../common/TextLink";
import deepCopy from "../../../utils/deepCopy";
......@@ -33,6 +30,9 @@ import RequestParams from "../../../redux/api/RequestParams";
import { getTheme } from "../../common/theme/utils";
import { CURRENT_USER_ID } from "../../../config/user";
import LicenseNotice from "../../common/LicenseNotice";
import withNetworkWrapper, {
NetWrapParam
} from "../../../hoc/withNetworkWrapper";
const isRgb = string => /#?([0-9a-f]{6})/i.test(string);
......@@ -40,10 +40,9 @@ const isRgb = string => /#?([0-9a-f]{6})/i.test(string);
* Component to handle website color customization
*
* @class ColorTool
* @extends {CustomComponentForAPI}
* @extends React.Component
*/
class ColorTool extends CustomComponentForAPI {
class ColorTool extends React.Component {
constructor(props) {
super(props);
......@@ -56,7 +55,7 @@ class ColorTool extends CustomComponentForAPI {
* @returns {object}
*/
getInitialTheme() {
return deepCopy(getLatestRead(this.props.userData).data.theme);
return deepCopy(this.props.userData.theme);
}
/**
......@@ -131,7 +130,7 @@ class ColorTool extends CustomComponentForAPI {
* Save the new theme on the server
*/
handleSendToServer() {
const userData = deepCopy(this.getLatestReadData("userData"));
const userData = deepCopy(this.props.userData);
const newUserData = Object.assign(userData, { theme: this.state.theme });
this.props.saveUserDataToServer(newUserData);
}
......@@ -258,7 +257,7 @@ class ColorTool extends CustomComponentForAPI {
);
}
customRender() {
render() {
const { classes } = this.props;
const themeForDemo = getTheme(this.state.theme);
const hasChanges = !isEqual(this.state.theme, this.getInitialTheme());
......@@ -314,9 +313,8 @@ ColorTool.propTypes = {
userData: PropTypes.object.isRequired
};
const mapStateToProps = state => ({
userData: state.api.userDataOne
});
// eslint-disable-next-line no-unused-vars
const mapStateToProps = state => ({});
const mapDispatchToProps = dispatch => ({
saveUserDataToServer: data =>
......@@ -326,15 +324,7 @@ const mapDispatchToProps = dispatch => ({
.withData(data)
.build()
)
),
api: {
userData: () =>
dispatch(
getActions("userData").readOne(
RequestParams.Builder.withId(CURRENT_USER_ID).build()
)
) // id not needed userData
}
)
});
const styles = theme => ({
......@@ -361,5 +351,13 @@ export default compose(
mapStateToProps,
mapDispatchToProps
),
withNetworkWrapper([
new NetWrapParam(
"userData",
"one",
"userData",
RequestParams.Builder.withId(CURRENT_USER_ID).build()
)
]),
withStyles(styles, { withTheme: true })
)(ColorTool);
......@@ -56,7 +56,7 @@ function CoreComponent({ rawModelData, cityName, countryName }) {
{cityName},&nbsp;{countryName}
</Typography>
<Typography variant="body2">
&nbsp; Site internet&nbsp;: &nbsp;
Site internet&nbsp;:&nbsp;
{website ? (
<TextLink href={website}>{website}</TextLink>
) : (
......
/* eslint-disable indent */
import React from "react";
import React, { useEffect, useState } from "react";
import PropTypes from "prop-types";
import compose from "recompose/compose";
import { connect } from "react-redux";
import Close from "@material-ui/icons/Close";
import DoneAll from "@material-ui/icons/DoneAll";
import Paper from "@material-ui/core/Paper";
import { makeStyles, withStyles } from "@material-ui/styles";
import { makeStyles } from "@material-ui/styles";
import Typography from "@material-ui/core/Typography";
import Chip from "@material-ui/core/Chip";
import getActions from "../../../redux/api/getActions";
import withUnivInfo from "../../../hoc/withUnivInfo";
import RequestParams from "../../../redux/api/RequestParams";
import CustomComponentForAPI from "../../common/CustomComponentForAPI";
import PaginatedData from "../../common/PaginatedData";
import withNetworkWrapper, {
getApiPropTypes,
NetWrapParam
} from "../../../hoc/withNetworkWrapper";
const style = theme => ({
const useStyles = makeStyles(theme => ({
paper: {
padding: theme.spacing(1)
},
......@@ -32,13 +32,11 @@ const style = theme => ({
marginTop: theme.spacing(1),
marginBottom: theme.spacing(1)
}
});
const useStyle = makeStyles(style);
}));
function Item(props) {
const { majors, semester, seats, comment, master, doubleDegree } = props;
const classes = useStyle();
const classes = useStyles();
return (
<div className={classes.itemRoot}>
......@@ -75,7 +73,7 @@ function Item(props) {
)}
<div>
<Typography>
Master
Master&nbsp;
{master ? (
<DoneAll className={classes.inlineIcon} color="primary" />
) : (
......@@ -83,7 +81,7 @@ function Item(props) {
)}
</Typography>
<Typography>
Double diplome
Double diplome&nbsp;
{doubleDegree ? (
<DoneAll className={classes.inlineIcon} color="primary" />
) : (
......@@ -94,7 +92,7 @@ function Item(props) {
<div>
{comment && (
<>
<Typography variant="caption">Commentaire de la DRI:</Typography>
<Typography variant="caption">Commentaire de la DRI :</Typography>
<Typography>
<em>{comment}</em>
</Typography>
......@@ -120,99 +118,91 @@ Item.defaultProps = {
seats: null
};
class UniversityOffers extends CustomComponentForAPI {
state = { page: 1 };
apiParams = {
universityOffers: ({ props, state }) =>
RequestParams.Builder.withQueryParam("university", props.univId)
.withQueryParam("page", state.page)
.withQueryParam("page_size", 3)
.build()
function renderEl(dataEl) {
const {
comment,
double_degree: doubleDegree,
is_master_offered: master,
semester,
year,
majors,
id,
nb_seats_offered: seats
} = dataEl;
const p = {
seats,
id,
doubleDegree,
comment,
master,
majors: typeof majors === "string" ? majors.split(",") : [],
semester: `${semester}${year}`
};
goToPage(pageNumber) {
this.setState({ page: pageNumber });
}
return <Item key={p.id} {...p} />;
}
renderEl(dataEl) {
const {
comment,
double_degree: doubleDegree,
is_master_offered: master,
semester,
year,
majors,
id,
nb_seats_offered: seats
} = dataEl;
const props = {
seats,
id,
doubleDegree,
comment,
master,
majors: typeof majors === "string" ? majors.split(",") : [],
semester: `${semester}${year}`
};
return <Item key={props.id} {...props} />;
}
const buildParams = (univId, page) =>
RequestParams.Builder.withQueryParam("university", univId)
.withQueryParam("page", page)
.withQueryParam("page_size", 1)
.build();
customRender() {
const { classes } = this.props;
const universityOffers = this.getLatestReadData("universityOffers");
return (
<Paper className={classes.paper}>
<Typography variant="h4">Possibilité(s) d'échanges</Typography>
<Typography variant="caption">
REX-DRI s'efforce d'être à jour avec l'ENT. Toutefois, seul l'ENT fait
foi à 100% concernant les possibilités passées et actuelles
d'échanges.
</Typography>
<PaginatedData
data={universityOffers}
goToPage={pageNumber => this.goToPage(pageNumber)}
render={dataEl => this.renderEl(dataEl)}
stepperOnBottom
stepperOnTop={false}
EmptyMessageComponent={
<Typography>
<em>
Aucune possibilité (passée ou présente) n'a été enregistrée à ce
jour.
</em>
</Typography>
}
/>
</Paper>
);
}
function UniversityOffers({ offers, api, univId, page, goToPage }) {
const classes = useStyles();
useEffect(() => {
api.offers.setParams(buildParams(univId, page));
}, [univId, page]);
return (
<Paper className={classes.paper}>
<Typography variant="h4">Possibilité(s) d'échanges</Typography>
<Typography variant="caption">
REX-DRI s'efforce d'être à jour avec l'ENT. Toutefois, seul l'ENT fait
foi à 100% concernant les possibilités passées et actuelles d'échanges.
</Typography>
<PaginatedData
data={offers}
goToPage={goToPage}
render={renderEl}
stepperOnBottom
stepperOnTop={false}
EmptyMessageComponent={
<Typography>
<em>
Aucune possibilité (passée ou présente) n'a été enregistrée à ce
jour.
</em>
</Typography>
}
/>
</Paper>
);
}
UniversityOffers.propTypes = {
univId: PropTypes.number.isRequired,
classes: PropTypes.object.isRequired
offers: PropTypes.object.isRequired,
api: getApiPropTypes("offers").isRequired,
page: PropTypes.number.isRequired,
goToPage: PropTypes.func.isRequired
};
const mapStateToProps = state => ({
universityOffers: state.api.offersAll
});
const mapDispatchToProps = dispatch => ({
api: {
universityOffers: params => dispatch(getActions("offers").readAll(params))
}
});
export default compose(
withStyles(style),
const ConnectedComponent = compose(
withUnivInfo(),
connect(
mapStateToProps,
mapDispatchToProps
)
withNetworkWrapper([
new NetWrapParam("offers", "all", "offers", props =>
buildParams(props.univId, props.page)
)
])
)(UniversityOffers);
export default () => {
// Lifting state up to work around NetworkWrapper limitations
const [page, goToPage] = useState(1);
return <ConnectedComponent page={page} goToPage={goToPage} />;
};
/* eslint-disable no-shadow */
import React, { useEffect, useState } from "react";
import PropTypes from "prop-types";
import compose from "recompose/compose";
......@@ -52,15 +53,19 @@ const buildUnivMajorMinorsParams = univId =>
/**
* Tab on the university page containing information related to previous exchange
*
* TODO fix bug new setup, component is unmounted and state is reseted
*/
function PreviousExchangesTab({ exchanges, univMajorMinors, univId, api }) {
const [page, goToPage] = useState(1);
const [major, setMajor] = useState(undefinedVal);
const [minor, setMinor] = useState(undefinedVal);
function PreviousExchangesTab({
exchanges,
major,
minor,
page,
setMajor,
setMinor,
goToPage,
univMajorMinors,
univId,
api
}) {
const classes = useStyles();
// keep the component updated
......@@ -161,17 +166,42 @@ PreviousExchangesTab.propTypes = {
univId: PropTypes.number.isRequired,
exchanges: PropTypes.object.isRequired,
univMajorMinors: PropTypes.array.isRequired,
api: getApiPropTypes("exchanges", "univMajorMinors").isRequired
api: getApiPropTypes("exchanges", "univMajorMinors").isRequired,
major: PropTypes.string.isRequired,
minor: PropTypes.string.isRequired,
page: PropTypes.string.isRequired,
goToPage: PropTypes.func.isRequired,
setMinor: PropTypes.func.isRequired,
setMajor: PropTypes.func.isRequired
};
export default compose(
const ConnectedComp = compose(
withUnivInfo(),
withNetworkWrapper([
new NetWrapParam("exchangeFeedbacks", "all", "exchanges", props =>
buildExchangesParams(props.univId)
buildExchangesParams(props.univId, props.page, props.major, props.minor)
),
new NetWrapParam("univMajorMinors", "all", "univMajorMinors", props =>
buildUnivMajorMinorsParams(props.univId)
)
])
)(PreviousExchangesTab);
export default () => {
// Lifting state up to work around NetworkWrapper limitations
const [page, goToPage] = useState(1);
const [major, setMajor] = useState(undefinedVal);
const [minor, setMinor] = useState(undefinedVal);
return (
<ConnectedComp
page={page}
goToPage={goToPage}
major={major}
setMajor={setMajor}
minor={minor}
setMinor={setMinor}
/>
);
};
......@@ -65,9 +65,7 @@ export default function withNetworkWrapper(
// eslint-disable-next-line react/display-name
React.forwardRef((props, ref) => {
const allData = parameters.map(el => [
useSingleApiData(
el.routeName,
el.variant,
useSingleApiData(el.routeName, el.variant, () =>
typeof el.params === "function" ? el.params(props) : el.params
),
el.propKey
......
import { useCallback, useEffect, useMemo, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { apiDataIsUsable, getLatestRead } from "../redux/api/utils";
import {
apiDataIsUsable,
getLatestRead,
getStoreState
} from "../redux/api/utils";
import getActions from "../redux/api/getActions";
import RequestParams from "../redux/api/RequestParams";
......@@ -19,6 +23,7 @@ function useSingleApiData(
initialParams = RequestParams.Builder.build()
) {
const [params, setParams] = useState(initialParams);
const action = useMemo(
() =>
variant === "all"
......@@ -27,18 +32,18 @@ function useSingleApiData(
[]
);
// redux
const data = useSelector(
state => state.api[`${routeName}${variant === "all" ? "All" : "One"}`]
const stateName = useMemo(
() => `${routeName}${variant === "all" ? "All" : "One"}`,
[]
);
// redux
const data = useSelector(state => state.api[stateName]);
const dispatch = useDispatch();
const performRead = useCallback(
newParams => {
dispatch(action(newParams));
},
[action]
);
const performRead = useCallback(newParams => {
dispatch(action(newParams));
}, []);
/**
*
......@@ -56,10 +61,21 @@ function useSingleApiData(
useEffect(() => {
if (data.readFailed.failed === true) return;
if (!data.readSucceeded.requestParams.equals(params)) performRead(params);
if (data.isInvalidated === true) performRead(params);
if (!data.isReading) {
if (data.isInvalidated === true) performRead(params);
}
}, [data, params]);
useEffect(() => {
// bug if inside same use effect as data
const realData = getStoreState().api[stateName];
if (!realData.isReading) {
if (!realData.readSucceeded.requestParams.equals(params)) {
performRead(params);
}
}
}, [params]);
return useMemo(() => {
const hasError = data.readFailed.failed;
const isLoading = !apiDataIsUsable(data);
......
......@@ -51,3 +51,11 @@ export function apiDataIsUsable(stateExtract) {
export function getLatestReadDataFromStore(entry) {
return getLatestRead(store.getState().api[entry]).data;
}
/**
* Direct access to the store state
* @returns {S & {}}
*/
export function getStoreState() {
return store.getState();
}
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