calendar/src/models/appointmentConfig.js

217 lines
5.9 KiB
JavaScript

/**
* @copyright Copyright (c) 2021 Richard Steinmetz <richard@steinmetz.cloud>
*
* @author Richard Steinmetz <richard@steinmetz.cloud>
*
* @license AGPL-3.0-or-later
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
import { generateUrl } from '@nextcloud/router'
import {
getEmptySlots,
vavailabilityToSlots,
} from '@nextcloud/calendar-availability-vue'
/** @class */
export default class AppointmentConfig {
/** @member {?number} */
id
/** @member {?string} */
token
/** @member {string} */
name
/** @member {string} */
description
/** @member {string} */
location
/** @member {string} */
visibility
/** @member {string} */
targetCalendarUri
/** @member {object} */
availability
/** @member {number} */
length
/** @member {number} */
increment
/** @member {number} */
preparationDuration
/** @member {number} */
followupDuration
/** @member {number} */
timeBeforeNextSlot
/** @member {?number} */
dailyMax
/** @member {?number} */
futureLimit
/** @member {?string[]} */
calendarFreeBusyUris
/** @member {bool} */
createTalkRoom
/**
* Create a new AppointmentConfig from the given plain object data
*
* @param {object} data Appointment config data to construct an instance from
* @param {?number} data.id Id
* @param {?string} data.token Token
* @param {string} data.name Name
* @param {string} data.description Description
* @param {string} data.location Location
* @param {string} data.visibility Visibility
* @param {string} data.targetCalendarUri Target calendar URI
* @param {object} data.availability Availability
* @param {number} data.length Length in seconds
* @param {number} data.increment Increment in seconds
* @param {number} data.preparationDuration Preparation duration in seconds
* @param {number} data.followupDuration Followup duration in seconds
* @param {number} data.timeBeforeNextSlot Time before next slot in seconds
* @param {?number} data.dailyMax Max daily slots
* @param {?number} data.futureLimit Limits how far in the future appointments can be booked
* @param {?string[]} data.calendarFreeBusyUris URIs of calendars to check for conflicts
* @param {bool} data.createTalkRoom Whether a Talk room should be created
*/
constructor(data) {
data ??= {}
this.id = data.id
this.token = data.token
this.name = data.name
this.description = data.description
this.location = data.location
this.visibility = data.visibility
this.targetCalendarUri = data.targetCalendarUri
this.availability = data.availability
this.length = tryParseInt(data.length) ?? 0
this.increment = tryParseInt(data.increment) ?? 0
this.preparationDuration = tryParseInt(data.preparationDuration)
this.followupDuration = tryParseInt(data.followupDuration)
this.timeBeforeNextSlot = tryParseInt(data.timeBeforeNextSlot)
this.dailyMax = tryParseInt(data.dailyMax)
this.futureLimit = tryParseInt(data.futureLimit)
this.calendarFreeBusyUris = data.calendarFreeBusyUris
this.createTalkRoom = data.createTalkRoom
}
/**
* Create a default appointment config instance
*
* @param {string} targetCalendarUri
* @param {ScheduleInbox} scheduleInbox
* @param {string} timezoneId fallback time zone when no schedule inbox availability is set
* @return {AppointmentConfig} Default appointment config instance
*/
static createDefault(targetCalendarUri, scheduleInbox, timezoneId) {
let slots = getEmptySlots()
if (scheduleInbox && scheduleInbox.availability) {
const converted = vavailabilityToSlots(scheduleInbox.availability)
slots = converted.slots
timezoneId = converted.timezoneId ?? timezoneId
} else {
// Set default availability to Mo-Fr 9-5
const tsAtTime = (hours, minutes) => Math.round((new Date()).setHours(hours, minutes, 0, 0) / 1000);
['MO', 'TU', 'WE', 'TH', 'FR'].forEach(day => slots[day].push({
start: tsAtTime(9, 0),
end: tsAtTime(17, 0),
}))
}
return new AppointmentConfig({
name: '',
description: '',
location: '',
targetCalendarUri,
availability: {
timezoneId,
slots,
},
visibility: 'PRIVATE',
length: 5 * 60,
increment: 15 * 60,
preparationDuration: 0,
followupDuration: 0,
timeBeforeNextSlot: 0,
calendarFreeBusyUris: [],
futureLimit: 2 * 30 * 24 * 60 * 60, // 2 months
})
}
/**
* Clone this instance
*
* @return {AppointmentConfig} A cloned version of this instance
*/
clone() {
return AppointmentConfig.fromJSON(JSON.stringify(this))
}
/**
* Parse a JSON string into a new AppointmentConfig instance
*
* @param {string} jsonString The JSON string to parse
* @return {AppointmentConfig} New instance parsed from given JSON string
*/
static fromJSON(jsonString) {
return new AppointmentConfig(JSON.parse(jsonString))
}
/**
* Get the absolute booking URL of this instance
*
* @return {string} Absolute URL
*/
get bookingUrl() {
const baseUrl = `${window.location.protocol}//${window.location.hostname}`
const relativeUrl = generateUrl('/apps/calendar/appointment/{token}', {
token: this.token,
})
return baseUrl + relativeUrl
}
}
/**
* Try to parse an int from the given value or return undefined
*
* @param {string|number|null|undefined} value The value to parse
* @return {number|undefined} Parsed number or undefined
*/
function tryParseInt(value) {
if (value === null || value === undefined) {
return undefined
}
return parseInt(value)
}