Javascript 在日历中编辑表单

Javascript 在日历中编辑表单,javascript,reactjs,redux,google-calendar-api,Javascript,Reactjs,Redux,Google Calendar Api,我有一个用React和Redux构建的日历。通过单击任何空日期,将显示允许添加某些事件的模式。 问题是,我不知道如何通过单击该事件来编辑该事件。什么是正确的算法 这是日历页代码 /* eslint-disable no-unused-vars */ import React, { PropTypes } from 'react'; import moment from 'moment-timezone'; import Helmet from 'react-helmet'; import _

我有一个用React和Redux构建的日历。通过单击任何空日期,将显示允许添加某些事件的模式。 问题是,我不知道如何通过单击该事件来编辑该事件。什么是正确的算法


这是日历页代码

/* eslint-disable no-unused-vars */

import React, { PropTypes } from 'react';
import moment from 'moment-timezone';
import Helmet from 'react-helmet';
import _ from 'lodash';
import { connect } from 'react-redux';
import { createStructuredSelector } from 'reselect';
import { GoogleLogin, GoogleLogout } from 'react-google-login';
import { reduxForm, reset } from 'redux-form';

import BigCalendar from 'react-big-calendar';
import withDragAndDrop from 'react-big-calendar/lib/addons/dragAndDrop';

import 'react-big-calendar/lib/less/styles.less';
import 'react-big-calendar/lib/addons/dragAndDrop/styles.less';

import AddCalendarEventForm from '../../../app/components/AddCalendarEventForm';

import { translate } from '../../../common/utilities/localization';

import {
  selectCurrentUser,
  selectCurrentGoogleUser,
} from '../../containers/App/selectors';

import {
  submitGoogleAuth,
  fetchGoogleCalendarEvents,
  editGoogleCalendarEvent,
  addGoogleCalendarEvent,
} from './actions';

import {
  selectGoogleAuth,
  selectCalendarEvents,
  selectAddEventProcess,
} from './selectors';

const formName = 'addCalendarEvent';

const DragAndDropCalendar = withDragAndDrop(BigCalendar);
const localizer = BigCalendar.momentLocalizer(moment);

const mapStateToProps = createStructuredSelector({
  currentUser: selectCurrentUser(),
  currentGoogleUser: selectCurrentGoogleUser(),
  googleAuth: selectGoogleAuth(),
  calendarEvents: selectCalendarEvents(),
  addEventProcess: selectAddEventProcess(),
});

const mapDispatchToProps = (dispatch) => ({
  submitGoogleAuth: (externalUserId, googleToken) => dispatch(submitGoogleAuth(externalUserId, googleToken)),
  fetchGoogleCalendarEvents: (data) => dispatch(fetchGoogleCalendarEvents(data)),
  editGoogleCalendarEvent: (data) => dispatch(editGoogleCalendarEvent(data)),
  addGoogleCalendarEvent: (data) => dispatch(addGoogleCalendarEvent(data)),
  resetForm: () => dispatch(reset(formName)),
});

@reduxForm({
  form: formName,
})
@connect(mapStateToProps, mapDispatchToProps)
export default class CalendarPage extends React.Component {
  static propTypes = {
    currentUser: PropTypes.any,
    currentGoogleUser: PropTypes.any,
    submitGoogleAuth: PropTypes.func.isRequired,
    googleAuth: PropTypes.object,
    fetchGoogleCalendarEvents: PropTypes.func,
    calendarEvents: PropTypes.object,
    editGoogleCalendarEvent: PropTypes.func,
    addGoogleCalendarEvent: PropTypes.func,
    addEventProcess: PropTypes.object,
    resetForm: PropTypes.func,
  };

  constructor(props) {
    super(props);

    this.state = {
      events: [],
      show: null,
      calendarEvent: null,
    };

    this.onSuccess = this.onSuccess.bind(this);
    this.onFailure = this.onFailure.bind(this);
    this.moveEvent = this.moveEvent.bind(this);
    this.newEvent = this.newEvent.bind(this);
    this.showEventModal = this.showEventModal.bind(this);
    this.hideEventModal = this.hideEventModal.bind(this);
  }

  componentDidMount() {
    const { currentUser, currentGoogleUser } = this.props;

    if (currentGoogleUser && currentGoogleUser.expires_at && moment(currentGoogleUser.expires_at).isAfter(moment())) {
      this.props.fetchGoogleCalendarEvents({ ...currentGoogleUser, userId: currentUser.id });
    }
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.currentGoogleUser !== this.props.currentGoogleUser) {
      this.props.fetchGoogleCalendarEvents({ ...nextProps.currentGoogleUser, userId: nextProps.currentUser.id });
    }

    if (nextProps.calendarEvents && nextProps.calendarEvents.details) {
      const events = [];
      for (const item of nextProps.calendarEvents.details.items) {
        if (item.start && item.end) {
          events.push({
            id: item.id,
            title: item.summary,
            start: moment(item.start.dateTime || item.start.date),
            end: moment(item.end.dateTime || item.end.date),
          });
        }
      }
      this.setState({ events });
    }

