diff options
| author | cedricnjanga | 2017-10-06 10:17:17 +0200 |
|---|---|---|
| committer | cedricnjanga | 2017-10-06 10:17:17 +0200 |
| commit | b6f08e58fae35d5dd8a610af31c2950b37746695 (patch) | |
| tree | 989843dd674c41ff73eb75bd630ce4cc91fff91b /app/javascript/time_tables/components | |
| parent | 08517c27551a2dd8b227f6662f4c41574a36d81e (diff) | |
| download | chouette-core-b6f08e58fae35d5dd8a610af31c2950b37746695.tar.bz2 | |
Add webpacker gem and migrate the React apps
Diffstat (limited to 'app/javascript/time_tables/components')
| -rw-r--r-- | app/javascript/time_tables/components/ConfirmModal.js | 50 | ||||
| -rw-r--r-- | app/javascript/time_tables/components/ErrorModal.js | 42 | ||||
| -rw-r--r-- | app/javascript/time_tables/components/ExceptionsInDay.js | 71 | ||||
| -rw-r--r-- | app/javascript/time_tables/components/Metas.js | 139 | ||||
| -rw-r--r-- | app/javascript/time_tables/components/Navigate.js | 88 | ||||
| -rw-r--r-- | app/javascript/time_tables/components/PeriodForm.js | 148 | ||||
| -rw-r--r-- | app/javascript/time_tables/components/PeriodManager.js | 85 | ||||
| -rw-r--r-- | app/javascript/time_tables/components/PeriodsInDay.js | 75 | ||||
| -rw-r--r-- | app/javascript/time_tables/components/SaveTimetable.js | 42 | ||||
| -rw-r--r-- | app/javascript/time_tables/components/TagsSelect2.js | 77 | ||||
| -rw-r--r-- | app/javascript/time_tables/components/TimeTableDay.js | 31 | ||||
| -rw-r--r-- | app/javascript/time_tables/components/Timetable.js | 115 |
12 files changed, 963 insertions, 0 deletions
diff --git a/app/javascript/time_tables/components/ConfirmModal.js b/app/javascript/time_tables/components/ConfirmModal.js new file mode 100644 index 000000000..d89170ee7 --- /dev/null +++ b/app/javascript/time_tables/components/ConfirmModal.js @@ -0,0 +1,50 @@ +import React, { PropTypes } from 'react' + +export default function ConfirmModal({dispatch, modal, onModalAccept, onModalCancel, timetable, metas}, {I18n}) { + return ( + <div className={'modal fade ' + ((modal.type == 'confirm') ? 'in' : '')} id='ConfirmModal'> + <div className='modal-container'> + <div className='modal-dialog'> + <div className='modal-content'> + <div className='modal-header'> + <h4 className='modal-title'>{I18n.time_tables.edit.confirm_modal.title}</h4> + </div> + <div className='modal-body'> + <div className='mt-md mb-md'> + <p>{I18n.time_tables.edit.confirm_modal.message}</p> + </div> + </div> + <div className='modal-footer'> + <button + className='btn btn-link' + data-dismiss='modal' + type='button' + onClick={() => { onModalCancel(modal.confirmModal.callback) }} + > + {I18n.cancel} + </button> + <button + className='btn btn-primary' + data-dismiss='modal' + type='button' + onClick={() => { onModalAccept(modal.confirmModal.callback, timetable, metas) }} + > + {I18n.actions.submit} + </button> + </div> + </div> + </div> + </div> + </div> + ) +} + +ConfirmModal.propTypes = { + modal: PropTypes.object.isRequired, + onModalAccept: PropTypes.func.isRequired, + onModalCancel: PropTypes.func.isRequired +} + +ConfirmModal.contextTypes = { + I18n: PropTypes.object +}
\ No newline at end of file diff --git a/app/javascript/time_tables/components/ErrorModal.js b/app/javascript/time_tables/components/ErrorModal.js new file mode 100644 index 000000000..e810f49ab --- /dev/null +++ b/app/javascript/time_tables/components/ErrorModal.js @@ -0,0 +1,42 @@ +import React, { PropTypes } from 'react' +import actions from '../actions' + +export default function ErrorModal({dispatch, modal, onModalClose}, {I18n}) { + return ( + <div className={'modal fade ' + ((modal.type == 'error') ? 'in' : '')} id='ErrorModal'> + <div className='modal-container'> + <div className='modal-dialog'> + <div className='modal-content'> + <div className='modal-header'> + <h4 className='modal-title'>{I18n.time_tables.edit.error_modal.title}</h4> + </div> + <div className='modal-body'> + <div className='mt-md mb-md'> + <p>{actions.errorModalMessage(modal.modalProps.error)}</p> + </div> + </div> + <div className='modal-footer'> + <button + className='btn btn-link' + data-dismiss='modal' + type='button' + onClick={() => { onModalClose() }} + > + {I18n.back} + </button> + </div> + </div> + </div> + </div> + </div> + ) +} + +ErrorModal.propTypes = { + modal: PropTypes.object.isRequired, + onModalClose: PropTypes.func.isRequired +} + +ErrorModal.contextTypes = { + I18n: PropTypes.object +}
\ No newline at end of file diff --git a/app/javascript/time_tables/components/ExceptionsInDay.js b/app/javascript/time_tables/components/ExceptionsInDay.js new file mode 100644 index 000000000..3335ee89d --- /dev/null +++ b/app/javascript/time_tables/components/ExceptionsInDay.js @@ -0,0 +1,71 @@ +import React, { PropTypes, Component } from 'react' +import actions from '../actions' + +export default class ExceptionsInDay extends Component { + constructor(props) { + super(props) + } + + handleClick() { + const {index, day, metas: {day_types} } = this.props + if (day.in_periods && day_types[day.wday]) { + day.excluded_date ? this.props.onRemoveExcludedDate(index, day_types, day.date) : this.props.onAddExcludedDate(index, day_types, day.date) + } else { + day.include_date ? this.props.onRemoveIncludedDate(index, day_types, day.date) : this.props.onAddIncludedDate(index, day_types, day.date) + } + } + + render() { + {/* display add or remove link, only if true in daytypes */} + {/* display add or remove link, according to context (presence in period, or not) */} + if(this.props.value.current_month[this.props.index].in_periods == true && this.props.blueDaytype == true) { + return ( + <div className='td'> + <button + type='button' + className={'btn btn-circle' + (this.props.value.current_month[this.props.index].excluded_date ? ' active' : '')} + data-actiontype='remove' + onClick={(e) => { + $(e.currentTarget).toggleClass('active') + this.handleClick() + }} + > + <span className='fa fa-times'></span> + </button> + </div> + ) + } else { + return ( + <div className='td'> + <button + type='button' + className={'btn btn-circle' + (this.props.value.current_month[this.props.index].include_date ? ' active' : '')} + data-actiontype='add' + onClick={(e) => { + $(e.currentTarget).toggleClass('active') + this.handleClick() + }} + > + <span className='fa fa-plus'></span> + </button> + </div> + ) + // } else if(this.props.value.current_month[this.props.index].in_periods == true && this.props.blueDaytype == false){ + // return ( + // <div className='td'></div> + // ) + // } else{ + // return false + // } + } + } +} + +ExceptionsInDay.propTypes = { + value: PropTypes.object.isRequired, + metas: PropTypes.object.isRequired, + blueDaytype: PropTypes.bool.isRequired, + onExcludeDateFromPeriod: PropTypes.func.isRequired, + onIncludeDateInPeriod: PropTypes.func.isRequired, + index: PropTypes.number.isRequired +} diff --git a/app/javascript/time_tables/components/Metas.js b/app/javascript/time_tables/components/Metas.js new file mode 100644 index 000000000..7098d2b82 --- /dev/null +++ b/app/javascript/time_tables/components/Metas.js @@ -0,0 +1,139 @@ +import React, { PropTypes } from 'react' +import actions from '../actions' +import TagsSelect2 from './TagsSelect2' + +export default function Metas({metas, onUpdateDayTypes, onUpdateComment, onUpdateColor, onSelect2Tags, onUnselect2Tags}, {I18n}) { + let colorList = ["", "#9B9B9B", "#FFA070", "#C67300", "#7F551B", "#41CCE3", "#09B09C", "#3655D7", "#6321A0", "#E796C6", "#DD2DAA"] + return ( + <div className='form-horizontal'> + <div className="row"> + <div className="col-lg-10 col-lg-offset-1"> + {/* comment (name) */} + <div className="form-group"> + <label htmlFor="" className="control-label col-sm-4 required"> + {I18n.time_tables.edit.metas.name} <abbr title="">*</abbr> + </label> + <div className="col-sm-8"> + <input + type='text' + className='form-control' + value={metas.comment} + required='required' + onChange={(e) => (onUpdateComment(e.currentTarget.value))} + /> + </div> + </div> + + {/* color */} + <div className="form-group"> + <label htmlFor="" className="control-label col-sm-4">{I18n.activerecord.attributes.time_table.color}</label> + <div className="col-sm-8"> + <div className="dropdown color_selector"> + <button + type='button' + className="btn btn-default dropdown-toggle" + id='dpdwn_color' + data-toggle='dropdown' + aria-haspopup='true' + aria-expanded='true' + > + <span + className='fa fa-circle mr-xs' + style={{color: (metas.color == '') ? 'transparent' : metas.color}} + ></span> + <span className='caret'></span> + </button> + + <div className="form-group dropdown-menu" aria-labelledby='dpdwn_color'> + {colorList.map((c, i) => + <span + className="radio" + key={i} + onClick={() => {onUpdateColor(c)}} + > + <label htmlFor=""> + <input + type='radio' + className='color_selector' + value={c} + /> + <span + className='fa fa-circle' + style={{color: ((c == '') ? 'transparent' : c)}} + ></span> + </label> + </span> + )} + </div> + </div> + </div> + </div> + + {/* tags */} + <div className="form-group"> + <label htmlFor="" className="control-label col-sm-4">{I18n.activerecord.attributes.time_table.tag_list}</label> + <div className="col-sm-8"> + <TagsSelect2 + initialTags={metas.initial_tags} + tags={metas.tags} + onSelect2Tags={(e) => onSelect2Tags(e)} + onUnselect2Tags={(e) => onUnselect2Tags(e)} + /> + </div> + </div> + + {/* calendar */} + <div className="form-group"> + <label htmlFor="" className="control-label col-sm-4">{I18n.activerecord.attributes.time_table.calendar}</label> + <div className="col-sm-8"> + <span>{metas.calendar ? metas.calendar.name : I18n.time_tables.edit.metas.no_calendar}</span> + </div> + </div> + + {/* day_types */} + <div className="form-group"> + <label htmlFor="" className="control-label col-sm-4"> + {I18n.time_tables.edit.metas.day_types} + </label> + <div className="col-sm-8"> + <div className="form-group labelled-checkbox-group"> + {metas.day_types.map((day, i) => + <div + className='lcbx-group-item' + data-wday={'day_' + i} + key={i} + > + <div className="checkbox"> + <label> + <input + onChange={(e) => {onUpdateDayTypes(i, metas.day_types)}} + id={i} + type="checkbox" + checked={day ? 'checked' : ''} + /> + <span className='lcbx-group-item-label'>{actions.weekDays()[i]}</span> + </label> + </div> + </div> + )} + </div> + </div> + </div> + </div> + </div> + </div> + ) +} + +Metas.propTypes = { + metas: PropTypes.object.isRequired, + onUpdateDayTypes: PropTypes.func.isRequired, + onUpdateColor: PropTypes.func.isRequired, + onUpdateColor: PropTypes.func.isRequired, + onSelect2Tags: PropTypes.func.isRequired, + onUnselect2Tags: PropTypes.func.isRequired +} + +Metas.contextTypes = { + I18n: PropTypes.object +} diff --git a/app/javascript/time_tables/components/Navigate.js b/app/javascript/time_tables/components/Navigate.js new file mode 100644 index 000000000..6ae80bce0 --- /dev/null +++ b/app/javascript/time_tables/components/Navigate.js @@ -0,0 +1,88 @@ +import React, { PropTypes, Component } from 'react' +import _ from 'lodash' +import actions from '../actions' + +export default function Navigate({ dispatch, metas, timetable, pagination, status, filters}) { + if(status.isFetching == true) { + return false + } + if(status.fetchSuccess == true) { + let pageIndex = pagination.periode_range.indexOf(pagination.currentPage) + let firstPage = pageIndex == 0 + let lastPage = pageIndex == pagination.periode_range.length - 1 + return ( + <div className="pagination pull-right"> + <form className='form-inline' onSubmit={e => {e.preventDefault()}}> + {/* date selector */} + <div className="form-group"> + <div className="dropdown month_selector" style={{display: 'inline-block'}}> + <div + className='btn btn-default dropdown-toggle' + id='date_selector' + data-toggle='dropdown' + aria-haspopup='true' + aria-expanded='true' + > + {pagination.currentPage ? (actions.monthName(pagination.currentPage) + ' ' + new Date(pagination.currentPage).getFullYear()) : ''} + <span className='caret'></span> + </div> + <ul + className='dropdown-menu' + aria-labelledby='date_selector' + > + {_.map(pagination.periode_range, (month, i) => ( + <li key={i}> + <button + type='button' + value={month} + onClick={e => { + e.preventDefault() + dispatch(actions.checkConfirmModal(e, actions.changePage(dispatch, e.currentTarget.value), pagination.stateChanged, dispatch, metas, timetable)) + }} + > + {actions.monthName(month) + ' ' + new Date(month).getFullYear()} + </button> + </li> + ))} + </ul> + </div> + </div> + + {/* prev/next */} + <div className="form-group"> + <div className="page_links"> + <button + onClick={e => { + e.preventDefault() + dispatch(actions.checkConfirmModal(e, actions.goToPreviousPage(dispatch, pagination), pagination.stateChanged, dispatch, metas, timetable)) + }} + type='button' + data-target='#ConfirmModal' + className={(firstPage ? 'disabled ' : '') + 'previous_page'} + disabled={(firstPage ? 'disabled' : '')} + ></button> + <button + onClick={e => { + e.preventDefault() + dispatch(actions.checkConfirmModal(e, actions.goToNextPage(dispatch, pagination), pagination.stateChanged, dispatch, metas, timetable)) + }} + type='button' + data-target='#ConfirmModal' + className={(lastPage ? 'disabled ' : '') + 'next_page'} + disabled={(lastPage ? 'disabled' : '')} + ></button> + </div> + </div> + </form> + </div> + ) + } else { + return false + } +} + +Navigate.propTypes = { + status: PropTypes.object.isRequired, + pagination: PropTypes.object.isRequired, + dispatch: PropTypes.func.isRequired +}
\ No newline at end of file diff --git a/app/javascript/time_tables/components/PeriodForm.js b/app/javascript/time_tables/components/PeriodForm.js new file mode 100644 index 000000000..893a1fa6a --- /dev/null +++ b/app/javascript/time_tables/components/PeriodForm.js @@ -0,0 +1,148 @@ +import React, { PropTypes } from 'react' +import _ from 'lodash' +let monthsArray = ['Janvier', 'Février', 'Mars', 'Avril', 'Mai', 'Juin', 'Juillet', 'Août', 'Septembre', 'Octobre', 'Novembre', 'Décembre'] + +const formatNumber = (val) => { + return ("0" + val).slice(-2) +} + +const makeDaysOptions = (daySelected) => { + let arr = [] + for(let i = 1; i < 32; i++) { + arr.push(<option value={formatNumber(i)} key={i}>{formatNumber(i)}</option>) + } + return arr +} + +const makeMonthsOptions = (monthSelected) => { + let arr = [] + for(let i = 1; i < 13; i++) { + arr.push(<option value={formatNumber(i)} key={i}>{monthsArray[i - 1]}</option>) + } + return arr +} + +const makeYearsOptions = (yearSelected) => { + let arr = [] + let startYear = new Date().getFullYear() - 3 + for(let i = startYear; i <= startYear + 6; i++) { + arr.push(<option key={i}>{i}</option>) + } + return arr +} + +export default function PeriodForm({modal, timetable, metas, onOpenAddPeriodForm, onClosePeriodForm, onUpdatePeriodForm, onValidatePeriodForm}, {I18n}) { + return ( + <div className="container-fluid"> + <div className="row"> + <div className="col lg-6 col-lg-offset-3"> + <div className='subform'> + {modal.modalProps.active && + <div> + <div className="nested-head"> + <div className="wrapper"> + <div> + <div className="form-group"> + <label htmlFor="" className="control-label required"> + {I18n.time_tables.edit.period_form.begin} + <abbr title="requis">*</abbr> + </label> + </div> + </div> + <div> + <div className="form-group"> + <label htmlFor="" className="control-label required"> + {I18n.time_tables.edit.period_form.end} + <abbr title="requis">*</abbr> + </label> + </div> + </div> + </div> + </div> + <div className="nested-fields"> + <div className="wrapper"> + <div> + <div className={'form-group date ' + (modal.modalProps.error ? ' has-error' : '')}> + <div className="form-inline"> + <select value={formatNumber(modal.modalProps.begin.day)} onChange={(e) => onUpdatePeriodForm(e, 'begin', 'day', modal.modalProps)} id="q_validity_period_begin_gteq_3i" className="date required form-control"> + {makeDaysOptions(modal.modalProps.begin.day)} + </select> + <select value={formatNumber(modal.modalProps.begin.month)} onChange={(e) => onUpdatePeriodForm(e, 'begin', 'month', modal.modalProps)} id="q_validity_period_begin_gteq_2i" className="date required form-control"> + {makeMonthsOptions(modal.modalProps.begin.month)} + </select> + <select value={modal.modalProps.begin.year} onChange={(e) => onUpdatePeriodForm(e, 'begin', 'year', modal.modalProps)} id="q_validity_period_begin_gteq_1i" className="date required form-control"> + {makeYearsOptions(modal.modalProps.begin.year)} + </select> + </div> + </div> + </div> + <div> + <div className={'form-group date ' + (modal.modalProps.error ? ' has-error' : '')}> + <div className="form-inline"> + <select value={formatNumber(modal.modalProps.end.day)} onChange={(e) => onUpdatePeriodForm(e, 'end', 'day', modal.modalProps)} id="q_validity_period_end_gteq_3i" className="date required form-control"> + {makeDaysOptions(modal.modalProps.end.day)} + </select> + <select value={formatNumber(modal.modalProps.end.month)} onChange={(e) => onUpdatePeriodForm(e, 'end', 'month', modal.modalProps)} id="q_validity_period_end_gteq_2i" className="date required form-control"> + {makeMonthsOptions(modal.modalProps.end.month)} + </select> + <select value={modal.modalProps.end.year} onChange={(e) => onUpdatePeriodForm(e, 'end', 'year', modal.modalProps)} id="q_validity_period_end_gteq_1i" className="date required form-control"> + {makeYearsOptions(modal.modalProps.end.year)} + </select> + </div> + </div> + </div> + </div> + </div> + + <div className='links nested-linker'> + <span className='help-block small text-danger pull-left mt-xs ml-sm'> + {modal.modalProps.error} + </span> + <button + type='button' + className='btn btn-link' + onClick={onClosePeriodForm} + > + {I18n.cancel} + </button> + <button + type='button' + className='btn btn-outline-primary mr-sm' + onClick={() => onValidatePeriodForm(modal.modalProps, timetable.time_table_periods, metas, _.filter(timetable.time_table_dates, ['in_out', true]))} + > + {I18n.actions.submit} + </button> + </div> + </div> + } + {!modal.modalProps.active && + <div className="text-right"> + <button + type='button' + className='btn btn-outline-primary' + onClick={onOpenAddPeriodForm} + > + {I18n.time_tables.actions.add_period} + </button> + </div> + } + </div> + </div> + </div> + </div> + ) +} + +PeriodForm.propTypes = { + modal: PropTypes.object.isRequired, + metas: PropTypes.object.isRequired, + onOpenAddPeriodForm: PropTypes.func.isRequired, + onClosePeriodForm: PropTypes.func.isRequired, + onUpdatePeriodForm: PropTypes.func.isRequired, + onValidatePeriodForm: PropTypes.func.isRequired, + timetable: PropTypes.object.isRequired +} + +PeriodForm.contextTypes = { + I18n: PropTypes.object +}
\ No newline at end of file diff --git a/app/javascript/time_tables/components/PeriodManager.js b/app/javascript/time_tables/components/PeriodManager.js new file mode 100644 index 000000000..9922ce2c4 --- /dev/null +++ b/app/javascript/time_tables/components/PeriodManager.js @@ -0,0 +1,85 @@ +import React, { PropTypes, Component } from 'react' +import actions from '../actions' + +export default class PeriodManager extends Component { + constructor(props, context) { + super(props, context) + } + + toEndPeriod(curr, end) { + let diff + + let startCurrM = curr.split('-')[1] + let endPeriodM = end.split('-')[1] + + let lastDayInM = new Date(curr.split('-')[2], startCurrM + 1, 0) + lastDayInM = lastDayInM.toJSON().substr(0, 10).split('-')[2] + + if(startCurrM === endPeriodM) { + diff = (end.split('-')[2] - curr.split('-')[2]) + } else { + diff = (lastDayInM - curr.split('-')[2]) + } + + return diff + } + + render() { + return ( + <div + className='period_manager' + id={this.props.value.id} + data-toendperiod={this.toEndPeriod(this.props.currentDate.toJSON().substr(0, 10), this.props.value.period_end)} + > + <p className='strong'> + {actions.getLocaleDate(this.props.value.period_start) + ' > ' + actions.getLocaleDate(this.props.value.period_end)} + </p> + + <div className='dropdown'> + <div + className='btn dropdown-toggle' + id='period_actions' + data-toggle='dropdown' + aria-haspopup='true' + aria-expanded='true' + > + <span className='fa fa-cog'></span> + </div> + <ul + className='dropdown-menu' + aria-labelledby='date_selector' + > + <li> + <button + type='button' + onClick={() => this.props.onOpenEditPeriodForm(this.props.value, this.props.index)} + > + Modifier + </button> + </li> + <li className='delete-action'> + <button + type='button' + onClick={() => this.props.onDeletePeriod(this.props.index, this.props.metas.day_types)} + > + <span className='fa fa-trash'></span> + Supprimer + </button> + </li> + </ul> + </div> + </div> + ) + } +} + +PeriodManager.propTypes = { + value: PropTypes.object.isRequired, + currentDate: PropTypes.object.isRequired, + onDeletePeriod: PropTypes.func.isRequired, + onOpenEditPeriodForm: PropTypes.func.isRequired +} + +PeriodManager.contextTypes = { + I18n: PropTypes.object +}
\ No newline at end of file diff --git a/app/javascript/time_tables/components/PeriodsInDay.js b/app/javascript/time_tables/components/PeriodsInDay.js new file mode 100644 index 000000000..888537579 --- /dev/null +++ b/app/javascript/time_tables/components/PeriodsInDay.js @@ -0,0 +1,75 @@ +import React, { PropTypes, Component } from 'react' +import PeriodManager from './PeriodManager' + +export default class PeriodsInDay extends Component { + constructor(props) { + super(props) + } + + isIn(date) { + let currentDate = date.getTime() + let cls = 'td' + let periods = this.props.value + + periods.map((p, i) => { + if (!p.deleted){ + let begin = new Date(p.period_start).getTime() + let end = new Date(p.period_end).getTime() + + if(currentDate >= begin && currentDate <= end) { + if(currentDate == begin) { + cls += ' in_periods start_period' + } else if(currentDate == end) { + cls += ' in_periods end_period' + } else { + cls += ' in_periods' + } + } + } + }) + return cls + } + + render() { + return ( + <div + className={this.isIn(this.props.currentDate) + (this.props.metas.day_types[this.props.day.wday] || !this.props.day.in_periods ? '' : ' out_from_daytypes')} + > + {this.props.value.map((p, i) => { + if(!p.deleted){ + let begin = new Date(p.period_start).getTime() + let end = new Date(p.period_end).getTime() + let d = this.props.currentDate.getTime() + + if(d >= begin && d <= end) { + if(d == begin || (this.props.currentDate.getUTCDate() == 1)) { + return ( + <PeriodManager + key={i} + index={i} + value={p} + metas={this.props.metas} + currentDate={this.props.currentDate} + onDeletePeriod={this.props.onDeletePeriod} + onOpenEditPeriodForm={this.props.onOpenEditPeriodForm} + /> + ) + } else { + return false + } + } + }else{ + return false + } + })} + </div> + ) + } +} + +PeriodsInDay.propTypes = { + value: PropTypes.array.isRequired, + currentDate: PropTypes.object.isRequired, + index: PropTypes.number.isRequired, + onDeletePeriod: PropTypes.func.isRequired +} diff --git a/app/javascript/time_tables/components/SaveTimetable.js b/app/javascript/time_tables/components/SaveTimetable.js new file mode 100644 index 000000000..0dffc7936 --- /dev/null +++ b/app/javascript/time_tables/components/SaveTimetable.js @@ -0,0 +1,42 @@ +import React, { PropTypes, Component } from 'react' +import _ from 'lodash' +import actions from '../actions' + +export default class SaveTimetable extends Component{ + constructor(props){ + super(props) + } + + render() { + const error = actions.errorModalKey(this.props.timetable.time_table_periods, this.props.metas.day_types) + + return ( + <div className='row mt-md'> + <div className='col-lg-12 text-right'> + <form className='time_tables formSubmitr ml-xs' onSubmit={e => {e.preventDefault()}}> + <button + className='btn btn-default' + type='button' + onClick={e => { + e.preventDefault() + if (error) { + this.props.onShowErrorModal(error) + } else { + actions.submitTimetable(this.props.getDispatch(), this.props.timetable, this.props.metas) + } + }} + > + Valider + </button> + </form> + </div> + </div> + ) + } +} + +SaveTimetable.propTypes = { + timetable: PropTypes.object.isRequired, + status: PropTypes.object.isRequired, + metas: PropTypes.object.isRequired +}
\ No newline at end of file diff --git a/app/javascript/time_tables/components/TagsSelect2.js b/app/javascript/time_tables/components/TagsSelect2.js new file mode 100644 index 000000000..24f473f42 --- /dev/null +++ b/app/javascript/time_tables/components/TagsSelect2.js @@ -0,0 +1,77 @@ +import React, { PropTypes, Component } from 'react' +import _ from 'lodash' +import Select2 from 'react-select2-wrapper' + +// get JSON full path +let origin = window.location.origin +let path = window.location.pathname.split('/', 4).join('/') + +export default class TagsSelect2 extends Component { + constructor(props, context) { + super(props, context) + } + + mapKeys(array){ + return array.map((item) => + _.mapKeys(item, (v, k) => + ((k == 'name') ? 'text' : k) + ) + ) + } + + render() { + return ( + <Select2 + value={(this.props.tags.length) ? _.map(this.props.tags, 'id') : undefined} + data={(this.props.initialTags.length) ? this.mapKeys(this.props.initialTags) : undefined} + onSelect={(e) => this.props.onSelect2Tags(e)} + onUnselect={(e) => setTimeout( () => this.props.onUnselect2Tags(e, 150))} + multiple={true} + ref='tags_id' + options={{ + tags:true, + createTag: function(params) { + return {name: params.term, text: params.term, id: params.term} + }, + allowClear: true, + theme: 'bootstrap', + width: '100%', + placeholder: this.context.I18n.time_tables.edit.select2.tag.placeholder, + ajax: { + url: origin + path + '/tags.json', + dataType: 'json', + delay: '500', + data: function(params) { + return { + tag: params.term, + }; + }, + processResults: function(data, params) { + let items = _.filter(data, ({name}) => name.includes(params.term) ) + return { + results: items.map( + item => _.assign( + {}, + item, + {text: item.name} + ) + ) + }; + }, + cache: true + }, + minimumInputLength: 1, + templateResult: formatRepo + }} + /> + ) + } +} + +const formatRepo = (props) => { + if(props.name) return props.name +} + +TagsSelect2.contextTypes = { + I18n: PropTypes.object +}
\ No newline at end of file diff --git a/app/javascript/time_tables/components/TimeTableDay.js b/app/javascript/time_tables/components/TimeTableDay.js new file mode 100644 index 000000000..165c7b848 --- /dev/null +++ b/app/javascript/time_tables/components/TimeTableDay.js @@ -0,0 +1,31 @@ +import React, { PropTypes, Component } from 'react' + +export default class TimeTableDay extends Component { + constructor(props) { + super(props) + } + + render() { + return ( + <span + className={'day' + (this.props.value.wday == 0 ? ' last_wday' : '')} + data-wday={'S' + this.props.value.wnumber} + > + <span className='dayname'> + {((this.props.value.day).charAt(0) == 'm') ? (this.props.value.day).substr(0, 2) : (this.props.value.day).charAt(0)} + </span> + <span + className={'daynumber' + (((this.props.value.in_periods && this.props.dayTypeActive && !this.props.value.excluded_date) || (this.props.value.include_date)) ? ' included' : '')} + > + {this.props.value.mday} + </span> + </span> + ) + } +} + +TimeTableDay.propTypes = { + value: PropTypes.object.isRequired, + index: PropTypes.number.isRequired, + dayTypeActive: PropTypes.bool.isRequired +} diff --git a/app/javascript/time_tables/components/Timetable.js b/app/javascript/time_tables/components/Timetable.js new file mode 100644 index 000000000..df6e6016b --- /dev/null +++ b/app/javascript/time_tables/components/Timetable.js @@ -0,0 +1,115 @@ +import React, { PropTypes, Component } from 'react' +import actions from '../actions' +import TimeTableDay from './TimeTableDay' +import PeriodsInDay from './PeriodsInDay' +import ExceptionsInDay from './ExceptionsInDay' + + +export default class Timetable extends Component { + constructor(props, context){ + super(props, context) + } + + currentDate(mFirstday, day) { + let currentMonth = mFirstday.split('-') + let twodigitsDay = day < 10 ? ('0' + day) : day + let currentDate = new Date(currentMonth[0] + '-' + currentMonth[1] + '-' + twodigitsDay) + + return currentDate + } + + render() { + if(this.props.status.isFetching == true) { + return ( + <div className="isLoading" style={{marginTop: 80, marginBottom: 80}}> + <div className="loader"></div> + </div> + ) + } else { + return ( + <div className="table table-2entries mb-sm"> + <div className="t2e-head w20"> + <div className="th"> + <div className="strong">{this.context.I18n.time_tables.synthesis}</div> + </div> + <div className="td"><span>{this.context.I18n.time_tables.edit.day_types}</span></div> + <div className="td"><span>{this.context.I18n.time_tables.edit.periods}</span></div> + <div className="td"><span>{this.context.I18n.time_tables.edit.exceptions}</span></div> + </div> + <div className="t2e-item-list w80"> + <div> + <div className="t2e-item"> + <div className="th"> + <div className="strong monthName"> + {actions.monthName(this.props.timetable.current_periode_range)} + </div> + + <div className='monthDays'> + {this.props.timetable.current_month.map((d, i) => + <TimeTableDay + key={i} + index={i} + value={d} + dayTypeActive={this.props.metas.day_types[d.wday]} + /> + )} + </div> + </div> + + {this.props.timetable.current_month.map((d, i) => + <div + key={i} + className={'td-group'+ (d.wday == 0 ? ' last_wday' : '')} + > + {/* day_types */} + <div className={"td" + (this.props.metas.day_types[d.wday] || !d.in_periods ? '' : ' out_from_daytypes') }></div> + + {/* periods */} + <PeriodsInDay + day={d} + index={i} + value={this.props.timetable.time_table_periods} + currentDate={this.currentDate(this.props.timetable.current_periode_range, d.mday)} + onDeletePeriod={this.props.onDeletePeriod} + onOpenEditPeriodForm={this.props.onOpenEditPeriodForm} + metas={this.props.metas} + /> + + {/* exceptions */} + <ExceptionsInDay + day={d} + index={i} + value={this.props.timetable} + currentDate={d.date} + metas={this.props.metas} + blueDaytype={this.props.metas.day_types[d.wday]} + onAddIncludedDate={this.props.onAddIncludedDate} + onRemoveIncludedDate={this.props.onRemoveIncludedDate} + onAddExcludedDate={this.props.onAddExcludedDate} + onRemoveExcludedDate={this.props.onRemoveExcludedDate} + onExcludeDateFromPeriod={this.props.onExcludeDateFromPeriod} + onIncludeDateInPeriod={this.props.onIncludeDateInPeriod} + /> + </div> + )} + </div> + </div> + </div> + </div> + ) + } + } +} + +Timetable.propTypes = { + metas: PropTypes.object.isRequired, + timetable: PropTypes.object.isRequired, + status: PropTypes.object.isRequired, + onDeletePeriod: PropTypes.func.isRequired, + onExcludeDateFromPeriod: PropTypes.func.isRequired, + onIncludeDateInPeriod: PropTypes.func.isRequired +} + +Timetable.contextTypes = { + I18n: PropTypes.object +} |
