Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
Rex Dri
Rex Dri
Commits
7266140c
Commit
7266140c
authored
Feb 24, 2019
by
Florent Chehab
Browse files
Merge branch 'cleaning' into 'master'
Cleaning See merge request
rex-dri/rex-dri!50
parents
0e42118e
4fe1bdf2
Pipeline
#35396
passed with stages
in 4 minutes and 15 seconds
Changes
106
Pipelines
1
Hide whitespace changes
Inline
Side-by-side
frontend/src/api/SmartActions.js
View file @
7266140c
import
Cookies
from
"
js-cookie
"
;
// TODO uodate parameters name to match new CRUD actions
/**
* Class to perform network request and store the "status" in the redux store.
* The request are performed in a smart way to prevent multiple request to same endpoint at the same time.
*
* @export
* @class SmartActions
*/
export
default
class
SmartActions
{
fetching
=
new
Map
();
...
...
@@ -22,51 +27,51 @@ export default class SmartActions {
* Generic function for handling a GET request to the API
*
* @param {string} pk
* @param {string} api
_end_p
oint
* @param {function}
_IsLo
ading
* @param {function}
_Fetch
DataSucce
ss
* @param {function}
_I
nvalidate
d
* @param {function}
_HasError
* @param {boolean} [pk
_r
equired=false]
* @param {string} api
EndP
oint
* @param {function}
isRe
ading
* @param {function}
read
DataSucce
eded
* @param {function}
i
nvalidate
* @param {function}
failed
* @param {boolean} [pk
R
equired=false]
* @returns {function} Function that takes the `dispatch` function of redux as argument.
* @memberof SmartActions
*/
_FetchData
(
pk
,
api
_end_p
oint
,
_IsLo
ading
,
_Fetch
DataSucce
ss
,
_I
nvalidate
d
,
_HasError
,
pk
_r
equired
=
false
)
{
if
(
pk
_r
equired
&&
(
typeof
pk
==
"
undefined
"
))
{
_FetchData
(
pk
,
api
EndP
oint
,
isRe
ading
,
read
DataSucce
eded
,
i
nvalidate
,
failed
,
pk
R
equired
=
false
)
{
if
(
pk
R
equired
&&
(
typeof
pk
==
"
undefined
"
))
{
throw
"
pk shouldn't be empty when requesting a specific element
"
;
}
if
(
pk
!=
""
)
{
api
_end_p
oint
+=
pk
+
"
/
"
;
api
EndP
oint
+=
pk
+
"
/
"
;
}
if
(
!
this
.
shouldFetchEndPoint
(
api
_end_p
oint
))
{
if
(
!
this
.
shouldFetchEndPoint
(
api
EndP
oint
))
{
return
()
=>
{
};
}
else
{
this
.
fetching
.
set
(
api
_end_p
oint
,
true
);
this
.
fetching
.
set
(
api
EndP
oint
,
true
);
}
return
(
dispatch
)
=>
{
dispatch
(
_IsLo
ading
(
true
));
dispatch
(
isRe
ading
(
true
));
let
token
=
Cookies
.
get
(
"
csrftoken
"
);
fetch
(
api
_end_p
oint
,
{
credentials
:
"
same-origin
"
,
headers
:
{
"
X-CSRFToken
"
:
token
}
})
fetch
(
api
EndP
oint
,
{
credentials
:
"
same-origin
"
,
headers
:
{
"
X-CSRFToken
"
:
token
}
})
.
then
((
response
)
=>
{
if
(
!
response
.
ok
)
{
this
.
fetching
.
delete
(
api
_end_p
oint
);
this
.
fetching
.
delete
(
api
EndP
oint
);
throw
Error
(
response
.
statusText
);
}
return
response
;
})
.
then
((
response
)
=>
response
.
json
())
.
then
((
obj
)
=>
{
dispatch
(
_I
nvalidate
d
(
false
));
dispatch
(
_Fetch
DataSucce
ss
(
obj
));
dispatch
(
_IsLo
ading
(
false
));
this
.
fetching
.
delete
(
api
_end_p
oint
);
dispatch
(
i
nvalidate
(
false
));
dispatch
(
read
DataSucce
eded
(
obj
));
dispatch
(
isRe
ading
(
false
));
this
.
fetching
.
delete
(
api
EndP
oint
);
})
.
catch
((
e
)
=>
{
dispatch
(
_HasError
(
true
,
e
));
dispatch
(
_IsLo
ading
(
false
));
this
.
fetching
.
delete
(
api
_end_p
oint
);
dispatch
(
failed
(
true
,
e
));
dispatch
(
isRe
ading
(
false
));
this
.
fetching
.
delete
(
api
EndP
oint
);
});
};
}
...
...
@@ -77,14 +82,14 @@ export default class SmartActions {
* Generic function for handling a PUT and POST requests to the API
*
* @param {object} data, to update the data, `data` should contains the `id` field.
* @param {string} api
_end_p
oint
* @param {function}
_ElI
sSaving
* @param {function}
_ElFetchData
Succe
ss
* @param {function}
_ElHasError
* @param {string} api
EndP
oint
* @param {function}
i
sSaving
Specific
* @param {function}
readSpecific
Succe
eded
* @param {function}
failedSpecific
* @returns {function} Function that takes the `dispatch` function of redux as argument.
* @memberof SmartActions
*/
_ElSaveData
(
data
,
api
_end_p
oint
,
_ElI
sSaving
,
_ElFetchDataSuccess
,
_ElHasError
)
{
_ElSaveData
(
data
,
api
EndP
oint
,
i
sSaving
Specific
,
readSpecificSucceeded
,
failedSpecific
)
{
return
(
dispatch
)
=>
{
let
method
=
"
POST
"
;
let
pk
=
""
;
...
...
@@ -99,9 +104,9 @@ export default class SmartActions {
let
errorStatusText
=
""
;
dispatch
(
_ElI
sSaving
(
true
));
dispatch
(
i
sSaving
Specific
(
true
));
let
token
=
Cookies
.
get
(
"
csrftoken
"
);
fetch
(
api
_end_p
oint
+
__apiAttr
+
pk
,
{
fetch
(
api
EndP
oint
+
__apiAttr
+
pk
,
{
method
:
method
,
credentials
:
"
same-origin
"
,
headers
:
{
...
...
@@ -119,8 +124,8 @@ export default class SmartActions {
return
response
.
json
();
})
.
then
((
_El
)
=>
{
dispatch
(
_ElFetchData
Succe
ss
(
_El
));
// we use the same here
dispatch
(
_ElI
sSaving
(
false
));
dispatch
(
readSpecific
Succe
eded
(
_El
));
// we use the same here
dispatch
(
i
sSaving
Specific
(
false
));
})
.
catch
((
e
)
=>
{
if
(
typeof
e
.
json
==
"
function
"
)
{
...
...
@@ -132,8 +137,8 @@ export default class SmartActions {
})
.
then
((
errorContent
)
=>
{
if
(
typeof
errorContent
!=
"
undefined
"
)
{
dispatch
(
_ElHasError
(
true
,
{
message
:
errorStatusText
,
content
:
errorContent
}));
dispatch
(
_ElI
sSaving
(
false
));
dispatch
(
failedSpecific
(
true
,
{
message
:
errorStatusText
,
content
:
errorContent
}));
dispatch
(
i
sSaving
Specific
(
false
));
}
});
};
...
...
frontend/src/api/buildApiActionsAndReducers.js
View file @
7266140c
/*
* This file dynamically create the redux actions and reducers related to the REST API and custom apis
*/
import
API_CONFIG
from
"
../../../shared/api_config.yml
"
;
import
CrudActions
from
"
./CrudActions
"
;
import
CrudReducers
from
"
./CrudReducers
"
;
...
...
@@ -6,7 +10,6 @@ import CrudReducers from "./CrudReducers";
let
apiActionsTmp
=
{};
let
apiReducersTmp
=
{};
// Simple APIs outside rest framework
// TODO merge with the shared file
const
otherAPI
=
[
...
...
@@ -19,7 +22,7 @@ const otherAPI = [
/**
* Create api actions and reducers for given the info in each arr obj
* Add those to apiActions and apiReducers objects
*
*
* @param {Array} arr
*/
function
addAPIs
(
arr
)
{
...
...
@@ -30,7 +33,6 @@ function addAPIs(arr) {
// TODO better generality with /api add to a shared parameter file
let
apiEndPoint
=
`/api/
${
info
.
api_end_point
}
/`
;
let
apiInfo
=
{
name
:
info
.
api_end_point
,
readOnly
:
info
.
read_only
,
...
...
frontend/src/components/App.js
View file @
7266140c
// Inspired by https://github.com/mui-org/material-ui/tree/master/docs/src/pages/page-layout-examples/dashboard
/** General app JS entry
* Inspired by https://github.com/mui-org/material-ui/tree/master/docs/src/pages/page-layout-examples/dashboard
*/
import
React
from
"
react
"
;
import
PropTypes
from
"
prop-types
"
;
...
...
@@ -19,7 +21,6 @@ import { mainListItems, secondaryListItems, thirdListItems } from "./template/li
import
{
connect
}
from
"
react-redux
"
;
import
CustomComponentForAPI
from
"
./CustomComponentForAPI
"
;
// import route Components here
import
{
Route
,
Redirect
...
...
@@ -33,82 +34,13 @@ import PageUniversity from "./pages/PageUniversity";
import
PageSearch
from
"
./pages/PageSearch
"
;
import
PageSettings
from
"
./pages/PageSettings
"
;
const
drawerWidth
=
240
;
const
styles
=
theme
=>
({
root
:
{
display
:
"
flex
"
,
},
toolbar
:
{
paddingRight
:
24
,
// keep right padding when drawer closed
},
toolbarIcon
:
{
display
:
"
flex
"
,
alignItems
:
"
center
"
,
justifyContent
:
"
flex-end
"
,
padding
:
"
0 8px
"
,
...
theme
.
mixins
.
toolbar
,
},
chip
:
{
margin
:
theme
.
spacing
.
unit
,
},
menuButton
:
{
marginRight
:
4
,
},
hideIt
:
{
display
:
"
none
"
,
},
title
:
{
flexGrow
:
1
,
},
drawerPaper
:
{
position
:
"
relative
"
,
whiteSpace
:
"
nowrap
"
,
width
:
drawerWidth
,
transition
:
theme
.
transitions
.
create
(
"
width
"
,
{
easing
:
theme
.
transitions
.
easing
.
sharp
,
duration
:
theme
.
transitions
.
duration
.
enteringScreen
,
}),
},
drawerPaperClose
:
{
overflowX
:
"
hidden
"
,
transition
:
theme
.
transitions
.
create
(
"
width
"
,
{
easing
:
theme
.
transitions
.
easing
.
sharp
,
duration
:
theme
.
transitions
.
duration
.
leavingScreen
,
}),
width
:
theme
.
spacing
.
unit
*
7
,
[
theme
.
breakpoints
.
up
(
"
sm
"
)]:
{
width
:
theme
.
spacing
.
unit
*
9
,
},
},
content
:
{
flexGrow
:
1
,
padding
:
theme
.
spacing
.
unit
*
3
,
height
:
"
100vh
"
,
overflow
:
"
auto
"
,
paddingTop
:
"
0px
"
},
paddingTop
:
{
paddingTop
:
"
24px
"
},
chartContainer
:
{
marginLeft
:
-
22
,
},
tableContainer
:
{
height
:
320
,
},
myPaper
:
{
padding
:
16
},
null
:
{}
});
const
DRAWER_WIDTH
=
240
;
class
App
extends
CustomComponentForAPI
{
state
=
{
open
:
true
,
};
handleDrawerOpen
=
()
=>
{
this
.
setState
({
open
:
true
});
};
...
...
@@ -123,6 +55,7 @@ class App extends CustomComponentForAPI {
return
(
<
React
.
Fragment
>
<
CssBaseline
/>
<
div
className
=
{
classes
.
root
}
>
<
Drawer
...
...
@@ -132,8 +65,8 @@ class App extends CustomComponentForAPI {
}}
open
=
{
this
.
state
.
open
}
>
<
div
className
=
{
classNames
(
classes
.
toolbarIcon
)
}
>
<
div
className
=
{
classNames
((
!
this
.
state
.
open
)
&&
classes
.
hideIt
,
classes
.
null
)
}
>
<
div
className
=
{
classes
.
toolbarIcon
}
>
<
div
className
=
{
!
this
.
state
.
open
?
classes
.
hideIt
:
classes
.
null
}
>
<
Chip
avatar
=
{
<
Avatar
>
<
SchoolIcon
/>
<
/Avatar>
}
label
=
"
Outgoing REX
"
...
...
@@ -141,12 +74,24 @@ class App extends CustomComponentForAPI {
color
=
"
primary
"
/>
<
/div
>
<
IconButton
onClick
=
{
this
.
handleDrawerOpen
}
className
=
{
classNames
(
classes
.
menuButton
,
this
.
state
.
open
&&
classes
.
hideIt
)}
>
<
IconButton
onClick
=
{
this
.
handleDrawerOpen
}
className
=
{
classNames
(
classes
.
menuButton
,
this
.
state
.
open
?
classes
.
hideIt
:
classes
.
null
)}
>
<
MenuIcon
/>
<
/IconButton
>
<
IconButton
onClick
=
{
this
.
handleDrawerClose
}
className
=
{
classNames
(
classes
.
menuButton
,
(
!
this
.
state
.
open
)
&&
classes
.
hideIt
)}
>
<
IconButton
onClick
=
{
this
.
handleDrawerClose
}
className
=
{
classNames
(
classes
.
menuButton
,
!
this
.
state
.
open
?
classes
.
hideIt
:
classes
.
null
)}
>
<
ChevronLeftIcon
/>
<
/IconButton
>
<
/div
>
...
...
@@ -158,6 +103,7 @@ class App extends CustomComponentForAPI {
<
List
>
{
secondaryListItems
}
<
/List
>
<
Divider
/>
<
List
>
{
thirdListItems
}
<
/List
>
<
/Drawer
>
<
main
className
=
{
classNames
(
classes
.
content
,
classes
.
noPaddingTop
)}
>
...
...
@@ -176,6 +122,7 @@ class App extends CustomComponentForAPI {
<
Route
path
=
"
/app/university/:id
"
component
=
{
PageUniversity
}
/
>
<
/div
>
<
/main
>
<
/div
>
<
/React.Fragment
>
);
...
...
@@ -203,4 +150,71 @@ const mapDispatchToProps = (dispatch) => {
};
};
const
styles
=
theme
=>
({
root
:
{
display
:
"
flex
"
,
},
toolbar
:
{
paddingRight
:
24
,
// keep right padding when drawer closed
},
toolbarIcon
:
{
display
:
"
flex
"
,
alignItems
:
"
center
"
,
justifyContent
:
"
flex-end
"
,
padding
:
"
0 8px
"
,
...
theme
.
mixins
.
toolbar
,
},
chip
:
{
margin
:
theme
.
spacing
.
unit
,
},
menuButton
:
{
marginRight
:
4
,
},
hideIt
:
{
display
:
"
none
"
,
},
title
:
{
flexGrow
:
1
,
},
drawerPaper
:
{
position
:
"
relative
"
,
whiteSpace
:
"
nowrap
"
,
width
:
DRAWER_WIDTH
,
transition
:
theme
.
transitions
.
create
(
"
width
"
,
{
easing
:
theme
.
transitions
.
easing
.
sharp
,
duration
:
theme
.
transitions
.
duration
.
enteringScreen
,
}),
},
drawerPaperClose
:
{
overflowX
:
"
hidden
"
,
transition
:
theme
.
transitions
.
create
(
"
width
"
,
{
easing
:
theme
.
transitions
.
easing
.
sharp
,
duration
:
theme
.
transitions
.
duration
.
leavingScreen
,
}),
width
:
theme
.
spacing
.
unit
*
7
,
[
theme
.
breakpoints
.
up
(
"
sm
"
)]:
{
width
:
theme
.
spacing
.
unit
*
9
,
},
},
content
:
{
flexGrow
:
1
,
padding
:
theme
.
spacing
.
unit
*
3
,
height
:
"
100vh
"
,
overflow
:
"
auto
"
,
paddingTop
:
"
0px
"
},
paddingTop
:
{
paddingTop
:
"
24px
"
},
chartContainer
:
{
marginLeft
:
-
22
,
},
tableContainer
:
{
height
:
320
,
},
null
:
{}
});
export
default
connect
(
mapStateToProps
,
mapDispatchToProps
)(
withStyles
(
styles
,
{
withTheme
:
true
})(
App
));
frontend/src/components/CustomComponentForAPI.js
View file @
7266140c
...
...
@@ -192,7 +192,7 @@ class CustomComponentForAPI extends Component {
{ readAt: 0 });
if (!("data" in out)) {
throw Error(`No read data from the api could be retr
e
ived for: ${propName}`);
throw Error(`No read data from the api could be retri
e
ved for: ${propName}`);
} else {
return out;
}
...
...
@@ -210,6 +210,17 @@ class CustomComponentForAPI extends Component {
return this.getReadDataAndTime(propName).data;
}
/**
* Get the time at which the latest data from the api corresponding to `propName` was read
*
* @param {string} propName
* @returns {Any}
* @memberof CustomComponentForAPI
*/
getReadTime(propName) {
return this.getReadDataAndTime(propName).readAt;
}
/**
* Access to all the data loaded from the api given the props
*
...
...
frontend/src/components/ThemeProvider.js
View file @
7266140c
...
...
@@ -14,6 +14,12 @@ import getActions from "../api/getActions";
import
{
saveAppTheme
}
from
"
../actions/theme
"
;
/**
* Component that handles theme customization and saving at the scale of the entire app
*
* @class ThemeProvider
* @extends {CustomComponentForAPI}
*/
class
ThemeProvider
extends
CustomComponentForAPI
{
state
=
{
theme
:
this
.
props
.
themeSavedInTheApp
.
theme
};
...
...
@@ -54,7 +60,10 @@ class ThemeProvider extends CustomComponentForAPI {
fontSize
:
14
,
htmlFontSize
:
14
},
useNextVariants
:
true
useNextVariants
:
true
,
myPaper
:
{
padding
:
16
}
};
const
theme
=
Object
.
assign
({},
this
.
state
.
theme
,
siteSettings
);
...
...
frontend/src/components/filter/DownshiftMultiple.js
View file @
7266140c
/**
*
* WARNING THIS FILE HAS NOT BEEN REVIEWED AS OF 22.02.2019
* THINGS MIGHT NOT BE SUPER CLEAR OR BROKEN
*
*
*
*
*
*
*
* TODO
*
*
*
*
*/
// Inspired by : https://material-ui.com/demos/autocomplete/
import
React
from
"
react
"
;
...
...
@@ -21,9 +42,7 @@ function renderInput(inputProps) {
<
TextField
InputProps
=
{{
inputRef
:
ref
,
classes
:
{
root
:
classes
.
inputRoot
,
},
classes
:
{
root
:
classes
.
inputRoot
},
...
InputProps
,
}}
{...
other
}
...
...
frontend/src/components/filter/Filter.js
View file @
7266140c
/**
*
* WARNING THIS FILE HAS NOT BEEN REVIEWED AS OF 22.02.2019
* THINGS MIGHT NOT BE SUPER CLEAR OR BROKEN
*
*
*
*
*
*
*
* TODO
*
*
*
*
*/
import
React
from
"
react
"
;
import
DownshiftMultiple
from
"
./DownshiftMultiple
"
;
...
...
@@ -15,18 +37,13 @@ import withStyles from "@material-ui/core/styles/withStyles";
import
getActions
from
"
../../api/getActions
"
;
const
styles
=
theme
=>
({
root
:
{
width
:
"
100%
"
,
},
heading
:
{
fontSize
:
theme
.
typography
.
pxToRem
(
15
),
fontWeight
:
theme
.
typography
.
fontWeightRegular
,
},
});
/**
* Implementation of a filter component
*
* @class Filter
* @extends {CustomComponentForAPI}
*/
class
Filter
extends
CustomComponentForAPI
{
saveCountriesFilterConfig
(
state
)
{
...
...
@@ -55,6 +72,7 @@ class Filter extends CustomComponentForAPI {
return
[...
res
.
values
()];
}
updateSelectedUniversities
(
selection
)
{
const
mainCampuses
=
this
.
getReadData
(
"
mainCampuses
"
),
listOfCountries
=
__map
(
selection
,
(
s
)
=>
s
.
id
);
...
...
@@ -92,7 +110,6 @@ class Filter extends CustomComponentForAPI {
}
const
mapStateToProps
=
(
state
)
=>
{
return
{
universities
:
state
.
api
.
universitiesAll
,
...
...
@@ -117,4 +134,14 @@ const mapDispatchToProps = (dispatch) => {
};
const
styles
=
theme
=>
({
root
:
{
width
:
"
100%
"
,
},
heading
:
{
fontSize
:
theme
.
typography
.
pxToRem
(
15
),
fontWeight
:
theme
.
typography
.
fontWeightRegular
,
},
});
export
default
connect
(
mapStateToProps
,
mapDispatchToProps
)(
withStyles
(
styles
)(
Filter
));
frontend/src/components/map/MyCardMedia.js
View file @
7266140c
...
...
@@ -2,13 +2,19 @@ import React, { Component } from "react";
import
PropTypes
from
"
prop-types
"
;
import
CardMedia
from
"
@material-ui/core/CardMedia
"
;
/**
* Custom MUI Card Media Component that renders a single div if no URL is provided
*
* @class MyCardMedia
* @extends {Component}
*/
class
MyCardMedia
extends
Component
{
render
()
{
const
{
title
,
height
,
url
}
=
this
.
props
;
if
(
url
==
""
)
{
return
(
<
div
/>
);
}
return
(
<
CardMedia
component
=
"
img
"
...
...
@@ -16,7 +22,6 @@ class MyCardMedia extends Component {
image
=
{
url
}
title
=
{
title
}
/
>
);
}
}
...
...
frontend/src/components/map/UnivMakers.js
View file @
7266140c
...
...
@@ -2,7 +2,6 @@ import React from "react";
import
PropTypes
from
"
prop-types
"
;
import
{
connect
}
from
"
react-redux
"
;
import
{
Marker
,
Popup
}
from
"
react-leaflet
"
;
// import MarkerClusterGroup from 'react-leaflet-markercluster';