️ This Gitlab will be shut down at 2021-12-31 23:59:59.
Students and staff can migrate to gitlab.utwente.nl.
SNT members can migrate to gitlab.snt.utwente.nl.
Contact bestuur@snt.utwente.nl for more information.

Migrate your projects today!
Export your project using the webinterface or use a script.

Commit 2c9c1a00 authored by TJHeeringa's avatar TJHeeringa

Various fixes

parent 6c3f9144
......@@ -35,7 +35,7 @@ const useStyles = makeStyles(theme => ({
},
media: {
minWidth: 100,
maxWidth: 200,
maxWidth: 250,
width: "88%",
marginLeft: 10,
marginRight: 10,
......
import { makeStyles, useTheme } from "@material-ui/core/styles";
import TextField from "App/Components/Fields/TextField";
import PropTypes from "prop-types";
import React from "react";
import TextField from "App/Components/Fields/TextField";
const useStyles = makeStyles(theme => ({
}));
......
const isUnique = {name: "isUnique", validate: (value)=> value};
export default isUnique;
\ No newline at end of file
......@@ -12,7 +12,6 @@ import PageContent from "../../Components/PageLayout/Content/Content";
const useStyles = makeStyles(theme => ({
}));
......@@ -21,27 +20,27 @@ export const BoardInfo = (props) => {
const classes = useStyles(theme);
let { data: boards, loading: loadingBoard } = useGet({
path: process.env.REACT_APP_API_URL+"/associations/student-union/boards",
queryParams: {limit: 10000},
path: "/groups",
queryParams: {current_board: true, association__slug: props.association.slug, board_group: true},
resolve: data => data && data.results
});
const morphBoardMemberToCard = (board_member) => {
let data = {
"Name": board_member.given_name + " " + board_member.surname,
"Name": board_member.profile.given_name + " " + board_member.profile.surname,
"Email": board_member.email
};
return {
duty: board_member.duty,
data: data,
given_name: board_member.given_name,
given_name: board_member.profile.given_name,
description: board_member.description || loremIpsum()[0],
photo: null,
order: board_member.order
};
};
const board = !loadingBoard && boards.find(board=>board.current_board===true);
const board = !loadingBoard && boards[0];
console.log(board);
let x = board && board.groupmemberships && orderBy(board.groupmemberships, ["order"], ["asc"]).map(board_member=>morphBoardMemberToCard(board_member));
console.log(x);
......
......@@ -350,7 +350,7 @@ export class AssociationDataFields extends Component {
getAssociationSpecificDataFields = () => {
return Helper.api_call(
process.env.REACT_APP_API_URL + "/data_fields?ordering=name&association__slug=" + this.props.association.slug,
process.env.REACT_APP_API_URL + "/association_data_fields?ordering=name&association__slug=" + this.props.association.slug,
"GET",
null,
"json",
......@@ -364,7 +364,7 @@ export class AssociationDataFields extends Component {
return (
<Container>
<Block>
<Typography variant={"h5"}>ASSOCIATION Membertypes</Typography>
<Typography variant={"h5"}>ASSOCIATION Data fields</Typography>
<hr className={"box-title-separator"}/>
{ specific_data_fields && specific_data_fields.map((specific_data_field, index) => { return (
<CollapsableList
......
......@@ -45,7 +45,9 @@ const useStyles = makeStyles(theme => ({
const Matchings = (props) => {
const classes = useStyles();
const alertHandler = useAlertHandler();
const [matches, setMatches] = useState([]);
const [show, setShowMatches] = useState(true);
const [selectedLeft, setLeft] = useState({});
......@@ -63,17 +65,17 @@ const Matchings = (props) => {
}, [classes]); // function is called whenever second attr changes; we don't want it to call aside from mounting so we use constant attr
let { data: members, loading: loadingMembers } = useGet({
path: process.env.REACT_APP_API_URL + "/memberships",
path: "/memberships",
queryParams: {limit: 10000, current: true, association__slug: props.association.slug},
resolve: data => data && data.results
});
let { data: data_fields, loading: loadingDataFields } = useGet({
path: process.env.REACT_APP_API_URL + "/association_data_fields",
path: "/association_data_fields",
queryParams: {limit: 10000, association__slug: props.association.slug},
resolve: data => data && data.results
});
let { data: membertypes, loading: loadingMembertypes } = useGet({
path: process.env.REACT_APP_API_URL + "/membertypes",
path: "/membertypes",
queryParams: {limit: 100, association__slug: props.association.slug},
resolve: data => data && data.results
});
......
......@@ -9,6 +9,7 @@ import Block from "App/Components/PageLayout/Content/Block";
import PageContent from "App/Components/PageLayout/Content/Content";
import PropTypes from "prop-types";
import React from "react";
import {Spinner} from "react-bootstrap";
import {useGet} from "restful-react";
import Info from "../../Components/Info/Info";
......@@ -30,7 +31,7 @@ const MyMatches = (props) => {
const { profile, association } = props;
const queryParams = association ? {limit: 10000, association__slug: association.slug} : {limit: 10000};
let { data: matchings } = useGet({
let { data: matchings, loading } = useGet({
path: "/matchings",
queryParams: queryParams,
resolve: data => data && data.results
......@@ -46,31 +47,46 @@ const MyMatches = (props) => {
matchings = left_matchings.concat(right_matchings);
}
return (
<PageContent title={"Matchings"}>
<Container>
<Block>
<Typography variant={"h5"}>You are matched with:</Typography>
<List>
{ matchings && matchings.map((matching, m)=>(
<ListItem key={m}>
<Avatar className={classes.avatar} key={m} src={matching.photo}/>
<div className={classes.info}>
<Info
headerless={true}
data={{
name: matching.name,
email: matching.email
}}
/>
</div>
</ListItem>
)) }
</List>
</Block>
</Container>
</PageContent>
);
if (loading) {
return (
<PageContent title={"Matchings"}>
<Container>
<Block>
<Spinner/>
</Block>
</Container>
</PageContent>
);
} else {
return (
<PageContent title={"Matchings"}>
<Container>
<Block>
<Typography variant={"h5"}>
{ matchings.length > 0 ? "You are matched with:" : "You are not matched to someone at the moment." }
</Typography>
<List>
{ matchings.map((matching, m) => (
<ListItem key={m}>
<Avatar className={classes.avatar} key={m} src={matching.photo}/>
<div className={classes.info}>
<Info
headerless={true}
data={{
name: matching.name,
email: matching.email
}}
/>
</div>
</ListItem>
))}
</List>
</Block>
</Container>
</PageContent>
);
}
};
MyMatches.propTypes = {
......
......@@ -8,6 +8,7 @@ import FormControlLabel from "@material-ui/core/FormControlLabel";
import IconButton from "@material-ui/core/IconButton";
import MenuItem from "@material-ui/core/MenuItem";
import Switch from "@material-ui/core/Switch";
import Typography from "@material-ui/core/Typography";
import BallotIcon from "@material-ui/icons/Ballot";
import CalendarToday from "@material-ui/icons/CalendarToday";
import Create from "@material-ui/icons/Create";
......@@ -17,12 +18,16 @@ import LocationOn from "@material-ui/icons/LocationOn";
import Notes from "@material-ui/icons/Notes";
import { KeyboardDateTimePicker, MuiPickersUtilsProvider } from "@material-ui/pickers";
import moment from "moment";
import React, { Component } from "react";
import React, {Component, useEffect, useState} from "react";
import Modal from "react-bootstrap/Modal";
import { SelectValidator,TextValidator, ValidatorForm } from "react-material-ui-form-validator";
import Wrapper from "App/Components/Fields/Wrapper";
import ConfirmationModal from "../../Components/Modals/ConfirmationModal";
import Typography from "@material-ui/core/Typography";
import TextField from "../../Components/Fields/TextField";
import NumberField from "../../Components/Fields/NumberField";
import DateTimeField from "../../Components/Fields/DateTimeField";
import isTruthy from "../../Components/ValidatorRules/isTruthy";
const containerStyles = theme => ({
......@@ -81,6 +86,30 @@ const containerStyles = theme => ({
},
});
const EventModalBoard2 = (props) => {
const [openConfirmationModal, setOpenConfirmationModal] = useState(false);
const toggleOpenConfirmationModal = () => setOpenConfirmationModal(!openConfirmationModal);
const handleValidSubmit = (event, values) => {this.props.submitModal();};
// use to check for duplicate names of enrollment options
// useEffect(()=>{
// // called when component is mounted
// ValidatorForm.addValidationRule(isTruthy.name, isTruthy.validate);
// // return function is called when component is unmounted
// return function (){
// ValidatorForm.removeValidationRule(isTruthy.name);
// };
// });
const handleEventEnrollmentOptionValidation = (value, ctx) => {
if (this.props.current_event.enrollment_options.filter((option)=>option.title === value).length > 1){
return "Two enrollment options cannot have the same name";
}
return true;
};
};
class EventModalBoard extends Component {
state = {
......@@ -128,7 +157,6 @@ class EventModalBoard extends Component {
>
<ValidatorForm
onSubmit={this.handleValidSubmit}
ref={"modal_form"}
onError={errors => console.log(errors)}
>
<Modal.Header>
......@@ -137,42 +165,40 @@ class EventModalBoard extends Component {
</Modal.Title>
</Modal.Header>
<Modal.Body>
<div className={classes.wrapper}>
<Wrapper>
<Create className={classes.icon} color={"action"} />
<TextValidator
{...textEditorProps("name")}
<TextField
name={"name"}
value={this.props.current_event.name}
onChange={(event) => this.props.handleEventChange("name", event.target.value)}
validators={["minStringLength:4"]}
errorMessages={["Name must have at least 4 characters"]}
/>
</div>
<div className={classes.wrapper}>
</Wrapper>
<Wrapper>
<CalendarToday className={classes.icon} color={"action"} />
<MuiPickersUtilsProvider libInstance={moment} utils={MomentUtils}>
<KeyboardDateTimePicker
value={this.props.current_event.start_date}
onChange={datetime_util_object => this.props.handleEventChange("start_date", datetime_util_object.toDate())}
{...pickerEditorProps("Start Date")}
/>
<KeyboardDateTimePicker
value={this.props.current_event.end_date}
onChange={datetime_util_object => this.props.handleEventChange("end_date", datetime_util_object.toDate())}
{...pickerEditorProps("End Date")}
/>
</MuiPickersUtilsProvider>
</div>
<div className={classes.wrapper}>
<KeyboardDateTimePicker
value={this.props.current_event.start_date}
onChange={datetime_util_object => this.props.handleEventChange("start_date", datetime_util_object.toDate())}
{...pickerEditorProps("Start Date")}
/>
<KeyboardDateTimePicker
value={this.props.current_event.end_date}
onChange={datetime_util_object => this.props.handleEventChange("end_date", datetime_util_object.toDate())}
{...pickerEditorProps("End Date")}
/>
</Wrapper>
<Wrapper>
<Notes className={classes.icon} color={"action"} />
<TextValidator
{...textEditorProps("description")}
<TextField
name={"description"}
value={this.props.current_event.description}
onChange={(event) => this.props.handleEventChange("description", event.target.value)}
multiline
rows={"6"}
/>
</div>
<div className={classes.wrapper}>
</Wrapper>
<Wrapper>
<BallotIcon className={classes.icon} color={"action"} />
<SelectValidator
{...textEditorProps("type")}
......@@ -187,30 +213,30 @@ class EventModalBoard extends Component {
</MenuItem>
)) }
</SelectValidator>
</div>
<div className={classes.wrapper}>
</Wrapper>
<Wrapper>
<LocationOn className={classes.icon} color={"action"} />
<TextValidator
{...textEditorProps("location")}
<TextField
name={"location"}
value={this.props.current_event.location}
onChange={(event) => this.props.handleEventChange("location", event.target.value)}
/>
</div>
{ /*<div className={classes.wrapper}>*/ }
{/*<FormControlLabel*/}
{/* control={<Switch color={"primary"} />}*/}
{/* label={"Public"}*/}
{/* labelPlacement={"start"}*/}
{/* checked={this.props.current_event.public}*/}
{/* onChange={(event) => this.props.handleEventChange("public", event.target.checked)}*/}
{/* disabled={true}*/}
{/*/>*/}
</Wrapper>
<FormControlLabel
control={<Switch color={"primary"} />}
label={"Public"}
labelPlacement={"start"}
checked={this.props.current_event.public}
onChange={(event) => this.props.handleEventChange("public", event.target.checked)}
disabled={true}
/>
<FormControlLabel
control={<Switch color={"primary"} />}
label={"Enrollable"}
labelPlacement={"start"}
checked={this.props.current_event.enrollable}
onChange={(event) => this.props.handleEventChange("enrollable", event.target.checked)}
// disabled={!this.props.current_event.public}
disabled={true}
/>
<FormControlLabel
......@@ -221,46 +247,36 @@ class EventModalBoard extends Component {
onChange={(event) => this.props.handleEventChange("unenrollable", event.target.checked)}
disabled={!this.props.current_event.enrollable}
/>
{ /*</div>*/ }
{ this.props.current_event.enrollable &&
<div className={classes.wrapper}>
<Wrapper>
<CalendarToday className={classes.icon} color={"action"}/>
<MuiPickersUtilsProvider utils={MomentUtils}>
<KeyboardDateTimePicker
label={"Enrollable from"}
{...pickerEditorProps("enrollable_from")}
value={this.props.current_event.enrollable_from}
onChange={(datetime_util_object) => this.props.handleEventChange("enrollable_from", datetime_util_object._d)}
/>
<KeyboardDateTimePicker
label={"Enrollable until"}
{...pickerEditorProps("enrollable_until")}
value={this.props.current_event.enrollable_until}
onChange={(datetime_util_object) => this.props.handleEventChange("enrollable_until", datetime_util_object._d)}
/>
</MuiPickersUtilsProvider>
</div>
<DateTimeField
name={"Enrollable from"}
value={this.props.current_event.enrollable_from}
onChange={(datetime_util_object) => this.props.handleEventChange("enrollable_from", datetime_util_object._d)}
/>
<DateTimeField
name={"Enrollable until"}
value={this.props.current_event.enrollable_until}
onChange={(datetime_util_object) => this.props.handleEventChange("enrollable_until", datetime_util_object._d)}
/>
</Wrapper>
}
{ this.props.current_event.unenrollable &&
<div className={classes.wrapper}>
<Wrapper>
<CalendarToday className={classes.icon} color={"action"}/>
<MuiPickersUtilsProvider utils={MomentUtils}>
<KeyboardDateTimePicker style={{width: "100%"}}
label={"Unenrollable until"}
{...pickerEditorProps("unenrollable_until")}
value={this.props.current_event.unenrollable_until}
onChange={(datetime_util_object) => this.props.handleEventChange("unenrollable_until", datetime_util_object._d)}
/>
</MuiPickersUtilsProvider>
</div>
<DateTimeField
name={"Unenrollable until"}
value={this.props.current_event.unenrollable_until}
onChange={(datetime_util_object) => this.props.handleEventChange("unenrollable_until", datetime_util_object._d)}
/>
</Wrapper>
}
{ this.props.current_event.enrollable &&
<div className={classes.wrapper}>
<Wrapper>
<LocationOn className={classes.icon} color={"action"}/>
<TextValidator
{...textEditorProps("max_number_of_enrollments")}
type={"number"}
label={"Maximum number of enrollments"}
<NumberField
name={"Maximum number of enrollments"}
value={this.props.current_event.max_number_of_enrollments}
onChange={(event) => this.props.handleEventChange("max_number_of_enrollments", event.target.value)}
InputProps={{
......@@ -269,25 +285,25 @@ class EventModalBoard extends Component {
}
}}
helperText={"A maximum of `0` means no limit to enrollments"}
validators={["required"]}
/>
</div>
</Wrapper>
}
{ this.props.current_event.enrollable &&
<div>
<h5>Enrollment options</h5>
<Divider/>
{ this.props.current_event.enrollment_options && this.props.current_event.enrollment_options.map((enrollment_option, id)=>
<div className={classes.wrapper}>
<Wrapper key={id}>
<Create className={classes.icon} color={"action"} />
<TextValidator
{...textEditorProps("name")}
<TextField
name={"name"}
value={enrollment_option.name}
onChange={(event) => this.props.handleEventEnrollmentOptionChange("name", id, event.target.value)}
/>
<EuroIcon className={classes.icon} color={"action"}/>
<TextValidator
{...textEditorProps("price")}
type={"number"}
<NumberField
name={"price"}
value={enrollment_option.participation_fee}
onChange={(event) => this.props.handleEventEnrollmentOptionChange("participation_fee", id, event.target.value)}
InputProps={{
......@@ -301,7 +317,7 @@ class EventModalBoard extends Component {
<DeleteIcon/>
</IconButton>
}
</div>
</Wrapper>
) }
<div className={classes.addEnrollmentButton}>
<Button variant={"outlined"} color={"primary"} onClick={()=>this.props.handleEventEnrollmentOptionAdd()}>Add enrollment option</Button>
......
......@@ -3,16 +3,16 @@ import "./Groups.css";
import Container from "@material-ui/core/Container";
import { Helper } from "App/Helper";
import PropTypes from "prop-types";
import React, { Component } from "react";
import { NavLink, Redirect , withRouter } from "react-router-dom";
import { NavLink, withRouter } from "react-router-dom";
import TreeMenu from "react-simple-tree-menu";
import { Button } from "reactstrap";
import Block from "../../../Components/PageLayout/Content/Block";
import { GroupRouter } from "./GroupRouter";
export class Boards extends Component {
class Boards extends Component {
constructor(props) {
super(props);
this.state = {
......@@ -39,10 +39,12 @@ export class Boards extends Component {
}
componentDidMount() {
Helper.api_call(this.props.association.url+"/boards?limit=1000", "GET", null,"json",
(data)=>{this.setState({boards: data.results, tree: this.boardArrayToTree(data.results)});
}
);
Helper.api_callv3({
url: "/groups?limit=1000&board_group=true&association__slug="+this.props.association.slug,
method: "GET",
on_succes: (data)=>{this.setState({boards: data.results, tree: this.boardArrayToTree(data.results)});},
on_failure: ()=>{},
});
}
currentBoardSlug(data) {
......@@ -54,7 +56,7 @@ export class Boards extends Component {
}
render() {
const {association} = this.props;
const { association } = this.props;
let path = this.props.path+"/boards";
return (
......@@ -96,4 +98,8 @@ export class Boards extends Component {
}
}
Boards.propTypes = {
association: PropTypes.object.isRequired
};
export const BoardsWithRouter = withRouter(Boards);
......@@ -3,39 +3,63 @@ import "../Board/Groups.css";
import { makeStyles, useTheme, withStyles } from "@material-ui/core/styles";
import { Helper } from "App/Helper";
import PropTypes from "prop-types";
import React, { Component } from "react";
import { Route , withRouter } from "react-router-dom";
import { Route , useLocation, withRouter } from "react-router-dom";
import { useGet } from "restful-react";
import CardGrid from "../../../Components/Card/CardGrid";
import GroupCard from "../../../Components/Card/GroupCardV2";
import PageContent from "../../../Components/PageLayout/Content/Content";
import Detail from "./Detail";
import Image from "img/default_photo.jpg"; // Import using relative path
const useStyles = theme => ({
photo_header: {
width: "100%",
height: "300px",
position: "absolute",
backgroundPosition: "center center",
backgroundSize: "cover",
backgroundRepeat: "no-repeat",
backgroundImage: `url(${Image})`,
},
header: {
display: "flex",
alignItems: "center",
justifyContent: "center",
lineHeight: "300px",
color: "#fff",
},
sub_photo_container: {