@aglyn/plugins-events-calendar 1.0.0-beta.220 → 1.0.0-beta.222
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/package.json +9 -9
- package/src/lib/components/events-console-page.js +65 -27
- package/src/lib/components/events-console-page.js.map +1 -1
- package/src/lib/declarations.console-server.d.ts +28 -0
- package/src/lib/declarations.console-server.js +62 -0
- package/src/lib/declarations.console-server.js.map +1 -0
- package/src/lib/model/event-write.d.ts +101 -0
- package/src/lib/model/event-write.js +104 -0
- package/src/lib/model/event-write.js.map +1 -0
- package/src/lib/plugin.d.ts +2 -1
- package/src/lib/plugin.js +14 -1
- package/src/lib/plugin.js.map +1 -1
- package/src/lib/transfer/events-transfer-catalog.d.ts +51 -0
- package/src/lib/transfer/events-transfer-catalog.js +279 -0
- package/src/lib/transfer/events-transfer-catalog.js.map +1 -0
- package/src/lib/transfer/events-transfer-key.d.ts +24 -0
- package/src/lib/transfer/events-transfer-key.js +23 -0
- package/src/lib/transfer/events-transfer-key.js.map +1 -0
- package/src/lib/transfer/events-transfer.d.ts +73 -0
- package/src/lib/transfer/events-transfer.js +530 -0
- package/src/lib/transfer/events-transfer.js.map +1 -0
- package/src/lib/transfer/events-transfer.server.d.ts +26 -0
- package/src/lib/transfer/events-transfer.server.js +45 -0
- package/src/lib/transfer/events-transfer.server.js.map +1 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ import { MEDIA_ALT_MAX_LENGTH } from "@aglyn/aglyn/app-utils/media-alt";
|
|
17
|
+
/**
|
|
18
|
+
* THE ONE RULE FOR WHAT AN EVENT STORES.
|
|
19
|
+
*
|
|
20
|
+
* `hosts/{hostId}/events/{id}` has two writers that must agree: the Events
|
|
21
|
+
* page's editor and the events import. Both hand what they were given to
|
|
22
|
+
* {@link eventWrite} and store exactly what it answers, so a title is cut,
|
|
23
|
+
* an end time is repaired and a cover description is dropped the same way
|
|
24
|
+
* whichever of them wrote the event.
|
|
25
|
+
*
|
|
26
|
+
* Pure: no Firestore, no clock. Each writer adds its own `updatedAt` /
|
|
27
|
+
* `createdAt` and turns {@link EventWrite.remove} into its SDK's delete
|
|
28
|
+
* sentinel.
|
|
29
|
+
*/ /** Longest stored title. */ export const EVENT_TITLE_MAX_LENGTH = 150;
|
|
30
|
+
/** Longest stored location. */ export const EVENT_LOCATION_MAX_LENGTH = 200;
|
|
31
|
+
/** Longest stored organizer. */ export const EVENT_ORGANIZER_MAX_LENGTH = 100;
|
|
32
|
+
/** Longest stored description. */ export const EVENT_DESCRIPTION_MAX_LENGTH = 2000;
|
|
33
|
+
/** Longest stored cover description: the platform's cap for any `alt`. */ export const EVENT_COVER_ALT_MAX_LENGTH = MEDIA_ALT_MAX_LENGTH;
|
|
34
|
+
/** How long an event without an end after its start lasts. */ export const EVENT_DEFAULT_DURATION_MS = 60 * 60 * 1000;
|
|
35
|
+
/** The statuses an editor or a file may set. `deleted` is only ever written by a delete. */ export const EVENT_STATUSES = [
|
|
36
|
+
'draft',
|
|
37
|
+
'published'
|
|
38
|
+
];
|
|
39
|
+
/** A status as typed, folded to one an event may hold, or `null` for anything else. */ export function eventStatusOf(value) {
|
|
40
|
+
const text = typeof value === 'string' ? value.trim().toLowerCase() : '';
|
|
41
|
+
return EVENT_STATUSES.includes(text) ? text : null;
|
|
42
|
+
}
|
|
43
|
+
/** Why an event cannot be written as given, or `null` when it can. */ export function eventWriteProblem(input) {
|
|
44
|
+
var _input_title;
|
|
45
|
+
if (!String((_input_title = input.title) != null ? _input_title : '').trim()) return 'An event needs a title.';
|
|
46
|
+
if (!Number.isFinite(input.startsAtMs) || !input.startsAtMs) return 'An event needs a start time.';
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
/** The end an event stores: the one given when it is after the start, else the default duration. */ export function eventEndsAtMs(startsAtMs, endsAtMs) {
|
|
50
|
+
return typeof endsAtMs === 'number' && Number.isFinite(endsAtMs) && endsAtMs > startsAtMs ? endsAtMs : startsAtMs + EVENT_DEFAULT_DURATION_MS;
|
|
51
|
+
}
|
|
52
|
+
function capped(value, max) {
|
|
53
|
+
return String(value != null ? value : '').trim().slice(0, max);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* The stored form of an event. Throws for input {@link eventWriteProblem}
|
|
57
|
+
* refuses; both writers check it first and say why in their own words.
|
|
58
|
+
*
|
|
59
|
+
* The cover description is tied to the cover: with no cover, or an empty
|
|
60
|
+
* description, it is removed rather than left describing a picture that
|
|
61
|
+
* changed.
|
|
62
|
+
*/ export function eventWrite(input, options = {}) {
|
|
63
|
+
const problem = eventWriteProblem(input);
|
|
64
|
+
if (problem) throw new Error(problem);
|
|
65
|
+
const remove = [];
|
|
66
|
+
const fields = {
|
|
67
|
+
title: capped(input.title, EVENT_TITLE_MAX_LENGTH),
|
|
68
|
+
startsAtMs: input.startsAtMs,
|
|
69
|
+
endsAtMs: eventEndsAtMs(input.startsAtMs, input.endsAtMs),
|
|
70
|
+
status: input.status
|
|
71
|
+
};
|
|
72
|
+
const optional = [
|
|
73
|
+
[
|
|
74
|
+
'location',
|
|
75
|
+
EVENT_LOCATION_MAX_LENGTH
|
|
76
|
+
],
|
|
77
|
+
[
|
|
78
|
+
'organizer',
|
|
79
|
+
EVENT_ORGANIZER_MAX_LENGTH
|
|
80
|
+
],
|
|
81
|
+
[
|
|
82
|
+
'description',
|
|
83
|
+
EVENT_DESCRIPTION_MAX_LENGTH
|
|
84
|
+
],
|
|
85
|
+
[
|
|
86
|
+
'coverImage',
|
|
87
|
+
Number.POSITIVE_INFINITY
|
|
88
|
+
]
|
|
89
|
+
];
|
|
90
|
+
for (const [key, max] of optional){
|
|
91
|
+
const value = capped(input[key], max);
|
|
92
|
+
if (value) fields[key] = value;
|
|
93
|
+
else if (options.clearBlank) remove.push(key);
|
|
94
|
+
}
|
|
95
|
+
const alt = capped(input.coverImageAlt, EVENT_COVER_ALT_MAX_LENGTH);
|
|
96
|
+
if (fields.coverImage && alt) fields.coverImageAlt = alt;
|
|
97
|
+
else remove.push('coverImageAlt');
|
|
98
|
+
return {
|
|
99
|
+
fields,
|
|
100
|
+
remove
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
//# sourceMappingURL=event-write.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/events-calendar/src/lib/model/event-write.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { MEDIA_ALT_MAX_LENGTH } from '@aglyn/aglyn/app-utils/media-alt'\n\n/**\n * THE ONE RULE FOR WHAT AN EVENT STORES.\n *\n * `hosts/{hostId}/events/{id}` has two writers that must agree: the Events\n * page's editor and the events import. Both hand what they were given to\n * {@link eventWrite} and store exactly what it answers, so a title is cut,\n * an end time is repaired and a cover description is dropped the same way\n * whichever of them wrote the event.\n *\n * Pure: no Firestore, no clock. Each writer adds its own `updatedAt` /\n * `createdAt` and turns {@link EventWrite.remove} into its SDK's delete\n * sentinel.\n */\n\n/** Longest stored title. */\nexport const EVENT_TITLE_MAX_LENGTH = 150\n/** Longest stored location. */\nexport const EVENT_LOCATION_MAX_LENGTH = 200\n/** Longest stored organizer. */\nexport const EVENT_ORGANIZER_MAX_LENGTH = 100\n/** Longest stored description. */\nexport const EVENT_DESCRIPTION_MAX_LENGTH = 2000\n/** Longest stored cover description: the platform's cap for any `alt`. */\nexport const EVENT_COVER_ALT_MAX_LENGTH = MEDIA_ALT_MAX_LENGTH\n/** How long an event without an end after its start lasts. */\nexport const EVENT_DEFAULT_DURATION_MS = 60 * 60 * 1000\n\n/** The statuses an editor or a file may set. `deleted` is only ever written by a delete. */\nexport const EVENT_STATUSES = ['draft', 'published'] as const\nexport type EventStatus = (typeof EVENT_STATUSES)[number]\n\n/** A status as typed, folded to one an event may hold, or `null` for anything else. */\nexport function eventStatusOf(value: unknown): EventStatus | null {\n const text = typeof value === 'string' ? value.trim().toLowerCase() : ''\n return (EVENT_STATUSES as readonly string[]).includes(text)\n ? (text as EventStatus)\n : null\n}\n\n/** What a writer was given for one event. Text may be untrimmed; blank means \"none\". */\nexport interface EventWriteInput {\n title: string\n /** Epoch milliseconds; required. */\n startsAtMs: number\n /** Epoch milliseconds; absent, zero or not after the start gives the default duration. */\n endsAtMs?: number | null\n location?: string | null\n organizer?: string | null\n description?: string | null\n coverImage?: string | null\n coverImageAlt?: string | null\n status: EventStatus\n}\n\n/** The content fields an event document stores. */\nexport interface EventStoredFields {\n title: string\n startsAtMs: number\n endsAtMs: number\n location?: string\n organizer?: string\n description?: string\n coverImage?: string\n coverImageAlt?: string\n status: EventStatus\n}\n\n/** The optional fields a write can take off a stored event. */\nexport type EventRemovableField =\n 'location' | 'organizer' | 'description' | 'coverImage' | 'coverImageAlt'\n\n/** What to store: the fields to write and the stored fields to delete. */\nexport interface EventWrite {\n fields: EventStoredFields\n remove: EventRemovableField[]\n}\n\nexport interface EventWriteOptions {\n /**\n * Whether a blank optional field deletes the stored one. The editor leaves\n * it off: it writes a whole event over a `merge`, and a blank box there\n * keeps what was stored. The import turns it on, because it hands over the\n * record as it will be — a blank there is a value it chose to clear.\n */\n clearBlank?: boolean\n}\n\n/** Why an event cannot be written as given, or `null` when it can. */\nexport function eventWriteProblem(\n input: Pick<EventWriteInput, 'title' | 'startsAtMs'>,\n): string | null {\n if (!String(input.title ?? '').trim()) return 'An event needs a title.'\n if (!Number.isFinite(input.startsAtMs) || !input.startsAtMs)\n return 'An event needs a start time.'\n return null\n}\n\n/** The end an event stores: the one given when it is after the start, else the default duration. */\nexport function eventEndsAtMs(\n startsAtMs: number,\n endsAtMs: number | null | undefined,\n): number {\n return typeof endsAtMs === 'number' &&\n Number.isFinite(endsAtMs) &&\n endsAtMs > startsAtMs\n ? endsAtMs\n : startsAtMs + EVENT_DEFAULT_DURATION_MS\n}\n\nfunction capped(value: string | null | undefined, max: number): string {\n return String(value ?? '')\n .trim()\n .slice(0, max)\n}\n\n/**\n * The stored form of an event. Throws for input {@link eventWriteProblem}\n * refuses; both writers check it first and say why in their own words.\n *\n * The cover description is tied to the cover: with no cover, or an empty\n * description, it is removed rather than left describing a picture that\n * changed.\n */\nexport function eventWrite(\n input: EventWriteInput,\n options: EventWriteOptions = {},\n): EventWrite {\n const problem = eventWriteProblem(input)\n if (problem) throw new Error(problem)\n const remove: EventRemovableField[] = []\n const fields: EventStoredFields = {\n title: capped(input.title, EVENT_TITLE_MAX_LENGTH),\n startsAtMs: input.startsAtMs,\n endsAtMs: eventEndsAtMs(input.startsAtMs, input.endsAtMs),\n status: input.status,\n }\n const optional: Array<\n [Exclude<EventRemovableField, 'coverImageAlt'>, number]\n > = [\n ['location', EVENT_LOCATION_MAX_LENGTH],\n ['organizer', EVENT_ORGANIZER_MAX_LENGTH],\n ['description', EVENT_DESCRIPTION_MAX_LENGTH],\n ['coverImage', Number.POSITIVE_INFINITY],\n ]\n for (const [key, max] of optional) {\n const value = capped(input[key], max)\n if (value) fields[key] = value\n else if (options.clearBlank) remove.push(key)\n }\n const alt = capped(input.coverImageAlt, EVENT_COVER_ALT_MAX_LENGTH)\n if (fields.coverImage && alt) fields.coverImageAlt = alt\n else remove.push('coverImageAlt')\n return { fields, remove }\n}\n"],"names":["MEDIA_ALT_MAX_LENGTH","EVENT_TITLE_MAX_LENGTH","EVENT_LOCATION_MAX_LENGTH","EVENT_ORGANIZER_MAX_LENGTH","EVENT_DESCRIPTION_MAX_LENGTH","EVENT_COVER_ALT_MAX_LENGTH","EVENT_DEFAULT_DURATION_MS","EVENT_STATUSES","eventStatusOf","value","text","trim","toLowerCase","includes","eventWriteProblem","input","String","title","Number","isFinite","startsAtMs","eventEndsAtMs","endsAtMs","capped","max","slice","eventWrite","options","problem","Error","remove","fields","status","optional","POSITIVE_INFINITY","key","clearBlank","push","alt","coverImageAlt","coverImage"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,SAASA,oBAAoB,QAAQ,mCAAkC;AAEvE;;;;;;;;;;;;CAYC,GAED,0BAA0B,GAC1B,OAAO,MAAMC,yBAAyB,IAAG;AACzC,6BAA6B,GAC7B,OAAO,MAAMC,4BAA4B,IAAG;AAC5C,8BAA8B,GAC9B,OAAO,MAAMC,6BAA6B,IAAG;AAC7C,gCAAgC,GAChC,OAAO,MAAMC,+BAA+B,KAAI;AAChD,wEAAwE,GACxE,OAAO,MAAMC,6BAA6BL,qBAAoB;AAC9D,4DAA4D,GAC5D,OAAO,MAAMM,4BAA4B,KAAK,KAAK,KAAI;AAEvD,0FAA0F,GAC1F,OAAO,MAAMC,iBAAiB;IAAC;IAAS;CAAY,CAAS;AAG7D,qFAAqF,GACrF,OAAO,SAASC,cAAcC,KAAc;IAC1C,MAAMC,OAAO,OAAOD,UAAU,WAAWA,MAAME,IAAI,GAAGC,WAAW,KAAK;IACtE,OAAO,AAACL,eAAqCM,QAAQ,CAACH,QACjDA,OACD;AACN;AAkDA,oEAAoE,GACpE,OAAO,SAASI,kBACdC,KAAoD;QAExCA;IAAZ,IAAI,CAACC,QAAOD,eAAAA,MAAME,KAAK,YAAXF,eAAe,IAAIJ,IAAI,IAAI,OAAO;IAC9C,IAAI,CAACO,OAAOC,QAAQ,CAACJ,MAAMK,UAAU,KAAK,CAACL,MAAMK,UAAU,EACzD,OAAO;IACT,OAAO;AACT;AAEA,kGAAkG,GAClG,OAAO,SAASC,cACdD,UAAkB,EAClBE,QAAmC;IAEnC,OAAO,OAAOA,aAAa,YACzBJ,OAAOC,QAAQ,CAACG,aAChBA,WAAWF,aACTE,WACAF,aAAad;AACnB;AAEA,SAASiB,OAAOd,KAAgC,EAAEe,GAAW;IAC3D,OAAOR,OAAOP,gBAAAA,QAAS,IACpBE,IAAI,GACJc,KAAK,CAAC,GAAGD;AACd;AAEA;;;;;;;CAOC,GACD,OAAO,SAASE,WACdX,KAAsB,EACtBY,UAA6B,CAAC,CAAC;IAE/B,MAAMC,UAAUd,kBAAkBC;IAClC,IAAIa,SAAS,MAAM,IAAIC,MAAMD;IAC7B,MAAME,SAAgC,EAAE;IACxC,MAAMC,SAA4B;QAChCd,OAAOM,OAAOR,MAAME,KAAK,EAAEhB;QAC3BmB,YAAYL,MAAMK,UAAU;QAC5BE,UAAUD,cAAcN,MAAMK,UAAU,EAAEL,MAAMO,QAAQ;QACxDU,QAAQjB,MAAMiB,MAAM;IACtB;IACA,MAAMC,WAEF;QACF;YAAC;YAAY/B;SAA0B;QACvC;YAAC;YAAaC;SAA2B;QACzC;YAAC;YAAeC;SAA6B;QAC7C;YAAC;YAAcc,OAAOgB,iBAAiB;SAAC;KACzC;IACD,KAAK,MAAM,CAACC,KAAKX,IAAI,IAAIS,SAAU;QACjC,MAAMxB,QAAQc,OAAOR,KAAK,CAACoB,IAAI,EAAEX;QACjC,IAAIf,OAAOsB,MAAM,CAACI,IAAI,GAAG1B;aACpB,IAAIkB,QAAQS,UAAU,EAAEN,OAAOO,IAAI,CAACF;IAC3C;IACA,MAAMG,MAAMf,OAAOR,MAAMwB,aAAa,EAAElC;IACxC,IAAI0B,OAAOS,UAAU,IAAIF,KAAKP,OAAOQ,aAAa,GAAGD;SAChDR,OAAOO,IAAI,CAAC;IACjB,OAAO;QAAEN;QAAQD;IAAO;AAC1B"}
|
package/src/lib/plugin.d.ts
CHANGED
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
*/
|
|
17
17
|
/**
|
|
18
18
|
* Console half only: registers the Events nav item + page + dashboard card
|
|
19
|
-
* in the ConsoleExtension registry
|
|
19
|
+
* in the ConsoleExtension registry, and how the import wizard and export
|
|
20
|
+
* dialog show this site's events. Safe to call at console app load — it
|
|
20
21
|
* pulls no besigner/canvas code (the page is lazy). The shell renders the
|
|
21
22
|
* nav item and, through its generic plugin route, the page — so the Events
|
|
22
23
|
* surface exists without any edit to the console's own nav or page files.
|
package/src/lib/plugin.js
CHANGED
|
@@ -14,9 +14,11 @@
|
|
|
14
14
|
* See the License for the specific language governing permissions and
|
|
15
15
|
* limitations under the License.
|
|
16
16
|
*/ import * as Aglyn from "@aglyn/aglyn";
|
|
17
|
+
import { registerPluginTransferResourceUi } from "@aglyn/aglyn/plugin-manager/plugin-transfer-resources";
|
|
17
18
|
import { mdiCalendarMonthOutline } from "@aglyn/shared-data-mdi";
|
|
18
19
|
import { lazy } from "react";
|
|
19
20
|
import { BUNDLE_ID } from "./constants/bundle-common.js";
|
|
21
|
+
import { EVENTS_TRANSFER_LABEL, EVENTS_TRANSFER_RESOURCE } from "./transfer/events-transfer-key.js";
|
|
20
22
|
/**
|
|
21
23
|
* The console page is code-split: the shell registers the extension at app
|
|
22
24
|
* load for the nav strip, but the manager UI only loads when a user opens
|
|
@@ -24,7 +26,8 @@ import { BUNDLE_ID } from "./constants/bundle-common.js";
|
|
|
24
26
|
*/ const EventsConsolePage = lazy(()=>import("./components/events-console-page.js"));
|
|
25
27
|
/**
|
|
26
28
|
* Console half only: registers the Events nav item + page + dashboard card
|
|
27
|
-
* in the ConsoleExtension registry
|
|
29
|
+
* in the ConsoleExtension registry, and how the import wizard and export
|
|
30
|
+
* dialog show this site's events. Safe to call at console app load — it
|
|
28
31
|
* pulls no besigner/canvas code (the page is lazy). The shell renders the
|
|
29
32
|
* nav item and, through its generic plugin route, the page — so the Events
|
|
30
33
|
* surface exists without any edit to the console's own nav or page files.
|
|
@@ -69,6 +72,16 @@ import { BUNDLE_ID } from "./constants/bundle-common.js";
|
|
|
69
72
|
}
|
|
70
73
|
]
|
|
71
74
|
});
|
|
75
|
+
// The client half of the `events` import and export; the server half is
|
|
76
|
+
// registered from `declarations.console-server.ts`.
|
|
77
|
+
registerPluginTransferResourceUi(EVENTS_TRANSFER_RESOURCE, {
|
|
78
|
+
label: EVENTS_TRANSFER_LABEL,
|
|
79
|
+
icon: {
|
|
80
|
+
path: mdiCalendarMonthOutline.path
|
|
81
|
+
}
|
|
82
|
+
}, {
|
|
83
|
+
pluginId: BUNDLE_ID
|
|
84
|
+
});
|
|
72
85
|
}
|
|
73
86
|
export * from "./site.js";
|
|
74
87
|
|
package/src/lib/plugin.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../libs/plugins/events-calendar/src/lib/plugin.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as Aglyn from '@aglyn/aglyn'\nimport { mdiCalendarMonthOutline } from '@aglyn/shared-data-mdi'\nimport { lazy } from 'react'\nimport { BUNDLE_ID } from './constants/bundle-common'\n\n/**\n * The console page is code-split: the shell registers the extension at app\n * load for the nav strip, but the manager UI only loads when a user opens\n * the Events page (the shell's plugin route wraps it in Suspense).\n */\nconst EventsConsolePage = lazy(() => import('./components/events-console-page'))\n\n/**\n * Console half only: registers the Events nav item + page + dashboard card\n * in the ConsoleExtension registry. Safe to call at console app load — it\n * pulls no besigner/canvas code (the page is lazy). The shell renders the\n * nav item and, through its generic plugin route, the page — so the Events\n * surface exists without any edit to the console's own nav or page files.\n */\nexport function registerEventsCalendarConsole(): void {\n Aglyn.registerConsoleExtension({\n pluginId: BUNDLE_ID,\n displayName: 'Events Calendar',\n featureFlag: 'eventCalendar',\n // `eventCalendar` is false on every plan — it is sold as a per-org\n // add-on and nothing else — so the shell's generic \"not included in your\n // current plan\" would send a reader to compare tiers that all say no.\n // The price comes from the constant the invoice is built from, so this\n // sentence cannot drift away from what a purchase actually charges.\n upgradeNotice: {\n message:\n 'The Event Calendar is a paid add-on ' +\n `($${Aglyn.EVENT_CALENDAR_ADDON_MONTHLY_USD}/mo for your whole ` +\n `workspace, supported directly by ${Aglyn.PLATFORM_BRAND_NAME}). ` +\n 'Enable it from Billing → Add-ons.',\n billingAnchor: 'addons',\n },\n navItems: [\n {\n label: 'Events',\n href: '/events',\n // Reuse the existing release-flag nav-tab so staff-preview gating\n // is unchanged now that the tab comes from the plugin (AGL-394).\n navTabId: 'nav-tab-events',\n icon: { path: mdiCalendarMonthOutline.path },\n header: {\n title: 'Events',\n icon: { path: mdiCalendarMonthOutline.path },\n docsTopic: 'events',\n },\n Component: EventsConsolePage,\n },\n ],\n dashboardCards: [{ cardId: 'events-upcoming', title: 'Upcoming events' }],\n })\n}\n\nexport * from './site'\n"],"names":["Aglyn","mdiCalendarMonthOutline","lazy","BUNDLE_ID","EventsConsolePage","registerEventsCalendarConsole","registerConsoleExtension","pluginId","displayName","featureFlag","upgradeNotice","message","EVENT_CALENDAR_ADDON_MONTHLY_USD","PLATFORM_BRAND_NAME","billingAnchor","navItems","label","href","navTabId","icon","path","header","title","docsTopic","Component","dashboardCards","cardId"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,YAAYA,WAAW,eAAc;AACrC,SAASC,uBAAuB,QAAQ,yBAAwB;AAChE,SAASC,IAAI,QAAQ,QAAO;AAC5B,SAASC,SAAS,QAAQ,+BAA2B;
|
|
1
|
+
{"version":3,"sources":["../../../../../../libs/plugins/events-calendar/src/lib/plugin.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as Aglyn from '@aglyn/aglyn'\nimport { registerPluginTransferResourceUi } from '@aglyn/aglyn/plugin-manager/plugin-transfer-resources'\nimport { mdiCalendarMonthOutline } from '@aglyn/shared-data-mdi'\nimport { lazy } from 'react'\nimport { BUNDLE_ID } from './constants/bundle-common'\nimport {\n EVENTS_TRANSFER_LABEL,\n EVENTS_TRANSFER_RESOURCE,\n} from './transfer/events-transfer-key'\n\n/**\n * The console page is code-split: the shell registers the extension at app\n * load for the nav strip, but the manager UI only loads when a user opens\n * the Events page (the shell's plugin route wraps it in Suspense).\n */\nconst EventsConsolePage = lazy(() => import('./components/events-console-page'))\n\n/**\n * Console half only: registers the Events nav item + page + dashboard card\n * in the ConsoleExtension registry, and how the import wizard and export\n * dialog show this site's events. Safe to call at console app load — it\n * pulls no besigner/canvas code (the page is lazy). The shell renders the\n * nav item and, through its generic plugin route, the page — so the Events\n * surface exists without any edit to the console's own nav or page files.\n */\nexport function registerEventsCalendarConsole(): void {\n Aglyn.registerConsoleExtension({\n pluginId: BUNDLE_ID,\n displayName: 'Events Calendar',\n featureFlag: 'eventCalendar',\n // `eventCalendar` is false on every plan — it is sold as a per-org\n // add-on and nothing else — so the shell's generic \"not included in your\n // current plan\" would send a reader to compare tiers that all say no.\n // The price comes from the constant the invoice is built from, so this\n // sentence cannot drift away from what a purchase actually charges.\n upgradeNotice: {\n message:\n 'The Event Calendar is a paid add-on ' +\n `($${Aglyn.EVENT_CALENDAR_ADDON_MONTHLY_USD}/mo for your whole ` +\n `workspace, supported directly by ${Aglyn.PLATFORM_BRAND_NAME}). ` +\n 'Enable it from Billing → Add-ons.',\n billingAnchor: 'addons',\n },\n navItems: [\n {\n label: 'Events',\n href: '/events',\n // Reuse the existing release-flag nav-tab so staff-preview gating\n // is unchanged now that the tab comes from the plugin (AGL-394).\n navTabId: 'nav-tab-events',\n icon: { path: mdiCalendarMonthOutline.path },\n header: {\n title: 'Events',\n icon: { path: mdiCalendarMonthOutline.path },\n docsTopic: 'events',\n },\n Component: EventsConsolePage,\n },\n ],\n dashboardCards: [{ cardId: 'events-upcoming', title: 'Upcoming events' }],\n })\n // The client half of the `events` import and export; the server half is\n // registered from `declarations.console-server.ts`.\n registerPluginTransferResourceUi(\n EVENTS_TRANSFER_RESOURCE,\n {\n label: EVENTS_TRANSFER_LABEL,\n icon: { path: mdiCalendarMonthOutline.path },\n },\n { pluginId: BUNDLE_ID },\n )\n}\n\nexport * from './site'\n"],"names":["Aglyn","registerPluginTransferResourceUi","mdiCalendarMonthOutline","lazy","BUNDLE_ID","EVENTS_TRANSFER_LABEL","EVENTS_TRANSFER_RESOURCE","EventsConsolePage","registerEventsCalendarConsole","registerConsoleExtension","pluginId","displayName","featureFlag","upgradeNotice","message","EVENT_CALENDAR_ADDON_MONTHLY_USD","PLATFORM_BRAND_NAME","billingAnchor","navItems","label","href","navTabId","icon","path","header","title","docsTopic","Component","dashboardCards","cardId"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED,YAAYA,WAAW,eAAc;AACrC,SAASC,gCAAgC,QAAQ,wDAAuD;AACxG,SAASC,uBAAuB,QAAQ,yBAAwB;AAChE,SAASC,IAAI,QAAQ,QAAO;AAC5B,SAASC,SAAS,QAAQ,+BAA2B;AACrD,SACEC,qBAAqB,EACrBC,wBAAwB,QACnB,oCAAgC;AAEvC;;;;CAIC,GACD,MAAMC,oBAAoBJ,KAAK,IAAM,MAAM,CAAC;AAE5C;;;;;;;CAOC,GACD,OAAO,SAASK;IACdR,MAAMS,wBAAwB,CAAC;QAC7BC,UAAUN;QACVO,aAAa;QACbC,aAAa;QACb,mEAAmE;QACnE,yEAAyE;QACzE,sEAAsE;QACtE,uEAAuE;QACvE,oEAAoE;QACpEC,eAAe;YACbC,SACE,yCACA,CAAC,EAAE,EAAEd,MAAMe,gCAAgC,CAAC,mBAAmB,CAAC,GAChE,CAAC,iCAAiC,EAAEf,MAAMgB,mBAAmB,CAAC,GAAG,CAAC,GAClE;YACFC,eAAe;QACjB;QACAC,UAAU;YACR;gBACEC,OAAO;gBACPC,MAAM;gBACN,kEAAkE;gBAClE,iEAAiE;gBACjEC,UAAU;gBACVC,MAAM;oBAAEC,MAAMrB,wBAAwBqB,IAAI;gBAAC;gBAC3CC,QAAQ;oBACNC,OAAO;oBACPH,MAAM;wBAAEC,MAAMrB,wBAAwBqB,IAAI;oBAAC;oBAC3CG,WAAW;gBACb;gBACAC,WAAWpB;YACb;SACD;QACDqB,gBAAgB;YAAC;gBAAEC,QAAQ;gBAAmBJ,OAAO;YAAkB;SAAE;IAC3E;IACA,wEAAwE;IACxE,oDAAoD;IACpDxB,iCACEK,0BACA;QACEa,OAAOd;QACPiB,MAAM;YAAEC,MAAMrB,wBAAwBqB,IAAI;QAAC;IAC7C,GACA;QAAEb,UAAUN;IAAU;AAE1B;AAEA,cAAc,YAAQ"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
import type { MatchKeySpec, TransferAliasDictionary, TransferCatalogInput, TransferField } from '@aglyn/aglyn/data-transfer';
|
|
18
|
+
/**
|
|
19
|
+
* What an events file can carry, and how a row finds its event: data only,
|
|
20
|
+
* so the console's boot declarations read the match keys and the aliases
|
|
21
|
+
* synchronously without loading the transfer core or the Admin SDK.
|
|
22
|
+
*/
|
|
23
|
+
/** The field ids an event is written through, in catalog order. */
|
|
24
|
+
export declare const EVENT_CONTENT_FIELD_IDS: readonly ["title", "startsAt", "endsAt", "location", "organizer", "description", "coverImage", "coverImageAlt", "status"];
|
|
25
|
+
export type EventContentFieldId = (typeof EVENT_CONTENT_FIELD_IDS)[number];
|
|
26
|
+
/**
|
|
27
|
+
* Every field of an event. `startsAt` and `endsAt` are moments (ISO in a
|
|
28
|
+
* file, epoch milliseconds as stored); the length caps are the editor's,
|
|
29
|
+
* so a longer cell is cut — and flagged — before the dry run shows it.
|
|
30
|
+
*/
|
|
31
|
+
export declare const EVENTS_CATALOG: TransferCatalogInput;
|
|
32
|
+
/**
|
|
33
|
+
* How a row finds its event: the Aglyn ID (`id`, the core's
|
|
34
|
+
* `TRANSFER_ID_FIELD`), then the title and the start together — a weekly
|
|
35
|
+
* class is several events under one title, so neither alone is an event.
|
|
36
|
+
*/
|
|
37
|
+
export declare const EVENTS_MATCH_KEYS: readonly MatchKeySpec[];
|
|
38
|
+
/**
|
|
39
|
+
* Other products' export headers. A calendar that splits the day and the
|
|
40
|
+
* time into two columns (Google Calendar's "Start Date" and "Start Time")
|
|
41
|
+
* cannot be joined into one moment here: its date column maps, and the
|
|
42
|
+
* event starts at midnight unless the cell carries a time.
|
|
43
|
+
*/
|
|
44
|
+
export declare const EVENTS_ALIASES: readonly TransferAliasDictionary[];
|
|
45
|
+
/**
|
|
46
|
+
* Whether a file's value and an event's are the same (AGL-3548): a status is
|
|
47
|
+
* stored in lower case, so `Published` is the `published` the event holds —
|
|
48
|
+
* no conflict on the Conflicts step and no change in the dry run, as the
|
|
49
|
+
* plan already folds it. Every other field is the core's to compare.
|
|
50
|
+
*/
|
|
51
|
+
export declare function eventsValuesEqual(field: TransferField, a: unknown, b: unknown): boolean | undefined;
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ import { eventStatusOf, EVENT_COVER_ALT_MAX_LENGTH, EVENT_DESCRIPTION_MAX_LENGTH, EVENT_LOCATION_MAX_LENGTH, EVENT_ORGANIZER_MAX_LENGTH, EVENT_TITLE_MAX_LENGTH } from "../model/event-write.js";
|
|
17
|
+
/**
|
|
18
|
+
* What an events file can carry, and how a row finds its event: data only,
|
|
19
|
+
* so the console's boot declarations read the match keys and the aliases
|
|
20
|
+
* synchronously without loading the transfer core or the Admin SDK.
|
|
21
|
+
*/ /** The field ids an event is written through, in catalog order. */ export const EVENT_CONTENT_FIELD_IDS = [
|
|
22
|
+
'title',
|
|
23
|
+
'startsAt',
|
|
24
|
+
'endsAt',
|
|
25
|
+
'location',
|
|
26
|
+
'organizer',
|
|
27
|
+
'description',
|
|
28
|
+
'coverImage',
|
|
29
|
+
'coverImageAlt',
|
|
30
|
+
'status'
|
|
31
|
+
];
|
|
32
|
+
/**
|
|
33
|
+
* Every field of an event. `startsAt` and `endsAt` are moments (ISO in a
|
|
34
|
+
* file, epoch milliseconds as stored); the length caps are the editor's,
|
|
35
|
+
* so a longer cell is cut — and flagged — before the dry run shows it.
|
|
36
|
+
*/ export const EVENTS_CATALOG = {
|
|
37
|
+
groups: [
|
|
38
|
+
{
|
|
39
|
+
id: 'event',
|
|
40
|
+
label: 'Event'
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
id: 'cover',
|
|
44
|
+
label: 'Cover image'
|
|
45
|
+
}
|
|
46
|
+
],
|
|
47
|
+
standard: [
|
|
48
|
+
{
|
|
49
|
+
id: 'title',
|
|
50
|
+
label: 'Title',
|
|
51
|
+
group: 'event',
|
|
52
|
+
type: 'text',
|
|
53
|
+
required: true,
|
|
54
|
+
maxLength: EVENT_TITLE_MAX_LENGTH,
|
|
55
|
+
aliases: [
|
|
56
|
+
'Name',
|
|
57
|
+
'Event',
|
|
58
|
+
'Event title',
|
|
59
|
+
'Summary'
|
|
60
|
+
]
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
id: 'startsAt',
|
|
64
|
+
label: 'Starts',
|
|
65
|
+
group: 'event',
|
|
66
|
+
type: 'datetime',
|
|
67
|
+
required: true,
|
|
68
|
+
aliases: [
|
|
69
|
+
'Start',
|
|
70
|
+
'Start date',
|
|
71
|
+
'Starts at',
|
|
72
|
+
'Start date and time',
|
|
73
|
+
'Begins'
|
|
74
|
+
]
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
id: 'endsAt',
|
|
78
|
+
label: 'Ends',
|
|
79
|
+
group: 'event',
|
|
80
|
+
type: 'datetime',
|
|
81
|
+
aliases: [
|
|
82
|
+
'End',
|
|
83
|
+
'End date',
|
|
84
|
+
'Ends at',
|
|
85
|
+
'End date and time'
|
|
86
|
+
],
|
|
87
|
+
description: 'Blank, or not after the start, gives the event one hour.'
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
id: 'location',
|
|
91
|
+
label: 'Location',
|
|
92
|
+
group: 'event',
|
|
93
|
+
type: 'text',
|
|
94
|
+
maxLength: EVENT_LOCATION_MAX_LENGTH,
|
|
95
|
+
aliases: [
|
|
96
|
+
'Venue',
|
|
97
|
+
'Place',
|
|
98
|
+
'Where'
|
|
99
|
+
]
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
id: 'organizer',
|
|
103
|
+
label: 'Organizer',
|
|
104
|
+
group: 'event',
|
|
105
|
+
type: 'text',
|
|
106
|
+
maxLength: EVENT_ORGANIZER_MAX_LENGTH,
|
|
107
|
+
aliases: [
|
|
108
|
+
'Organiser',
|
|
109
|
+
'Hosted by'
|
|
110
|
+
]
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
id: 'description',
|
|
114
|
+
label: 'Description',
|
|
115
|
+
group: 'event',
|
|
116
|
+
type: 'longText',
|
|
117
|
+
maxLength: EVENT_DESCRIPTION_MAX_LENGTH,
|
|
118
|
+
aliases: [
|
|
119
|
+
'Details',
|
|
120
|
+
'About'
|
|
121
|
+
]
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
id: 'status',
|
|
125
|
+
label: 'Status',
|
|
126
|
+
group: 'event',
|
|
127
|
+
type: 'text',
|
|
128
|
+
aliases: [
|
|
129
|
+
'Event status'
|
|
130
|
+
],
|
|
131
|
+
description: 'draft or published. A new event without one is a draft.'
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
id: 'coverImage',
|
|
135
|
+
label: 'Cover image',
|
|
136
|
+
group: 'cover',
|
|
137
|
+
type: 'text',
|
|
138
|
+
aliases: [
|
|
139
|
+
'Cover image URL',
|
|
140
|
+
'Image',
|
|
141
|
+
'Image URL',
|
|
142
|
+
'Featured image'
|
|
143
|
+
],
|
|
144
|
+
description: 'A web address, or a path on this site.'
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
id: 'coverImageAlt',
|
|
148
|
+
label: 'Cover image alt text',
|
|
149
|
+
group: 'cover',
|
|
150
|
+
type: 'text',
|
|
151
|
+
maxLength: EVENT_COVER_ALT_MAX_LENGTH,
|
|
152
|
+
aliases: [
|
|
153
|
+
'Cover image description',
|
|
154
|
+
'Image alt text',
|
|
155
|
+
'Alt text'
|
|
156
|
+
],
|
|
157
|
+
description: 'Kept only beside a cover image.'
|
|
158
|
+
}
|
|
159
|
+
],
|
|
160
|
+
system: [
|
|
161
|
+
{
|
|
162
|
+
id: 'createdAt',
|
|
163
|
+
label: 'Created',
|
|
164
|
+
type: 'datetime',
|
|
165
|
+
readOnly: true
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
id: 'updatedAt',
|
|
169
|
+
label: 'Updated',
|
|
170
|
+
type: 'datetime',
|
|
171
|
+
readOnly: true
|
|
172
|
+
}
|
|
173
|
+
]
|
|
174
|
+
};
|
|
175
|
+
/**
|
|
176
|
+
* How a row finds its event: the Aglyn ID (`id`, the core's
|
|
177
|
+
* `TRANSFER_ID_FIELD`), then the title and the start together — a weekly
|
|
178
|
+
* class is several events under one title, so neither alone is an event.
|
|
179
|
+
*/ export const EVENTS_MATCH_KEYS = [
|
|
180
|
+
{
|
|
181
|
+
fieldId: 'id',
|
|
182
|
+
normalizer: 'aglynId'
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
fieldId: 'title',
|
|
186
|
+
normalizer: 'name',
|
|
187
|
+
with: [
|
|
188
|
+
{
|
|
189
|
+
fieldId: 'startsAt',
|
|
190
|
+
normalizer: 'instant'
|
|
191
|
+
}
|
|
192
|
+
]
|
|
193
|
+
}
|
|
194
|
+
];
|
|
195
|
+
/**
|
|
196
|
+
* Other products' export headers. A calendar that splits the day and the
|
|
197
|
+
* time into two columns (Google Calendar's "Start Date" and "Start Time")
|
|
198
|
+
* cannot be joined into one moment here: its date column maps, and the
|
|
199
|
+
* event starts at midnight unless the cell carries a time.
|
|
200
|
+
*/ export const EVENTS_ALIASES = [
|
|
201
|
+
{
|
|
202
|
+
source: 'Google Calendar',
|
|
203
|
+
aliases: {
|
|
204
|
+
title: [
|
|
205
|
+
'Subject'
|
|
206
|
+
],
|
|
207
|
+
startsAt: [
|
|
208
|
+
'Start Date'
|
|
209
|
+
],
|
|
210
|
+
endsAt: [
|
|
211
|
+
'End Date'
|
|
212
|
+
],
|
|
213
|
+
location: [
|
|
214
|
+
'Location'
|
|
215
|
+
],
|
|
216
|
+
description: [
|
|
217
|
+
'Description'
|
|
218
|
+
]
|
|
219
|
+
}
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
source: 'iCalendar',
|
|
223
|
+
aliases: {
|
|
224
|
+
title: [
|
|
225
|
+
'SUMMARY'
|
|
226
|
+
],
|
|
227
|
+
startsAt: [
|
|
228
|
+
'DTSTART'
|
|
229
|
+
],
|
|
230
|
+
endsAt: [
|
|
231
|
+
'DTEND'
|
|
232
|
+
],
|
|
233
|
+
location: [
|
|
234
|
+
'LOCATION'
|
|
235
|
+
],
|
|
236
|
+
description: [
|
|
237
|
+
'DESCRIPTION'
|
|
238
|
+
]
|
|
239
|
+
}
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
source: 'The Events Calendar (WordPress)',
|
|
243
|
+
aliases: {
|
|
244
|
+
title: [
|
|
245
|
+
'Event Name'
|
|
246
|
+
],
|
|
247
|
+
startsAt: [
|
|
248
|
+
'Event Start Date'
|
|
249
|
+
],
|
|
250
|
+
endsAt: [
|
|
251
|
+
'Event End Date'
|
|
252
|
+
],
|
|
253
|
+
location: [
|
|
254
|
+
'Event Venue Name'
|
|
255
|
+
],
|
|
256
|
+
organizer: [
|
|
257
|
+
'Event Organizer Name'
|
|
258
|
+
],
|
|
259
|
+
description: [
|
|
260
|
+
'Event Description'
|
|
261
|
+
],
|
|
262
|
+
coverImage: [
|
|
263
|
+
'Event Featured Image'
|
|
264
|
+
]
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
];
|
|
268
|
+
/**
|
|
269
|
+
* Whether a file's value and an event's are the same (AGL-3548): a status is
|
|
270
|
+
* stored in lower case, so `Published` is the `published` the event holds —
|
|
271
|
+
* no conflict on the Conflicts step and no change in the dry run, as the
|
|
272
|
+
* plan already folds it. Every other field is the core's to compare.
|
|
273
|
+
*/ export function eventsValuesEqual(field, a, b) {
|
|
274
|
+
if (field.id !== 'status') return undefined;
|
|
275
|
+
const status = eventStatusOf(a);
|
|
276
|
+
return status !== null && status === eventStatusOf(b) ? true : undefined;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
//# sourceMappingURL=events-transfer-catalog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/events-calendar/src/lib/transfer/events-transfer-catalog.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type {\n MatchKeySpec,\n TransferAliasDictionary,\n TransferCatalogInput,\n TransferField,\n} from '@aglyn/aglyn/data-transfer'\nimport {\n eventStatusOf,\n EVENT_COVER_ALT_MAX_LENGTH,\n EVENT_DESCRIPTION_MAX_LENGTH,\n EVENT_LOCATION_MAX_LENGTH,\n EVENT_ORGANIZER_MAX_LENGTH,\n EVENT_TITLE_MAX_LENGTH,\n} from '../model/event-write'\n\n/**\n * What an events file can carry, and how a row finds its event: data only,\n * so the console's boot declarations read the match keys and the aliases\n * synchronously without loading the transfer core or the Admin SDK.\n */\n\n/** The field ids an event is written through, in catalog order. */\nexport const EVENT_CONTENT_FIELD_IDS = [\n 'title',\n 'startsAt',\n 'endsAt',\n 'location',\n 'organizer',\n 'description',\n 'coverImage',\n 'coverImageAlt',\n 'status',\n] as const\nexport type EventContentFieldId = (typeof EVENT_CONTENT_FIELD_IDS)[number]\n\n/**\n * Every field of an event. `startsAt` and `endsAt` are moments (ISO in a\n * file, epoch milliseconds as stored); the length caps are the editor's,\n * so a longer cell is cut — and flagged — before the dry run shows it.\n */\nexport const EVENTS_CATALOG: TransferCatalogInput = {\n groups: [\n { id: 'event', label: 'Event' },\n { id: 'cover', label: 'Cover image' },\n ],\n standard: [\n {\n id: 'title',\n label: 'Title',\n group: 'event',\n type: 'text',\n required: true,\n maxLength: EVENT_TITLE_MAX_LENGTH,\n aliases: ['Name', 'Event', 'Event title', 'Summary'],\n },\n {\n id: 'startsAt',\n label: 'Starts',\n group: 'event',\n type: 'datetime',\n required: true,\n aliases: [\n 'Start',\n 'Start date',\n 'Starts at',\n 'Start date and time',\n 'Begins',\n ],\n },\n {\n id: 'endsAt',\n label: 'Ends',\n group: 'event',\n type: 'datetime',\n aliases: ['End', 'End date', 'Ends at', 'End date and time'],\n description: 'Blank, or not after the start, gives the event one hour.',\n },\n {\n id: 'location',\n label: 'Location',\n group: 'event',\n type: 'text',\n maxLength: EVENT_LOCATION_MAX_LENGTH,\n aliases: ['Venue', 'Place', 'Where'],\n },\n {\n id: 'organizer',\n label: 'Organizer',\n group: 'event',\n type: 'text',\n maxLength: EVENT_ORGANIZER_MAX_LENGTH,\n aliases: ['Organiser', 'Hosted by'],\n },\n {\n id: 'description',\n label: 'Description',\n group: 'event',\n type: 'longText',\n maxLength: EVENT_DESCRIPTION_MAX_LENGTH,\n aliases: ['Details', 'About'],\n },\n {\n id: 'status',\n label: 'Status',\n group: 'event',\n type: 'text',\n aliases: ['Event status'],\n description: 'draft or published. A new event without one is a draft.',\n },\n {\n id: 'coverImage',\n label: 'Cover image',\n group: 'cover',\n type: 'text',\n aliases: ['Cover image URL', 'Image', 'Image URL', 'Featured image'],\n description: 'A web address, or a path on this site.',\n },\n {\n id: 'coverImageAlt',\n label: 'Cover image alt text',\n group: 'cover',\n type: 'text',\n maxLength: EVENT_COVER_ALT_MAX_LENGTH,\n aliases: ['Cover image description', 'Image alt text', 'Alt text'],\n description: 'Kept only beside a cover image.',\n },\n ],\n system: [\n { id: 'createdAt', label: 'Created', type: 'datetime', readOnly: true },\n { id: 'updatedAt', label: 'Updated', type: 'datetime', readOnly: true },\n ],\n}\n\n/**\n * How a row finds its event: the Aglyn ID (`id`, the core's\n * `TRANSFER_ID_FIELD`), then the title and the start together — a weekly\n * class is several events under one title, so neither alone is an event.\n */\nexport const EVENTS_MATCH_KEYS: readonly MatchKeySpec[] = [\n { fieldId: 'id', normalizer: 'aglynId' },\n {\n fieldId: 'title',\n normalizer: 'name',\n with: [{ fieldId: 'startsAt', normalizer: 'instant' }],\n },\n]\n\n/**\n * Other products' export headers. A calendar that splits the day and the\n * time into two columns (Google Calendar's \"Start Date\" and \"Start Time\")\n * cannot be joined into one moment here: its date column maps, and the\n * event starts at midnight unless the cell carries a time.\n */\nexport const EVENTS_ALIASES: readonly TransferAliasDictionary[] = [\n {\n source: 'Google Calendar',\n aliases: {\n title: ['Subject'],\n startsAt: ['Start Date'],\n endsAt: ['End Date'],\n location: ['Location'],\n description: ['Description'],\n },\n },\n {\n source: 'iCalendar',\n aliases: {\n title: ['SUMMARY'],\n startsAt: ['DTSTART'],\n endsAt: ['DTEND'],\n location: ['LOCATION'],\n description: ['DESCRIPTION'],\n },\n },\n {\n source: 'The Events Calendar (WordPress)',\n aliases: {\n title: ['Event Name'],\n startsAt: ['Event Start Date'],\n endsAt: ['Event End Date'],\n location: ['Event Venue Name'],\n organizer: ['Event Organizer Name'],\n description: ['Event Description'],\n coverImage: ['Event Featured Image'],\n },\n },\n]\n\n/**\n * Whether a file's value and an event's are the same (AGL-3548): a status is\n * stored in lower case, so `Published` is the `published` the event holds —\n * no conflict on the Conflicts step and no change in the dry run, as the\n * plan already folds it. Every other field is the core's to compare.\n */\nexport function eventsValuesEqual(field: TransferField, a: unknown, b: unknown): boolean | undefined {\n if (field.id !== 'status') return undefined\n const status = eventStatusOf(a)\n return status !== null && status === eventStatusOf(b) ? true : undefined\n}\n"],"names":["eventStatusOf","EVENT_COVER_ALT_MAX_LENGTH","EVENT_DESCRIPTION_MAX_LENGTH","EVENT_LOCATION_MAX_LENGTH","EVENT_ORGANIZER_MAX_LENGTH","EVENT_TITLE_MAX_LENGTH","EVENT_CONTENT_FIELD_IDS","EVENTS_CATALOG","groups","id","label","standard","group","type","required","maxLength","aliases","description","system","readOnly","EVENTS_MATCH_KEYS","fieldId","normalizer","with","EVENTS_ALIASES","source","title","startsAt","endsAt","location","organizer","coverImage","eventsValuesEqual","field","a","b","undefined","status"],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* The events transfer resource's key, as `plugins.config.json` declares it
|
|
19
|
+
* under `transferResources`. Its own module so the console page and
|
|
20
|
+
* registrar name it without loading the server half.
|
|
21
|
+
*/
|
|
22
|
+
export declare const EVENTS_TRANSFER_RESOURCE = "events";
|
|
23
|
+
/** What the hub and the wizard call it. */
|
|
24
|
+
export declare const EVENTS_TRANSFER_LABEL = "Events";
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright 2026 Aglyn LLC
|
|
4
|
+
*
|
|
5
|
+
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
* you may not use this file except in compliance with the License.
|
|
7
|
+
* You may obtain a copy of the License at
|
|
8
|
+
*
|
|
9
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
*
|
|
11
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
* See the License for the specific language governing permissions and
|
|
15
|
+
* limitations under the License.
|
|
16
|
+
*/ /**
|
|
17
|
+
* The events transfer resource's key, as `plugins.config.json` declares it
|
|
18
|
+
* under `transferResources`. Its own module so the console page and
|
|
19
|
+
* registrar name it without loading the server half.
|
|
20
|
+
*/ export const EVENTS_TRANSFER_RESOURCE = 'events';
|
|
21
|
+
/** What the hub and the wizard call it. */ export const EVENTS_TRANSFER_LABEL = 'Events';
|
|
22
|
+
|
|
23
|
+
//# sourceMappingURL=events-transfer-key.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../../../../../libs/plugins/events-calendar/src/lib/transfer/events-transfer-key.ts"],"sourcesContent":["/**\n * @license\n * Copyright 2026 Aglyn LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/**\n * The events transfer resource's key, as `plugins.config.json` declares it\n * under `transferResources`. Its own module so the console page and\n * registrar name it without loading the server half.\n */\nexport const EVENTS_TRANSFER_RESOURCE = 'events'\n\n/** What the hub and the wizard call it. */\nexport const EVENTS_TRANSFER_LABEL = 'Events'\n"],"names":["EVENTS_TRANSFER_RESOURCE","EVENTS_TRANSFER_LABEL"],"mappings":"AAAA;;;;;;;;;;;;;;;CAeC,GAED;;;;CAIC,GACD,OAAO,MAAMA,2BAA2B,SAAQ;AAEhD,yCAAyC,GACzC,OAAO,MAAMC,wBAAwB,SAAQ"}
|