    if (!nextProps.addEventProcess.isSubmitting && this.props.addEventProcess.isSubmitting) {
      this.hideEventModal();
    }
  }

  onSuccess(ev) {
    const { submitGoogleAuth, currentUser } = this.props;
    submitGoogleAuth(currentUser.id, { ...ev.tokenObj, profileEmail: ev.profileObj.email });
  }

  onFailure(ev) {
    console.log('onFailure', ev);
  }

  moveEvent({ event, start, end, isAllDay: droppedOnAllDaySlot }) {
    const { currentUser, editGoogleCalendarEvent, calendarEvents } = this.props;
    const { events } = this.state;
    let onlyDate = false;

    const idx = events.indexOf(event);
    const eventIndex = _.findIndex(calendarEvents.details.items, { id: event.id });
    let allDay = event.allDay;
    if (!event.allDay && droppedOnAllDaySlot) {
      allDay = true;
    } else if (event.allDay && !droppedOnAllDaySlot) {
      allDay = false;
    }

    const updatedEvent = { ...event, start, end, allDay };

    const nextEvents = [...events];
    nextEvents.splice(idx, 1, updatedEvent);
    if (eventIndex !== -1) {
      const item = calendarEvents.details.items[eventIndex];
      if (item.start.date && item.end.date) {
        updatedEvent.start = moment(start).format('YYYY-MM-DD');
        updatedEvent.end = moment(end).format('YYYY-MM-DD');
        onlyDate = true;
      }
    }

    this.setState({
      events: nextEvents,
    }, () => {
      editGoogleCalendarEvent({ ...updatedEvent, userId: currentUser.id, timezone: currentUser.timezone, onlyDate });
    });
  }

  resizeEvent = ({ event, start, end }) => {
    const { events } = this.state;

    const nextEvents = events.map(existingEvent => {
      return existingEvent.id === event.id
        ? { ...existingEvent, start, end }
        : existingEvent;
    });

    this.setState({
      events: nextEvents,
    });

    // console.log(`${event.title} was resized to ${start}-${end}`);
  }

  newEvent(params) {
    const { currentUser, addGoogleCalendarEvent } = this.props;
    const { event, formValues } = params;

    const newEvent = {
      title: formValues.title,
      description: formValues.description ? formValues.description : null,
      allDay: event.slots.length === 1,
      start: moment(event.start).hours(formValues.period === 'AM' ? formValues.hour % 12 : (formValues.hour % 12) + 12).minutes(formValues.minute).toISOString(),
      end: moment(event.end).hours(formValues.period === 'AM' ? formValues.hour % 12 : (formValues.hour % 12) + 12).minutes(formValues.minute).toISOString(),
    };
    this.setState({
      calendarEvent: null,
    }, () => {
      addGoogleCalendarEvent({ ...newEvent, userId: currentUser.id, timezone: currentUser.timezone });
    });
  }

  showEventModal(event) {
    this.setState({ calendarEvent: event, show: true });
  }

  hideEventModal() {
    const { resetForm } = this.props;
    this.setState({ show: false, calendarEvent: null }, () => {
      resetForm();
    });
  }

  render() {
    const { currentGoogleUser, addEventProcess } = this.props;
    let authorized = false;

    if (currentGoogleUser && currentGoogleUser.expires_at) {
      authorized = moment(currentGoogleUser.expires_at).isAfter(moment());
    }

    return (
      <div>
        <div className="container-fluid">
          <Helmet title={translate('portals.page.calendarPage.helmetTitle')} />
          <section className="calendar-section">
            <h2 className="main-heading">{translate('portals.page.calendarPage.pageTitle')}</h2>
            {!authorized &&
              <GoogleLogin
                clientId={GOOGLE_CLIENT_ID}
                scope="https://www.googleapis.com/auth/calendar"
                className="google-login"
                onSuccess={this.onSuccess}
                onFailure={this.onFailure}
              >
                <i className="google-image" />
                <span> Sign in with Google</span>
              </GoogleLogin>
            }
            {authorized &&
              <DragAndDropCalendar
                selectable
                events={this.state.events}
                localizer={localizer}
                onEventDrop={this.moveEvent}
                resizable
                onEventResize={this.resizeEvent}
                onSelectSlot={this.showEventModal}
                onSelectEvent={(e) => { console.log('e', e); }}
                defaultView={BigCalendar.Views.MONTH}
                defaultDate={new Date()}
                views={{ month: true }}
              />
            }

            <AddCalendarEventForm
              show={this.state.show}
              isSubmitting={addEventProcess.isSubmitting}
              calendarEvent={this.state.calendarEvent}
              onSubmit={this.newEvent}
              onHide={this.hideEventModal}
            />
          </section>
        </div>
      </div>
    );
  }
}
/* eslint-disable no-unused-vars */

