Commit 945e00a3 authored by Florent Chehab's avatar Florent Chehab

University fully connected

Textfield ready
bug corrected
parent 8a0c601c
......@@ -27,7 +27,7 @@ class University(MyModel):
acronym = models.CharField(max_length=20, null=True, blank=True)
logo = models.URLField(null=True, blank=True, validators=[
validate_extension_django])
website = models.URLField(null=True, blank=True)
website = models.URLField(null=True, blank=True, max_length=300)
utc_id = models.IntegerField(unique=True)
......
......@@ -30,6 +30,17 @@ class MyComponent extends Component {
return res;
}
findMainCampus(univId) {
const mainCampuses = this.getFetchedData('mainCampuses');
for (let main_campus_pk in mainCampuses) {
const campus = mainCampuses[main_campus_pk]
if (campus.university == univId) {
return campus;
}
}
return null;
}
checkProps(val) {
const { props } = this;
for (let el in props) {
......
import React 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 Editor from '../shared/Editor';
import editorStyle from '../shared/editorStyle';
import TextField from '../shared/fields/TextField';
import {
universitiesElSaveData,
universitiesElSavingHasError
} from '../../../generated/actions';
const styles = theme => ({
...editorStyle(theme)
});
class UniversityGeneral extends Editor {
renderEditor() {
const { modelData } = this.props;
return (
<div>
<TextField label={"Nom de l'université"}
value={modelData.name}
required={true}
maxLength={200}
formManager={this}
fieldMapping={'name'}
/>
<TextField label={"Acronyme de l'université"}
value={modelData.acronym}
maxLength={20}
formManager={this}
fieldMapping={'acronym'}
/>
<TextField label={"Site internet de l'université"}
value={modelData.website}
maxLength={300}
isUrl={true}
formManager={this}
fieldMapping={'website'}
/>
<TextField label={"Logo de l'université"}
value={modelData.logo}
maxLength={300}
isUrl={true}
urlExtensions={['jpg', 'png', 'svg']}
formManager={this}
fieldMapping={'logo'}
/>
</div>
)
}
}
UniversityGeneral.propTypes = {
modelData: PropTypes.object.isRequired,
};
const mapStateToProps = (state) => {
let lastUpdateTime = null;
const tmp = state.universitiesEl.fetched;
if (tmp.fetchedAt) {
lastUpdateTime = tmp.data.updated_on;
}
return {
savingHasError: state.universitiesEl.savingHasError,
lastSave: state.universitiesEl.fetched.fetchedAt,
lastUpdateTime,
};
};
const mapDispatchToProps = (dispatch) => {
return {
saveData: (data) => dispatch(universitiesElSaveData(data)),
clearSaveError: () => dispatch(universitiesElSavingHasError(false))
};
};
export default compose(
withStyles(styles, { withTheme: true }),
connect(mapStateToProps, mapDispatchToProps)
)(UniversityGeneral);
\ No newline at end of file
import React 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 _ from 'underscore';
import Markdown from '../../shared/Markdown';
import Typography from '@material-ui/core/Typography';
import PhotoIcon from '@material-ui/icons/Photo';
import MyComponent from '../../MyComponent';
import TextLink from '../../other/TextLink';
import GenericModule from '../shared/GenericModule';
import Grid from '@material-ui/core/Grid';
import Divider from '@material-ui/core/Divider';
import GenericModule from '../shared/GenericModule';
import UniversityGeneralEditor from '../editors/UniversityGeneralEditor';
const styles = theme => ({
import {
universitiesElFetchData,
citiesFetchData,
countriesFetchData,
mainCampusesFetchData,
} from '../../../generated/actions';
const styles = theme => ({
});
function renderCore(rawModelData, classes, outsideData) {
const univInfos = rawModelData;
const { name, acronym, logo, website, comment } = univInfos;
const { city, country } = outsideData;
return (
<div>
<Grid container spacing={16} direction='row'>
<Grid item xs={4}>
<img style={{ width: "100%" }} src={logo} />
</Grid>
<Grid item xs>
<Typography variant='headline'>{name}</Typography>
<Typography variant='title'>{acronym}</Typography>
<Divider />
<Typography variant='subheading'>{city}, {country}</Typography>
<Typography variant='body1'> Site internet : <TextLink href={website}>{website}</TextLink> </Typography>
</Grid>
</Grid>
<Markdown source={comment} />
</div>
)
}
class UniversityGeneral extends React.Component {
render() {
const { classes, theme } = this.props;
return (
<GenericModule title={"Introduction"}>
<Grid container spacing={16} direction='row'>
<Grid item xs={4}>
<img style={{ width: "100%" }} src={"https://upload.wikimedia.org/wikipedia/commons/f/f4/Logo_EPFL.svg"} />
</Grid>
<Grid item xs>
<Typography variant='headline'> École Polytechnique Fédérale de Lausanne</Typography>
<Typography variant='title'>EPFL</Typography>
<Divider />
<Typography variant='subheading'>Lausanne, Suisse</Typography>
<Typography variant='body1'> Site internet : <TextLink href={"https://www.epfl.ch"}> www.epfl.ch </TextLink> </Typography>
</Grid>
</Grid>
function parseRawModelData(rawModelData) {
// reverse serialization
const univInfos = rawModelData;
const modelData = _.pick(univInfos,
[
"name",
"acronym",
"logo",
"website",
// "useful_links",
// "comment",
"university",
// "obj_moderation_level",
"id"
]);
</GenericModule>
return modelData;
}
class UniversityGeneral extends MyComponent {
idToUse = "univId";
myRender() {
const univInfos = this.getFetchedData('universitiesEl');
const { classes } = this.props;
const univMainCampus = this.findMainCampus(this.props.univId);
const { cities, countries } = this.getAllFetchedData();
const cityModel = cities[univMainCampus.city];
const countryModel = countries[cityModel.country];
const outsideData = {
city: cityModel.name,
country: countryModel.name
}
return (
<GenericModule
buildTitle={() => "Présentation"}
rawModelData={univInfos}
parseRawModelData={parseRawModelData}
editor={UniversityGeneralEditor}
renderCore={renderCore}
coreClasses={classes}
outsideData={outsideData}
/>
)
}
}
export default withStyles(styles, { withTheme: true })(UniversityGeneral);
UniversityGeneral.propTypes = {
classes: PropTypes.object.isRequired,
theme: PropTypes.object.isRequired,
univId: PropTypes.string.isRequired
};
const mapStateToProps = (state) => {
return {
universitiesEl: state.universitiesEl,
countries: state.countries,
cities: state.cities,
mainCampuses: state.mainCampuses
};
};
const mapDispatchToProps = (dispatch) => {
return {
fetchData: {
universitiesEl: (univId) => dispatch(universitiesElFetchData(univId)),
countries: () => dispatch(countriesFetchData()),
cities: () => dispatch(citiesFetchData()),
mainCampuses: () => dispatch(mainCampusesFetchData()),
},
};
};
export default compose(
withStyles(styles, { withTheme: true }),
connect(mapStateToProps, mapDispatchToProps)
)(UniversityGeneral);
\ No newline at end of file
......@@ -177,7 +177,7 @@ class GenericModule extends MyComponent {
renderCore = (rawModelData) => (
<div>
{this.props.renderCore(rawModelData, this.props.coreClasses)}
{this.props.renderCore(rawModelData, this.props.coreClasses, this.props.outsideData)}
{renderUsefulLinks(rawModelData, this.props.classes, this.props.theme)}
</div>
)
......@@ -229,7 +229,7 @@ class GenericModule extends MyComponent {
/>
<Paper className={classes.root} square={true}>
{renderFirstRow.bind(this)(userCanModerate)}
{this.renderCore(this.props.rawModelData, this.props.coreClasses)}
{this.renderCore(this.props.rawModelData)}
</Paper>
</div>
)
......@@ -249,6 +249,7 @@ GenericModule.propTypes = {
renderCore: PropTypes.func.isRequired,
parseRawModelData: PropTypes.func.isRequired,
coreClasses: PropTypes.object.isRequired,
outsideData: PropTypes.object,
};
......
......@@ -2,28 +2,42 @@
import React from 'react';
import PropTypes from 'prop-types';
import withStyles from '@material-ui/core/styles/withStyles';
import Grid from '@material-ui/core/Grid';
import compose from 'recompose/compose';
import FieldWrapper from './FieldWrapper';
import { TextField as MuiTextField } from '@material-ui/core/TextField';
import { TextField as MuiTextField } from '@material-ui/core';
import Typography from '@material-ui/core/Typography';
import Markdown from '../../../shared/Markdown';
import LinkText from '../../../other/TextLink';
import Field from './Field';
import isUrl from '../../../../utils/isUrl';
import stringHasExtension from '../../../../utils/stringHasExtension';
const styles = theme => ({
})
class TextField extends Field {
hasError(value) {
let messages = Array();
if (this.props.required && value == '') {
messages.push("Ce champ est requis");
messages.push("Ce champ est requis mais il est vide.");
}
if (this.props.maxLength && value.length > this.props.maxLength) {
messages.push("Il y a trop de caractères.")
messages.push("L'URL est trop long.");
}
if (this.props.isUrl && !isUrl(value)) {
messages.push("L'URL entrer n'est pas reconnu.")
}
return this.buildError(messages);
if (this.props.isUrl && this.props.urlExtensions.length > 0) {
if (!stringHasExtension(value, this.props.urlExtensions)) {
messages.push("Extension de l'URL non conforme")
}
}
return this.buildError(messages)
}
handleChangeValue = (val) => {
......@@ -33,14 +47,12 @@ class TextField extends Field {
if (maxLength && value.length > maxLength + 1) {
value = value.substring(0, maxLength + 1);
}
this.setState({ value });
}
render() {
const { classes } = this.props;
return (
<FieldWrapper
required={this.props.required}
......@@ -48,7 +60,20 @@ class TextField extends Field {
errors={this.state.error.messages}
label={this.props.label}
>
{
this.props.isUrl ?
<div>
<Typography variant='caption'>Un URL est attendu ici (Le protocole - http/https/ftp - est requis !)</Typography>
{
this.props.urlExtensions.length > 0 ?
<Typography variant='caption'>L'url doit terminer par l'une des extensions suivantes (majuscule ou miniscule) : {JSON.stringify(this.props.urlExtensions)}</Typography>
:
<div></div>
}
</div>
:
<div></div>
}
{
this.props.maxLength ?
<Typography variant='caption'>Nombre de caractères : {this.state.value.length}/{this.props.maxLength}</Typography>
......@@ -58,10 +83,10 @@ class TextField extends Field {
<MuiTextField
placeholder={"Le champ est vide"}
fullWidth={true}
multiline={false}
value={this.state.value}
onChange={(e) => this.handleChangeValue(e.target.value)}
/>
</FieldWrapper>
)
}
......@@ -71,11 +96,15 @@ class TextField extends Field {
TextField.defaultProps = {
value: '',
maxLength: 0,
isUrl: false,
urlExtensions: [],
}
TextField.propTypes = {
value: PropTypes.string,
maxLength: PropTypes.number,
isUrl: PropTypes.bool.isRequired,
urlExtensions: PropTypes.arrayOf(PropTypes.string.isRequired)
};
......
export default function getVersionTooltipAndClass(nbVersions) {
if (typeof nbVersions == 'undefined') {
if (typeof nbVersions == 'undefined' || isNaN(nbVersions)) {
return {
versionTooltip: "Ce contenu n'est pas versionné.",
versionClass: "disabled"
......
......@@ -6,7 +6,11 @@ import LinkIcon from '@material-ui/icons/Link';
export default function renderUsefulLinks(rawModelData, classes, theme) {
const usefulLinks = rawModelData.useful_links;
if (!usefulLinks) {
return (<div></div>)
}
const nbItems = usefulLinks.length;
if (nbItems == 0) {
return (<div></div>)
......
......@@ -39,7 +39,7 @@ class GeneralInfoTab extends MyComponent {
<div style={{ flexGrow: 8, paddingRight: 2 * theme.spacing.unit }}>
<Grid container direction='column' >
<Grid item xs style={{ paddingBottom: 2 * theme.spacing.unit }} >
{/* <UniversityGeneral univId={this.props.univId} /> */}
<UniversityGeneral univId={this.props.univId} />
</Grid>
<Grid item xs>
{/* <UniversityDri univId={this.props.univId} /> */}
......@@ -66,7 +66,7 @@ class GeneralInfoTab extends MyComponent {
<Grid container spacing={16} >
<Grid item xs={12}>
{/* <UniversityGeneral univId={this.props.univId} /> */}
<UniversityGeneral univId={this.props.univId} />
</Grid>
<Grid item xs={12}>
{/* <UniversityDri univId={this.props.univId} /> */}
......
export default function stringHasExtension(str, allowedExtensions) {
if (!str || typeof str != 'string') {
return false;
}
const strExtension = str.split('.').slice(-1)[0].toLowerCase();
return allowedExtensions.some(ext => {
if (ext.toLowerCase() == strExtension) {
return true;
} else {
return false;
}
});
}
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