@oveasoft/planning
Version:
An AngularJS planning component
304 lines (257 loc) • 9.98 kB
JavaScript
import './ovaPlanning.scss';
import template from './ovaPlanning.template.html';
import moment from 'moment';
import _ from 'lodash';
// import uuid from 'uuid';
import angular from 'angular';
import { MomentHelper } from './../classes/moment.helper';
import { Configuration } from './../classes/configuration.class';
import { Debugger } from './../classes/debugger.class';
/**
* @module ova-planning
* @class OvaPlanningComponent
* @name OvaPlanningComponent
* @property {Array<Object>} appointments
* @property {Object} config
* @property {moment.Moment} currentWeek
*
* Days:
* 0 to 6
* | FR | Lundi to Dimanche |
* | EN | Sunday to Saturday |
*/
class OvaPlanningComponent {
constructor () {
this.informations = {};
this.week = ['0', '1', '2', '3', '4', '5', '6'];
/**
* Exclude days from the week according to the configuration 'excludedDays' key.
* @private
*/
this._excludeDays = () => {
_.remove(this.week, day => _.find(this.config.excludedDays, exclude => exclude === day));
if (this.week.length <= 0) {
throw new Error(`[ova-planning] The week does not contain any days. Did you have excluded all days of the week ?`);
}
};
}
$onInit () {
this.oldAppointments = angular.copy(this.appointments);
// Set the momentJs local.
moment.locale(this.config.momentLocale);
// Merge configuration with default configuration.
this.config = new Configuration(this.config);
this.oldConfig = angular.copy(this.config);
this.Debugger = new Debugger(this.config.debug);
this.Debugger.output('OvaPlanning $onInit');
this.buildView();
}
switchView () {
this.config.viewer = this.config.viewer === 'month' ? 'week' : 'month';
this.buildView();
}
buildView () {
this.informations = {};
switch (this.config.viewer) {
case 'month':
this.buildMonthView();
break;
case 'week':
// Exclude days from the week.
this._excludeDays();
// Build the chronoline.
this.buildChronoline();
// Calculate the first week.
this.buildWeek();
break;
case 'trimester':
this.buildTrimesterView();
break;
default:
return null;
}
}
buildTrimesterView () {
this.labels = [];
this.cols = [];
const past = this.config.views.trimester.range.past;
const future = this.config.views.trimester.range.future;
this.columnType = this.config.views.trimester.columnType;
const now = moment(this.config.currentWeek);
const firstCol = moment(now).subtract(past, this.columnType);
const lastCol = moment(now).add(future, this.columnType);
for (let i = moment(firstCol); i.isSameOrBefore(lastCol); i.add(1, this.columnType)) {
const data = [];
if (angular.isFunction(this.config.views.trimester.labels)) {
this.labels.push(this.config.views.trimester.labels(i));
} else {
this.labels.push(`${i.get(this.columnType)}`);
}
if (angular.isArray(this.appointments)) {
let events = angular.copy(this.appointments);
events.forEach(event => {
const eventDate = _.get(event, this.config.datePath);
if (moment(eventDate).isSame(i, this.columnType)) {
data.push(event);
}
});
}
this.cols.push({ date: moment(i), data: data });
}
}
buildMonthView () {
this.days = [];
this.labels = [];
this.range = [];
const startOfWeek = moment().startOf('week');
const endOfWeek = moment().endOf('week');
for (let i = startOfWeek; i.isBefore(endOfWeek); i.add(1, 'day')) {
if (this.config.excludedDays.indexOf(i.day().toString()) < 0) {
this.labels.push(i.format('dddd'));
}
}
for (let i = moment(this.config.currentWeek); i.isBefore(moment(this.config.currentWeek).add(this.config.views.month.weeksPerView.current, 'weeks')); i.add(1, 'week')) {
let days = [];
let currentDate = moment(this.config.currentWeek).week(i.week());
let cursor = moment(currentDate).startOf('week');
let lastDayOfWeek = moment(currentDate).endOf('week');
while (cursor.isBefore(lastDayOfWeek)) {
if (this.config.excludedDays.indexOf(cursor.day().toString()) >= 0) {
cursor.add(1, 'day');
continue;
}
let data = [];
_.forEach(this.appointments, appointment => {
if (moment(_.get(appointment, this.config.datePath)).isSame(cursor, 'day')) {
data.push(appointment);
}
});
days.push({ date: moment(cursor), data: data });
cursor.add(1, 'day');
}
this.range.push({ week: i.get('week'), days: days });
}
}
/**
* Use to zoom in/out a week
* @param {Number} week
*/
collapseToWeek (week) {
if ((moment(this.config.currentWeek).week() === week) && this.config.views.month.weeksPerView.current === 1) {
this.config.views.month.weeksPerView.current = this.config.views.month.weeksPerView.default;
this.config.currentWeek = moment(this.previousWeek);
this.config.onChangeWeek(this.config.currentWeek);
} else {
this.previousWeek = this.config.currentWeek;
this.config.views.month.weeksPerView.current = 1;
this.config.currentWeek = moment().week(week);
}
}
/**
* Push data to information object.
* @param {*} data
* @param {string} key
* @memberof OvaPlanningComponent
*/
pushToInformations (data, key) {
if (!this.informations.hasOwnProperty(key)) {
this.informations[key] = [];
}
this.informations[key].push(data);
}
rebuild () {
this.informations = {};
this.buildView();
this.oldConfig = angular.copy(this.config);
}
$doCheck () {
// _.map(this.appointments, appointment => (appointment.$$id = uuid()));
if (!angular.equals(this.config.currentWeek, this.oldConfig.currentWeek) || !angular.equals(this.config.viewer, this.oldConfig.viewer)) {
this.Debugger.output(`The configuration changed.`, 'log');
this.rebuild();
} else if (!angular.equals(this.appointments, this.oldAppointments)) {
this.informations = {};
this.buildView();
this.oldAppointments = angular.copy(this.appointments);
}
}
/**
* Push an item in the Chronoline.
* @param {String} date
* @param {Boolean} display
*/
pushToChronoline (date, display = false) {
this.chronoLine.push({
date,
display
});
}
/**
* Build the week to be displayed.
*/
buildWeek () {
this.days = [];
let firstDayOfWeek = moment(this.config.currentWeek).startOf('week');
let lastDayOfWeek = moment(this.config.currentWeek).endOf('week');
while (firstDayOfWeek.isBefore(lastDayOfWeek)) {
if (this.config.excludedDays.indexOf(firstDayOfWeek.day().toString()) < 0) {
let dayItem = this.buildDay(firstDayOfWeek.day().toString());
this.days.push(dayItem);
}
firstDayOfWeek.add(1, 'day');
}
this.config.onLoad(this.informations);
}
/**
* Return the moment object of the given day index for the current week.
* @param dayIndex
* @returns {{date: moment.Moment, slots: Array}}
*/
buildDay (dayIndex) {
const date = moment(this.config.currentWeek).day(dayIndex).startOf('day');
return {
date: date,
data: _.filter(this.appointments, appointment => _.get(appointment, this.config.datePath, false) && (moment(_.get(appointment, this.config.datePath, false)).isSame(date, 'day')))
};
}
/**
* Build the Chronoline.
* This is the first column at the left of the table, it's a legend that display the slots hour for each line.
*/
buildChronoline () {
this.chronoLine = [];
_.each(this.config.slot, (slotRanges) => {
let startingHour = MomentHelper.buildSlot(undefined, slotRanges[0]);
let endingHour = MomentHelper.buildSlot(undefined, slotRanges[1]);
// We put the first hour in the array.
this.pushToChronoline(startingHour.format('HH:mm'), true);
// Then we fill the array every $config.slotInterval
while (startingHour.isBefore(endingHour)) {
let item = startingHour.add(this.config.slotInterval, 'minutes');
if (item.isAfter(endingHour)) {
this.pushToChronoline(endingHour.format('HH:mm'), true);
} else {
this.pushToChronoline(item.format('HH:mm'), true);
}
}
});
}
previous (step = 'week') {
this.config.currentWeek.subtract(1, step);
this.config.onChangeWeek(this.config.currentWeek);
this.buildView();
}
next (step = 'week') {
this.config.currentWeek.add(1, step);
this.config.onChangeWeek(this.config.currentWeek);
this.buildView();
}
}
export default {
controller: OvaPlanningComponent,
template,
bindings: {
config: '=',
appointments: '='
}
};