@openinc/parse-server-opendash 4.1.8 → 4.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/features/openservice/init.js +4 -0
- package/dist/features/openservice/schedules/calendarSync/types/Event.d.ts +2 -0
- package/dist/features/openservice/serviceSchedules/acl/assignmentAcl.d.ts +30 -0
- package/dist/features/openservice/serviceSchedules/acl/assignmentAcl.js +85 -0
- package/dist/features/openservice/serviceSchedules/acl/index.d.ts +1 -0
- package/dist/features/openservice/serviceSchedules/acl/index.js +6 -0
- package/dist/features/openservice/serviceSchedules/calendarSync/functions/eventToPlan.d.ts +50 -0
- package/dist/features/openservice/serviceSchedules/calendarSync/functions/eventToPlan.js +115 -0
- package/dist/features/openservice/serviceSchedules/calendarSync/functions/outlookRecurrence.d.ts +59 -0
- package/dist/features/openservice/serviceSchedules/calendarSync/functions/outlookRecurrence.js +353 -0
- package/dist/features/openservice/serviceSchedules/calendarSync/functions/planToEvent.d.ts +27 -0
- package/dist/features/openservice/serviceSchedules/calendarSync/functions/planToEvent.js +145 -0
- package/dist/features/openservice/serviceSchedules/calendarSync/index.d.ts +8 -0
- package/dist/features/openservice/serviceSchedules/calendarSync/index.js +28 -0
- package/dist/features/openservice/serviceSchedules/calendarSync/service/ServiceCalendarSyncPoller.d.ts +54 -0
- package/dist/features/openservice/serviceSchedules/calendarSync/service/ServiceCalendarSyncPoller.js +248 -0
- package/dist/features/openservice/serviceSchedules/calendarSync/service/serviceCalendarMeta.d.ts +55 -0
- package/dist/features/openservice/serviceSchedules/calendarSync/service/serviceCalendarMeta.js +109 -0
- package/dist/features/openservice/serviceSchedules/calendarSync/service/syncPlanToCalendar.d.ts +26 -0
- package/dist/features/openservice/serviceSchedules/calendarSync/service/syncPlanToCalendar.js +122 -0
- package/dist/features/openservice/serviceSchedules/calendarSync/types/ServiceSyncTypes.d.ts +55 -0
- package/dist/features/openservice/serviceSchedules/calendarSync/types/ServiceSyncTypes.js +8 -0
- package/dist/features/openservice/serviceSchedules/initServiceSchedulesFeature.d.ts +10 -0
- package/dist/features/openservice/serviceSchedules/initServiceSchedulesFeature.js +39 -0
- package/dist/features/openservice/serviceTickets/alarmActions/functions/alarmTicketFields.d.ts +35 -0
- package/dist/features/openservice/serviceTickets/alarmActions/functions/alarmTicketFields.js +103 -0
- package/dist/features/openservice/serviceTickets/alarmActions/index.d.ts +7 -0
- package/dist/features/openservice/serviceTickets/alarmActions/index.js +16 -0
- package/dist/features/openservice/serviceTickets/alarmActions/service/createAlarmTicket.d.ts +33 -0
- package/dist/features/openservice/serviceTickets/alarmActions/service/createAlarmTicket.js +179 -0
- package/dist/features/openservice/serviceTickets/alarmActions/service/ensureAlarmTicketAction.d.ts +22 -0
- package/dist/features/openservice/serviceTickets/alarmActions/service/ensureAlarmTicketAction.js +127 -0
- package/dist/features/openservice/serviceTickets/alarmActions/types/AlarmTicketTypes.d.ts +80 -0
- package/dist/features/openservice/serviceTickets/alarmActions/types/AlarmTicketTypes.js +28 -0
- package/dist/features/permissions/types/Permissions.d.ts +1 -2
- package/dist/features/permissions/types/Permissions.js +8 -2
- package/dist/functions/openinc-openservice-alarm-create-ticket.d.ts +25 -0
- package/dist/functions/openinc-openservice-alarm-create-ticket.js +63 -0
- package/dist/functions/openinc-openservice-notify-contacts.d.ts +16 -0
- package/dist/functions/openinc-openservice-notify-contacts.js +82 -0
- package/dist/functions/openinc-openservice-schedule-calendar-sync.d.ts +15 -0
- package/dist/functions/openinc-openservice-schedule-calendar-sync.js +60 -0
- package/dist/hooks/Service_Schedule.js +89 -16
- package/dist/hooks/Service_Schedule_Template.js +28 -7
- package/dist/hooks/Service_Ticket.js +2 -2
- package/dist/types/Service_Schedule.d.ts +12 -0
- package/dist/types/Service_Schedule.js +21 -0
- package/dist/types/Service_Schedule_Execution.d.ts +6 -3
- package/dist/types/Service_Schedule_Execution.js +6 -0
- package/dist/types/Service_Schedule_Template.d.ts +7 -0
- package/dist/types/Service_Schedule_Template.js +12 -0
- package/package.json +5 -4
- package/schema/Service_Schedule.json +17 -0
- package/schema/Service_Schedule_Execution.json +5 -0
- package/schema/Service_Schedule_Template.json +9 -0
- package/dist/features/ruleset/applyRuleSet.d.ts +0 -41
- package/dist/features/ruleset/applyRuleSet.js +0 -81
|
@@ -5,6 +5,8 @@ const initIssuecategory_js_1 = require("./issuecategory/initIssuecategory.js");
|
|
|
5
5
|
const initKanbanStates_js_1 = require("./kanbanState/initKanbanStates.js");
|
|
6
6
|
const initMessages_js_1 = require("./messages/initMessages.js");
|
|
7
7
|
const initSchedulesFeature_js_1 = require("./schedules/initSchedulesFeature.js");
|
|
8
|
+
const initServiceSchedulesFeature_js_1 = require("./serviceSchedules/initServiceSchedulesFeature.js");
|
|
9
|
+
const index_js_1 = require("./serviceTickets/alarmActions/index.js");
|
|
8
10
|
const checkStatus_js_1 = require("./schedules/mappings/checkStatus.js");
|
|
9
11
|
const reassignScheduleExecutions_js_1 = require("./schedules/mappings/reassignScheduleExecutions.js");
|
|
10
12
|
const initTicket_js_1 = require("./ticket/initTicket.js");
|
|
@@ -20,5 +22,7 @@ async function init() {
|
|
|
20
22
|
await (0, reassignScheduleExecutions_js_1.reassignScheduleExecutions)();
|
|
21
23
|
await (0, checkStatus_js_1.checkStatus)();
|
|
22
24
|
await (0, initSchedulesFeature_js_1.initSchedulesFeature)();
|
|
25
|
+
await (0, initServiceSchedulesFeature_js_1.initServiceSchedulesFeature)();
|
|
26
|
+
await (0, index_js_1.ensureAlarmTicketAction)();
|
|
23
27
|
console.log("open.SERVICE feature initialized.");
|
|
24
28
|
}
|
|
@@ -35,6 +35,8 @@ export type RecurrencePattern = {
|
|
|
35
35
|
daysOfWeek?: Array<"Sunday" | "Monday" | "Tuesday" | "Wednesday" | "Thursday" | "Friday" | "Saturday">;
|
|
36
36
|
firstDayOfWeek?: "Sunday" | "Monday" | "Tuesday" | "Wednesday" | "Thursday" | "Friday" | "Saturday";
|
|
37
37
|
month?: number;
|
|
38
|
+
/** Day of the month for `absoluteMonthly` / `absoluteYearly` patterns. */
|
|
39
|
+
dayOfMonth?: number;
|
|
38
40
|
index?: "first" | "second" | "third" | "fourth" | "last";
|
|
39
41
|
};
|
|
40
42
|
export type RecurrenceRange = {
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Marks a save this module made itself, so the `afterSave` that follows it does
|
|
3
|
+
* not start over. Without it every ACL write would trigger another pass — the
|
|
4
|
+
* second one is a no-op and would stop, but the round trip is pure waste.
|
|
5
|
+
*/
|
|
6
|
+
export declare const ASSIGNMENT_ACL_CONTEXT_FLAG = "fromAssignmentAcl";
|
|
7
|
+
/** Which relations grant read access on this class. */
|
|
8
|
+
export interface AssignmentAclOptions {
|
|
9
|
+
/** Relation of `_Role`s that may read the record. */
|
|
10
|
+
roleRelations: string[];
|
|
11
|
+
/** Relation of `_User`s that may read the record. */
|
|
12
|
+
userRelations?: string[];
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Who may see a maintenance plan (or a template): its creator, the roles and
|
|
16
|
+
* users assigned to it, and the administrators — nobody else.
|
|
17
|
+
*
|
|
18
|
+
* **Why this is rebuilt rather than amended.** Un-assigning somebody has to take
|
|
19
|
+
* their access away again, and an ACL carries no record of *why* a grant is
|
|
20
|
+
* there. Adding grants on every save would therefore be a one-way door: a role
|
|
21
|
+
* removed from the plan would keep reading it forever. The ACL is derived from
|
|
22
|
+
* the assignments instead, so it always says exactly what the assignments say.
|
|
23
|
+
*
|
|
24
|
+
* `od-admin` and `od-tenant-admin-<tenant>` are re-added by `defaultAclHandler`
|
|
25
|
+
* on the save that follows, so an assignment can never lock administrators out.
|
|
26
|
+
*
|
|
27
|
+
* Runs in `afterSave`, not `beforeSave`: relations are only queryable once the
|
|
28
|
+
* pending add/remove operations have been applied, which happens on save.
|
|
29
|
+
*/
|
|
30
|
+
export declare function syncAssignmentAcl(object: Parse.Object, options: AssignmentAclOptions): Promise<void>;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.ASSIGNMENT_ACL_CONTEXT_FLAG = void 0;
|
|
7
|
+
exports.syncAssignmentAcl = syncAssignmentAcl;
|
|
8
|
+
const node_1 = __importDefault(require("parse/node"));
|
|
9
|
+
/**
|
|
10
|
+
* Marks a save this module made itself, so the `afterSave` that follows it does
|
|
11
|
+
* not start over. Without it every ACL write would trigger another pass — the
|
|
12
|
+
* second one is a no-op and would stop, but the round trip is pure waste.
|
|
13
|
+
*/
|
|
14
|
+
exports.ASSIGNMENT_ACL_CONTEXT_FLAG = "fromAssignmentAcl";
|
|
15
|
+
/**
|
|
16
|
+
* Who may see a maintenance plan (or a template): its creator, the roles and
|
|
17
|
+
* users assigned to it, and the administrators — nobody else.
|
|
18
|
+
*
|
|
19
|
+
* **Why this is rebuilt rather than amended.** Un-assigning somebody has to take
|
|
20
|
+
* their access away again, and an ACL carries no record of *why* a grant is
|
|
21
|
+
* there. Adding grants on every save would therefore be a one-way door: a role
|
|
22
|
+
* removed from the plan would keep reading it forever. The ACL is derived from
|
|
23
|
+
* the assignments instead, so it always says exactly what the assignments say.
|
|
24
|
+
*
|
|
25
|
+
* `od-admin` and `od-tenant-admin-<tenant>` are re-added by `defaultAclHandler`
|
|
26
|
+
* on the save that follows, so an assignment can never lock administrators out.
|
|
27
|
+
*
|
|
28
|
+
* Runs in `afterSave`, not `beforeSave`: relations are only queryable once the
|
|
29
|
+
* pending add/remove operations have been applied, which happens on save.
|
|
30
|
+
*/
|
|
31
|
+
async function syncAssignmentAcl(object, options) {
|
|
32
|
+
const acl = new node_1.default.ACL();
|
|
33
|
+
// Administrators. Mirrors `defaultAclHandler` so the ACL is already complete
|
|
34
|
+
// when it is written, rather than only after the next save.
|
|
35
|
+
acl.setRoleReadAccess("od-admin", true);
|
|
36
|
+
acl.setRoleWriteAccess("od-admin", true);
|
|
37
|
+
const tenant = object.get("tenant");
|
|
38
|
+
if (tenant) {
|
|
39
|
+
acl.setRoleReadAccess(`od-tenant-admin-${tenant.id}`, true);
|
|
40
|
+
acl.setRoleWriteAccess(`od-tenant-admin-${tenant.id}`, true);
|
|
41
|
+
}
|
|
42
|
+
// The creator keeps write — the record is theirs.
|
|
43
|
+
const creator = object.get("user");
|
|
44
|
+
if (creator?.id) {
|
|
45
|
+
acl.setReadAccess(creator.id, true);
|
|
46
|
+
acl.setWriteAccess(creator.id, true);
|
|
47
|
+
}
|
|
48
|
+
for (const relation of options.roleRelations) {
|
|
49
|
+
for (const role of await readRelation(object, relation)) {
|
|
50
|
+
const name = role.get("name");
|
|
51
|
+
// Roles are addressed by name in an ACL, not by id.
|
|
52
|
+
if (name)
|
|
53
|
+
acl.setRoleReadAccess(name, true);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
for (const relation of options.userRelations ?? []) {
|
|
57
|
+
for (const user of await readRelation(object, relation)) {
|
|
58
|
+
if (user.id)
|
|
59
|
+
acl.setReadAccess(user.id, true);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (sameAcl(object.getACL(), acl))
|
|
63
|
+
return;
|
|
64
|
+
object.setACL(acl);
|
|
65
|
+
await object.save(null, {
|
|
66
|
+
useMasterKey: true,
|
|
67
|
+
context: { [exports.ASSIGNMENT_ACL_CONTEXT_FLAG]: true },
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
/** The members of a relation, or an empty list when it has none. */
|
|
71
|
+
async function readRelation(object, field) {
|
|
72
|
+
try {
|
|
73
|
+
return await object.relation(field).query().find({ useMasterKey: true });
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
// A relation that was never written has no backing table yet.
|
|
77
|
+
return [];
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
/** Whether two ACLs grant exactly the same thing. */
|
|
81
|
+
function sameAcl(current, next) {
|
|
82
|
+
if (!current)
|
|
83
|
+
return false;
|
|
84
|
+
return JSON.stringify(current.toJSON()) === JSON.stringify(next.toJSON());
|
|
85
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { ASSIGNMENT_ACL_CONTEXT_FLAG, syncAssignmentAcl, type AssignmentAclOptions, } from "./assignmentAcl.js";
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.syncAssignmentAcl = exports.ASSIGNMENT_ACL_CONTEXT_FLAG = void 0;
|
|
4
|
+
var assignmentAcl_js_1 = require("./assignmentAcl.js");
|
|
5
|
+
Object.defineProperty(exports, "ASSIGNMENT_ACL_CONTEXT_FLAG", { enumerable: true, get: function () { return assignmentAcl_js_1.ASSIGNMENT_ACL_CONTEXT_FLAG; } });
|
|
6
|
+
Object.defineProperty(exports, "syncAssignmentAcl", { enumerable: true, get: function () { return assignmentAcl_js_1.syncAssignmentAcl; } });
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { GraphEventResponse } from "../../../schedules/calendarSync/types/Event.js";
|
|
2
|
+
import type { ServiceCadence, ServiceNotificationRule } from "../types/ServiceSyncTypes.js";
|
|
3
|
+
/**
|
|
4
|
+
* The rule id the sync owns.
|
|
5
|
+
*
|
|
6
|
+
* An Outlook reminder becomes one notification rule on the plan, and it always
|
|
7
|
+
* gets **this** id: the sync may replace or remove its own rule on every
|
|
8
|
+
* inbound change, and must never touch the rules a user wrote in the plan
|
|
9
|
+
* editor. A stable id also keeps the scheduler's dedup key
|
|
10
|
+
* (`{scheduleId, ruleId, occurrence}`) stable, so re-syncing an unchanged event
|
|
11
|
+
* does not notify twice.
|
|
12
|
+
*/
|
|
13
|
+
export declare const OUTLOOK_REMINDER_RULE_ID = "outlook-reminder";
|
|
14
|
+
/** What an Outlook event says about a maintenance plan. */
|
|
15
|
+
export type PlanFieldsFromEvent = {
|
|
16
|
+
/** Plan title — the subject, minus the machine prefix if there was one. */
|
|
17
|
+
title: string;
|
|
18
|
+
/** Machine name from the subject convention, if the subject used it. */
|
|
19
|
+
equipmentName?: string;
|
|
20
|
+
cadence: ServiceCadence;
|
|
21
|
+
/** The event's note, kept as the description of the plan's first step. */
|
|
22
|
+
description?: string;
|
|
23
|
+
/** The event's reminder, as the sync's own notification rule. */
|
|
24
|
+
notification?: ServiceNotificationRule;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Read a calendar event as maintenance plan fields.
|
|
28
|
+
*
|
|
29
|
+
* Pure — resolving the machine and writing the plan is the poller's job.
|
|
30
|
+
* Returns `undefined` for an event without a usable start, which is the one
|
|
31
|
+
* thing a plan cannot do without.
|
|
32
|
+
*/
|
|
33
|
+
export declare function eventToPlanFields(event: GraphEventResponse): PlanFieldsFromEvent | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* Split `"Flex 1" - Ölwechsel` into machine and title.
|
|
36
|
+
*
|
|
37
|
+
* Same convention as v1 (separator `" - "`, quotes optional), where the prefix
|
|
38
|
+
* named a `Maintenance_Source`; in v2 it names the machine. Unlike v1 the
|
|
39
|
+
* prefix is *removed* from the title — the machine is a field of its own here,
|
|
40
|
+
* and repeating it in the title is what made v1's plan lists unreadable.
|
|
41
|
+
*/
|
|
42
|
+
export declare function parseSubject(subject: string | undefined): {
|
|
43
|
+
equipmentName?: string;
|
|
44
|
+
title: string;
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Put the sync's rule into a plan's rule list, replacing an older one of its
|
|
48
|
+
* own and leaving every user-written rule untouched.
|
|
49
|
+
*/
|
|
50
|
+
export declare function mergeReminderRule(existing: ServiceNotificationRule[] | undefined, rule: ServiceNotificationRule | undefined): ServiceNotificationRule[];
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OUTLOOK_REMINDER_RULE_ID = void 0;
|
|
4
|
+
exports.eventToPlanFields = eventToPlanFields;
|
|
5
|
+
exports.parseSubject = parseSubject;
|
|
6
|
+
exports.mergeReminderRule = mergeReminderRule;
|
|
7
|
+
const outlookRecurrence_js_1 = require("./outlookRecurrence.js");
|
|
8
|
+
/**
|
|
9
|
+
* The rule id the sync owns.
|
|
10
|
+
*
|
|
11
|
+
* An Outlook reminder becomes one notification rule on the plan, and it always
|
|
12
|
+
* gets **this** id: the sync may replace or remove its own rule on every
|
|
13
|
+
* inbound change, and must never touch the rules a user wrote in the plan
|
|
14
|
+
* editor. A stable id also keeps the scheduler's dedup key
|
|
15
|
+
* (`{scheduleId, ruleId, occurrence}`) stable, so re-syncing an unchanged event
|
|
16
|
+
* does not notify twice.
|
|
17
|
+
*/
|
|
18
|
+
exports.OUTLOOK_REMINDER_RULE_ID = "outlook-reminder";
|
|
19
|
+
/**
|
|
20
|
+
* Read a calendar event as maintenance plan fields.
|
|
21
|
+
*
|
|
22
|
+
* Pure — resolving the machine and writing the plan is the poller's job.
|
|
23
|
+
* Returns `undefined` for an event without a usable start, which is the one
|
|
24
|
+
* thing a plan cannot do without.
|
|
25
|
+
*/
|
|
26
|
+
function eventToPlanFields(event) {
|
|
27
|
+
const start = eventStart(event);
|
|
28
|
+
if (!start)
|
|
29
|
+
return undefined;
|
|
30
|
+
const { equipmentName, title } = parseSubject(event.subject);
|
|
31
|
+
return {
|
|
32
|
+
title: title || "Outlook",
|
|
33
|
+
equipmentName,
|
|
34
|
+
cadence: (0, outlookRecurrence_js_1.eventToCadence)(start, event.recurrence ?? undefined),
|
|
35
|
+
description: event.bodyPreview?.trim() || undefined,
|
|
36
|
+
notification: reminderToRule(event),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The start of the series as an absolute instant.
|
|
41
|
+
*
|
|
42
|
+
* Graph returns `dateTime` in the time zone named next to it, and this server
|
|
43
|
+
* asks for none — so in practice it is UTC and the time of day survives, which
|
|
44
|
+
* matters because the plan's `dtstart` is what the frontend shows. If the event
|
|
45
|
+
* ever arrives in another zone, the day is kept and the time falls back to
|
|
46
|
+
* midnight rather than being shifted by a guess.
|
|
47
|
+
*/
|
|
48
|
+
function eventStart(event) {
|
|
49
|
+
const raw = event.start?.dateTime;
|
|
50
|
+
if (!raw)
|
|
51
|
+
return undefined;
|
|
52
|
+
const zone = event.start?.timeZone;
|
|
53
|
+
if (!zone || /^utc$/i.test(zone)) {
|
|
54
|
+
// Graph omits the offset; the trailing fraction can be 7 digits long.
|
|
55
|
+
const normalized = raw.endsWith("Z") ? raw : `${raw.slice(0, 23)}Z`;
|
|
56
|
+
const parsed = new Date(normalized);
|
|
57
|
+
if (!Number.isNaN(parsed.getTime()))
|
|
58
|
+
return parsed;
|
|
59
|
+
}
|
|
60
|
+
const dateOnly = new Date(`${raw.slice(0, 10)}T00:00:00.000Z`);
|
|
61
|
+
return Number.isNaN(dateOnly.getTime()) ? undefined : dateOnly;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Split `"Flex 1" - Ölwechsel` into machine and title.
|
|
65
|
+
*
|
|
66
|
+
* Same convention as v1 (separator `" - "`, quotes optional), where the prefix
|
|
67
|
+
* named a `Maintenance_Source`; in v2 it names the machine. Unlike v1 the
|
|
68
|
+
* prefix is *removed* from the title — the machine is a field of its own here,
|
|
69
|
+
* and repeating it in the title is what made v1's plan lists unreadable.
|
|
70
|
+
*/
|
|
71
|
+
function parseSubject(subject) {
|
|
72
|
+
const value = (subject ?? "").trim();
|
|
73
|
+
const separator = value.indexOf(" - ");
|
|
74
|
+
if (separator === -1)
|
|
75
|
+
return { title: value };
|
|
76
|
+
const prefix = value
|
|
77
|
+
.slice(0, separator)
|
|
78
|
+
.trim()
|
|
79
|
+
.replace(/^"(.*)"$/, "$1")
|
|
80
|
+
.trim();
|
|
81
|
+
const rest = value.slice(separator + 3).trim();
|
|
82
|
+
if (!prefix || !rest)
|
|
83
|
+
return { title: value };
|
|
84
|
+
return { equipmentName: prefix, title: rest };
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* An Outlook reminder as a notification rule.
|
|
88
|
+
*
|
|
89
|
+
* Outlook counts minutes, a plan counts days — a maintenance date is a day, not
|
|
90
|
+
* a minute. Anything under a day therefore becomes "on the due date" instead of
|
|
91
|
+
* being rounded up to a day early.
|
|
92
|
+
*/
|
|
93
|
+
function reminderToRule(event) {
|
|
94
|
+
if (!event.isReminderOn)
|
|
95
|
+
return undefined;
|
|
96
|
+
const minutes = event.reminderMinutesBeforeStart;
|
|
97
|
+
if (typeof minutes !== "number" || minutes < 0)
|
|
98
|
+
return undefined;
|
|
99
|
+
const offsetDays = Math.floor(minutes / (60 * 24));
|
|
100
|
+
return {
|
|
101
|
+
id: exports.OUTLOOK_REMINDER_RULE_ID,
|
|
102
|
+
trigger: offsetDays > 0 ? "before" : "due",
|
|
103
|
+
...(offsetDays > 0 ? { offsetDays } : {}),
|
|
104
|
+
assignedRoles: true,
|
|
105
|
+
serviceProviders: true,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Put the sync's rule into a plan's rule list, replacing an older one of its
|
|
110
|
+
* own and leaving every user-written rule untouched.
|
|
111
|
+
*/
|
|
112
|
+
function mergeReminderRule(existing, rule) {
|
|
113
|
+
const others = (existing ?? []).filter((entry) => entry.id !== exports.OUTLOOK_REMINDER_RULE_ID);
|
|
114
|
+
return rule ? [...others, rule] : others;
|
|
115
|
+
}
|
package/dist/features/openservice/serviceSchedules/calendarSync/functions/outlookRecurrence.d.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { Recurrence } from "../../../schedules/calendarSync/types/Event.js";
|
|
2
|
+
import type { ServiceCadence } from "../types/ServiceSyncTypes.js";
|
|
3
|
+
/** A recurrence rule taken apart into its parts. */
|
|
4
|
+
export type RRuleParts = {
|
|
5
|
+
freq?: "DAILY" | "WEEKLY" | "MONTHLY" | "YEARLY";
|
|
6
|
+
interval?: number;
|
|
7
|
+
byday?: string[];
|
|
8
|
+
bymonthday?: number[];
|
|
9
|
+
bymonth?: number[];
|
|
10
|
+
count?: number;
|
|
11
|
+
wkst?: string;
|
|
12
|
+
/**
|
|
13
|
+
* "The nth of the matching days" — `BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1` is
|
|
14
|
+
* "last workday of the month".
|
|
15
|
+
*
|
|
16
|
+
* Outlook has no equivalent: its `relativeMonthly` pattern takes *one*
|
|
17
|
+
* weekday and a position. With a single weekday the two agree (that is the
|
|
18
|
+
* ordinal `BYDAY` form); with several, the rule cannot be expressed — see
|
|
19
|
+
* {@link calendarUnsupportedReason}.
|
|
20
|
+
*/
|
|
21
|
+
bysetpos?: number;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* Read an RRULE string into its parts.
|
|
25
|
+
*
|
|
26
|
+
* Tolerant on purpose: an unknown key is dropped rather than throwing, because
|
|
27
|
+
* a rule that came from Outlook, from the editor, or from a hand-written import
|
|
28
|
+
* all end up here, and a single unexpected part must not stop a sync.
|
|
29
|
+
*/
|
|
30
|
+
export declare function parseRRule(rrule: string | undefined): RRuleParts;
|
|
31
|
+
/** Write the parts back into an RRULE string, in the canonical part order. */
|
|
32
|
+
export declare function buildRRule(parts: RRuleParts): string;
|
|
33
|
+
/**
|
|
34
|
+
* An Outlook event's timing, as a v2 cadence.
|
|
35
|
+
*
|
|
36
|
+
* A non-recurring event becomes a one-off — the same shape the frontend's
|
|
37
|
+
* `oneOffCadence()` produces (`FREQ=DAILY;INTERVAL=1;COUNT=1`), so a plan
|
|
38
|
+
* created from a single Outlook appointment is indistinguishable from one
|
|
39
|
+
* created by hand.
|
|
40
|
+
*/
|
|
41
|
+
export declare function eventToCadence(start: Date, recurrence?: Recurrence): ServiceCadence;
|
|
42
|
+
/**
|
|
43
|
+
* Why this cadence cannot be written to a calendar — `undefined` when it can.
|
|
44
|
+
*
|
|
45
|
+
* Outlook's recurrence vocabulary is a **subset** of RFC 5545, and the gap is
|
|
46
|
+
* not cosmetic: a rule we cannot express would have to be approximated, and an
|
|
47
|
+
* approximated maintenance date in someone's calendar is worse than no entry
|
|
48
|
+
* at all. The sync therefore leaves such plans alone instead of guessing (see
|
|
49
|
+
* `syncPlanToCalendar`).
|
|
50
|
+
*/
|
|
51
|
+
export declare function calendarUnsupportedReason(cadence: ServiceCadence | undefined): string | undefined;
|
|
52
|
+
/**
|
|
53
|
+
* A v2 cadence as an Outlook recurrence — `undefined` for a one-off, which
|
|
54
|
+
* Graph expects as an event without a `recurrence` at all.
|
|
55
|
+
*
|
|
56
|
+
* `startDate` of the range is always the series start: Graph rejects a
|
|
57
|
+
* recurrence whose range starts on a different day than the event.
|
|
58
|
+
*/
|
|
59
|
+
export declare function cadenceToRecurrence(cadence: ServiceCadence | undefined, start: Date): Recurrence | undefined;
|