Commit 59070d6b authored by TJHeeringa's avatar TJHeeringa

Buttload of changes

parent 8143c9dc
......@@ -8,10 +8,9 @@ import React from "react";
import { BrowserRouter as Router } from "react-router-dom";
import AlertHandlerProvider from "./Contexts/AlertHandler";
import Authenticator from "./Contexts/Authentication";
import API from "./Contexts/API";
import LocaleProvider from "./Contexts/Locale";
import PageLayoutProvider from "./Contexts/PageLayout";
import RestProvider from "./Contexts/RestProvider";
import ThemeProvider from "./Contexts/Theme";
import Routing from "./Routing/Routing";
......@@ -25,18 +24,16 @@ const App = (props) => {
<div className={"App"}>
<Router>
<AlertHandlerProvider>
<Authenticator>
<API>
<ThemeProvider>
<CssBaseline/>
<LocaleProvider>
<PageLayoutProvider>
<RestProvider>
<Routing/>
</RestProvider>
<Routing/>
</PageLayoutProvider>
</LocaleProvider>
</ThemeProvider>
</Authenticator>
</API>
</AlertHandlerProvider>
</Router>
</div>
......
import Button from "@material-ui/core/Button";
import PropTypes from "prop-types";
import React from "react";
const EventAgenda = ({ event, handleEventSelect }) => {
return (
<span>
<b>{ event.name }</b>
<p>{ event.description }</p>
<Button color={"primary"} onClick={()=>handleEventSelect(event)}>Read More</Button>
</span>
);
};
EventAgenda.propTypes = {
handleEventSelect: PropTypes.func.isRequired,
event: PropTypes.object.isRequired,
};
export default EventAgenda;
\ No newline at end of file
import PropTypes from "prop-types";
import React from "react";
const Event = ({ event }) => {
return (
<span>
<strong>{ event.name }</strong>
</span>
);
};
Event.propTypes = {
event: PropTypes.object.isRequired
};
export default Event;
\ No newline at end of file
import PropTypes from "prop-types";
import React, { Component } from "react";
import React from "react";
import { Link } from "react-router-dom";
import { Button, Card, CardBody, CardSubtitle, CardText,CardTitle } from "reactstrap";
import Poll from "restful-react";
......
......@@ -2,6 +2,15 @@ import React from "react";
import DateTimeField from "./DateTimeField";
export const DateField = (props) => {return <DateTimeField format={"YYYY/MM/DD"} {...props}/>;};
const DateField = (props) => {
return <DateTimeField
apiFormat={"YYYY-MM-DD"}
format={"YYYY-MM-DD"}
{...props}
/>;
};
DateField.propTypes = {
};
export default DateField;
\ No newline at end of file
import { makeStyles, useTheme } from "@material-ui/core/styles";
import { KeyboardDatePicker } from "@material-ui/pickers";
import moment from "moment";
import PropTypes from "prop-types";
import React from "react";
......@@ -14,33 +15,41 @@ const useStyles = makeStyles(theme => ({
},
}));
export const DateTimeField = (props) => {
const DateTimeField = ({name, value, onChange, apiFormat, ...remaining_props}) => {
const theme = useTheme();
const classes = useStyles(theme);
const {name, format, ...remaining_props} = props;
const pickerEditorProps = field => ({
className: classes.picker,
ampm: false,
inputVariant: "outlined",
format: format,
label: field[0].toUpperCase() + field.slice(1).replaceAll("_", " "),
});
const handleChange = (datetime_util_object) => {
onChange(datetime_util_object.format(apiFormat));
};
return (
<KeyboardDatePicker
{...pickerEditorProps(name)}
{...remaining_props}
onChange={handleChange}
value={value && moment(value)}
/>
);
};
DateTimeField.propTypes = {
name: PropTypes.string.isRequired,
format: PropTypes.string
value: PropTypes.string,
onChange: PropTypes.func.isRequired,
apiFormat: PropTypes.string,
format: PropTypes.string,
};
DateTimeField.defaultProps = {
apiFormat: "YYYY/MM/DD HH:mm",
format: "YYYY/MM/DD HH:mm"
};
......
import MenuItem from "@material-ui/core/MenuItem";
import { makeStyles, useTheme } from "@material-ui/core/styles";
import PropTypes from "prop-types";
import React from "react";
......@@ -9,10 +10,9 @@ const useStyles = makeStyles(theme => ({
},
}));
const SelectField = (props) => {
const SelectField = ({name, children, value, ...remaining_props}) => {
const theme = useTheme();
const classes = useStyles(theme);
const {name, children, ...remaining_props} = props;
const selectEditorProps = field => ({
variant: "outlined",
......@@ -24,15 +24,18 @@ const SelectField = (props) => {
return (
<SelectValidator
{...selectEditorProps(name)}
value={value === null ? "" : value}
{...remaining_props}
>
{ props.children }
{ children || <MenuItem/> }
</SelectValidator>
);
};
SelectField.propTypes = {
name: PropTypes.string
name: PropTypes.string.isRequired,
value: PropTypes.any,
children: PropTypes.node
};
export default SelectField;
\ No newline at end of file
......@@ -56,8 +56,8 @@ const SpecificDataField = (props) =>{
onChange={(event)=>onChange(field.url, event.target.value)}
{...rest_props}
>
<MenuItem value={true}>Yes</MenuItem>
<MenuItem value={false}>No</MenuItem>
<MenuItem value={"True"}>Yes</MenuItem>
<MenuItem value={"False"}>No</MenuItem>
</SelectField>
);
}
......
import { makeStyles, useTheme } from "@material-ui/core/styles";
import PropTypes from "prop-types";
import React from "react";
import {TextValidator} from "react-material-ui-form-validator";
import {SelectValidator, TextValidator} from "react-material-ui-form-validator";
const useStyles = makeStyles(theme => ({
textField: {
......@@ -9,10 +9,9 @@ const useStyles = makeStyles(theme => ({
},
}));
const TextField = (props) => {
const TextField = ({name, value, ...remaining_props}) => {
const theme = useTheme();
const classes = useStyles(theme);
const {name, ...remaining_props} = props;
const textEditorProps = field => ({
variant: "outlined",
......@@ -24,13 +23,15 @@ const TextField = (props) => {
return (
<TextValidator
{...textEditorProps(name)}
value={value === null ? "" : value}
{...remaining_props}
/>
);
};
TextField.propTypes = {
name: PropTypes.string
name: PropTypes.string.isRequired,
value: PropTypes.any
};
export default TextField;
\ No newline at end of file
......@@ -20,9 +20,9 @@ import PhotoSizeSelectLargeIcon from "@material-ui/icons/PhotoSizeSelectLarge";
import RoomServiceIcon from "@material-ui/icons/RoomService";
import DateField from "App/Components/Fields/DateField";
import FileField from "App/Components/Fields/FileField";
import IconHolder from "App/Components/Fields/IconHolder";
import TextField from "App/Components/Fields/TextField";
import Wrapper from "App/Components/Fields/Wrapper";
import IconHolder from "App/Components/Fields/IconHolder";
import YearlessDateField from "App/Components/Fields/YearlessDateField";
import { useAlertHandler } from "App/Contexts/AlertHandler";
import { Helper } from "App/Helper";
......@@ -57,9 +57,6 @@ export const AssociationForm = (props) => {
(props.association.terms_of_service === association.terms_of_service) && delete to_be_patched_association.terms_of_service;
Helper.api_call(props.association.url, "PATCH", to_be_patched_association, "form-data",
(patched_association)=>{
delete patched_association.membership;
delete patched_association.board;
delete patched_association.committees;
props.handleAssociationChange(patched_association);
alertHandler.handleAlertHandler("green", "Save successful");
},
......@@ -175,8 +172,8 @@ export const AssociationForm = (props) => {
<DateField
name={"founding_date"}
value={association.founding_date}
onChange={datetime_util_object => handleAssociationChange("founding_date", datetime_util_object.toDate())}
helperText={"This is the date you are planning on collection the debt collection."}
onChange={date => handleAssociationChange("founding_date", date)}
helperText={""}
disableFuture
/>
</Wrapper>
......@@ -185,7 +182,7 @@ export const AssociationForm = (props) => {
<DateField
name={"dissolution_date"}
value={association.dissolution_date}
onChange={datetime_util_object => handleAssociationChange("dissolution_date", datetime_util_object.toDate())}
onChange={date => handleAssociationChange("dissolution_date", date)}
disabled
helperText={"Connect the SU if you want to dissolve your association."}
/>
......@@ -196,7 +193,7 @@ export const AssociationForm = (props) => {
<Wrapper>
<IconHolder Icon={AssignmentLateIcon}/>
<FileField
onChange={(event)=>handleAssociationChange("privacy_statement",event.target.files[0])}
onChange={(event)=>handleAssociationChange("privacy_statement", event.target.files[0])}
name={"Privacy Statement"}
/>
</Wrapper>
......@@ -204,7 +201,7 @@ export const AssociationForm = (props) => {
<IconHolder Icon={RoomServiceIcon}/>
<FileField
name={"Terms of Service"}
onChange={(event)=>handleAssociationChange("terms_of_service",event.target.files[0])}
onChange={(event)=>handleAssociationChange("terms_of_service", event.target.files[0])}
/>
</Wrapper>
......
.membership_form_label_narrow {
font-size: 1rem !important;
}
.membership_form_col_narrow {
padding-left: 0px !important;
}
\ No newline at end of file
This diff is collapsed.
import Button from "@material-ui/core/Button";
import Container from "@material-ui/core/Container";
import MenuItem from "@material-ui/core/MenuItem";
import Typography from "@material-ui/core/Typography";
import { Osiris } from "App/Components/Forms/Osiris";
import { Helper } from "App/Helper";
import PropTypes from "prop-types";
import React from "react";
import { ValidatorForm } from "react-material-ui-form-validator";
import { Button, Col, Row } from "reactstrap";
import { useGet } from "restful-react";
import { v4 as uuidv4 } from "uuid";
import Container from "@material-ui/core/Container";
import { useAlertHandler} from "../../Contexts/AlertHandler";
import {useAPI} from "../../Contexts/API";
import DateField from "../Fields/DateField";
import SelectField from "../Fields/SelectField";
import TextField from "../Fields/TextField";
import Wrapper from "../Fields/Wrapper";
import Block from "../PageLayout/Content/Block";
import CheckboxField from "../Fields/CheckboxField";
const ProfileForm = ({container, ...rest}) => {
......@@ -61,9 +61,10 @@ const ProfileFormWithContainer = (props) => {
const ProfileFormWithoutContainer = ({ update, profile, onSuccess, disabled, handleProfileChange }) => {
const alerthandler = useAlertHandler();
const API = useAPI();
let { data: studies, loading: loadingStudies, refetch } = useGet({
path: process.env.REACT_APP_API_URL + "/studies",
path: "/studies",
queryParams: {limit: 10000},
resolve: data => data && data.results
});
......@@ -77,22 +78,32 @@ const ProfileFormWithoutContainer = ({ update, profile, onSuccess, disabled, han
};
const postProfile = (profile) => {
return Helper.api_call(process.env.REACT_APP_API_URL + "/profiles", "POST", profile, "json",
(data)=>{
return API.callv3({
url: "/profiles",
method: "POST",
object: profile,
on_succes: (data) => {
alerthandler.handleAlertHandler("green", "Save successful");
onSuccess(data);
},
(data)=>{
on_failure: (data) => {
alerthandler.handleAlertHandler("red", "Save failed");
}
);
});
};
const patchProfile = (profile) => {
return Helper.api_call(profile.url, "PUT", profile, "json",
()=>{alerthandler.handleAlertHandler("green", "Save successful");},
()=>{alerthandler.handleAlertHandler("red", "Save failed");},
);
return API.callv3({
url: profile.url,
method: "PUT",
object: profile,
on_succes: () => {
alerthandler.handleAlertHandler("green", "Save successful");
},
on_failure: () => {
alerthandler.handleAlertHandler("red", "Save failed");
},
});
};
return (
......@@ -107,6 +118,7 @@ const ProfileFormWithoutContainer = ({ update, profile, onSuccess, disabled, han
value={profile.given_name}
onChange={(event) => handleProfileChange("given_name", event.target.value)}
disabled={disabled}
required
/>
</Wrapper>
<Wrapper>
......@@ -115,6 +127,7 @@ const ProfileFormWithoutContainer = ({ update, profile, onSuccess, disabled, han
value={profile.first_names}
onChange={(event) => handleProfileChange("first_names", event.target.value)}
disabled={disabled}
required
/>
</Wrapper>
<Wrapper>
......@@ -133,17 +146,16 @@ const ProfileFormWithoutContainer = ({ update, profile, onSuccess, disabled, han
onChange={(event) => handleProfileChange("gender", event.target.value)}
disabled={disabled}
>
<MenuItem></MenuItem>
<MenuItem>Other</MenuItem>
<MenuItem>Female</MenuItem>
<MenuItem>Male</MenuItem>
<MenuItem value={"Other"}>Other</MenuItem>
<MenuItem value={"Female"}>Female</MenuItem>
<MenuItem value={"Male"}>Male</MenuItem>
</SelectField>
</Wrapper>
<Wrapper>
<DateField
name={"Date of birth"}
value={profile.date_of_birth}
onChange={(event) => handleProfileChange("date_of_birth", event.target.value)}
onChange={date => handleProfileChange("date_of_birth", date)}
disabled={disabled}
required
/>
......@@ -163,6 +175,8 @@ const ProfileFormWithoutContainer = ({ update, profile, onSuccess, disabled, han
name={"Email"}
value={profile.email}
onChange={(event) => handleProfileChange("email", event.target.value)}
validators={["isEmail"]}
errorMessages={["This is not a valid email"]}
disabled={disabled}
required
/>
......@@ -173,6 +187,7 @@ const ProfileFormWithoutContainer = ({ update, profile, onSuccess, disabled, han
value={profile.address}
onChange={(event) => handleProfileChange("address", event.target.value)}
disabled={disabled}
required
/>
</Wrapper>
<Wrapper>
......@@ -181,6 +196,7 @@ const ProfileFormWithoutContainer = ({ update, profile, onSuccess, disabled, han
value={profile.zip_code}
onChange={(event) => handleProfileChange("zip_code", event.target.value)}
disabled={disabled}
required
/>
</Wrapper>
<Wrapper>
......@@ -189,6 +205,7 @@ const ProfileFormWithoutContainer = ({ update, profile, onSuccess, disabled, han
value={profile.city}
onChange={(event) => handleProfileChange("city", event.target.value)}
disabled={disabled}
required
/>
</Wrapper>
<Typography variant={"h5"}>Financial</Typography>
......@@ -197,7 +214,7 @@ const ProfileFormWithoutContainer = ({ update, profile, onSuccess, disabled, han
name={"IBAN"}
value={profile.iban}
onChange={(event) => handleProfileChange("iban", event.target.value)}
helpMessage={"Enter the IBAN without spaces, so NL12RABO0123456789 instead of NL12 RABO 0123 4567 89."}
helperText={"Enter the IBAN without spaces, so NL12RABO0123456789 instead of NL12 RABO 0123 4567 89."}
disabled={disabled}
required
/>
......@@ -206,7 +223,7 @@ const ProfileFormWithoutContainer = ({ update, profile, onSuccess, disabled, han
<TextField
name={"BIC"}
value={profile.bic_code}
helpMessage={"This will be derived from your IBAN. This will be entered automatically when the IBAN is valid."}
helperText={"This will be derived from your IBAN. This will be entered automatically when the IBAN is valid."}
disabled
required
/>
......@@ -230,17 +247,18 @@ const ProfileFormWithoutContainer = ({ update, profile, onSuccess, disabled, han
onChange={(event) => handleProfileChange("study", event.target.value)}
disabled={disabled}
>
<MenuItem value={undefined}>Not listed</MenuItem>
{ studies && studies.map(study =>
<MenuItem key={uuidv4()} value={study.url}>{ study.studycode } -- { study.name_en }</MenuItem>
<MenuItem value={null}>Not Listed</MenuItem>
{ studies && studies.map((study, s) =>
<MenuItem key={s} value={study.url}>{ study.studycode } -- { study.name_en }</MenuItem>
) }
</SelectField>
</Wrapper>
{ !disabled && <Button color={"primary"} outline block type={"submit"}>Save profile</Button> }
{ !disabled && <Button color={"primary"} variant={"outlined"} type={"submit"}>Save profile</Button> }
</ValidatorForm>
);
};
ProfileFormWithoutContainer.propTypes = {
update: PropTypes.bool.isRequired,
profile: PropTypes.object.isRequired,
......@@ -248,6 +266,7 @@ ProfileFormWithoutContainer.propTypes = {
onSuccess: PropTypes.func,
disabled: PropTypes.bool,
};
ProfileFormWithoutContainer.defaultProps = {
onSuccess: ()=>{},
disabled: false
......
import {Helper} from "App/Helper";
import {AvField,AvForm, AvInput} from "availity-reactstrap-validation";
import React, { Component } from "react";
import {Button, Col, FormGroup, Row} from "reactstrap";
import { v4 as uuidv4 } from "uuid";
import Button from "@material-ui/core/Button";
import PropTypes from "prop-types";
import React, { useState } from "react";
import {ValidatorForm} from "react-material-ui-form-validator";
import {useGet} from "restful-react";
import {AlertHandlerContext} from "../../Contexts/AlertHandler";
import {useAlertHandler} from "../../Contexts/AlertHandler";
import {useAPI} from "../../Contexts/API";
import SpecificDataField from "../Fields/SpecificDataField";
import Wrapper from "../Fields/Wrapper";
export class AssociationSpecificDataForm extends Component {
handleValidSubmit = (event, values) => {
let {association_specific_data} = this.props;
association_specific_data.value = values.value;
this.patchSpecificData(association_specific_data);
};
export const SpecificDataForm = ({ specificData: propSpecificData, association }) => {
// We need to save the specific data in a state to be able to use it in a form.
// The number of fields is not always the same. We could create a state for each.
// However having a dynamic number of state variables leads to all sorts of problems.
// Hence we do some manipulation to save all in a single state variable.
// To save the data in a single state variable we need to map it to a single object
// This changes [{...}, {...},...] to ['field1', 'field1',...]
const data_field_slugs = propSpecificData.map(dataField=>dataField.data_field);
// This changes ['field1', 'field1',...] to {'field1': {...}, 'field1': {...},...}
const init_for_state = data_field_slugs.reduce(
(options, option) => ({
...options,
[option]: {...propSpecificData.find(field=>field.data_field===option)}
}),
{}
);
const [specificData, setSpecificData] = useState(init_for_state);
patchSpecificData(association_specific_data){
Helper.api_call(association_specific_data.url, "PATCH", association_specific_data, "json",
()=>{this.context.handleAlertHandler("green", "Save successful");},
()=>{this.context.handleAlertHandler("red", "Save failed");});
}
// updating all data even if the use changes only one is silly.
// Create a list that keeps track of which data has changes and which has not.
// This creates an object {'field1': false, 'field1': false,...}
const init_for_change_array = data_field_slugs.reduce(
(change_array, slug) => ({
...change_array,
[slug]: false
}),
{}
);
const [changeArray, setChangeArray] = useState(init_for_change_array);
getDataField(specific_data){
return this.props.association_specific_data_fields.filter((field)=>{return specific_data.data_field === field.url;})[0];
}
const alerthandler = useAlertHandler();
const API = useAPI();
getType(data_field){
let type_dict = {
"Boolean": "select",
"Number": "number",
"String": "text",
"Choice": "select",
};
return type_dict[data_field.type];
}
let { data: dataFields, loading: loadingDataFields } = useGet({
path: "/association_data_fields",
queryParams: {limit: 50, association__slug: association.slug},
resolve: data => data && data.results
});
getChoices(data_field){
return data_field.choices.split(",");
}
const handleSubmit = () => {
alerthandler.handleAlertHandler("info", "Not implemented");
// TODO: write this one
// API.callv3({
// url: membership.url,
// method: "PATCH",
// object: membership,
// on_succes: (data) => {
// alerthandler.handleAlertHandler("green", "Saved membership");
// },
// on_failure: (err) => {
// alerthandler.handleAlertHandler("red", "Saving of membership went wrong");
// console.error(err);