summaryrefslogtreecommitdiffstats
path: root/front/odiparpack/app/components/Calendar
diff options
context:
space:
mode:
Diffstat (limited to 'front/odiparpack/app/components/Calendar')
-rw-r--r--front/odiparpack/app/components/Calendar/AddEvent.js49
-rw-r--r--front/odiparpack/app/components/Calendar/AddEventForm.js178
-rw-r--r--front/odiparpack/app/components/Calendar/DetailEvent.js167
-rw-r--r--front/odiparpack/app/components/Calendar/EventCalendar.js70
-rw-r--r--front/odiparpack/app/components/Calendar/calendar-jss.js118
5 files changed, 582 insertions, 0 deletions
diff --git a/front/odiparpack/app/components/Calendar/AddEvent.js b/front/odiparpack/app/components/Calendar/AddEvent.js
new file mode 100644
index 0000000..ef1f5a5
--- /dev/null
+++ b/front/odiparpack/app/components/Calendar/AddEvent.js
@@ -0,0 +1,49 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import Add from '@material-ui/icons/Add';
+import { Fab, Tooltip } from '@material-ui/core';
+import FloatingPanel from '../Panel/FloatingPanel';
+import AddEventForm from './AddEventForm';
+import styles from './calendar-jss.js';
+
+
+class AddEvent extends React.Component {
+ showResult(values) {
+ setTimeout(() => {
+ this.props.submit(values);
+ }, 500); // simulate server latency
+ }
+
+ render() {
+ const {
+ classes,
+ openForm,
+ closeForm,
+ addEvent
+ } = this.props;
+ const branch = '';
+ return (
+ <div>
+ <Tooltip title="Add New Event">
+ <Fab color="secondary" onClick={() => addEvent()} className={classes.addBtn}>
+ <Add />
+ </Fab>
+ </Tooltip>
+ <FloatingPanel title="Add New Event" openForm={openForm} branch={branch} closeForm={() => closeForm()}>
+ <AddEventForm onSubmit={(values) => this.showResult(values)} />
+ </FloatingPanel>
+ </div>
+ );
+ }
+}
+
+AddEvent.propTypes = {
+ classes: PropTypes.object.isRequired,
+ openForm: PropTypes.bool.isRequired,
+ addEvent: PropTypes.func.isRequired,
+ closeForm: PropTypes.func.isRequired,
+ submit: PropTypes.func.isRequired,
+};
+
+export default withStyles(styles)(AddEvent);
diff --git a/front/odiparpack/app/components/Calendar/AddEventForm.js b/front/odiparpack/app/components/Calendar/AddEventForm.js
new file mode 100644
index 0000000..9edccca
--- /dev/null
+++ b/front/odiparpack/app/components/Calendar/AddEventForm.js
@@ -0,0 +1,178 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import { KeyboardDatePicker, MuiPickersUtilsProvider } from '@material-ui/pickers';
+import MomentUtils from '@date-io/moment';
+import { reduxForm, Field } from 'redux-form/immutable';
+import { connect } from 'react-redux';
+import css from 'ba-styles/Form.scss';
+import { Button, Radio, RadioGroup, FormLabel, FormControlLabel } from '@material-ui/core';
+import { TextFieldRedux } from '../Forms/ReduxFormMUI';
+import styles from './calendar-jss';
+
+
+// validation functions
+const required = value => (value == null ? 'Required' : undefined);
+
+const DateTimePickerRow = props => {
+ const {
+ showErrorsInline,
+ dispatch,
+ input: { onChange, value },
+ meta: { touched, error },
+ ...other
+ } = props;
+
+ const showError = showErrorsInline || touched;
+ return (
+ <MuiPickersUtilsProvider utils={MomentUtils}>
+ <KeyboardDatePicker
+ error={!!(showError && error)}
+ helperText={showError && error}
+ value={value || new Date()}
+ onChange={onChange}
+ disablePast
+ label="DateTimePicker"
+ {...other}
+ />
+ </MuiPickersUtilsProvider>
+ );
+};
+
+DateTimePickerRow.propTypes = {
+ showErrorsInline: PropTypes.bool,
+ dispatch: PropTypes.func,
+ input: PropTypes.object.isRequired,
+ meta: PropTypes.object.isRequired,
+};
+
+const renderRadioGroup = ({ input, ...rest }) => (
+ <RadioGroup
+ {...input}
+ {...rest}
+ valueselected={input.value}
+ onChange={(event, value) => input.onChange(value)}
+ />
+);
+
+renderRadioGroup.propTypes = {
+ input: PropTypes.object.isRequired,
+};
+
+DateTimePickerRow.defaultProps = {
+ showErrorsInline: false,
+ dispatch: () => {},
+};
+
+class AddEventForm extends React.Component {
+ state = {
+ selectedDate: new Date(),
+ }
+
+ onChangeDate = date => {
+ this.setState({ selectedDate: date });
+ }
+
+ saveRef = ref => {
+ this.ref = ref;
+ return this.ref;
+ };
+
+ render() {
+ const {
+ classes,
+ reset,
+ pristine,
+ submitting,
+ handleSubmit,
+ } = this.props;
+ const { selectedDate } = this.state;
+ return (
+ <div>
+ <form onSubmit={handleSubmit}>
+ <section className={css.bodyForm}>
+ <div>
+ <Field
+ name="title"
+ component={TextFieldRedux}
+ placeholder="Event Name"
+ label="Event Name"
+ validate={required}
+ required
+ ref={this.saveRef}
+ className={classes.field}
+ />
+ </div>
+ <div>
+ <Field
+ name="start"
+ component={DateTimePickerRow}
+ placeholder="Start Date"
+ value={selectedDate}
+ onChange={this.onChangeDate}
+ label="Start Date"
+ className={classes.field}
+ />
+ </div>
+ <div>
+ <Field
+ name="end"
+ component={DateTimePickerRow}
+ placeholder="End Date"
+ value={selectedDate}
+ onChange={this.onChangeDate}
+ label="End Date"
+ className={classes.field}
+ />
+ </div>
+ <div className={classes.fieldBasic}>
+ <FormLabel component="label">Label Color</FormLabel>
+ <Field name="hexColor" className={classes.inlineWrap} component={renderRadioGroup}>
+ <FormControlLabel value="F8BBD0" control={<Radio className={classes.redRadio} classes={{ root: classes.redRadio, checked: classes.checked }} />} label="Red" />
+ <FormControlLabel value="C8E6C9" control={<Radio className={classes.greenRadio} classes={{ root: classes.greenRadio, checked: classes.checked }} />} label="Green" />
+ <FormControlLabel value="B3E5FC" control={<Radio className={classes.blueRadio} classes={{ root: classes.blueRadio, checked: classes.checked }} />} label="Blue" />
+ <FormControlLabel value="D1C4E9" control={<Radio className={classes.violetRadio} classes={{ root: classes.violetRadio, checked: classes.checked }} />} label="Violet" />
+ <FormControlLabel value="FFECB3" control={<Radio className={classes.orangeRadio} classes={{ root: classes.orangeRadio, checked: classes.checked }} />} label="Orange" />
+ </Field>
+ </div>
+ </section>
+ <div className={css.buttonArea}>
+ <Button variant="contained" color="secondary" type="submit" disabled={submitting}>
+ Submit
+ </Button>
+ <Button
+ type="button"
+ disabled={pristine || submitting}
+ onClick={reset}
+ >
+ Reset
+ </Button>
+ </div>
+ </form>
+ </div>
+ );
+ }
+}
+
+AddEventForm.propTypes = {
+ classes: PropTypes.object.isRequired,
+ handleSubmit: PropTypes.func.isRequired,
+ reset: PropTypes.func.isRequired,
+ pristine: PropTypes.bool.isRequired,
+ submitting: PropTypes.bool.isRequired,
+};
+
+const AddEventFormRedux = reduxForm({
+ form: 'immutableAddCalendar',
+ enableReinitialize: true,
+})(AddEventForm);
+
+const reducer = 'calendar';
+const AddEventInit = connect(
+ state => ({
+ force: state,
+ initialValues: state.getIn([reducer, 'formValues'])
+ }),
+)(AddEventFormRedux);
+
+export default withStyles(styles)(AddEventInit);
diff --git a/front/odiparpack/app/components/Calendar/DetailEvent.js b/front/odiparpack/app/components/Calendar/DetailEvent.js
new file mode 100644
index 0000000..b25d8b9
--- /dev/null
+++ b/front/odiparpack/app/components/Calendar/DetailEvent.js
@@ -0,0 +1,167 @@
+import React, { Fragment } from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import MoreVertIcon from '@material-ui/icons/MoreVert';
+import Today from '@material-ui/icons/Today';
+import { Typography, IconButton, Menu, MenuItem, Divider, Popover } from '@material-ui/core';
+import styles from './calendar-jss';
+
+
+const ITEM_HEIGHT = 48;
+
+class DetailEvent extends React.Component {
+ state = {
+ anchorElOpt: null,
+ };
+
+ handleClickOpt = event => {
+ this.setState({ anchorElOpt: event.currentTarget });
+ };
+
+ handleCloseOpt = () => {
+ this.setState({ anchorElOpt: null });
+ };
+
+ handleDeleteEvent = (event) => {
+ this.setState({ anchorElOpt: null });
+ this.props.remove(event);
+ this.props.close();
+ };
+
+ render() {
+ const getDate = date => {
+ if (date._isAMomentObject) {
+ return date.format('MMMM Do YYYY');
+ }
+ let dd = date.getDate();
+ const monthNames = [
+ 'January', 'February', 'March', 'April', 'May', 'June',
+ 'July', 'August', 'September', 'October', 'November', 'December'
+ ];
+ const mm = monthNames[date.getMonth()]; // January is 0!
+ const yyyy = date.getFullYear();
+
+ if (dd < 10) {
+ dd = '0' + dd;
+ }
+
+ const convertedDate = mm + ', ' + dd + ' ' + yyyy;
+
+ return convertedDate;
+ };
+
+ const getTime = time => {
+ if (time._isAMomentObject) {
+ return time.format('LT');
+ }
+ let h = time.getHours();
+ let m = time.getMinutes();
+
+ if (h < 10) {
+ h = '0' + h;
+ }
+
+ if (m < 10) {
+ m = '0' + m;
+ }
+
+ const convertedTime = h + ':' + m;
+ return convertedTime;
+ };
+
+ const {
+ classes,
+ anchorEl,
+ event,
+ close,
+ anchorPos
+ } = this.props;
+ const { anchorElOpt } = this.state;
+ return (
+ <Popover
+ open={anchorEl}
+ anchorReference="anchorPosition"
+ anchorPosition={anchorPos}
+ className={classes.eventDetail}
+ onClose={close}
+ anchorOrigin={{
+ vertical: 'bottom',
+ horizontal: 'center',
+ }}
+ transformOrigin={{
+ vertical: 'top',
+ horizontal: 'center',
+ }}
+ >
+ <IconButton
+ aria-label="More"
+ aria-owns={anchorElOpt ? 'long-menu' : null}
+ aria-haspopup="true"
+ className={classes.moreOpt}
+ onClick={this.handleClickOpt}
+ >
+ <MoreVertIcon />
+ </IconButton>
+ {event !== null
+ && (
+ <Fragment>
+ <Menu
+ id="long-menu"
+ anchorEl={anchorElOpt}
+ open={Boolean(anchorElOpt)}
+ onClose={this.handleCloseOpt}
+ PaperProps={{
+ style: {
+ maxHeight: ITEM_HEIGHT * 4.5,
+ width: 200,
+ },
+ }}
+ >
+ <MenuItem onClick={() => this.handleDeleteEvent(event)}>
+ Delete Event
+ </MenuItem>
+ </Menu>
+ <Typography variant="h5" style={{ background: `#${event.hexColor}` }} className={classes.eventName}>
+ <Today />
+ {' '}
+ {event.title}
+ </Typography>
+ <div className={classes.time}>
+ <Typography>
+Start:
+ {getDate(event.start)}
+ {' '}
+-
+ {getTime(event.start)}
+ </Typography>
+ <Divider className={classes.divider} />
+ <Typography>
+End:
+ {getDate(event.end)}
+ {' '}
+-
+ {getTime(event.end)}
+ </Typography>
+ </div>
+ </Fragment>
+ )
+ }
+ </Popover>
+ );
+ }
+}
+
+DetailEvent.propTypes = {
+ classes: PropTypes.object.isRequired,
+ anchorEl: PropTypes.bool.isRequired,
+ anchorPos: PropTypes.object.isRequired,
+ event: PropTypes.object,
+ close: PropTypes.func.isRequired,
+ remove: PropTypes.func.isRequired,
+};
+
+DetailEvent.defaultProps = {
+ event: null,
+};
+
+export default withStyles(styles)(DetailEvent);
diff --git a/front/odiparpack/app/components/Calendar/EventCalendar.js b/front/odiparpack/app/components/Calendar/EventCalendar.js
new file mode 100644
index 0000000..fe7b76e
--- /dev/null
+++ b/front/odiparpack/app/components/Calendar/EventCalendar.js
@@ -0,0 +1,70 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withStyles } from '@material-ui/core/styles';
+import BigCalendar from 'react-big-calendar';
+import moment from 'moment';
+import { Paper } from '@material-ui/core';
+import styles from './calendar-jss';
+
+BigCalendar.setLocalizer(BigCalendar.momentLocalizer(moment));
+
+function Event(event) {
+ return (
+ <span className="eventBlock">{event.title}</span>
+ );
+}
+
+class EventCalendar extends React.Component {
+ eventStyleGetter = event => {
+ const backgroundColor = '#' + event.hexColor;
+ const style = {
+ backgroundColor,
+ };
+ return {
+ style
+ };
+ }
+
+ render() {
+ const allViews = Object.keys(BigCalendar.Views).map(k => BigCalendar.Views[k]);
+ const {
+ classes,
+ events,
+ handleEventClick
+ } = this.props;
+ return (
+ <Paper className={classes.root}>
+ <BigCalendar
+ className={classes.calendarWrap}
+ selectable
+ events={events}
+ defaultView="month"
+ views={allViews}
+ step={60}
+ showMultiDayTimes
+ scrollToTime={new Date(1970, 1, 1, 6)}
+ defaultDate={new Date(2015, 3, 12)}
+ onSelectEvent={(selectedEvent) => handleEventClick(selectedEvent)}
+ eventPropGetter={(this.eventStyleGetter)}
+ onSelectSlot={slotInfo => console.log(
+ `selected slot: \n\nstart ${slotInfo.start.toLocaleString()} `
+ + `\nend: ${slotInfo.end.toLocaleString()}`
+ + `\naction: ${slotInfo.action}`
+ )
+ }
+ components={{
+ event: Event
+ }}
+ />
+ </Paper>
+ );
+ }
+}
+
+EventCalendar.propTypes = {
+ classes: PropTypes.object.isRequired,
+ events: PropTypes.array.isRequired,
+ handleEventClick: PropTypes.func.isRequired,
+};
+
+export default withStyles(styles)(EventCalendar);
diff --git a/front/odiparpack/app/components/Calendar/calendar-jss.js b/front/odiparpack/app/components/Calendar/calendar-jss.js
new file mode 100644
index 0000000..36ed0a4
--- /dev/null
+++ b/front/odiparpack/app/components/Calendar/calendar-jss.js
@@ -0,0 +1,118 @@
+import {
+ pink as red,
+ lightGreen as green,
+ lightBlue as blue,
+ deepPurple as violet,
+ orange,
+} from '@material-ui/core/colors';
+
+const styles = theme => ({
+ root: {
+ padding: 20,
+ [theme.breakpoints.down('sm')]: {
+ padding: '20px 8px'
+ },
+ },
+ calendarWrap: {
+ minHeight: 600
+ },
+ addBtn: {
+ position: 'fixed',
+ bottom: 30,
+ right: 30,
+ zIndex: 100
+ },
+ typography: {
+ margin: theme.spacing(2),
+ },
+ divider: {
+ margin: '5px 0',
+ textAlign: 'center'
+ },
+ button: {
+ margin: theme.spacing(1),
+ },
+ eventName: {
+ padding: '50px 20px 10px 30px',
+ minWidth: 400,
+ color: 'rgba(0, 0, 0, 0.7)',
+ '& svg': {
+ top: -2,
+ position: 'relative'
+ }
+ },
+ time: {
+ padding: 20
+ },
+ moreOpt: {
+ position: 'absolute',
+ top: 10,
+ right: 10
+ },
+ field: {
+ width: '100%',
+ marginBottom: 20
+ },
+ fieldBasic: {
+ width: '100%',
+ marginBottom: 20,
+ marginTop: 10
+ },
+ inlineWrap: {
+ display: 'flex',
+ flexDirection: 'row'
+ },
+ redRadio: {
+ color: red[600],
+ '& svg': {
+ borderRadius: '50%',
+ background: red[100],
+ },
+ '&$checked': {
+ color: red[500],
+ },
+ },
+ greenRadio: {
+ color: green[600],
+ '& svg': {
+ borderRadius: '50%',
+ background: green[100],
+ },
+ '&$checked': {
+ color: green[500],
+ },
+ },
+ blueRadio: {
+ color: blue[600],
+ '& svg': {
+ borderRadius: '50%',
+ background: blue[100],
+ },
+ '&$checked': {
+ color: blue[500],
+ },
+ },
+ violetRadio: {
+ color: violet[600],
+ '& svg': {
+ borderRadius: '50%',
+ background: violet[100],
+ },
+ '&$checked': {
+ color: violet[500],
+ },
+ },
+ orangeRadio: {
+ color: orange[600],
+ '& svg': {
+ borderRadius: '50%',
+ background: orange[100],
+ },
+ '&$checked': {
+ color: orange[500],
+ },
+ },
+ checked: {},
+});
+
+export default styles;