import React, { PropTypes } from 'react';
import moment from 'moment-timezone';
import Helmet from 'react-helmet';
import _ from 'lodash';
import { connect } from 'react-redux';
import { createStructuredSelector } from 'reselect';
import { GoogleLogin, GoogleLogout } from 'react-google-login';
import { reduxForm, reset } from 'redux-form';

import BigCalendar from 'react-big-calendar';
import withDragAndDrop from 'react-big-calendar/lib/addons/dragAndDrop';

import 'react-big-calendar/lib/less/styles.less';
import 'react-big-calendar/lib/addons/dragAndDrop/styles.less';

import AddCalendarEventForm from '../../../app/components/AddCalendarEventForm';

import { translate } from '../../../common/utilities/localization';

import {
  selectCurrentUser,
  selectCurrentGoogleUser,
} from '../../containers/App/selectors';

import {
  submitGoogleAuth,
  fetchGoogleCalendarEvents,
  editGoogleCalendarEvent,
  addGoogleCalendarEvent,
} from './actions';

import {
  selectGoogleAuth,
  selectCalendarEvents,
  selectAddEventProcess,
} from './selectors';

const formName = 'addCalendarEvent';

const DragAndDropCalendar = withDragAndDrop(BigCalendar);
const localizer = BigCalendar.momentLocalizer(moment);

const mapStateToProps = createStructuredSelector({
  currentUser: selectCurrentUser(),
  currentGoogleUser: selectCurrentGoogleUser(),
  googleAuth: selectGoogleAuth(),
  calendarEvents: selectCalendarEvents(),
  addEventProcess: selectAddEventProcess(),
});

const mapDispatchToProps = (dispatch) => ({
  submitGoogleAuth: (externalUserId, googleToken) => dispatch(submitGoogleAuth(externalUserId, googleToken)),
  fetchGoogleCalendarEvents: (data) => dispatch(fetchGoogleCalendarEvents(data)),
  editGoogleCalendarEvent: (data) => dispatch(editGoogleCalendarEvent(data)),
  addGoogleCalendarEvent: (data) => dispatch(addGoogleCalendarEvent(data)),
  resetForm: () => dispatch(reset(formName)),
});

@reduxForm({
  form: formName,
})
@connect(mapStateToProps, mapDispatchToProps)
export default class CalendarPage extends React.Component {
  static propTypes = {
    currentUser: PropTypes.any,
    currentGoogleUser: PropTypes.any,
    submitGoogleAuth: PropTypes.func.isRequired,
    googleAuth: PropTypes.object,
    fetchGoogleCalendarEvents: PropTypes.func,
    calendarEvents: PropTypes.object,
    editGoogleCalendarEvent: PropTypes.func,
    addGoogleCalendarEvent: PropTypes.func,
    addEventProcess: PropTypes.object,
    resetForm: PropTypes.func,
  };

  constructor(props) {
    super(props);

    this.state = {
      events: [],
      show: null,
      calendarEvent: null,
    };

    this.onSuccess = this.onSuccess.bind(this);
    this.onFailure = this.onFailure.bind(this);
    this.moveEvent = this.moveEvent.bind(this);
    this.newEvent = this.newEvent.bind(this);
    this.showEventModal = this.showEventModal.bind(this);
    this.hideEventModal = this.hideEventModal.bind(this);
  }

  componentDidMount() {
    const { currentUser, currentGoogleUser } = this.props;

    if (currentGoogleUser && currentGoogleUser.expires_at && moment(currentGoogleUser.expires_at).isAfter(moment())) {
      this.props.fetchGoogleCalendarEvents({ ...currentGoogleUser, userId: currentUser.id });
    }
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.currentGoogleUser !== this.props.currentGoogleUser) {
      this.props.fetchGoogleCalendarEvents({ ...nextProps.currentGoogleUser, userId: nextProps.currentUser.id });
    }

    if (nextProps.calendarEvents && nextProps.calendarEvents.details) {
      const events = [];
      for (const item of nextProps.calendarEvents.details.items) {
        if (item.start && item.end) {
          events.push({
            id: item.id,
            title: item.summary,
            start: moment(item.start.dateTime || item.start.date),
            end: moment(item.end.dateTime || item.end.date),
          });
        }
      }
      this.setState({ events });
    }

