Commit 4856959e authored by TJHeeringa's avatar TJHeeringa

Various fixes

parent f4c3dcdf
......@@ -41,6 +41,7 @@
"react-hooks/rules-of-hooks": "error",
"react-hooks/exhaustive-deps": "warn",
"react/default-props-match-prop-types": [1, { "allowRequiredDefaults": false }],
"react/require-default-props": [1, { "forbidDefaultForRequired": false, "ignoreFunctionalComponents": false }],
"react/display-name": 0,
"react/no-children-prop": 2,
"react/prop-types": 1,
......
This diff is collapsed.
......@@ -2,7 +2,18 @@ import "@icon/open-iconic/open-iconic.css";
import { library } from "@fortawesome/fontawesome-svg-core";
import { fab } from "@fortawesome/free-brands-svg-icons";
import { faBars,faCheck,faChevronLeft,faHome,faIgloo,faPlus,faSignOutAlt,faTimes,faUser } from "@fortawesome/free-solid-svg-icons";
import {
faBars,
faCheck,
faChevronLeft, faExchangeAlt,
faHome,
faIgloo, faNewspaper,
faPlus, faShoppingCart,
faSignOutAlt, faSocks,
faTimes,
faUser,
faWarehouse
} from "@fortawesome/free-solid-svg-icons";
import CssBaseline from "@material-ui/core/CssBaseline";
import React from "react";
import { BrowserRouter as Router } from "react-router-dom";
......@@ -16,7 +27,7 @@ import Routing from "./Routing/Routing";
require("bootstrap");
library.add(fab,faSignOutAlt, faUser, faIgloo, faBars, faCheck, faTimes, faHome, faPlus, faChevronLeft);
library.add(fab,faSignOutAlt, faUser, faIgloo, faBars, faCheck, faTimes, faHome, faPlus, faChevronLeft, faWarehouse, faShoppingCart, faSocks, faExchangeAlt, faNewspaper);
const App = (props) => {
......
import { makeStyles } from "@material-ui/core/styles";
import { KeyboardDatePicker } from "@material-ui/pickers";
import moment from "moment";
import PropTypes from "prop-types";
import React from "react";
import DateTimeField from "./DateTimeField";
const DateField = (props) => {
return <DateTimeField
apiFormat={"YYYY-MM-DD"}
format={"YYYY-MM-DD"}
{...props}
/>;
const useStyles = makeStyles(theme => ({
picker: {
marginRight: theme.spacing(2),
"&:last-child": {
marginRight: 0,
},
width: "100%",
},
}));
const DateTimeField = ({name, value, onChange, apiFormat, ...remaining_props}) => {
const classes = useStyles();
const pickerEditorProps = field => ({
className: classes.picker,
ampm: false,
inputVariant: "outlined",
label: field[0].toUpperCase() + field.slice(1).replaceAll("_", " "),
});
const handleChange = (datetime_util_object) => {
onChange(datetime_util_object === null ? null : datetime_util_object.format(apiFormat));
};
return (
<KeyboardDatePicker
{...pickerEditorProps(name)}
{...remaining_props}
onChange={handleChange}
value={value && moment(value)}
/>
);
};
DateTimeField.propTypes = {
name: PropTypes.string.isRequired,
value: PropTypes.string,
onChange: PropTypes.func.isRequired,
apiFormat: PropTypes.string,
format: PropTypes.string,
};
DateField.propTypes = {
DateTimeField.defaultProps = {
apiFormat: "YYYY-MM-DD",
format: "YYYY-MM-DD"
};
export default DateField;
\ No newline at end of file
export default DateTimeField;
\ No newline at end of file
import { makeStyles, useTheme } from "@material-ui/core/styles";
import { KeyboardDatePicker } from "@material-ui/pickers";
import { KeyboardDateTimePicker } from "@material-ui/pickers";
import moment from "moment";
import PropTypes from "prop-types";
import React from "react";
......@@ -16,8 +16,7 @@ const useStyles = makeStyles(theme => ({
}));
const DateTimeField = ({name, value, onChange, apiFormat, ...remaining_props}) => {
const theme = useTheme();
const classes = useStyles(theme);
const classes = useStyles();
const pickerEditorProps = field => ({
className: classes.picker,
......@@ -31,7 +30,7 @@ const DateTimeField = ({name, value, onChange, apiFormat, ...remaining_props}) =
};
return (
<KeyboardDatePicker
<KeyboardDateTimePicker
{...pickerEditorProps(name)}
{...remaining_props}
onChange={handleChange}
......
......@@ -9,10 +9,8 @@ const useStyles = makeStyles(theme => ({
},
}));
export const FileField = (props) => {
const theme = useTheme();
const classes = useStyles(theme);
const {name, ...remaining_props} = props;
const FileField = ({name, ...remaining_props}) => {
const classes = useStyles();
const textEditorProps = field => ({
variant: "outlined",
......@@ -31,7 +29,7 @@ export const FileField = (props) => {
};
FileField.propTypes = {
name: PropTypes.string
name: PropTypes.string.isRequired
};
export default FileField;
\ No newline at end of file
import { makeStyles, useTheme } from "@material-ui/core/styles";
import PropTypes from "prop-types";
import React from "react";
import {SelectValidator, TextValidator} from "react-material-ui-form-validator";
import {TextValidator} from "react-material-ui-form-validator";
const useStyles = makeStyles(theme => ({
textField: {
......
import Button from "@material-ui/core/Button";
import { makeStyles, useTheme } from "@material-ui/core/styles";
import Typography from "@material-ui/core/Typography";
import AccountTreeIcon from "@material-ui/icons/AccountTree";
import AssignmentLateIcon from "@material-ui/icons/AssignmentLate";
......@@ -10,15 +9,19 @@ import DescriptionIcon from "@material-ui/icons/Description";
import EmailIcon from "@material-ui/icons/Email";
import EventAvailableIcon from "@material-ui/icons/EventAvailable";
import EventBusyIcon from "@material-ui/icons/EventBusy";
import FacebookIcon from "@material-ui/icons/Facebook";
import GavelIcon from "@material-ui/icons/Gavel";
import LineStyleIcon from "@material-ui/icons/LineStyle";
import LinkedInIcon from "@material-ui/icons/LinkedIn";
import LocationCityIcon from "@material-ui/icons/LocationCity";
import WhatsAppIcon from '@material-ui/icons/WhatsApp';
import MarkunreadMailboxIcon from "@material-ui/icons/MarkunreadMailbox";
import MeetingRoomIcon from "@material-ui/icons/MeetingRoom";
import InstagramIcon from "@material-ui/icons/Instagram";
import PhoneIcon from "@material-ui/icons/Phone";
import PhotoIcon from "@material-ui/icons/Photo";
import PhotoSizeSelectLargeIcon from "@material-ui/icons/PhotoSizeSelectLarge";
import RoomServiceIcon from "@material-ui/icons/RoomService";
import WhatsAppIcon from "@material-ui/icons/WhatsApp";
import DateField from "App/Components/Fields/DateField";
import FileField from "App/Components/Fields/FileField";
import IconHolder from "App/Components/Fields/IconHolder";
......@@ -30,18 +33,11 @@ import { Helper } from "App/Helper";
import PropTypes from "prop-types";
import React, { useState } from "react";
import { ValidatorForm } from "react-material-ui-form-validator";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import FontAwesomeIconHolder from "../Fields/FontAwesomeIconHolder";
import FontAwesomeIconHolder from "../Fields/FontAwesomeIconHolder";
const useStyles = makeStyles(theme => ({
}));
export const AssociationForm = (props) => {
const theme = useTheme();
const classes = useStyles(theme);
const alertHandler = useAlertHandler();
let [association, setAssociation] = useState({...props.association});
......@@ -146,6 +142,39 @@ export const AssociationForm = (props) => {
onChange={(event)=>handleAssociationChange("discord",event.target.value)}
/>
</Wrapper>
<Wrapper>
<IconHolder Icon={InstagramIcon}/>
<TextField
name={"instagram"}
value={association.instagram}
onChange={(event)=>handleAssociationChange("instagram",event.target.value)}
/>
</Wrapper>
<Wrapper>
<IconHolder Icon={FacebookIcon}/>
<TextField
name={"facebook"}
value={association.facebook}
onChange={(event)=>handleAssociationChange("facebook",event.target.value)}
/>
</Wrapper>
<Wrapper>
<FontAwesomeIconHolder icon={["fab", "snapchat"]}/>
<TextField
name={"snapchat"}
value={association.snapchat}
onChange={(event)=>handleAssociationChange("snapchat",event.target.value)}
/>
</Wrapper>
<Wrapper>
<IconHolder Icon={LinkedInIcon}/>
<TextField
name={"LinkedIn"}
value={association.linkedin}
onChange={(event)=>handleAssociationChange("linkedin",event.target.value)}
/>
</Wrapper>
<Typography variant={"h5"}>Visiting Location</Typography>
<Wrapper>
<IconHolder Icon={AccountTreeIcon}/>
......@@ -171,6 +200,15 @@ export const AssociationForm = (props) => {
onChange={(event)=>handleAssociationChange("address",event.target.value)}
/>
</Wrapper>
<Wrapper>
<IconHolder Icon={MeetingRoomIcon}/>
<TextField
name={"Room"}
value={association.room}
onChange={(event)=>handleAssociationChange("room",event.target.value)}
/>
</Wrapper>
<Typography variant={"h5"}>Postal</Typography>
<Wrapper>
<IconHolder Icon={AccountTreeIcon}/>
......@@ -233,7 +271,6 @@ export const AssociationForm = (props) => {
/>
</Wrapper>
<Typography variant={"h5"}>Legal</Typography>
<Wrapper>
<IconHolder Icon={AssignmentLateIcon}/>
......@@ -273,7 +310,7 @@ export const AssociationForm = (props) => {
<Wrapper>
<div>
<Button variant={"outlined"} disabled color={"secondary"}>
<Button variant={"outlined"} color={"secondary"} disabled>
Delete
</Button>
</div>
......
import Button from "@material-ui/core/Button";
import DraftsIcon from "@material-ui/icons/Drafts";
import SubjectIcon from "@material-ui/icons/Subject";
import IconHolder from "App/Components/Fields/IconHolder";
import TextField from "App/Components/Fields/TextField";
import Wrapper from "App/Components/Fields/Wrapper";
import { useAlertHandler } from "App/Contexts/AlertHandler";
import PropTypes from "prop-types";
import React, { useState } from "react";
import { ValidatorForm } from "react-material-ui-form-validator";
import {useAPI} from "../../Contexts/API";
const EmailTemplateForm = ({ association, emailTemplate, onTemplateChange}) => {
const API = useAPI();
const alertHandler = useAlertHandler();
let [template, setTemplate] = useState(emailTemplate);
const resetAssociation = () => setTemplate(emailTemplate);
const handleTemplateChange = (field, value) => setTemplate(prevState=>(
{...prevState, [field]: value}
));
const handleSubmit = () => {
API.callv4({
url: association.url,
method: "PATCH",
object: {
[template.what_it_is_for+"_subject"]: template.subject,
[template.what_it_is_for+"_message"]: template.message,
},
on_succes: (association_with_updated_template) => {
onTemplateChange(association_with_updated_template);
},
on_failure: () => {
alertHandler.handleAlertHandler("red", "Update template failed");
}
});
};
return (
<ValidatorForm
onSubmit={handleSubmit}
onError={errors => console.log(errors)}
>
<Wrapper>
<IconHolder Icon={SubjectIcon}/>
<TextField
name={"subject"}
value={template.subject}
onChange={(event)=>handleTemplateChange("subject",event.target.value)}
placeholder={"You have been matched"}
/>
</Wrapper>
<Wrapper>
<IconHolder Icon={DraftsIcon}/>
<TextField
name={"message"}
value={template.message}
onChange={(event)=>handleTemplateChange("message",event.target.value)}
placeholder={"Hi {MEMBER}\n\nYou have been matched with {MEMBER} by {ASSOCIATION}. You can contact your match by email using {EMAIL}.\n\nKind regards,\n\nThe MySU team"}
multiline
/>
</Wrapper>
<Wrapper>
<div>
<Button variant={"outlined"} color={"secondary"}>
Reset to default
</Button>
</div>
<div>
<Button type={"submit"} variant={"contained"} color={"primary"}>Save</Button>
&nbsp;
<Button variant={"contained"} onClick={resetAssociation}>Cancel</Button>
</div>
</Wrapper>
</ValidatorForm>
);
};
EmailTemplateForm.propTypes = {
association: PropTypes.object.isRequired,
onTemplateChange: PropTypes.func.isRequired,
emailTemplate: PropTypes.shape({
what_it_is_for: PropTypes.string,
subject: PropTypes.string,
message: PropTypes.string,
}).isRequired,
};
export default EmailTemplateForm;
\ No newline at end of file
import Button from "@material-ui/core/Button";
import Create from "@material-ui/icons/Create";
import EuroIcon from "@material-ui/icons/Euro";
import Notes from "@material-ui/icons/Notes";
import PropTypes from "prop-types";
import React, {useEffect, useState} from "react";
import {ValidatorForm} from "react-material-ui-form-validator";
import {useAlertHandler} from "../../Contexts/AlertHandler";
import {useAPI} from "../../Contexts/API";
import IconHolder from "../Fields/IconHolder";
import NumberField from "../Fields/NumberField";
import TextField from "../Fields/TextField";
import Wrapper from "../Fields/Wrapper";
const EnrollmentOptionForm = ({ enrollment_option: initEnrollmentOption, association, calendarEvent, update, onSucces, onCancel}) => {
const API = useAPI();
const alerthandler = useAlertHandler();
const [enrollmentOption, setEnrollmentOption] = useState(initEnrollmentOption);
useEffect(()=>setEnrollmentOption(initEnrollmentOption),[initEnrollmentOption]);
const handleEnrollmentOptionChange = (field, value) => setEnrollmentOption(prevState => ({...prevState, [field]: value}));
console.log(calendarEvent);
const postEnrollmentOption = () => {
API.callv4({
url: "/enrollment_options",
method: "POST",
object: {...enrollmentOption, association: association.url, event: calendarEvent.url},
on_succes: (response) => {
onSucces(response);
alerthandler.handleAlertHandler("success", "Enrollment option created successfully");
},
on_failure: () => {
alerthandler.handleAlertHandler("red", "Enrollment option creation failed");
}
});
};
const patchEnrollmentOption = () => {
API.callv4({
url: enrollmentOption.url,
method: "PATCH",
object: enrollmentOption,
on_succes: (response) => {
onSucces(response);
alerthandler.handleAlertHandler("success", "Enrollment option updated successfully");
},
on_failure: () => {
alerthandler.handleAlertHandler("red", "Updating enrollment option failed");
}
});
};
const handleSubmit = () => {
if (update) {
patchEnrollmentOption();
} else {
postEnrollmentOption();
}
};
return (
<ValidatorForm
onSubmit={handleSubmit}
onError={errors => console.log(errors)}
>
<Wrapper>
<IconHolder Icon={Create}/>
<TextField
name={"name"}
value={enrollmentOption.name}
onChange={(event) => handleEnrollmentOptionChange("name", event.target.value)}
validators={["required"]}
errorMessages={["Every option must have a name", "Other option has already this name; Options must have unique names"]}
required
/>
<IconHolder Icon={EuroIcon}/>
<NumberField
name={"price"}
value={enrollmentOption.participation_fee}
onChange={(event) => handleEnrollmentOptionChange("participation_fee", event.target.value)}
InputProps={{
inputProps: {
step: 0.1, min: 0
}
}}
required
/>
</Wrapper>
<Wrapper>
<IconHolder Icon={Notes}/>
<TextField
name={"Description"}
value={enrollmentOption.description}
onChange={(event) => handleEnrollmentOptionChange("description", event.target.value)}
multiline
/>
</Wrapper>
<Wrapper>
<div>
<Button variant={"outlined"} disabled color={"secondary"}>
Delete
</Button>
</div>
<div>
<Button type={"submit"} variant={"contained"} color={"primary"}>Save</Button>
&nbsp;
<Button variant={"contained"} onClick={onCancel}>Cancel</Button>
</div>
</Wrapper>
</ValidatorForm>
);
};
EnrollmentOptionForm.propTypes = {
enrollment_option: PropTypes.object,
update: PropTypes.bool.isRequired,
association: PropTypes.object.isRequired,
calendarEvent: PropTypes.object.isRequired,
onSucces: PropTypes.func,
onCancel: PropTypes.func,
};
EnrollmentOptionForm.defaultProps = {
enrollment_option: {
name: "",
participation_fee: 0,
description: ""
}
};
export default EnrollmentOptionForm;
\ No newline at end of file
import Button from "@material-ui/core/Button";
import FormControlLabel from "@material-ui/core/FormControlLabel";
import MenuItem from "@material-ui/core/MenuItem";
import { makeStyles, useTheme } from "@material-ui/core/styles";
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";
import LocationOn from "@material-ui/icons/LocationOn";
import Notes from "@material-ui/icons/Notes";
import IconHolder from "App/Components/Fields/IconHolder";
import TextField from "App/Components/Fields/TextField";
import Wrapper from "App/Components/Fields/Wrapper";
import { useAlertHandler } from "App/Contexts/AlertHandler";
import PropTypes from "prop-types";
import React from "react";
import { ValidatorForm } from "react-material-ui-form-validator";
import {useGet} from "restful-react";
import {useAPI} from "../../Contexts/API";
import DateTimeField from "../Fields/DateTimeField";
import NumberField from "../Fields/NumberField";
import SelectField from "../Fields/SelectField";
const useStyles = makeStyles(theme => ({
}));
export const EventForm = ({ association, event, handleEventChange, update, onSucces, onCancel}) => {
const classes = useStyles();
const API = useAPI();
const alerthandler = useAlertHandler();
let { data: event_types, loading: loadingStudies, refetch } = useGet({
path: "/event_types",
queryParams: {limit: 100, association__slug: association.slug},
resolve: data => data && data.results
});
const handleSubmit = () => {
// make a copy of the event so that the null key operation don't accidentally change the event prop
let toBeSentEvent = {...event, permissions: []};
// Drop all field with value null
let null_keys = [];
Object.entries(event).forEach(([key, value])=> {
if (value === null) {
null_keys.push(key);
}
});
null_keys.forEach(null_key=> delete toBeSentEvent[null_key]);
if (update) {
patchEvent(toBeSentEvent);
} else {
postEvent(toBeSentEvent);
}
};
const postEvent = (event) => {
return API.callv4({
url: "/events",
method: "POST",
object: event,
on_succes: (data) => {
alerthandler.handleAlertHandler("green", "Save successful");
onSucces(data);
},
on_failure: (data) => {
alerthandler.handleAlertHandler("red", "Save failed");
}
});
};