month-class
Version:
Month class, manages information of the month from a given date
535 lines (497 loc) • 19.1 kB
JavaScript
/**
* @file Manages the month class.
*/
// ━━ IMPORT MODULES ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
const MonthError = require('./MonthError');
const utils = require('./helpers/utils');
const isValid = require('./helpers/isValid');
const readonly = require('./helpers/readonly');
const createPlanner = require('./services/createPlanner');
const createWeekends = require('./services/createWeekends');
const createHolidays = require('./services/createHolidays');
const createNonWorkdays = require('./services/createNonWorkdays');
const createDays = require('./services/createDays');
const createSummary = require('./services/createSummary');
// ━━ TYPEDEF ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
/**
* The `weekend` option indicates the days of the week that are taken as the
* weekend in the month.
*
* Must be a `string` or integer `number` between 0 to 15. If a `string` is
* passed, the string must match to regex `/^[0-1]{7}$/`. For example if want to
* indicate that weekends are Fridays, the value must be `"0000100"`.
*
* @private
* @typedef {(string|number)} WeekendOption
*/
/**
* An activity scheduled on a datebook.
*
* An `Object` that contains the details of a task, event, appointment or
* meeting.
*
* @private
* @typedef {object} Activity
* @property {string} date - Scheduled date for the activity, ISO format (ISO 8601) `"YYYY-MM-DD"`.
* @property {string} title - Activity title.
* @property {string} description - Activity description.
* @property {boolean} holiday - If the activity is taken as a holiday.
* @property {'task'|'event'|'appointment'|'meeting'} type - Activity type, allowed values are `task`, `event`, `appointment`, `meeting`.
*/
/**
* The `datebook` option is a collection of scheduled activities.
*
* Must be an object `array`, every object must have the properties `date`,
* `title`, `description`, `holiday` and `type`.
*
* @private
* @typedef {Array.<Activity>} Datebook
*/
/**
* A task-type scheduled activity.
*
* An `Object` with the properties `date`, `title`, `description`, `holiday`, `type`, and `YYMMDD`.
*
* @private
* @typedef {object} Task
* @property {string} date - Scheduled date for the task `"YYYY-MM-DD"`.
* @property {string} title - Task title.
* @property {string} description - Task description.
* @property {boolean} holiday - If the task is taken as a holiday.
* @property {'task'} type - Type of scheduled activity `task`.
* @property {Array.<number>} YYMMDD - Scheduled date for the task `[YY, MM, DD]`.
*/
/**
* An event-type scheduled activity.
*
* An `Object` that contains the details of an event, with the properties `date`,
* `title`, `description`, `holiday`, `type`, and `YYMMDD`.
*
* @private
* @typedef {object} Event
* @property {string} date - Scheduled date for the event `"YYYY-MM-DD"`.
* @property {string} title - Event title.
* @property {string} description - Event description.
* @property {boolean} holiday - If the event is taken as a holiday.
* @property {'event'} type - Type of scheduled activity `event`.
* @property {Array.<number>} YYMMDD - Scheduled date for the event `[YY, MM, DD]`.
*/
/**
* An appointment-type scheduled activity.
*
* An `Object` that contains the details of an appointment, with the properties
* `date`, `title`, `description`, `holiday`, `type`, and `YYMMDD`.
*
* @private
* @typedef {object} Appointment
* @property {string} date - Scheduled date for the appointment `"YYYY-MM-DD"`.
* @property {string} title - Appointment title.
* @property {string} description - Appointment description.
* @property {boolean} holiday - If the appointment is taken as a holiday.
* @property {'appointment'} type - Type of scheduled activity `appointment`.
* @property {Array.<number>} YYMMDD - Scheduled date for the appointment `[YY, MM, DD]`.
*/
/**
* An meeting-type scheduled activity.
*
* An object that contains the details of a meeting, with the properties `date`,
* `title`, `description`, `holiday`, `type`, and `YYMMDD`.
*
* @private
* @typedef {object} Meeting
* @property {string} date - Scheduled date for the meeting `"YYYY-MM-DD"`.
* @property {string} title - Meeting title.
* @property {string} description - Meeting description.
* @property {boolean} holiday - If the meeting is taken as a holiday.
* @property {'meeting'} type - Type of scheduled activity `meeting`.
* @property {Array.<number>} YYMMDD - Scheduled date for the meeting `[YY, MM, DD]`.
*/
/**
* Collection of activities of the month, organized by type.
*
* An `Object` with the properties `tasks`, `events`, `appointments` and
* `meetings`, every property of the object is an array.
*
* @private
* @typedef {object} Planner
* @property {Array.<Task>} tasks - Collection of all tasks for the month.
* @property {Array.<Event>} events - Collection of all events for the month.
* @property {Array.<Appointment>} appointments - Collection of all appointments for the month.
* @property {Array.<Meeting>} meetings - Collection of all meetings for the month.
*/
/**
* Count of activities of the day, organized by type.
*
* An `Object` with the properties `tasks`, `events`, `appointments`, `meetings`
* and total, every property of the object is an `number`.
*
* @private
* @typedef {object} Scheduled
* @property {number} tasks - Total number of the tasks of a day.
* @property {number} events - Total number of the events of a day.
* @property {number} appointments - Total number of the appointments of a day.
* @property {number} meetings - Total number of the meetings of a day.
* @property {number} total - Total number of activities of a day.
*/
/**
* Details of a day.
*
* An `Object` with the properties `day`, `date`, `weekday`, `type`, `week`,
* `workday`, `isWorkday`, `isWeekend`, and `scheduled`.
*
* @private
* @typedef {object} Day
* @property {number} day - Day of the month.
* @property {Array.<number>} date - The date of the day `[YY, MM, DD]`.
* @property {number} weekday - The day of the week, value is like `Date.prototype.getDay()`.
* @property {string} type - `elapsed`, `current`, `remaining`.
* @property {number} week - Week number of the month.
* @property {number} workday - Workday number of the month.
* @property {boolean} isWorkday - If the day is a work day.
* @property {boolean} isWeekend - If the day is a weekend.
* @property {Scheduled} scheduled - Number of the activities of a day.
*/
/**
* The details of the days of the month, a collection of information about all
* the days of the month.
*
* An object `array`, every `array` element contains information about a day of
* the month, If it is a weekend or a work day, if it has already elapsed, it is
* the current day, week number.
*
* @private
* @typedef {Array.<Day>} Days
*/
/**
* Month's dates summary.
*
* @private
* @typedef {object} DatesSummary
* @property {Date} start - Month's start date.
* @property {Date} current - Month's current date.
* @property {Date} end - Month's end date.
*/
/**
* Month's days summary.
*
* @private
* @typedef {object} DaysSummary
* @property {number} current - Month's current day.
* @property {number} total - Month's total days.
* @property {number} elapsed - Month's elapsed days.
* @property {number} remaining - Month's remaining days.
* @property {number} percentage - Month's percentage remaining days.
*/
/**
* Month's weeks summary.
*
* @private
* @typedef {object} WeeksSumary
* @property {number} current - Month's current week.
* @property {number} total - Month's total weeks.
* @property {number} elapsed - Month's elapsed weeks.
* @property {number} remaining - Month's remaining weeks.
* @property {number} percentage - Month's percentage remaining weeks.
*/
/**
* Month's work days summary.
*
* @private
* @typedef {object} WorkdaysSumary
* @property {number} current - Month's current work day.
* @property {number} total - Month's total work days.
* @property {number} elapsed - Month's elapsed work days.
* @property {number} remaining - Month's remaining work days.
* @property {number} percentage - Month's percentage remaining work days.
*/
/**
* Summary of the month with information on weeks, working days and dates.
*
* An `Object` with the properties `dates`, `days`, `weeks`, and `workdays`.
*
* @private
* @typedef {object} Summary
* @property {DatesSummary} dates - Month's dates Summary.
* @property {DaysSummary} days - Month's days Summary.
* @property {WeeksSumary} weeks - Month's weeks Summary.
* @property {WorkdaysSumary} workdays - Month's work days Summary.
*/
// ━━ CONSTANTS ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
const ERRORS = MonthError.messages;
// ━━ MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
/**
* The `Month` class manages the information of a month from given date, such as
* number of weeks, number of work days, number of weekend.
*
* @version 1.0.0
*/
class Month {
/**
* Creates an instance of Month.
*
* The class `Month` requires an optional parameter `options`, must be an
* `Object` with the properties `current`, `weekend` and `datebook`.
*
* The `options.current` value must be type `date`, it is used as a base to identify
* the days that have `elapsed`, the `remaining` days of the month and `current` day,
* the `default` value is `new Date()`.
*
* The `options.weekend` value must be a `string` or integer `number` between `0` to `15`.
* If a `string` is passed, the string must match to regex `/^[0-1]{7}$/`, the
* `default` value is `0`.
*
* The `options.datebook` value must be an object `array`, that represents a
* collection of scheduled activities from a Datebook, the `default` value is `[]`.
*
* @memberof Month
* @param {object} [options] - Month class options.
* @param {Date} [options.current] - Current date of month.
* @param {WeekendOption} [options.weekend] - Option to specify weekends.
* @param {Datebook} [options.datebook] - Scheduled activities of the month.
* @throws {MonthError} If `options.current` is not instance Date.
* @throws {MonthError} If `options.weekend` is not a valid weekend option.
* @throws {MonthError} If `options.datebook` is not an array.
* @throws {MonthError} If any `options.datebook` elements is not a valid object.
* @example const month = new Month();
*
* @example const month = new Month({
* current: new Date(2021, 0, 15),
* weekend: 0,
* datebook: [
* {
* date: '2021-01-20',
* title: "Mom's birthday",
* description: "Don't forget to buy a gift",
* type: 'event',
* holiday: true,
* },
* {
* date: '2021-01-10',
* title: 'Send final sales report',
* description: "Don't forget to attach graphics",
* type: 'task',
* holiday: true,
* },
* ],
* });
*
*/
constructor({ current = new Date(), weekend = 0, datebook = [] } = {}) {
if (!utils.isDate(current)) {
throw new MonthError(ERRORS.TYPE_CURRENT_OPTION);
}
if (!isValid.weekend(weekend)) {
throw new MonthError(ERRORS.INVALID_WEEKEND_OPTION);
}
if (!utils.isArray(datebook)) {
throw new MonthError(ERRORS.TYPE_DATEBOOK_OPTION);
}
if (!utils.isEmptyArray(datebook) && !isValid.datebook(datebook)) {
throw new MonthError(ERRORS.INVALID_DATEBOOK_SCHEMA);
}
// » DEFINE READ-ONLY PROPERTIES
/**
* Its a number `array` with three elements.
*
* The array elements represent the year number, month number and day
* number `[YY, MM, DD]`.
*
* @type {Array.<number>}
* @member YYMMDD
* @memberof Month
* @instance
* @readonly
*/
Object.defineProperty(this, 'YYMMDD', { value: readonly.createYYMMDD(current) });
/**
* Its a number `array`.
*
* The array elements represent days of the week that are not working days
* and takes as weekend, its value is like like as `Date.prototype.getDay()`.
*
* @type {Array.<number>}
* @member WEEKEND
* @memberof Month
* @instance
* @readonly
*/
Object.defineProperty(this, 'WEEKEND', { value: readonly.createWeekend(weekend) });
/**
* Its a number `array`, with three elements.
*
* The array elements represent one day of the month, the day month start,
* the day month current and the day month end, its value is like
* `Date.prototype.getDate()`.
*
* @type {Array.<number>}
* @member SCE
* @memberof Month
* @instance
* @readonly
*/
Object.defineProperty(this, 'SCE', { value: readonly.createSCE(this.YYMMDD) });
/**
* The year of the month.
*
* Its value is like `Date.prototype.getFullYear()`.
*
* @type {number}
*/
this.year = this.YYMMDD[0]; // eslint-disable-line prefer-destructuring
/**
* The month number.
*
* Its value is like `Date.prototype.getDate()`.
*
* @type {number}
*/
this.number = this.YYMMDD[1]; // eslint-disable-line prefer-destructuring
/**
* Collection of activities of the month, organized by type.
*
* An `Object` with the properties `tasks`, `events`, `appointments` and
* `meetings`, every property of the object is an `array`.
*
* @type {Planner}
*/
this.planner = createPlanner({
datebook,
YYMMDD: this.YYMMDD,
SCE: this.SCE,
});
/**
* The month days that are weekends.
*
* A number `array` that value of elements is like `Date.prototype.getDate()`.
*
* @type {Array.<number>}
*/
this.weekends = createWeekends({
YYMMDD: this.YYMMDD,
WEEKEND: this.WEEKEND,
SCE: this.SCE,
});
/**
* The month days that are holidays.
*
* A number `array` that value of elements is like a `Date.prototype.getDate()`.
*
* @type {Array.<number>}
*/
this.holidays = createHolidays(this.planner);
/**
* The month days that are non workdays.
*
* A number `array` that value of elements is like a `Date.prototype.getDate()`.
*
* @type {Array.<number>}
*/
this.nonworkdays = createNonWorkdays({
weekends: this.weekends,
holidays: this.holidays,
});
/**
* The details of the days of the month, a collection of information about
* all the days of the month.
*
* An object `array`, every `array` element contains information about a day
* of the month, If it is a weekend or a work day, if it has already elapsed,
* it is the current day, week number.
*
* @type {Days}
*/
this.days = createDays({
YYMMDD: this.YYMMDD,
SCE: this.SCE,
weekends: this.weekends,
nonworkdays: this.nonworkdays,
planner: this.planner,
});
/**
* Summary of the month with information on weeks, working days and dates.
*
* An `Object` with the properties `dates`, `days`, `weeks`, and `workdays`.
*
* @type {Summary}
*/
this.summary = createSummary({
YYMMDD: this.YYMMDD,
nonworkdays: this.nonworkdays,
});
}
/**
* The `addDatebook()` method adds scheduled activities.
*
* `datebook` value must be an object `array`, .
*
* @memberof Month
* @param {Datebook} datebook - Plans of the month.
* @returns {this} To chain methods.
* @throws {MonthError} If `datebook` is not an array.
* @throws {MonthError} If any `datebook` elements is not a valid object.
* @example const month = new Month();
*
* month.addDatebook([
* {
* date: '2021-01-10',
* title: 'Send final sales report',
* description: "Don't forget to attach graphics",
* type: 'task',
* holiday: true,
* },
* ]);
*
*/
addDatebook(datebook) {
if (!utils.isArray(datebook)) {
throw new MonthError(ERRORS.TYPE_DATEBOOK_OPTION);
}
if (!utils.isEmptyArray(datebook) && !isValid.datebook(datebook)) {
throw new MonthError(ERRORS.INVALID_DATEBOOK_SCHEMA);
}
const { tasks, events, appointments, meetings } = createPlanner({
datebook,
YYMMDD: this.YYMMDD,
SCE: this.SCE,
});
const {
tasks: Tasks,
events: Events,
appointments: Appointments,
meetings: Meetings,
} = this.planner;
// » JOIND OLD AND NEW DATEBOOK ITEMS
const addedTasks = [...Tasks, ...tasks];
const addedEvents = [...Events, ...events];
const addedAppointments = [...Appointments, ...appointments];
const addedMeetings = [...Meetings, ...meetings];
// » UPDATE DATEBOOK PROPERTIES
this.planner = {
tasks: addedTasks,
events: addedEvents,
appointments: addedAppointments,
meetings: addedMeetings,
};
// » UPDATE DAYS PROPERTIES
this.holidays = createHolidays(this.planner);
this.nonworkdays = createNonWorkdays({
weekends: this.weekends,
holidays: this.holidays,
});
this.days = createDays({
YYMMDD: this.YYMMDD,
SCE: this.SCE,
weekends: this.weekends,
nonworkdays: this.nonworkdays,
planner: this.planner,
});
// » UPDATE SUMMARY PROPERTIES
this.summary = createSummary({
YYMMDD: this.YYMMDD,
nonworkdays: this.nonworkdays,
});
return this;
}
}
// ━━ EXPORT MODULE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
module.exports = Month;