    if (!nextProps.addEventProcess.isSubmitting && this.props.addEventProcess.isSubmitting) {
      this.hideEventModal();
    }
  }

  onSuccess(ev) {
    const { submitGoogleAuth, currentUser } = this.props;
    submitGoogleAuth(currentUser.id, { ...ev.tokenObj, profileEmail: ev.profileObj.email });
  }

  onFailure(ev) {
    console.log('onFailure', ev);
  }

  moveEvent({ event, start, end, isAllDay: droppedOnAllDaySlot }) {
    const { currentUser, editGoogleCalendarEvent, calendarEvents } = this.props;
    const { events } = this.state;
    let onlyDate = false;

    const idx = events.indexOf(event);
    const eventIndex = _.findIndex(calendarEvents.details.items, { id: event.id });
    let allDay = event.allDay;
    if (!event.allDay && droppedOnAllDaySlot) {
      allDay = true;
    } else if (event.allDay && !droppedOnAllDaySlot) {
      allDay = false;
    }

    const updatedEvent = { ...event, start, end, allDay };

    const nextEvents = [...events];
    nextEvents.splice(idx, 1, updatedEvent);
    if (eventIndex !== -1) {
      const item = calendarEvents.details.items[eventIndex];
      if (item.start.date && item.end.date) {
        updatedEvent.start = moment(start).format('YYYY-MM-DD');
        updatedEvent.end = moment(end).format('YYYY-MM-DD');
        onlyDate = true;
      }
    }

    this.setState({
      events: nextEvents,
    }, () => {
      editGoogleCalendarEvent({ ...updatedEvent, userId: currentUser.id, timezone: currentUser.timezone, onlyDate });
    });
  }

  resizeEvent = ({ event, start, end }) => {
    const { events } = this.state;

    const nextEvents = events.map(existingEvent => {
      return existingEvent.id === event.id
        ? { ...existingEvent, start, end }
        : existingEvent;
    });

    this.setState({
      events: nextEvents,
    });

    // console.log(`${event.title} was resized to ${start}-${end}`);
  }

  newEvent(params) {
    const { currentUser, addGoogleCalendarEvent } = this.props;
    const { event, formValues } = params;

    const newEvent = {
      title: formValues.title,
      description: formValues.description ? formValues.description : null,
      allDay: event.slots.length === 1,
      start: moment(event.start).hours(formValues.period === 'AM' ? formValues.hour % 12 : (formValues.hour % 12) + 12).minutes(formValues.minute).toISOString(),
      end: moment(event.end).hours(formValues.period === 'AM' ? formValues.hour % 12 : (formValues.hour % 12) + 12).minutes(formValues.minute).toISOString(),
    };
    this.setState({
      calendarEvent: null,
    }, () => {
      addGoogleCalendarEvent({ ...newEvent, userId: currentUser.id, timezone: currentUser.timezone });
    });
  }

  showEventModal(event) {
    this.setState({ calendarEvent: event, show: true });
  }

  hideEventModal() {
    const { resetForm } = this.props;
    this.setState({ show: false, calendarEvent: null }, () => {
      resetForm();
    });
  }

  render() {
    const { currentGoogleUser, addEventProcess } = this.props;
    let authorized = false;

    if (currentGoogleUser && currentGoogleUser.expires_at) {
      authorized = moment(currentGoogleUser.expires_at).isAfter(moment());
    }

    return (
      <div>
        <div className="container-fluid">
          <Helmet title={translate('portals.page.calendarPage.helmetTitle')} />
          <section className="calendar-section">
            <h2 className="main-heading">{translate('portals.page.calendarPage.pageTitle')}</h2>
            {!authorized &&
              <GoogleLogin
                clientId={GOOGLE_CLIENT_ID}
                scope="https://www.googleapis.com/auth/calendar"
                className="google-login"
                onSuccess={this.onSuccess}
                onFailure={this.onFailure}
              >
                <i className="google-image" />
                <span> Sign in with Google</span>
              </GoogleLogin>
            }
            {authorized &&
              <DragAndDropCalendar
                selectable
                events={this.state.events}
                localizer={localizer}
                onEventDrop={this.moveEvent}
                resizable
                onEventResize={this.resizeEvent}
                onSelectSlot={this.showEventModal}
                onSelectEvent={(e) => { console.log('e', e); }}
                defaultView={BigCalendar.Views.MONTH}
                defaultDate={new Date()}
                views={{ month: true }}
              />
            }

            <AddCalendarEventForm
              show={this.state.show}
              isSubmitting={addEventProcess.isSubmitting}
              calendarEvent={this.state.calendarEvent}
              onSubmit={this.newEvent}
              onHide={this.hideEventModal}
            />
          </section>
        </div>
      </div>
    );
  }
}
/*eslint禁用未使用的变量*/
从“React”导入React,{PropTypes};
从“时刻时区”导入时刻;
从“反应头盔”导入头盔;
从“lodash”进口;
从'react redux'导入{connect};
从“重新选择”导入{createStructuredSelector};
从'react google login'导入{GoogleLogin,GoogleLogout};
从'redux form'导入{reduxForm,reset};
从“react big calendar”导入BigCalendar;
使用dragAndDrop从“react big calendar/lib/addons/dragAndDrop”导入;
导入“react big calendar/lib/less/styles.less”;
导入“react big calendar/lib/addons/dragandrop/styles.less”;
从“../../../app/components/AddCalendarEventForm”导入AddCalendarEventForm;
从“../../../common/utilities/localization”导入{translate};
进口{
选择当前用户,
选择当前谷歌用户,
}来自“../../containers/App/selectors”;
进口{
提交给谷歌,
获取Google日历事件,
编辑谷歌日历事件,
添加谷歌日历事件,
}来自“./操作”;
进口{
选择Googleauth,
选择日历事件,
选择AddEventProcess,
}从“./选择器”;
const formName='addCalendarEvent';
常量DragAndDropCalendar=带DragandDrop(大日历);
常量定位器=BigCalendar.momentLocalizer(矩);
const mapStateToProps=createStructuredSelector({
currentUser:selectCurrentUser(),
currentGoogleUser:选择currentGoogleUser(),
googleAuth:选择googleAuth(),
calendarEvents:selectCalendarEvents(),
addEventProcess:选择addEventProcess(),
});
const mapDispatchToProps=(调度)=>({
submitGoogleAuth:(externalUserId,googleToken)=>dispatch(submitGoogleAuth(externalUserId,googleToken)),
fetchGoogleCalendarEvents:(数据)=>dispatch(fetchGoogleCalendarEvents(数据)),
editGoogleCalendarEvent:(数据)=>dispatch(editGoogleCalendarEvent(数据)),
addGoogleCalendarEvent:(数据)=>dispatch(addGoogleCalendarEvent(数据)),
resetForm:()=>调度(reset(formName)),
});
@红肿({
表格:formName,,
})
@连接(MapStateTrops、mapDispatchToProps)
导出默认类CalendarPage扩展React.Component{
静态类型={
当前用户:PropTypes.any,
currentGoogleUser:PropTypes.any,
submitGoogleAuth:PropTypes.func.isRequired,
googleAuth:PropTypes.object,
fetchGoogleCalendarEvents:PropTypes.func,
calendarEvents:PropTypes.object,
editGoogleCalendarEvent:PropTypes.func,
addGoogleCalendarEvent:PropTypes.func,
addEventProcess:PropTypes.object,
resetForm:PropTypes.func,
};
建造师(道具){
超级(道具);
此.state={
事件:[],
show:null,
calendarEvent:null,
};
this.onSuccess=this.onSuccess.bind(this);
this.onFailure=this.onFailure.bind(this);
this.moveEvent=this.moveEvent.bind(this);
this.newEvent=this.newEvent.bind(this);
this.showEventModal=this.showEventModal.bind(this);
this.hideEventModal=this.hideEventModal.bind(this);
}
componentDidMount(){
const{currentUser,currentGoogleUser}=this.props;
if(currentGoogleUser&¤tGoogleUser.expires_在&&moment(currentGoogleUser.expires_在).isAfter(矩()){
this.props.fetchGoogleCalendarEvents({…currentGoogleUser,userId:currentUser.id});
}
}
组件将接收道具(下一步){
if(nextrops.currentGoogleUser!==this.props.currentGoogleUser){
this.props.fetchGoogleCalendarEvents({…nextrops.currentGoogleUser,userId:nextrops.currentUser.id});
}
if(nextrops.calendarEvents&&nextrops.calendarEvents.details){
常量事件=[];
for(nextProps.calendarEvents.details.items的常量项){
if(item.start&&item.end){
事件。推({
id:item.id,
标题:项目摘要,
开始:时刻(item.start.dateTime | | item.start.date),
结束:时刻(item.end.dateTime | | item.end.date),
});
}
}
this.setState({events});
}
if(!nextrops.addEventProcess.isSubmitting&&this.props.addEventProcess.isSubmitting){
this.hideEventModal();
}
}
成功(ev){
const{submitGoogleAuth,currentUser}=this.props;
submitGoogleAuth(currentUser.id,{…ev.tokenObj,profileEmail:ev.profileObj.email});
}
无故障(ev){
控制台日志('onFailure',ev);
}
moveEvent({event,start,end,isAllDay:droppedOnAllDaySlot}){
const{currentUser,editGoogleCalendarEvent,calendarEvents}=this.props;
const{events}=this.state;
让onlyDate=假;
const idx=events.indexOf(event);
const eventIndex=u.findIndex(calendarEvents.details.items,{id:event.id});
让allDay=event.allDay;
如果(!event.allDay&&droppedonaldayslot){
全天=真;
}else if(event.allDay&!droppedonaldayslot){
全天=假;
}
const updateEvent={…事件,开始,结束,全天};
const nextEvents=[…事件];
拼接(idx,1,UpdateEvent);
如果(eventIndex!=-1){
const item=calendarEvents.details.items[eventIndex];
if(item.start.date&&item.end.date){
updateEvent.start=时刻(开始).format('YYYY-MM-DD');
updateEvent.end=力矩(end).format('YYYY-MM-DD');
onlyDate=true;
}
}
这是我的国家({
事件:nextEvents,
}, () => {
editGoogleCalendarEvent({…UpdateEvent,用户id:currentUser.id,时区:currentUser.timezone,onlyDate});
});
}
resizeEvent=({event,start,end})=>{
const{events}=this.state;
const nextEvents=events.map(existingEvent=>{
返回existingEvent.id==event.id
现有的
/* eslint-disable no-unused-vars */

import React, { PropTypes } from 'react';
import moment from 'moment-timezone';
import Helmet from 'react-helmet';
import _ from 'lodash';
import { connect } from 'react-redux';
import { createStructuredSelector } from 'reselect';
import { GoogleLogin, GoogleLogout } from 'react-google-login';
import { reduxForm, reset } from 'redux-form';

import BigCalendar from 'react-big-calendar';
import withDragAndDrop from 'react-big-calendar/lib/addons/dragAndDrop';

import 'react-big-calendar/lib/less/styles.less';
import 'react-big-calendar/lib/addons/dragAndDrop/styles.less';

import AddCalendarEventForm from '../../../app/components/AddCalendarEventForm';

import { translate } from '../../../common/utilities/localization';

import {
  selectCurrentUser,
  selectCurrentGoogleUser,
} from '../../containers/App/selectors';

import {
  submitGoogleAuth,
  fetchGoogleCalendarEvents,
  editGoogleCalendarEvent,
  addGoogleCalendarEvent,
} from './actions';

import {
  selectGoogleAuth,
  selectCalendarEvents,
  selectAddEventProcess,
} from './selectors';

const formName = 'addCalendarEvent';

const DragAndDropCalendar = withDragAndDrop(BigCalendar);
const localizer = BigCalendar.momentLocalizer(moment);

const mapStateToProps = createStructuredSelector({
  currentUser: selectCurrentUser(),
  currentGoogleUser: selectCurrentGoogleUser(),
  googleAuth: selectGoogleAuth(),
  calendarEvents: selectCalendarEvents(),
  addEventProcess: selectAddEventProcess(),
});

const mapDispatchToProps = (dispatch) => ({
  submitGoogleAuth: (externalUserId, googleToken) => dispatch(submitGoogleAuth(externalUserId, googleToken)),
  fetchGoogleCalendarEvents: (data) => dispatch(fetchGoogleCalendarEvents(data)),
  editGoogleCalendarEvent: (data) => dispatch(editGoogleCalendarEvent(data)),
  addGoogleCalendarEvent: (data) => dispatch(addGoogleCalendarEvent(data)),
  resetForm: () => dispatch(reset(formName)),
});

@reduxForm({
  form: formName,
})
@connect(mapStateToProps, mapDispatchToProps)
export default class CalendarPage extends React.Component {
  static propTypes = {
    currentUser: PropTypes.any,
    currentGoogleUser: PropTypes.any,
    submitGoogleAuth: PropTypes.func.isRequired,
    googleAuth: PropTypes.object,
    fetchGoogleCalendarEvents: PropTypes.func,
    calendarEvents: PropTypes.object,
    editGoogleCalendarEvent: PropTypes.func,
    addGoogleCalendarEvent: PropTypes.func,
    addEventProcess: PropTypes.object,
    resetForm: PropTypes.func,
  };

  constructor(props) {
    super(props);

    this.state = {
      events: [],
      show: null,
      calendarEvent: null,
    };

    this.onSuccess = this.onSuccess.bind(this);
    this.onFailure = this.onFailure.bind(this);
    this.moveEvent = this.moveEvent.bind(this);
    this.newEvent = this.newEvent.bind(this);
    this.showEventModal = this.showEventModal.bind(this);
    this.hideEventModal = this.hideEventModal.bind(this);
  }

  componentDidMount() {
    const { currentUser, currentGoogleUser } = this.props;

    if (currentGoogleUser && currentGoogleUser.expires_at && moment(currentGoogleUser.expires_at).isAfter(moment())) {
      this.props.fetchGoogleCalendarEvents({ ...currentGoogleUser, userId: currentUser.id });
    }
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.currentGoogleUser !== this.props.currentGoogleUser) {
      this.props.fetchGoogleCalendarEvents({ ...nextProps.currentGoogleUser, userId: nextProps.currentUser.id });
    }

    if (nextProps.calendarEvents && nextProps.calendarEvents.details) {
      const events = [];
      for (const item of nextProps.calendarEvents.details.items) {
        if (item.start && item.end) {
          events.push({
            id: item.id,
            title: item.summary,
            start: moment(item.start.dateTime || item.start.date),
            end: moment(item.end.dateTime || item.end.date),
          });
        }
      }
      this.setState({ events });
    }

    if (!nextProps.addEventProcess.isSubmitting && this.props.addEventProcess.isSubmitting) {
      this.hideEventModal();
    }
  }

  onSuccess(ev) {
    const { submitGoogleAuth, currentUser } = this.props;
    submitGoogleAuth(currentUser.id, { ...ev.tokenObj, profileEmail: ev.profileObj.email });
  }

  onFailure(ev) {
    console.log('onFailure', ev);
  }

  moveEvent({ event, start, end, isAllDay: droppedOnAllDaySlot }) {
    const { currentUser, editGoogleCalendarEvent, calendarEvents } = this.props;
    const { events } = this.state;
    let onlyDate = false;

    const idx = events.indexOf(event);
    const eventIndex = _.findIndex(calendarEvents.details.items, { id: event.id });
    let allDay = event.allDay;
    if (!event.allDay && droppedOnAllDaySlot) {
      allDay = true;
    } else if (event.allDay && !droppedOnAllDaySlot) {
      allDay = false;
    }

    const updatedEvent = { ...event, start, end, allDay };

    const nextEvents = [...events];
    nextEvents.splice(idx, 1, updatedEvent);
    if (eventIndex !== -1) {
      const item = calendarEvents.details.items[eventIndex];
      if (item.start.date && item.end.date) {
        updatedEvent.start = moment(start).format('YYYY-MM-DD');
        updatedEvent.end = moment(end).format('YYYY-MM-DD');
        onlyDate = true;
      }
    }

    this.setState({
      events: nextEvents,
    }, () => {
      editGoogleCalendarEvent({ ...updatedEvent, userId: currentUser.id, timezone: currentUser.timezone, onlyDate });
    });
  }

  resizeEvent = ({ event, start, end }) => {
    const { events } = this.state;

    const nextEvents = events.map(existingEvent => {
      return existingEvent.id === event.id
        ? { ...existingEvent, start, end }
        : existingEvent;
    });

    this.setState({
      events: nextEvents,
    });

    // console.log(`${event.title} was resized to ${start}-${end}`);
  }

  newEvent(params) {
    const { currentUser, addGoogleCalendarEvent } = this.props;
    const { event, formValues } = params;

    const newEvent = {
      title: formValues.title,
      description: formValues.description ? formValues.description : null,
      allDay: event.slots.length === 1,
      start: moment(event.start).hours(formValues.period === 'AM' ? formValues.hour % 12 : (formValues.hour % 12) + 12).minutes(formValues.minute).toISOString(),
      end: moment(event.end).hours(formValues.period === 'AM' ? formValues.hour % 12 : (formValues.hour % 12) + 12).minutes(formValues.minute).toISOString(),
    };
    this.setState({
      calendarEvent: null,
    }, () => {
      addGoogleCalendarEvent({ ...newEvent, userId: currentUser.id, timezone: currentUser.timezone });
    });
  }

  showEventModal(event) {
    this.setState({ calendarEvent: event, show: true });
  }

  hideEventModal() {
    const { resetForm } = this.props;
    this.setState({ show: false, calendarEvent: null }, () => {
      resetForm();
    });
  }

  render() {
    const { currentGoogleUser, addEventProcess } = this.props;
    let authorized = false;

    if (currentGoogleUser && currentGoogleUser.expires_at) {
      authorized = moment(currentGoogleUser.expires_at).isAfter(moment());
    }

    return (
      <div>
        <div className="container-fluid">
          <Helmet title={translate('portals.page.calendarPage.helmetTitle')} />
          <section className="calendar-section">
            <h2 className="main-heading">{translate('portals.page.calendarPage.pageTitle')}</h2>
            {!authorized &&
              <GoogleLogin
                clientId={GOOGLE_CLIENT_ID}
                scope="https://www.googleapis.com/auth/calendar"
                className="google-login"
                onSuccess={this.onSuccess}
                onFailure={this.onFailure}
              >
                <i className="google-image" />
                <span> Sign in with Google</span>
              </GoogleLogin>
            }
            {authorized &&
              <DragAndDropCalendar
                selectable
                events={this.state.events}
                localizer={localizer}
                onEventDrop={this.moveEvent}
                resizable
                onEventResize={this.resizeEvent}
                onSelectSlot={this.showEventModal}
                onSelectEvent={(e) => { console.log('e', e); }}
                defaultView={BigCalendar.Views.MONTH}
                defaultDate={new Date()}
                views={{ month: true }}
              />
            }

            <AddCalendarEventForm
              show={this.state.show}
              isSubmitting={addEventProcess.isSubmitting}
              calendarEvent={this.state.calendarEvent}
              onSubmit={this.newEvent}
              onHide={this.hideEventModal}
            />
          </section>
        </div>
      </div>
    );
  }
}
import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import { createStructuredSelector } from 'reselect';
import { Field, touch, reduxForm, reset } from 'redux-form';
import { Modal } from 'react-bootstrap';
import _ from 'lodash';

import Input from '../../../common/components/Input';
import ReactSelect from '../../../common/components/Input/ReactSelect';
import CenteredModal from '../../../common/components/CenteredModal/index';
import LoadingSpinner from '../../components/LoadingSpinner';
import { selectCurrentUser } from '../../containers/App/selectors';
import { selectSyncErrorBool, selectSyncErrors, selectValues } from '../../common/selectors/form.selector';

import validator, { fields } from './validator';

const formName = 'addCalendarEvent';

function numberSequenceCreator(start, end) {
  return _.range(start, end).map(n => {
    if (n < 10) {
      return {
        label: `0${n}`,
        value: n.toString(),
      };
    }
    return {
      label: n.toString(),
      value: n.toString(),
    };
  });
}

const hourOptions = numberSequenceCreator(1, 13);
const minuteOptions = numberSequenceCreator(0, 60);
const periodOptions = [
  { label: 'AM', value: 'AM' },
  { label: 'PM', value: 'PM' },
];

const mapDispatchToProps = (dispatch) => ({
  touchFields: () => dispatch(touch(formName, ...fields)),
  resetForm: () => dispatch(reset(formName)),
});

const mapStateToProps = createStructuredSelector({
  currentUser: selectCurrentUser(),
  formError: selectSyncErrorBool(formName),
  formErrors: selectSyncErrors(formName),
  formValues: selectValues(formName),
});

@reduxForm({
  form: formName,
  validate: validator,
  enableReinitialize: true,
  keepDirtyOnReinitialize: false,
})
@connect(mapStateToProps, mapDispatchToProps)
export default class AddCalendarEventForm extends Component { // eslint-disable-line react/prefer-stateless-function
  static propTypes = {
    resetForm: PropTypes.func,
    currentUser: PropTypes.object,
    formError: PropTypes.bool,
    formErrors: PropTypes.object,
    formValues: PropTypes.object,
    show: PropTypes.bool,
    onHide: PropTypes.func,
    onSubmit: PropTypes.func,
    touchFields: React.PropTypes.func.isRequired,
    calendarEvent: PropTypes.object,
    isSubmitting: PropTypes.bool,
  };

  constructor(props) {
    super(props);

    this.handleCloseModal = this.handleCloseModal.bind(this);
    this.handleFormSubmit = this.handleFormSubmit.bind(this);
  }

  handleCloseModal() {
    this.props.resetForm();
    this.props.onHide(false);
  }

  handleFormSubmit(event) {
    event.preventDefault();
    const { formError, formValues, onSubmit, calendarEvent, touchFields } = this.props;

    if (!formValues.title || !formValues.period || !formValues.hour || !formValues.minute) {
      touchFields();
    } else if (!formError) {
      onSubmit({ event: calendarEvent, formValues });
    }
  }

  render() {
    const { show, isSubmitting } = this.props;

    return (
      <div>
        <Modal
          className="edit-study-modal"
          id="edit-study"
          dialogComponentClass={CenteredModal}
          show={show}
          onHide={this.handleCloseModal}
          backdrop
          keyboard
        >
          <Modal.Header>
            <Modal.Title>Add Calendar Event</Modal.Title>
            <a className="lightbox-close close" onClick={this.handleCloseModal}>
              <i className="icomoon-icon_close" />
            </a>
          </Modal.Header>
          <Modal.Body>
            <div className="form">
              <div className="inner">
                <form className="" onSubmit={this.handleFormSubmit}>
                  <div className="form-lightbox">
                    <div className="clearfix">
                      <div className="field-row">
                        <strong className="required label">
                          <label>Title</label>
                        </strong>
                        <div className="field">
                          <Field
                            name="title"
                            component={Input}
                            type="text"
                          />
                        </div>
                      </div>
                      <div className="field-row time-field-row">
                        <strong className="label required">
                          <label>Time</label>
                        </strong>
                        <div className="field time-field">
                          <div className="row">
                            <div className="col-small pull-left hours">
                              <Field
                                name="hour"
                                placeholder="Hours"
                                options={hourOptions}
                                component={ReactSelect}
                              />
                            </div>
                            <div className="col-small pull-left minutes">
                              <Field
                                name="minute"
                                placeholder="Minutes"
                                options={minuteOptions}
                                component={ReactSelect}
                              />
                            </div>
                            <div className="col-small pull-left time-mode">
                              <Field
                                name="period"
                                placeholder="AM/PM"
                                options={periodOptions}
                                component={ReactSelect}
                              />
                            </div>
                          </div>
                        </div>
                      </div>
                      <div className="field-row">
                        <strong className="label">
                          <label>Description</label>
                        </strong>
                        <Field
                          name="description"
                          component={Input}
                          className="field"
                          bsClass="form-control input-lg"
                          componentClass="textarea"
                        />
                      </div>
                      <button
                        type="submit"
                        className="btn btn-default btn-submit pull-right"
                      >
                        {isSubmitting
                          ? <span><LoadingSpinner showOnlyIcon size={20} /></span>
                          : <span>Submit</span>
                        }
                      </button>
                    </div>
                  </div>
                </form>
              </div>
            </div>
          </Modal.Body>
        </Modal>
      </div>
    );
  }
}