@vicacha-devs/strapi-plugin-calendar 0.0.0-stage → 1.0.0

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.
Files changed (90) hide show
  1. package/LICENSE.md +21 -0
  2. package/README.md +163 -2
  3. package/dist/admin/CalendarPage-CVqowKst.cjs +1015 -0
  4. package/dist/admin/CalendarPage-Dq6YfSXF.js +1015 -0
  5. package/dist/admin/SettingsPage-7SecVQEi.js +2088 -0
  6. package/dist/admin/SettingsPage-D0aCDb9w.cjs +2088 -0
  7. package/dist/admin/ar-O6QL_nJZ.js +331 -0
  8. package/dist/admin/ar-yKsEpX_y.cjs +331 -0
  9. package/dist/admin/de-BpDxjctz.js +331 -0
  10. package/dist/admin/de-ryVtwWOz.cjs +331 -0
  11. package/dist/admin/en-BHYSUqXv.cjs +331 -0
  12. package/dist/admin/en-BoRq-9QD.js +331 -0
  13. package/dist/admin/es-BZ7WX8Ev.js +331 -0
  14. package/dist/admin/es-T5v93hXW.cjs +331 -0
  15. package/dist/admin/fr-BnDr9DC8.js +331 -0
  16. package/dist/admin/fr-DTdTwx7e.cjs +331 -0
  17. package/dist/admin/index-DO0QbnXX.cjs +3223 -0
  18. package/dist/admin/index-USqxqyAm.js +3216 -0
  19. package/dist/admin/index.cjs +9 -0
  20. package/dist/admin/index.js +9 -0
  21. package/dist/admin/index.mjs +9 -0
  22. package/dist/admin/it-BkJNzy27.cjs +331 -0
  23. package/dist/admin/it-DkdUInap.js +331 -0
  24. package/dist/admin/nl-C2NFORLS.js +331 -0
  25. package/dist/admin/nl-CjnlbeUM.cjs +331 -0
  26. package/dist/admin/pl-BJArPgCQ.cjs +331 -0
  27. package/dist/admin/pl-mv5eXA8W.js +331 -0
  28. package/dist/admin/pt-CSYqSGnz.cjs +331 -0
  29. package/dist/admin/pt-WpvZGj3b.js +331 -0
  30. package/dist/admin/src/components/CalendarSidePanel.d.ts +13 -0
  31. package/dist/admin/src/components/Initializer.d.ts +6 -0
  32. package/dist/admin/src/components/PluginIcon.d.ts +3 -0
  33. package/dist/admin/src/components/RRuleInput.d.ts +20 -0
  34. package/dist/admin/src/components/calendar/CalendarToolbar.d.ts +32 -0
  35. package/dist/admin/src/components/calendar/FullCalendarCanvas.d.ts +21 -0
  36. package/dist/admin/src/components/calendar/FullCalendarDayCell.d.ts +11 -0
  37. package/dist/admin/src/components/calendar/OccurrenceDetailDrawer.d.ts +15 -0
  38. package/dist/admin/src/components/calendar/QuickCreatePanel.d.ts +29 -0
  39. package/dist/admin/src/components/calendar/QuickCreateSubcomponents.d.ts +40 -0
  40. package/dist/admin/src/components/calendar/calendarPageHelpers.d.ts +52 -0
  41. package/dist/admin/src/components/calendar/calendarThemeStyles.d.ts +2 -0
  42. package/dist/admin/src/components/settings/CalendarThemeSection.d.ts +9 -0
  43. package/dist/admin/src/components/settings/CalendarViewsSection.d.ts +10 -0
  44. package/dist/admin/src/components/settings/CollectionSourcesSection.d.ts +12 -0
  45. package/dist/admin/src/components/settings/CompanionSuiteSettingsSection.d.ts +29 -0
  46. package/dist/admin/src/components/settings/SecondaryCalendarsSection.d.ts +11 -0
  47. package/dist/admin/src/fields/rruleField.d.ts +19 -0
  48. package/dist/admin/src/index.d.ts +18 -0
  49. package/dist/admin/src/pages/CalendarPage.d.ts +3 -0
  50. package/dist/admin/src/pages/SettingsPage.d.ts +3 -0
  51. package/dist/admin/src/pages/calendarPageSupport.d.ts +8 -0
  52. package/dist/admin/tr-BhZKvFoK.js +331 -0
  53. package/dist/admin/tr-zUVxuw9X.cjs +331 -0
  54. package/dist/package.json +3 -0
  55. package/dist/server/index.cjs +2585 -0
  56. package/dist/server/index.js +2585 -0
  57. package/dist/server/index.mjs +2585 -0
  58. package/dist/server/src/bootstrap.d.ts +5 -0
  59. package/dist/server/src/config/index.d.ts +7 -0
  60. package/dist/server/src/controllers/admin.d.ts +15 -0
  61. package/dist/server/src/controllers/content-api.d.ts +10 -0
  62. package/dist/server/src/controllers/index.d.ts +21 -0
  63. package/dist/server/src/destroy.d.ts +5 -0
  64. package/dist/server/src/index.d.ts +85 -0
  65. package/dist/server/src/register.d.ts +5 -0
  66. package/dist/server/src/routes/admin.d.ts +17 -0
  67. package/dist/server/src/routes/content-api.d.ts +10 -0
  68. package/dist/server/src/routes/index.d.ts +28 -0
  69. package/dist/server/src/services/builtinSources.d.ts +3 -0
  70. package/dist/server/src/services/ical.d.ts +6 -0
  71. package/dist/server/src/services/index.d.ts +23 -0
  72. package/dist/server/src/services/occurrence.d.ts +6 -0
  73. package/dist/server/src/services/occurrenceDates.d.ts +7 -0
  74. package/dist/server/src/services/quickCreateHelpers.d.ts +20 -0
  75. package/dist/server/src/services/registry.d.ts +19 -0
  76. package/dist/server/src/services/settings.d.ts +6 -0
  77. package/dist/server/src/types.d.ts +98 -0
  78. package/dist/shared/calendarSystems.d.ts +41 -0
  79. package/dist/shared/icalGenerator.d.ts +18 -0
  80. package/dist/shared/index.cjs +1612 -0
  81. package/dist/shared/index.d.ts +6 -0
  82. package/dist/shared/index.js +1612 -0
  83. package/dist/shared/index.mjs +1612 -0
  84. package/dist/shared/localization.d.ts +14 -0
  85. package/dist/shared/pluginId.d.ts +2 -0
  86. package/dist/shared/rruleEngine.d.ts +22 -0
  87. package/dist/shared/themePalettes.d.ts +27 -0
  88. package/dist/shared/themePresets.d.ts +32 -0
  89. package/dist/shared/types.d.ts +311 -0
  90. package/package.json +120 -3
@@ -0,0 +1,3223 @@
1
+ "use strict";
2
+ const jsxRuntime = require("react/jsx-runtime");
3
+ const designSystem = require("@strapi/design-system");
4
+ const reactRouterDom = require("react-router-dom");
5
+ const allLocales = require("@fullcalendar/core/locales-all");
6
+ const dayGridPlugin = require("@fullcalendar/daygrid");
7
+ const interactionPlugin = require("@fullcalendar/interaction");
8
+ const listPlugin = require("@fullcalendar/list");
9
+ const multiMonthPlugin = require("@fullcalendar/multimonth");
10
+ const FullCalendar = require("@fullcalendar/react");
11
+ const timeGridPlugin = require("@fullcalendar/timegrid");
12
+ const react = require("react");
13
+ const styledComponents = require("styled-components");
14
+ const admin = require("@strapi/strapi/admin");
15
+ const reactIntl = require("react-intl");
16
+ const icons = require("@strapi/icons");
17
+ const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
18
+ const allLocales__default = /* @__PURE__ */ _interopDefault(allLocales);
19
+ const dayGridPlugin__default = /* @__PURE__ */ _interopDefault(dayGridPlugin);
20
+ const interactionPlugin__default = /* @__PURE__ */ _interopDefault(interactionPlugin);
21
+ const listPlugin__default = /* @__PURE__ */ _interopDefault(listPlugin);
22
+ const multiMonthPlugin__default = /* @__PURE__ */ _interopDefault(multiMonthPlugin);
23
+ const FullCalendar__default = /* @__PURE__ */ _interopDefault(FullCalendar);
24
+ const timeGridPlugin__default = /* @__PURE__ */ _interopDefault(timeGridPlugin);
25
+ const __variableDynamicImportRuntimeHelper = (glob, path, segs) => {
26
+ const v = glob[path];
27
+ if (v) {
28
+ return typeof v === "function" ? v() : Promise.resolve(v);
29
+ }
30
+ return new Promise((_, reject) => {
31
+ (typeof queueMicrotask === "function" ? queueMicrotask : setTimeout)(
32
+ reject.bind(
33
+ null,
34
+ new Error(
35
+ "Unknown variable dynamic import: " + path + (path.split("/").length !== segs ? ". Note that variables only represent file names one level deep." : "")
36
+ )
37
+ )
38
+ );
39
+ });
40
+ };
41
+ function freezeOptions(items) {
42
+ return Object.freeze(items.map((item) => Object.freeze({ ...item })));
43
+ }
44
+ freezeOptions([
45
+ { value: "es", label: "es — Español (Spanish)" },
46
+ { value: "en", label: "en — English" },
47
+ { value: "ar", label: "ar — العربية (Arabic)" },
48
+ { value: "tr", label: "tr — Türkçe (Turkish)" },
49
+ { value: "fr", label: "fr — Français (French)" },
50
+ { value: "de", label: "de — Deutsch (German)" },
51
+ { value: "pt", label: "pt — Português (Portuguese)" },
52
+ { value: "it", label: "it — Italiano (Italian)" },
53
+ { value: "nl", label: "nl — Nederlands (Dutch)" },
54
+ { value: "pl", label: "pl — Polski (Polish)" },
55
+ { value: "ru", label: "ru — Русский (Russian)" },
56
+ { value: "id", label: "id — Bahasa Indonesia" },
57
+ { value: "ur", label: "ur — اردو (Urdu)" },
58
+ { value: "zh", label: "zh — 中文 (Chinese)" }
59
+ ]);
60
+ freezeOptions([
61
+ {
62
+ value: "islamic-umalqura",
63
+ label: "islamic-umalqura (Hijri Umm al-Qura)"
64
+ },
65
+ { value: "islamic-civil", label: "islamic-civil (Tabular Islamic Civil)" },
66
+ { value: "islamic-tbla", label: "islamic-tbla (Tabular Astronomical)" },
67
+ { value: "hebrew", label: "hebrew (Hebrew)" },
68
+ { value: "persian", label: "persian (Solar Hijri)" },
69
+ { value: "none", label: "none (Gregorian Only)" }
70
+ ]);
71
+ const VALID_LOCALE_RE = /^[A-Za-z]{2,3}(?:-[A-Za-z0-9]{2,8}){0,4}$/;
72
+ function isTranslationRecord(value) {
73
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
74
+ }
75
+ function extractDictionary(loaded) {
76
+ if (!isTranslationRecord(loaded)) {
77
+ return void 0;
78
+ }
79
+ if ("default" in loaded) {
80
+ return isTranslationRecord(loaded.default) ? loaded.default : void 0;
81
+ }
82
+ return loaded;
83
+ }
84
+ function getLookupCandidates(locale, fallbackToLanguage) {
85
+ const trimmed = locale.trim();
86
+ if (!VALID_LOCALE_RE.test(trimmed)) {
87
+ return [];
88
+ }
89
+ if (!fallbackToLanguage || !trimmed.includes("-")) {
90
+ return [trimmed];
91
+ }
92
+ const base = trimmed.split("-")[0];
93
+ return base && base !== trimmed ? [trimmed, base] : [trimmed];
94
+ }
95
+ function prefixPluginTranslations(trad, pluginId, options) {
96
+ const normalizedPluginId = typeof pluginId === "string" ? pluginId.trim() : "";
97
+ if (!normalizedPluginId) {
98
+ throw new TypeError("pluginId can't be empty");
99
+ }
100
+ const result = {};
101
+ if (options?.baseTranslations) {
102
+ for (const [key, value] of Object.entries(options.baseTranslations)) {
103
+ if (typeof value === "string") {
104
+ result[key] = value;
105
+ }
106
+ }
107
+ }
108
+ const prefix = `${normalizedPluginId}.`;
109
+ const explicitPrefixed = [];
110
+ for (const [key, value] of Object.entries(trad)) {
111
+ if (typeof value !== "string") {
112
+ continue;
113
+ }
114
+ if (options?.keepUnprefixed) {
115
+ result[key] = value;
116
+ }
117
+ if (key.startsWith(prefix)) {
118
+ explicitPrefixed.push([key, value]);
119
+ } else {
120
+ result[`${prefix}${key}`] = value;
121
+ }
122
+ }
123
+ for (const [key, value] of explicitPrefixed) {
124
+ result[key] = value;
125
+ }
126
+ return result;
127
+ }
128
+ function createRegisterTrads(options) {
129
+ const { pluginId, defaultLocales = ["en", "es", "ar", "tr"], supportedLocales, keepUnprefixed = true, fallbackToLanguageCode = true, loadTranslation, translations, getBaseTranslations, onLoaded } = options;
130
+ const normalizedPluginId = typeof pluginId === "string" ? pluginId.trim() : "";
131
+ if (!normalizedPluginId) {
132
+ throw new TypeError("pluginId can't be empty");
133
+ }
134
+ const supportedSet = supportedLocales ? new Set(supportedLocales) : void 0;
135
+ const effectiveDefaults = supportedSet ? defaultLocales.filter((loc) => getLookupCandidates(loc, fallbackToLanguageCode).some((candidate) => supportedSet.has(candidate))) : defaultLocales;
136
+ return async function registerTrads({ locales = [] } = {}) {
137
+ const allLocales2 = effectiveDefaults.length > 0 ? Array.from(/* @__PURE__ */ new Set([...locales, ...effectiveDefaults])) : Array.from(new Set(locales));
138
+ return Promise.all(allLocales2.map(async (locale) => {
139
+ const candidates = getLookupCandidates(locale, fallbackToLanguageCode);
140
+ if (candidates.length === 0) {
141
+ return { data: {}, locale };
142
+ }
143
+ if (supportedSet && !candidates.some((candidate) => supportedSet.has(candidate))) {
144
+ return { data: {}, locale };
145
+ }
146
+ let rawData;
147
+ for (const candidate of candidates) {
148
+ if (translations && Object.hasOwn(translations, candidate)) {
149
+ rawData = extractDictionary(translations[candidate]);
150
+ if (rawData) {
151
+ break;
152
+ }
153
+ }
154
+ if (loadTranslation) {
155
+ try {
156
+ const loaded = await loadTranslation(candidate);
157
+ rawData = extractDictionary(loaded);
158
+ if (rawData) {
159
+ break;
160
+ }
161
+ } catch {
162
+ }
163
+ }
164
+ }
165
+ const baseTranslations = getBaseTranslations?.(locale);
166
+ const hasBase = Boolean(baseTranslations) && Object.keys(baseTranslations).length > 0;
167
+ if (!rawData && !hasBase) {
168
+ return { data: {}, locale };
169
+ }
170
+ const prefixed = prefixPluginTranslations(rawData ?? {}, normalizedPluginId, {
171
+ keepUnprefixed,
172
+ baseTranslations
173
+ });
174
+ onLoaded?.(locale, prefixed);
175
+ return { data: prefixed, locale };
176
+ }));
177
+ };
178
+ }
179
+ const PLUGIN_ID = "calendar";
180
+ const RTL_LANGUAGE_PREFIXES = /* @__PURE__ */ new Set([
181
+ "ar",
182
+ "he",
183
+ "iw",
184
+ "fa",
185
+ "ur",
186
+ "ps",
187
+ "sd",
188
+ "ug",
189
+ "yi"
190
+ ]);
191
+ const SUPPORTED_CALENDAR_LOCALES = [
192
+ {
193
+ code: "en",
194
+ label: "English",
195
+ nativeName: "English",
196
+ direction: "ltr",
197
+ week: { dow: 0, doy: 4 },
198
+ buttonText: {
199
+ prev: "Prev",
200
+ next: "Next",
201
+ today: "Today",
202
+ year: "Year",
203
+ month: "Month",
204
+ week: "Week",
205
+ day: "Day",
206
+ list: "Agenda"
207
+ },
208
+ weekText: "Wk",
209
+ allDayText: "all-day",
210
+ moreLinkText: "more",
211
+ noEventsText: "No events to display"
212
+ },
213
+ {
214
+ code: "es",
215
+ label: "Spanish",
216
+ nativeName: "Español",
217
+ direction: "ltr",
218
+ week: { dow: 1, doy: 4 },
219
+ buttonText: {
220
+ prev: "Ant",
221
+ next: "Sig",
222
+ today: "Hoy",
223
+ year: "Año",
224
+ month: "Mes",
225
+ week: "Semana",
226
+ day: "Día",
227
+ list: "Agenda"
228
+ },
229
+ weekText: "Sm",
230
+ allDayText: "todo el día",
231
+ moreLinkText: "más",
232
+ noEventsText: "No hay eventos para mostrar"
233
+ },
234
+ {
235
+ code: "fr",
236
+ label: "French",
237
+ nativeName: "Français",
238
+ direction: "ltr",
239
+ week: { dow: 1, doy: 4 },
240
+ buttonText: {
241
+ prev: "Préc",
242
+ next: "Suiv",
243
+ today: "Aujourd'hui",
244
+ year: "Année",
245
+ month: "Mois",
246
+ week: "Semaine",
247
+ day: "Jour",
248
+ list: "Planning"
249
+ },
250
+ weekText: "Sem.",
251
+ allDayText: "toute la journée",
252
+ moreLinkText: "en plus",
253
+ noEventsText: "Aucun événement à afficher"
254
+ },
255
+ {
256
+ code: "de",
257
+ label: "German",
258
+ nativeName: "Deutsch",
259
+ direction: "ltr",
260
+ week: { dow: 1, doy: 4 },
261
+ buttonText: {
262
+ prev: "Zurück",
263
+ next: "Weiter",
264
+ today: "Heute",
265
+ year: "Jahr",
266
+ month: "Monat",
267
+ week: "Woche",
268
+ day: "Tag",
269
+ list: "Terminübersicht"
270
+ },
271
+ weekText: "KW",
272
+ allDayText: "ganztägig",
273
+ moreLinkText: "weitere",
274
+ noEventsText: "Keine Ereignisse anzuzeigen"
275
+ },
276
+ {
277
+ code: "pt",
278
+ label: "Portuguese",
279
+ nativeName: "Português",
280
+ direction: "ltr",
281
+ week: { dow: 0, doy: 4 },
282
+ buttonText: {
283
+ prev: "Ant",
284
+ next: "Próx",
285
+ today: "Hoje",
286
+ year: "Ano",
287
+ month: "Mês",
288
+ week: "Semana",
289
+ day: "Dia",
290
+ list: "Agenda"
291
+ },
292
+ weekText: "Sem",
293
+ allDayText: "dia inteiro",
294
+ moreLinkText: "mais",
295
+ noEventsText: "Não há eventos para mostrar"
296
+ },
297
+ {
298
+ code: "it",
299
+ label: "Italian",
300
+ nativeName: "Italiano",
301
+ direction: "ltr",
302
+ week: { dow: 1, doy: 4 },
303
+ buttonText: {
304
+ prev: "Prec",
305
+ next: "Succ",
306
+ today: "Oggi",
307
+ year: "Anno",
308
+ month: "Mese",
309
+ week: "Settimana",
310
+ day: "Giorno",
311
+ list: "Agenda"
312
+ },
313
+ weekText: "Set",
314
+ allDayText: "tutto il giorno",
315
+ moreLinkText: "altri",
316
+ noEventsText: "Non ci sono eventi da visualizzare"
317
+ },
318
+ {
319
+ code: "nl",
320
+ label: "Dutch",
321
+ nativeName: "Nederlands",
322
+ direction: "ltr",
323
+ week: { dow: 1, doy: 4 },
324
+ buttonText: {
325
+ prev: "Vorige",
326
+ next: "Volgende",
327
+ today: "Vandaag",
328
+ year: "Jaar",
329
+ month: "Maand",
330
+ week: "Week",
331
+ day: "Dag",
332
+ list: "Lijst"
333
+ },
334
+ weekText: "Wk",
335
+ allDayText: "hele dag",
336
+ moreLinkText: "extra",
337
+ noEventsText: "Geen evenementen om te laten zien"
338
+ },
339
+ {
340
+ code: "pl",
341
+ label: "Polish",
342
+ nativeName: "Polski",
343
+ direction: "ltr",
344
+ week: { dow: 1, doy: 4 },
345
+ buttonText: {
346
+ prev: "Poprzedni",
347
+ next: "Następny",
348
+ today: "Dziś",
349
+ year: "Rok",
350
+ month: "Miesiąc",
351
+ week: "Tydzień",
352
+ day: "Dzień",
353
+ list: "Plan dnia"
354
+ },
355
+ weekText: "Tydz",
356
+ allDayText: "cały dzień",
357
+ moreLinkText: "więcej",
358
+ noEventsText: "Brak wydarzeń do wyświetlenia"
359
+ },
360
+ {
361
+ code: "tr",
362
+ label: "Turkish",
363
+ nativeName: "Türkçe",
364
+ direction: "ltr",
365
+ week: { dow: 1, doy: 7 },
366
+ buttonText: {
367
+ prev: "geri",
368
+ next: "ileri",
369
+ today: "bugün",
370
+ year: "Yıl",
371
+ month: "Ay",
372
+ week: "Hafta",
373
+ day: "Gün",
374
+ list: "Ajanda"
375
+ },
376
+ weekText: "Hf",
377
+ allDayText: "tüm gün",
378
+ moreLinkText: "daha fazla",
379
+ noEventsText: "Gösterilecek etkinlik yok"
380
+ },
381
+ {
382
+ code: "ar",
383
+ label: "Arabic",
384
+ nativeName: "العربية",
385
+ direction: "rtl",
386
+ week: { dow: 6, doy: 12 },
387
+ buttonText: {
388
+ prev: "السابق",
389
+ next: "التالي",
390
+ today: "اليوم",
391
+ year: "سنة",
392
+ month: "شهر",
393
+ week: "أسبوع",
394
+ day: "يوم",
395
+ list: "أجندة"
396
+ },
397
+ weekText: "أسبوع",
398
+ allDayText: "اليوم كله",
399
+ moreLinkText: "أخرى",
400
+ noEventsText: "أي أحداث لعرض"
401
+ }
402
+ ];
403
+ function normalizeLocaleCode(locale) {
404
+ if (!locale || typeof locale !== "string") {
405
+ return "en";
406
+ }
407
+ const cleaned = locale.trim().toLowerCase().replace("_", "-");
408
+ if (!cleaned || cleaned === "auto") {
409
+ return "en";
410
+ }
411
+ const exact = SUPPORTED_CALENDAR_LOCALES.find((l) => l.code === cleaned);
412
+ if (exact) {
413
+ return exact.code;
414
+ }
415
+ const baseLang = cleaned.split("-")[0];
416
+ const baseMatch = SUPPORTED_CALENDAR_LOCALES.find((l) => l.code === baseLang);
417
+ return baseMatch ? baseMatch.code : baseLang || "en";
418
+ }
419
+ function isRtlLocale(locale) {
420
+ const normalized = normalizeLocaleCode(locale);
421
+ const baseLang = normalized.split("-")[0];
422
+ return RTL_LANGUAGE_PREFIXES.has(baseLang);
423
+ }
424
+ function getLocalizedCalendarBundle(locale) {
425
+ const code = normalizeLocaleCode(locale);
426
+ return SUPPORTED_CALENDAR_LOCALES.find((l) => l.code === code) ?? SUPPORTED_CALENDAR_LOCALES[0];
427
+ }
428
+ function resolveFullCalendarLocale(uiLocale, defaultLocaleSetting, runtimeOverrideLocale) {
429
+ if (runtimeOverrideLocale && runtimeOverrideLocale !== "auto" && runtimeOverrideLocale.trim()) {
430
+ return normalizeLocaleCode(runtimeOverrideLocale);
431
+ }
432
+ if (defaultLocaleSetting && defaultLocaleSetting !== "auto" && defaultLocaleSetting.trim()) {
433
+ return normalizeLocaleCode(defaultLocaleSetting);
434
+ }
435
+ return normalizeLocaleCode(uiLocale || "en");
436
+ }
437
+ function resolveCalendarDirection(locale, directionSetting) {
438
+ if (directionSetting === "ltr" || directionSetting === "rtl") {
439
+ return directionSetting;
440
+ }
441
+ return isRtlLocale(locale) ? "rtl" : "ltr";
442
+ }
443
+ function resolveLocaleFirstDay(locale, configuredFirstDay) {
444
+ if (typeof configuredFirstDay === "number" && configuredFirstDay >= 0 && configuredFirstDay <= 6) {
445
+ return configuredFirstDay;
446
+ }
447
+ const bundle = getLocalizedCalendarBundle(locale);
448
+ return bundle.week.dow;
449
+ }
450
+ function buildFullCalendarTimeFormat(timeFormat24h = true) {
451
+ if (timeFormat24h) {
452
+ return {
453
+ hour: "2-digit",
454
+ minute: "2-digit",
455
+ meridiem: false,
456
+ hour12: false
457
+ };
458
+ }
459
+ return {
460
+ hour: "numeric",
461
+ minute: "2-digit",
462
+ meridiem: "short",
463
+ hour12: true
464
+ };
465
+ }
466
+ const SUPPORTED_INTL_CALENDARS = [
467
+ { id: "gregory", intlCalendar: "gregory", defaultLabel: "Gregorian" },
468
+ { id: "iso8601", intlCalendar: "iso8601", defaultLabel: "ISO 8601" },
469
+ {
470
+ id: "islamic-umalqura",
471
+ intlCalendar: "islamic-umalqura",
472
+ defaultLabel: "Hijri (Umm al-Qura)"
473
+ },
474
+ {
475
+ id: "islamic-civil",
476
+ intlCalendar: "islamic-civil",
477
+ defaultLabel: "Hijri (Civil)"
478
+ },
479
+ {
480
+ id: "islamic-tbla",
481
+ intlCalendar: "islamic-tbla",
482
+ defaultLabel: "Hijri (Tabular)"
483
+ },
484
+ { id: "hebrew", intlCalendar: "hebrew", defaultLabel: "Hebrew" },
485
+ {
486
+ id: "persian",
487
+ intlCalendar: "persian",
488
+ defaultLabel: "Persian (Solar Hijri)"
489
+ },
490
+ { id: "buddhist", intlCalendar: "buddhist", defaultLabel: "Buddhist" },
491
+ { id: "japanese", intlCalendar: "japanese", defaultLabel: "Japanese" },
492
+ { id: "chinese", intlCalendar: "chinese", defaultLabel: "Chinese Lunar" },
493
+ { id: "indian", intlCalendar: "indian", defaultLabel: "Indian National" },
494
+ { id: "coptic", intlCalendar: "coptic", defaultLabel: "Coptic" },
495
+ { id: "ethiopic", intlCalendar: "ethiopic", defaultLabel: "Ethiopic" }
496
+ ];
497
+ const customFormatters = /* @__PURE__ */ new Map();
498
+ function applyDayOffset(date, dayOffset) {
499
+ if (!dayOffset || !Number.isFinite(dayOffset)) {
500
+ return date;
501
+ }
502
+ const offsetDays = Math.trunc(dayOffset);
503
+ return new Date(date.getTime() + offsetDays * 864e5);
504
+ }
505
+ const intlFormatterCache = /* @__PURE__ */ new Map();
506
+ function getCachedIntlFormatter(locale, options) {
507
+ const key = `${locale}|${options.year ?? ""}|${options.month ?? ""}|${options.day ?? ""}|${options.timeZone ?? "UTC"}`;
508
+ let fmt = intlFormatterCache.get(key);
509
+ if (!fmt) {
510
+ if (intlFormatterCache.size >= 200) {
511
+ const oldestKey = intlFormatterCache.keys().next().value;
512
+ if (oldestKey !== void 0) intlFormatterCache.delete(oldestKey);
513
+ }
514
+ fmt = new Intl.DateTimeFormat(locale, options);
515
+ intlFormatterCache.set(key, fmt);
516
+ }
517
+ return fmt;
518
+ }
519
+ function resolveCalendarLocaleTag(systemLocale, fallbackLocale) {
520
+ const rawLocale = systemLocale && systemLocale !== "auto" ? systemLocale : fallbackLocale && fallbackLocale !== "auto" ? fallbackLocale : "en";
521
+ return rawLocale.split("-u-")[0];
522
+ }
523
+ function tryFormatWithCustomFormatter(effectiveDate, system, effectiveLocale, compact) {
524
+ const customFn = customFormatters.get(system.id) ?? customFormatters.get(system.intlCalendar);
525
+ if (!customFn) return null;
526
+ try {
527
+ return customFn(effectiveDate, {
528
+ locale: effectiveLocale,
529
+ system,
530
+ compact
531
+ });
532
+ } catch {
533
+ return null;
534
+ }
535
+ }
536
+ function formatWithIntlFallback(effectiveDate, isoOrDateKey, intlLocale, compact) {
537
+ try {
538
+ const formatter = getCachedIntlFormatter(
539
+ intlLocale,
540
+ compact ? { day: "numeric", month: "short", timeZone: "UTC" } : { day: "numeric", month: "long", year: "numeric", timeZone: "UTC" }
541
+ );
542
+ return formatter.format(effectiveDate);
543
+ } catch {
544
+ try {
545
+ const fallback = getCachedIntlFormatter("en-u-ca-gregory", {
546
+ day: "numeric",
547
+ month: "short",
548
+ year: "numeric",
549
+ timeZone: "UTC"
550
+ });
551
+ return fallback.format(effectiveDate);
552
+ } catch {
553
+ return isoOrDateKey.slice(0, 10);
554
+ }
555
+ }
556
+ }
557
+ function formatDateInCalendarSystem(isoOrDateKey, system, locale = "en", options) {
558
+ const rawDate = /^\d{4}-\d{2}-\d{2}$/.test(isoOrDateKey) ? /* @__PURE__ */ new Date(`${isoOrDateKey}T12:00:00.000Z`) : new Date(isoOrDateKey);
559
+ if (Number.isNaN(rawDate.getTime())) {
560
+ return "";
561
+ }
562
+ const effectiveDate = applyDayOffset(rawDate, system.dayOffset);
563
+ const effectiveLocale = resolveCalendarLocaleTag(system.locale, locale);
564
+ const compact = Boolean(options?.compact);
565
+ const customResult = tryFormatWithCustomFormatter(
566
+ effectiveDate,
567
+ system,
568
+ effectiveLocale,
569
+ compact
570
+ );
571
+ if (customResult !== null) {
572
+ return customResult;
573
+ }
574
+ const calendarTag = (system.intlCalendar || "gregory").trim();
575
+ return formatWithIntlFallback(
576
+ effectiveDate,
577
+ isoOrDateKey,
578
+ `${effectiveLocale}-u-ca-${calendarTag}`,
579
+ compact
580
+ );
581
+ }
582
+ const FALLBACK_RECURRENCE_PRESETS = [
583
+ { id: "daily", label: "Daily", rrule: "FREQ=DAILY;INTERVAL=1" },
584
+ {
585
+ id: "weekly-mon",
586
+ label: "Weekly on Mondays",
587
+ rrule: "FREQ=WEEKLY;INTERVAL=1;BYDAY=MO"
588
+ },
589
+ {
590
+ id: "weekly-tue",
591
+ label: "Weekly on Tuesdays",
592
+ rrule: "FREQ=WEEKLY;INTERVAL=1;BYDAY=TU"
593
+ },
594
+ {
595
+ id: "weekly-wed",
596
+ label: "Weekly on Wednesdays",
597
+ rrule: "FREQ=WEEKLY;INTERVAL=1;BYDAY=WE"
598
+ },
599
+ {
600
+ id: "weekly-thu",
601
+ label: "Weekly on Thursdays",
602
+ rrule: "FREQ=WEEKLY;INTERVAL=1;BYDAY=TH"
603
+ },
604
+ {
605
+ id: "weekly-fri",
606
+ label: "Weekly on Fridays",
607
+ rrule: "FREQ=WEEKLY;INTERVAL=1;BYDAY=FR"
608
+ },
609
+ {
610
+ id: "weekly-sat",
611
+ label: "Weekly on Saturdays",
612
+ rrule: "FREQ=WEEKLY;INTERVAL=1;BYDAY=SA"
613
+ },
614
+ {
615
+ id: "weekly-sun",
616
+ label: "Weekly on Sundays",
617
+ rrule: "FREQ=WEEKLY;INTERVAL=1;BYDAY=SU"
618
+ },
619
+ { id: "monthly", label: "Monthly", rrule: "FREQ=MONTHLY;INTERVAL=1" },
620
+ { id: "yearly", label: "Yearly", rrule: "FREQ=YEARLY;INTERVAL=1" }
621
+ ];
622
+ const WEEKDAYS$1 = [
623
+ { code: "MO", label: "Mon", fullName: "Monday", jsDay: 1 },
624
+ { code: "TU", label: "Tue", fullName: "Tuesday", jsDay: 2 },
625
+ { code: "WE", label: "Wed", fullName: "Wednesday", jsDay: 3 },
626
+ { code: "TH", label: "Thu", fullName: "Thursday", jsDay: 4 },
627
+ { code: "FR", label: "Fri", fullName: "Friday", jsDay: 5 },
628
+ { code: "SA", label: "Sat", fullName: "Saturday", jsDay: 6 },
629
+ { code: "SU", label: "Sun", fullName: "Sunday", jsDay: 0 }
630
+ ];
631
+ function parseOptionalStartDate(startDate) {
632
+ if (typeof startDate === "string" && startDate.trim()) {
633
+ const d = new Date(startDate);
634
+ return Number.isNaN(d.getTime()) ? void 0 : d;
635
+ }
636
+ if (startDate instanceof Date && !Number.isNaN(startDate.getTime())) {
637
+ return startDate;
638
+ }
639
+ return void 0;
640
+ }
641
+ function buildOrderedRecurrencePresets(recurrencePresets, startDate) {
642
+ const byRRule = /* @__PURE__ */ new Map();
643
+ for (const def of FALLBACK_RECURRENCE_PRESETS) {
644
+ byRRule.set(def.rrule, def);
645
+ }
646
+ for (const p of recurrencePresets) {
647
+ if (p?.rrule) byRRule.set(p.rrule, p);
648
+ }
649
+ const parsedDate = parseOptionalStartDate(startDate);
650
+ const activeWeekday = parsedDate ? WEEKDAYS$1.find((w) => w.jsDay === parsedDate.getDay()) : void 0;
651
+ const result = [];
652
+ if (activeWeekday) {
653
+ const activeWeeklyRRule = `FREQ=WEEKLY;INTERVAL=1;BYDAY=${activeWeekday.code}`;
654
+ result.push({
655
+ id: `every-${activeWeekday.code.toLowerCase()}`,
656
+ label: `Every ${activeWeekday.fullName}`,
657
+ rrule: activeWeeklyRRule
658
+ });
659
+ byRRule.delete(activeWeeklyRRule);
660
+ }
661
+ for (const coreRRule of [
662
+ "FREQ=DAILY;INTERVAL=1",
663
+ "FREQ=MONTHLY;INTERVAL=1",
664
+ "FREQ=YEARLY;INTERVAL=1"
665
+ ]) {
666
+ const preset = byRRule.get(coreRRule);
667
+ if (preset) {
668
+ result.push(preset);
669
+ byRRule.delete(coreRRule);
670
+ }
671
+ }
672
+ for (const preset of byRRule.values()) {
673
+ result.push(preset);
674
+ }
675
+ return result;
676
+ }
677
+ const VALID_WEEKDAY_CODES = new Set(WEEKDAYS$1.map((w) => w.code));
678
+ function extractByDays(rrule) {
679
+ const match = rrule.match(/BYDAY=([A-Z,]+)/i);
680
+ if (!match?.[1]) return [];
681
+ return match[1].toUpperCase().split(",").map((d) => d.trim()).filter((d) => VALID_WEEKDAY_CODES.has(d));
682
+ }
683
+ function formatCategoryLabel(category) {
684
+ const trimmed = category.trim();
685
+ if (!trimmed) return "";
686
+ return trimmed.split(/[\s_-]+/).filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
687
+ }
688
+ function parseDateTimePickerValue(raw) {
689
+ if (!raw || !raw.trim()) return void 0;
690
+ const trimmed = raw.trim();
691
+ if (/^\d{4}-\d{2}-\d{2}$/.test(trimmed)) {
692
+ const [y, m, d] = trimmed.split("-").map(Number);
693
+ const localDt = new Date(y, m - 1, d, 9, 0, 0, 0);
694
+ return Number.isNaN(localDt.getTime()) ? void 0 : localDt;
695
+ }
696
+ const dt = new Date(trimmed);
697
+ return Number.isNaN(dt.getTime()) ? void 0 : dt;
698
+ }
699
+ function formatLocalYmd(date) {
700
+ const y = date.getFullYear();
701
+ const m = String(date.getMonth() + 1).padStart(2, "0");
702
+ const d = String(date.getDate()).padStart(2, "0");
703
+ return `${y}-${m}-${d}`;
704
+ }
705
+ function alignStartDateToWeekday(startStr, targetCode) {
706
+ if (!startStr) return startStr;
707
+ const target = WEEKDAYS$1.find((w) => w.code === targetCode);
708
+ if (!target) return startStr;
709
+ if (/^\d{4}-\d{2}-\d{2}$/.test(startStr)) {
710
+ const dt2 = /* @__PURE__ */ new Date(`${startStr}T00:00:00.000Z`);
711
+ if (Number.isNaN(dt2.getTime())) return startStr;
712
+ const currentDay2 = dt2.getUTCDay();
713
+ const diff2 = (target.jsDay - currentDay2 + 7) % 7;
714
+ if (diff2 === 0) return startStr;
715
+ dt2.setUTCDate(dt2.getUTCDate() + diff2);
716
+ return dt2.toISOString().slice(0, 10);
717
+ }
718
+ const dt = new Date(startStr);
719
+ if (Number.isNaN(dt.getTime())) return startStr;
720
+ const currentDay = dt.getDay();
721
+ const diff = (target.jsDay - currentDay + 7) % 7;
722
+ if (diff === 0) return startStr;
723
+ dt.setDate(dt.getDate() + diff);
724
+ return dt.toISOString();
725
+ }
726
+ function buildInitialDateWindow() {
727
+ const now = /* @__PURE__ */ new Date();
728
+ const y = now.getUTCFullYear();
729
+ return {
730
+ from: new Date(Date.UTC(y, 0, 1, 0, 0, 0)).toISOString(),
731
+ to: new Date(Date.UTC(y, 11, 31, 23, 59, 59)).toISOString()
732
+ };
733
+ }
734
+ function expandDateWindowToYear(currentStart, fromIso, toIso) {
735
+ const year = currentStart.getUTCFullYear();
736
+ const yearStart = new Date(Date.UTC(year, 0, 1, 0, 0, 0)).toISOString();
737
+ const yearEnd = new Date(Date.UTC(year, 11, 31, 23, 59, 59)).toISOString();
738
+ return {
739
+ from: fromIso < yearStart ? fromIso : yearStart,
740
+ to: toIso > yearEnd ? toIso : yearEnd
741
+ };
742
+ }
743
+ function matchLoadedSourceId(loadedSources, sourceFilter) {
744
+ if (!sourceFilter || sourceFilter === "all") return null;
745
+ if (loadedSources.some((s) => s.id === sourceFilter)) return sourceFilter;
746
+ const matched = loadedSources.find(
747
+ (s) => s.uid === sourceFilter || sourceFilter === "events" && (s.id.includes("event") || s.uid.includes("event")) || sourceFilter === "bookings" && (s.id.includes("booking") || s.uid.includes("booking"))
748
+ );
749
+ return matched ? matched.id : null;
750
+ }
751
+ function normalizeSelectedEndSlot(startStr, endStr) {
752
+ if (!endStr) return "";
753
+ if (/^\d{4}-\d{2}-\d{2}$/.test(startStr) && /^\d{4}-\d{2}-\d{2}$/.test(endStr)) {
754
+ const startMs = (/* @__PURE__ */ new Date(`${startStr}T00:00:00.000Z`)).getTime();
755
+ const endMs = (/* @__PURE__ */ new Date(`${endStr}T00:00:00.000Z`)).getTime();
756
+ if (!Number.isNaN(startMs) && !Number.isNaN(endMs) && endMs - startMs <= 864e5) {
757
+ return "";
758
+ }
759
+ }
760
+ return endStr;
761
+ }
762
+ function resolveActiveSecondaryCalendars(secondaryLangMode, configuredCalendars) {
763
+ if (secondaryLangMode === "off") {
764
+ return [];
765
+ }
766
+ const configured = (configuredCalendars ?? []).filter((c) => c.enabled);
767
+ if (configured.length > 0) {
768
+ return configured;
769
+ }
770
+ const hijriMatch = SUPPORTED_INTL_CALENDARS.find(
771
+ (c) => c.id === "islamic-umalqura"
772
+ );
773
+ return hijriMatch ? [
774
+ {
775
+ id: "hijri",
776
+ label: hijriMatch.defaultLabel,
777
+ intlCalendar: hijriMatch.intlCalendar,
778
+ enabled: true
779
+ }
780
+ ] : [];
781
+ }
782
+ function filterVisibleOccurrences(occurrences, sourceFilter) {
783
+ if (sourceFilter === "all") return occurrences;
784
+ return occurrences.filter(
785
+ (occ) => occ.sourceId === sourceFilter || occ.uid === sourceFilter || sourceFilter === "events" && (occ.sourceId.includes("event") || occ.uid.includes("event")) || sourceFilter === "bookings" && (occ.sourceId.includes("booking") || occ.uid.includes("booking"))
786
+ );
787
+ }
788
+ function computeSourceCounts(occurrences, sources) {
789
+ const counts = {};
790
+ for (const src of sources ?? []) {
791
+ counts[src.id] = 0;
792
+ }
793
+ for (const occ of occurrences) {
794
+ counts[occ.sourceId] = (counts[occ.sourceId] ?? 0) + 1;
795
+ }
796
+ return counts;
797
+ }
798
+ function buildSecondaryHeaderLabel(activeSecondaryCalendars, anchorDate, effectiveLocale, secondaryLangMode) {
799
+ if (activeSecondaryCalendars.length === 0 || secondaryLangMode === "off")
800
+ return "";
801
+ const anchorIso = anchorDate.toISOString().slice(0, 10);
802
+ const targetLoc = secondaryLangMode === "ar" ? "ar-SA" : effectiveLocale;
803
+ return activeSecondaryCalendars.map((sys) => {
804
+ const formatted = formatDateInCalendarSystem(
805
+ anchorIso,
806
+ { ...sys, locale: targetLoc },
807
+ targetLoc,
808
+ { compact: false }
809
+ );
810
+ return formatted ? `${sys.label}: ${formatted}` : "";
811
+ }).filter(Boolean).join(" • ");
812
+ }
813
+ function buildEnabledCalendarViews(settings, effectiveLocale) {
814
+ const b = getLocalizedCalendarBundle(effectiveLocale).buttonText;
815
+ const list = [];
816
+ if (settings?.monthView !== false)
817
+ list.push({ mode: "dayGridMonth", label: b.month });
818
+ if (settings?.weekView !== false)
819
+ list.push({ mode: "timeGridWeek", label: b.week });
820
+ if (settings?.dayView !== false)
821
+ list.push({ mode: "timeGridDay", label: b.day });
822
+ if (settings?.agendaView !== false)
823
+ list.push({ mode: "listMonth", label: b.list });
824
+ if (settings?.yearView !== false)
825
+ list.push({ mode: "multiMonthYear", label: b.year });
826
+ return list;
827
+ }
828
+ function toExclusiveAllDayEnd(startDate, endDate) {
829
+ const sDay = startDate.slice(0, 10);
830
+ const eDay = (endDate || startDate).slice(0, 10);
831
+ const baseDay = eDay >= sDay ? eDay : sDay;
832
+ const dt = /* @__PURE__ */ new Date(`${baseDay}T00:00:00.000Z`);
833
+ if (Number.isNaN(dt.getTime())) return endDate || startDate;
834
+ dt.setUTCDate(dt.getUTCDate() + 1);
835
+ return dt.toISOString().slice(0, 10);
836
+ }
837
+ function mapOccurrencesToFullCalendarEvents(occurrences, primaryColor) {
838
+ const fallbackColor = primaryColor || "#0284C7";
839
+ return occurrences.map((occ) => ({
840
+ id: occ.occurrenceId,
841
+ title: occ.title,
842
+ start: occ.allDay ? occ.startDate.slice(0, 10) : occ.startDate,
843
+ end: occ.allDay ? toExclusiveAllDayEnd(occ.startDate, occ.endDate) : occ.endDate,
844
+ allDay: occ.allDay,
845
+ backgroundColor: occ.color || fallbackColor,
846
+ borderColor: occ.color || fallbackColor,
847
+ extendedProps: { occurrence: occ }
848
+ }));
849
+ }
850
+ function addSourceCategoryKeys(cats, src, sourceFilter) {
851
+ if (src.enabled === false || sourceFilter && src.id !== sourceFilter)
852
+ return;
853
+ for (const key of Object.keys(src.categoryColors ?? {})) {
854
+ if (key) cats.add(key);
855
+ }
856
+ }
857
+ function collectAvailableCategories(sources, sourceFilter, visibleOccurrences) {
858
+ const cats = /* @__PURE__ */ new Set();
859
+ for (const src of sources ?? []) {
860
+ addSourceCategoryKeys(cats, src, sourceFilter);
861
+ }
862
+ for (const occ of visibleOccurrences) {
863
+ if (occ.category) cats.add(occ.category);
864
+ }
865
+ return Array.from(cats);
866
+ }
867
+ function computeStartEndIsoPair(quickStart, quickEnd) {
868
+ const startIso = quickStart.includes("T") ? new Date(quickStart).toISOString() : `${quickStart}T10:00:00.000Z`;
869
+ const hasValidEnd = Boolean(quickEnd) && !Number.isNaN(new Date(quickEnd).getTime());
870
+ const endIso = hasValidEnd ? new Date(quickEnd).toISOString() : new Date(new Date(startIso).getTime() + 36e5).toISOString();
871
+ return { startIso, endIso };
872
+ }
873
+ function buildQuickCreateRequestPayload(args) {
874
+ const { startIso, endIso } = computeStartEndIsoPair(
875
+ args.quickStart,
876
+ args.quickEnd
877
+ );
878
+ const targetSource = args.quickSourceId || (args.sourceFilter !== "all" ? args.sourceFilter : void 0);
879
+ return {
880
+ sourceId: targetSource,
881
+ title: args.quickTitle.trim(),
882
+ startDate: startIso,
883
+ endDate: endIso,
884
+ location: args.quickLocation.trim() || void 0,
885
+ category: args.quickCategory,
886
+ rrule: args.quickRRule || void 0,
887
+ customerName: args.quickCustomerName.trim() || void 0,
888
+ customerEmail: args.quickCustomerEmail.trim() || void 0,
889
+ partySize: args.quickPartySize || 1
890
+ };
891
+ }
892
+ function resolveActiveContentLocale(fallbackLocale) {
893
+ if (typeof window !== "undefined") {
894
+ const urlLoc = new URLSearchParams(window.location.search).get(
895
+ "plugins[i18n][locale]"
896
+ );
897
+ if (urlLoc) return urlLoc;
898
+ }
899
+ return fallbackLocale || "en";
900
+ }
901
+ function formatPluginMessageWithUrlLocale(pluginId, id, defaultMessage, formatMessage) {
902
+ const fullKey = `${pluginId}.${id}`;
903
+ if (typeof window !== "undefined") {
904
+ const urlLoc = new URLSearchParams(window.location.search).get(
905
+ "plugins[i18n][locale]"
906
+ );
907
+ const globalMessages = globalThis.__CM_I18N_MESSAGES__;
908
+ const override = urlLoc ? globalMessages?.[urlLoc]?.[fullKey] : void 0;
909
+ if (override) return override;
910
+ }
911
+ return formatMessage({ id: fullKey, defaultMessage });
912
+ }
913
+ const QuickCreateButtons = ({ showCreateButton, enabledSources, t, onOpenQuickCreate }) => {
914
+ if (!showCreateButton) return null;
915
+ if (enabledSources.length > 1) {
916
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: enabledSources.map((src, idx) => /* @__PURE__ */ jsxRuntime.jsxs(
917
+ designSystem.Button,
918
+ {
919
+ variant: idx === 0 ? "default" : "secondary",
920
+ size: "S",
921
+ onClick: () => onOpenQuickCreate(src.id),
922
+ children: [
923
+ "+ ",
924
+ src.label
925
+ ]
926
+ },
927
+ src.id
928
+ )) });
929
+ }
930
+ return /* @__PURE__ */ jsxRuntime.jsxs(
931
+ designSystem.Button,
932
+ {
933
+ variant: "default",
934
+ size: "S",
935
+ onClick: () => onOpenQuickCreate(enabledSources[0]?.id),
936
+ children: [
937
+ "+ ",
938
+ t("calendar.quickCreate", "New Entry")
939
+ ]
940
+ }
941
+ );
942
+ };
943
+ const CalendarToolbar = ({
944
+ t,
945
+ effectiveLocale,
946
+ effectiveDirection,
947
+ secondaryLangMode,
948
+ monthLabel,
949
+ secondaryHeaderLabel,
950
+ loading,
951
+ settings,
952
+ totalOccurrences,
953
+ sourceCounts,
954
+ sourceFilter,
955
+ onSourceFilterChange,
956
+ categoryFilter,
957
+ onCategoryFilterChange,
958
+ availableCategories,
959
+ viewMode,
960
+ enabledViews,
961
+ onViewChange,
962
+ onNavigate,
963
+ onOpenQuickCreate
964
+ }) => {
965
+ const localeBundle = getLocalizedCalendarBundle(effectiveLocale);
966
+ const isRtl = effectiveDirection === "rtl";
967
+ const enabledSources = (settings?.sources ?? []).filter(
968
+ (src) => src.enabled !== false
969
+ );
970
+ const showSecondaryBadge = Boolean(
971
+ secondaryHeaderLabel && secondaryLangMode !== "off"
972
+ );
973
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 4, children: [
974
+ /* @__PURE__ */ jsxRuntime.jsxs(
975
+ designSystem.Flex,
976
+ {
977
+ justifyContent: "space-between",
978
+ alignItems: "center",
979
+ wrap: "wrap",
980
+ gap: 3,
981
+ children: [
982
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
983
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "alpha", fontWeight: "bold", children: t("calendar.title", "Calendar & Scheduler") }),
984
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { paddingTop: 1, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", wrap: "wrap", children: [
985
+ /* @__PURE__ */ jsxRuntime.jsx(
986
+ designSystem.Typography,
987
+ {
988
+ variant: "delta",
989
+ fontWeight: "bold",
990
+ textColor: "neutral800",
991
+ style: { textTransform: "uppercase", letterSpacing: "0.03em" },
992
+ children: monthLabel.toUpperCase()
993
+ }
994
+ ),
995
+ showSecondaryBadge && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "secondary100", textColor: "secondary700", children: secondaryHeaderLabel }),
996
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Badge, { backgroundColor: "neutral150", textColor: "neutral700", children: [
997
+ localeBundle.nativeName,
998
+ " (",
999
+ effectiveDirection.toUpperCase(),
1000
+ ")"
1001
+ ] }),
1002
+ loading && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral500", children: t("calendar.loading", "Syncing...") })
1003
+ ] }) })
1004
+ ] }),
1005
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, wrap: "wrap", children: [
1006
+ /* @__PURE__ */ jsxRuntime.jsx(
1007
+ QuickCreateButtons,
1008
+ {
1009
+ showCreateButton: settings?.showCreateButton !== false,
1010
+ enabledSources,
1011
+ t,
1012
+ onOpenQuickCreate
1013
+ }
1014
+ ),
1015
+ /* @__PURE__ */ jsxRuntime.jsx(
1016
+ designSystem.Button,
1017
+ {
1018
+ variant: "secondary",
1019
+ size: "S",
1020
+ tag: "a",
1021
+ href: "/api/calendar/feed.ics",
1022
+ target: "_blank",
1023
+ rel: "noopener noreferrer",
1024
+ children: t("calendar.downloadIcsFeed", "iCal Feed (.ics)")
1025
+ }
1026
+ ),
1027
+ /* @__PURE__ */ jsxRuntime.jsx(
1028
+ designSystem.Button,
1029
+ {
1030
+ variant: "tertiary",
1031
+ size: "S",
1032
+ tag: reactRouterDom.NavLink,
1033
+ to: `/settings/${PLUGIN_ID}`,
1034
+ children: t("calendar.settings", "Settings")
1035
+ }
1036
+ )
1037
+ ] })
1038
+ ]
1039
+ }
1040
+ ),
1041
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { padding: 3, background: "neutral0", hasRadius: true, shadow: "filterShadow", children: /* @__PURE__ */ jsxRuntime.jsxs(
1042
+ designSystem.Flex,
1043
+ {
1044
+ justifyContent: "space-between",
1045
+ alignItems: "center",
1046
+ wrap: "wrap",
1047
+ gap: 3,
1048
+ children: [
1049
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", wrap: "wrap", children: [
1050
+ /* @__PURE__ */ jsxRuntime.jsxs(
1051
+ designSystem.Button,
1052
+ {
1053
+ variant: "tertiary",
1054
+ size: "S",
1055
+ onClick: () => onNavigate("prev"),
1056
+ children: [
1057
+ isRtl ? "→" : "←",
1058
+ " ",
1059
+ localeBundle.buttonText.prev
1060
+ ]
1061
+ }
1062
+ ),
1063
+ settings?.showTodayButton !== false && /* @__PURE__ */ jsxRuntime.jsx(
1064
+ designSystem.Button,
1065
+ {
1066
+ variant: "secondary",
1067
+ size: "S",
1068
+ onClick: () => onNavigate("today"),
1069
+ children: localeBundle.buttonText.today
1070
+ }
1071
+ ),
1072
+ /* @__PURE__ */ jsxRuntime.jsxs(
1073
+ designSystem.Button,
1074
+ {
1075
+ variant: "tertiary",
1076
+ size: "S",
1077
+ onClick: () => onNavigate("next"),
1078
+ children: [
1079
+ localeBundle.buttonText.next,
1080
+ " ",
1081
+ isRtl ? "←" : "→"
1082
+ ]
1083
+ }
1084
+ )
1085
+ ] }),
1086
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, wrap: "wrap", alignItems: "center", children: [
1087
+ /* @__PURE__ */ jsxRuntime.jsxs(
1088
+ designSystem.Button,
1089
+ {
1090
+ size: "S",
1091
+ variant: sourceFilter === "all" ? "default" : "tertiary",
1092
+ onClick: () => onSourceFilterChange("all"),
1093
+ children: [
1094
+ t("calendar.allSources", "All Sources"),
1095
+ " (",
1096
+ totalOccurrences,
1097
+ ")"
1098
+ ]
1099
+ }
1100
+ ),
1101
+ (settings?.sources ?? []).map((src) => {
1102
+ const srcCount = sourceCounts?.[src.id] ?? 0;
1103
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1104
+ designSystem.Button,
1105
+ {
1106
+ size: "S",
1107
+ variant: sourceFilter === src.id ? "default" : "tertiary",
1108
+ onClick: () => onSourceFilterChange(src.id),
1109
+ children: [
1110
+ /* @__PURE__ */ jsxRuntime.jsx(
1111
+ "span",
1112
+ {
1113
+ style: {
1114
+ display: "inline-block",
1115
+ width: 8,
1116
+ height: 8,
1117
+ borderRadius: "50%",
1118
+ background: src.color,
1119
+ marginRight: 6
1120
+ }
1121
+ }
1122
+ ),
1123
+ src.label,
1124
+ " (",
1125
+ srcCount,
1126
+ ")"
1127
+ ]
1128
+ },
1129
+ src.id
1130
+ );
1131
+ }),
1132
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { minWidth: 175, maxWidth: 220 }, children: /* @__PURE__ */ jsxRuntime.jsxs(
1133
+ designSystem.SingleSelect,
1134
+ {
1135
+ size: "S",
1136
+ "aria-label": t("calendar.toolbar.category", "Category Filter"),
1137
+ placeholder: t("placeholder.all_categories", "All Categories"),
1138
+ value: categoryFilter,
1139
+ onChange: (val) => onCategoryFilterChange(String(val)),
1140
+ children: [
1141
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "all", children: t("calendar.allCategories", "All Categories") }),
1142
+ availableCategories.map((cat) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: cat, children: formatCategoryLabel(cat) }, cat))
1143
+ ]
1144
+ }
1145
+ ) })
1146
+ ] }),
1147
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { gap: 1, wrap: "wrap", children: enabledViews.map((v) => /* @__PURE__ */ jsxRuntime.jsx(
1148
+ designSystem.Button,
1149
+ {
1150
+ size: "S",
1151
+ variant: viewMode === v.mode ? "default" : "tertiary",
1152
+ onClick: () => onViewChange(v.mode),
1153
+ children: v.label
1154
+ },
1155
+ v.mode
1156
+ )) })
1157
+ ]
1158
+ }
1159
+ ) })
1160
+ ] });
1161
+ };
1162
+ const CALENDAR_COLOR_PALETTES = {
1163
+ default: {
1164
+ id: "default",
1165
+ label: "Theme Default",
1166
+ lightPrimary: "#0284C7",
1167
+ darkPrimary: "#38BDF8",
1168
+ lightHeaderBg: "#E0F2FE",
1169
+ lightHeaderText: "#0C4A6E",
1170
+ lightTodayBg: "#F0F9FF",
1171
+ lightBadgeBg: "#E0F2FE",
1172
+ lightBadgeText: "#0369A1",
1173
+ lightBadgeBorder: "#7DD3FC",
1174
+ darkHeaderBg: "#1E293B",
1175
+ darkHeaderText: "#E2E8F0",
1176
+ darkTodayBg: "#172554",
1177
+ darkBadgeBg: "#1E293B",
1178
+ darkBadgeText: "#7DD3FC",
1179
+ darkBadgeBorder: "#0369A1"
1180
+ },
1181
+ purple: {
1182
+ id: "purple",
1183
+ label: "Monarch Purple",
1184
+ lightPrimary: "#6750A4",
1185
+ darkPrimary: "#D0BCFF",
1186
+ lightHeaderBg: "#F3EDF7",
1187
+ lightHeaderText: "#381E72",
1188
+ lightTodayBg: "#F7F2FA",
1189
+ lightBadgeBg: "#EADDFF",
1190
+ lightBadgeText: "#4F378B",
1191
+ lightBadgeBorder: "#D0BCFF",
1192
+ darkHeaderBg: "#211F26",
1193
+ darkHeaderText: "#E6E0E9",
1194
+ darkTodayBg: "#2B223C",
1195
+ darkBadgeBg: "#381E72",
1196
+ darkBadgeText: "#EADDFF",
1197
+ darkBadgeBorder: "#4F378B"
1198
+ },
1199
+ blue: {
1200
+ id: "blue",
1201
+ label: "Forma / Pulse Blue",
1202
+ lightPrimary: "#0F6CBD",
1203
+ darkPrimary: "#60A5FA",
1204
+ lightHeaderBg: "#EFF6FC",
1205
+ lightHeaderText: "#0A4478",
1206
+ lightTodayBg: "#F0F7FF",
1207
+ lightBadgeBg: "#DBEAFE",
1208
+ lightBadgeText: "#1E40AF",
1209
+ lightBadgeBorder: "#93C5FD",
1210
+ darkHeaderBg: "#172130",
1211
+ darkHeaderText: "#DBEAFE",
1212
+ darkTodayBg: "#172554",
1213
+ darkBadgeBg: "#1E3A8A",
1214
+ darkBadgeText: "#BFDBFE",
1215
+ darkBadgeBorder: "#2563EB"
1216
+ },
1217
+ indigo: {
1218
+ id: "indigo",
1219
+ label: "Breezy Indigo",
1220
+ lightPrimary: "#4F46E5",
1221
+ darkPrimary: "#818CF8",
1222
+ lightHeaderBg: "#EEF2FF",
1223
+ lightHeaderText: "#312E81",
1224
+ lightTodayBg: "#EEF2FF",
1225
+ lightBadgeBg: "#E0E7FF",
1226
+ lightBadgeText: "#3730A3",
1227
+ lightBadgeBorder: "#A5B4FC",
1228
+ darkHeaderBg: "#1E1B4B",
1229
+ darkHeaderText: "#E0E7FF",
1230
+ darkTodayBg: "#1E1B4B",
1231
+ darkBadgeBg: "#312E81",
1232
+ darkBadgeText: "#C7D2FE",
1233
+ darkBadgeBorder: "#4F46E5"
1234
+ },
1235
+ emerald: {
1236
+ id: "emerald",
1237
+ label: "Emerald Green",
1238
+ lightPrimary: "#059669",
1239
+ darkPrimary: "#34D399",
1240
+ lightHeaderBg: "#ECFDF5",
1241
+ lightHeaderText: "#065F46",
1242
+ lightTodayBg: "#F0FDF4",
1243
+ lightBadgeBg: "#D1FAE5",
1244
+ lightBadgeText: "#065F46",
1245
+ lightBadgeBorder: "#6EE7B7",
1246
+ darkHeaderBg: "#064E3B",
1247
+ darkHeaderText: "#D1FAE5",
1248
+ darkTodayBg: "#064E3B",
1249
+ darkBadgeBg: "#065F46",
1250
+ darkBadgeText: "#A7F3D0",
1251
+ darkBadgeBorder: "#059669"
1252
+ },
1253
+ rose: {
1254
+ id: "rose",
1255
+ label: "Pulse Rose",
1256
+ lightPrimary: "#E11D48",
1257
+ darkPrimary: "#FB7185",
1258
+ lightHeaderBg: "#FFF1F2",
1259
+ lightHeaderText: "#881337",
1260
+ lightTodayBg: "#FFF1F2",
1261
+ lightBadgeBg: "#FFE4E6",
1262
+ lightBadgeText: "#9F1239",
1263
+ lightBadgeBorder: "#FDA4AF",
1264
+ darkHeaderBg: "#3B0A19",
1265
+ darkHeaderText: "#FFE4E6",
1266
+ darkTodayBg: "#4C0519",
1267
+ darkBadgeBg: "#881337",
1268
+ darkBadgeText: "#FECDD3",
1269
+ darkBadgeBorder: "#E11D48"
1270
+ },
1271
+ amber: {
1272
+ id: "amber",
1273
+ label: "Monarch Amber",
1274
+ lightPrimary: "#D97706",
1275
+ darkPrimary: "#FBBF24",
1276
+ lightHeaderBg: "#FFFBEB",
1277
+ lightHeaderText: "#78350F",
1278
+ lightTodayBg: "#FEF3C7",
1279
+ lightBadgeBg: "#FEF3C7",
1280
+ lightBadgeText: "#92400E",
1281
+ lightBadgeBorder: "#FCD34D",
1282
+ darkHeaderBg: "#3B2506",
1283
+ darkHeaderText: "#FEF3C7",
1284
+ darkTodayBg: "#451A03",
1285
+ darkBadgeBg: "#78350F",
1286
+ darkBadgeText: "#FDE68A",
1287
+ darkBadgeBorder: "#D97706"
1288
+ }
1289
+ };
1290
+ function detectFromHexLuminance(strapiNeutral0) {
1291
+ if (typeof strapiNeutral0 !== "string" || !strapiNeutral0.startsWith("#")) {
1292
+ return null;
1293
+ }
1294
+ const hex = strapiNeutral0.slice(1).trim();
1295
+ const r = hex.length === 3 ? Number.parseInt(hex[0] + hex[0], 16) : Number.parseInt(hex.slice(0, 2), 16);
1296
+ if (Number.isNaN(r)) return null;
1297
+ return r < 128 ? "dark" : "light";
1298
+ }
1299
+ function detectFromBrowserEnvironment() {
1300
+ if (typeof window === "undefined") return "light";
1301
+ try {
1302
+ const storedTheme = window.localStorage?.getItem("STRAPI_THEME");
1303
+ if (storedTheme === "dark" || storedTheme === "light") {
1304
+ return storedTheme;
1305
+ }
1306
+ } catch {
1307
+ }
1308
+ return window.matchMedia?.("(prefers-color-scheme: dark)")?.matches ? "dark" : "light";
1309
+ }
1310
+ function detectEffectiveColorScheme(colorScheme = "auto", strapiNeutral0) {
1311
+ if (colorScheme === "dark" || colorScheme === "light") {
1312
+ return colorScheme;
1313
+ }
1314
+ return detectFromHexLuminance(strapiNeutral0) ?? detectFromBrowserEnvironment();
1315
+ }
1316
+ const CALENDAR_THEME_PRESETS = {
1317
+ breeze: {
1318
+ id: "breeze",
1319
+ label: "Breezy (Modern Default)",
1320
+ description: "FullCalendar Breezy style — airy sky & slate grid, soft-tinted event pills, and rounded-lg container",
1321
+ category: "modern",
1322
+ fontFamily: "'Inter Variable', Inter, system-ui, sans-serif",
1323
+ containerRadius: "10px",
1324
+ containerShadow: "0 1px 3px rgba(15, 23, 42, 0.06)",
1325
+ eventRadius: "6px",
1326
+ eventStyle: "tinted",
1327
+ dayNumberAlign: "start",
1328
+ todayHeaderAccent: "pill",
1329
+ primaryColor: "#0284C7",
1330
+ headerBg: "#F8FAFC",
1331
+ headerText: "#0F172A",
1332
+ cellBg: "#FFFFFF",
1333
+ cellAltBg: "#F8FAFC",
1334
+ cellHoverBg: "#F0F9FF",
1335
+ cellText: "#111827",
1336
+ cellMutedText: "#6B7280",
1337
+ borderColor: "#E5E7EB",
1338
+ todayBg: "#F0F9FF",
1339
+ todayBorder: "#0284C7",
1340
+ todayNumberBg: "#0284C7",
1341
+ todayNumberText: "#FFFFFF",
1342
+ badgeBg: "#E0F2FE",
1343
+ badgeText: "#0369A1",
1344
+ badgeBorder: "#7DD3FC"
1345
+ },
1346
+ monarch: {
1347
+ id: "monarch",
1348
+ label: "Monarch (Material 3)",
1349
+ description: "FullCalendar Monarch style — pill-shaped headers, centered day badges, tonal surfaces & rounded-xl frame",
1350
+ category: "modern",
1351
+ fontFamily: "'Roboto Variable', Roboto, system-ui, sans-serif",
1352
+ containerRadius: "14px",
1353
+ containerShadow: "0 2px 8px rgba(103, 80, 164, 0.08)",
1354
+ eventRadius: "6px",
1355
+ eventStyle: "solid",
1356
+ dayNumberAlign: "center",
1357
+ todayHeaderAccent: "pill",
1358
+ primaryColor: "#6750A4",
1359
+ headerBg: "#F7F2FA",
1360
+ headerText: "#1D1B20",
1361
+ cellBg: "#FFFFFF",
1362
+ cellAltBg: "#FAF8FC",
1363
+ cellHoverBg: "#F3EDF7",
1364
+ cellText: "#0C090C",
1365
+ cellMutedText: "#79747E",
1366
+ borderColor: "#DEDCE1",
1367
+ todayBg: "#F7F2FA",
1368
+ todayBorder: "#6750A4",
1369
+ todayNumberBg: "#895264",
1370
+ todayNumberText: "#FFFFFF",
1371
+ badgeBg: "#EADDFF",
1372
+ badgeText: "#4F378B",
1373
+ badgeBorder: "#D0BCFF"
1374
+ },
1375
+ forma: {
1376
+ id: "forma",
1377
+ label: "Forma (Fluent Enterprise)",
1378
+ description: "FullCalendar Forma style — architectural 6px left-accent event bars, top-accent Today bar & crisp 4px corners",
1379
+ category: "modern",
1380
+ fontFamily: "'Noto Sans Variable', 'Noto Sans', system-ui, sans-serif",
1381
+ containerRadius: "4px",
1382
+ containerShadow: "0 1px 2px rgba(0, 0, 0, 0.06)",
1383
+ eventRadius: "3px",
1384
+ eventStyle: "accent-bar",
1385
+ dayNumberAlign: "start",
1386
+ todayHeaderAccent: "top-bar",
1387
+ primaryColor: "#0F6CBD",
1388
+ headerBg: "#FFFFFF",
1389
+ headerText: "#030712",
1390
+ cellBg: "#FFFFFF",
1391
+ cellAltBg: "#F9FAFB",
1392
+ cellHoverBg: "#EFF6FC",
1393
+ cellText: "#030712",
1394
+ cellMutedText: "#6B7280",
1395
+ borderColor: "#E0E0E0",
1396
+ todayBg: "#F5F9FF",
1397
+ todayBorder: "#0F6CBD",
1398
+ todayNumberBg: "#0F6CBD",
1399
+ todayNumberText: "#FFFFFF",
1400
+ badgeBg: "#EFF6FC",
1401
+ badgeText: "#0F6CBD",
1402
+ badgeBorder: "#B4D6FA"
1403
+ },
1404
+ pulse: {
1405
+ id: "pulse",
1406
+ label: "Pulse (Apple / macOS)",
1407
+ description: "FullCalendar Pulse style — right-aligned day numbers, crimson Today circle, elevated controls & dotted minor slots",
1408
+ category: "modern",
1409
+ fontFamily: "ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, sans-serif",
1410
+ containerRadius: "8px",
1411
+ containerShadow: "0 1px 3px rgba(0, 0, 0, 0.12)",
1412
+ eventRadius: "6px",
1413
+ eventStyle: "solid",
1414
+ dayNumberAlign: "end",
1415
+ todayHeaderAccent: "pill",
1416
+ primaryColor: "#117AFF",
1417
+ headerBg: "#FFFFFF",
1418
+ headerText: "#232D3E",
1419
+ cellBg: "#FFFFFF",
1420
+ cellAltBg: "#F9FAFB",
1421
+ cellHoverBg: "#F0F7FF",
1422
+ cellText: "#232D3E",
1423
+ cellMutedText: "#545C6C",
1424
+ borderColor: "#E5E7EB",
1425
+ todayBg: "#FFF5F5",
1426
+ todayBorder: "#FD453B",
1427
+ todayNumberBg: "#FD453B",
1428
+ todayNumberText: "#FFFFFF",
1429
+ badgeBg: "#EFF6FF",
1430
+ badgeText: "#1D4ED8",
1431
+ badgeBorder: "#BFDBFE"
1432
+ },
1433
+ classic: {
1434
+ id: "classic",
1435
+ label: "Classic (FullCalendar Standard)",
1436
+ description: "Traditional FullCalendar v6/v7 studio grid with slate headers, warm yellow Today cell tint & solid events",
1437
+ category: "modern",
1438
+ fontFamily: "ui-sans-serif, system-ui, sans-serif",
1439
+ containerRadius: "6px",
1440
+ containerShadow: "none",
1441
+ eventRadius: "4px",
1442
+ eventStyle: "solid",
1443
+ dayNumberAlign: "end",
1444
+ todayHeaderAccent: "tint",
1445
+ primaryColor: "#3788D8",
1446
+ headerBg: "#F8FAFC",
1447
+ headerText: "#1E293B",
1448
+ cellBg: "#FFFFFF",
1449
+ cellAltBg: "#F8FAFC",
1450
+ cellHoverBg: "#F1F5F9",
1451
+ cellText: "#030712",
1452
+ cellMutedText: "#6B7280",
1453
+ borderColor: "#DDDDDD",
1454
+ todayBg: "#FEFCE8",
1455
+ todayBorder: "#EAB308",
1456
+ todayNumberBg: "#334155",
1457
+ todayNumberText: "#FFFFFF",
1458
+ badgeBg: "#F1F5F9",
1459
+ badgeText: "#334155",
1460
+ badgeBorder: "#CBD5E1"
1461
+ },
1462
+ shadcn: {
1463
+ id: "shadcn",
1464
+ label: "Shadcn UI (Zinc Minimal)",
1465
+ description: "Design-system theme matching Shadcn UI — ultra-clean 1px zinc borders, monochrome primary & tinted badges",
1466
+ category: "design-system",
1467
+ fontFamily: "'Inter Variable', Inter, system-ui, sans-serif",
1468
+ containerRadius: "8px",
1469
+ containerShadow: "0 1px 2px 0 rgba(0, 0, 0, 0.05)",
1470
+ eventRadius: "5px",
1471
+ eventStyle: "tinted",
1472
+ dayNumberAlign: "start",
1473
+ todayHeaderAccent: "pill",
1474
+ primaryColor: "#18181B",
1475
+ headerBg: "#FAFAFA",
1476
+ headerText: "#09090B",
1477
+ cellBg: "#FFFFFF",
1478
+ cellAltBg: "#FAFAFA",
1479
+ cellHoverBg: "#F4F4F5",
1480
+ cellText: "#09090B",
1481
+ cellMutedText: "#71717A",
1482
+ borderColor: "#E4E4E7",
1483
+ todayBg: "#F4F4F5",
1484
+ todayBorder: "#18181B",
1485
+ todayNumberBg: "#18181B",
1486
+ todayNumberText: "#FAFAFA",
1487
+ badgeBg: "#F4F4F5",
1488
+ badgeText: "#18181B",
1489
+ badgeBorder: "#D4D4D8"
1490
+ },
1491
+ mui: {
1492
+ id: "mui",
1493
+ label: "Material UI (MUI Paper)",
1494
+ description: "Design-system theme matching Material UI — Paper surface elevation, Roboto uppercase headers & Material blue",
1495
+ category: "design-system",
1496
+ fontFamily: "'Roboto Variable', Roboto, Helvetica, Arial, sans-serif",
1497
+ containerRadius: "8px",
1498
+ containerShadow: "0px 2px 4px -1px rgba(0,0,0,0.08), 0px 4px 5px 0px rgba(0,0,0,0.05)",
1499
+ eventRadius: "4px",
1500
+ eventStyle: "solid",
1501
+ dayNumberAlign: "center",
1502
+ todayHeaderAccent: "pill",
1503
+ primaryColor: "#1976D2",
1504
+ headerBg: "#F5F7FA",
1505
+ headerText: "#1E293B",
1506
+ cellBg: "#FFFFFF",
1507
+ cellAltBg: "#FAFAFA",
1508
+ cellHoverBg: "#E3F2FD",
1509
+ cellText: "#212121",
1510
+ cellMutedText: "#757575",
1511
+ borderColor: "#E0E0E0",
1512
+ todayBg: "#E3F2FD",
1513
+ todayBorder: "#1976D2",
1514
+ todayNumberBg: "#1976D2",
1515
+ todayNumberText: "#FFFFFF",
1516
+ badgeBg: "#E3F2FD",
1517
+ badgeText: "#1565C0",
1518
+ badgeBorder: "#90CAF9"
1519
+ },
1520
+ emerald: {
1521
+ id: "emerald",
1522
+ label: "Emerald Mosque",
1523
+ description: "Lush emerald & teal palette inspired by traditional Islamic architecture",
1524
+ category: "heritage",
1525
+ fontFamily: "'Inter Variable', Inter, system-ui, sans-serif",
1526
+ containerRadius: "10px",
1527
+ containerShadow: "0 1px 3px rgba(13, 148, 136, 0.08)",
1528
+ eventRadius: "6px",
1529
+ eventStyle: "solid",
1530
+ dayNumberAlign: "start",
1531
+ todayHeaderAccent: "pill",
1532
+ primaryColor: "#0D9488",
1533
+ headerBg: "#CCFBF1",
1534
+ headerText: "#115E59",
1535
+ cellBg: "#FFFFFF",
1536
+ cellAltBg: "#F0FDFA",
1537
+ cellHoverBg: "#CCFBF1",
1538
+ cellText: "#0F172A",
1539
+ cellMutedText: "#475569",
1540
+ borderColor: "#99F6E4",
1541
+ todayBg: "#F0FDFA",
1542
+ todayBorder: "#0D9488",
1543
+ todayNumberBg: "#0D9488",
1544
+ todayNumberText: "#FFFFFF",
1545
+ badgeBg: "#CCFBF1",
1546
+ badgeText: "#0F766E",
1547
+ badgeBorder: "#5EEAD4"
1548
+ },
1549
+ sand: {
1550
+ id: "sand",
1551
+ label: "Warm Sand & Gold",
1552
+ description: "Warm parchment & amber tones for high-comfort reading",
1553
+ category: "heritage",
1554
+ fontFamily: "'Noto Sans Variable', 'Noto Sans', system-ui, sans-serif",
1555
+ containerRadius: "10px",
1556
+ containerShadow: "0 1px 3px rgba(180, 83, 9, 0.08)",
1557
+ eventRadius: "6px",
1558
+ eventStyle: "solid",
1559
+ dayNumberAlign: "start",
1560
+ todayHeaderAccent: "pill",
1561
+ primaryColor: "#B45309",
1562
+ headerBg: "#FEF3C7",
1563
+ headerText: "#78350F",
1564
+ cellBg: "#FFFBEB",
1565
+ cellAltBg: "#FEF9C3",
1566
+ cellHoverBg: "#FEF3C7",
1567
+ cellText: "#1C1917",
1568
+ cellMutedText: "#78716C",
1569
+ borderColor: "#FDE68A",
1570
+ todayBg: "#FEF3C7",
1571
+ todayBorder: "#D97706",
1572
+ todayNumberBg: "#B45309",
1573
+ todayNumberText: "#FFFFFF",
1574
+ badgeBg: "#FEF3C7",
1575
+ badgeText: "#92400E",
1576
+ badgeBorder: "#FCD34D"
1577
+ },
1578
+ midnight: {
1579
+ id: "midnight",
1580
+ label: "Midnight Slate",
1581
+ description: "Deep dark-mode slate grid with luminous sky accents",
1582
+ category: "heritage",
1583
+ fontFamily: "'Inter Variable', Inter, system-ui, sans-serif",
1584
+ containerRadius: "10px",
1585
+ containerShadow: "0 4px 12px rgba(2, 6, 23, 0.35)",
1586
+ eventRadius: "6px",
1587
+ eventStyle: "tinted",
1588
+ dayNumberAlign: "start",
1589
+ todayHeaderAccent: "pill",
1590
+ primaryColor: "#38BDF8",
1591
+ headerBg: "#1E293B",
1592
+ headerText: "#E2E8F0",
1593
+ cellBg: "#0F172A",
1594
+ cellAltBg: "#162032",
1595
+ cellHoverBg: "#1E293B",
1596
+ cellText: "#F8FAFC",
1597
+ cellMutedText: "#94A3B8",
1598
+ borderColor: "#334155",
1599
+ todayBg: "#172554",
1600
+ todayBorder: "#38BDF8",
1601
+ todayNumberBg: "#0284C7",
1602
+ todayNumberText: "#FFFFFF",
1603
+ badgeBg: "#1E293B",
1604
+ badgeText: "#7DD3FC",
1605
+ badgeBorder: "#0369A1"
1606
+ }
1607
+ };
1608
+ function applyDarkSurfaceOverride(base, themeKey) {
1609
+ return {
1610
+ ...base,
1611
+ headerBg: themeKey === "monarch" ? "#141218" : "#111827",
1612
+ headerText: "#F3F4F6",
1613
+ cellBg: "#030712",
1614
+ cellAltBg: "#0B0F19",
1615
+ cellHoverBg: "#1F2937",
1616
+ cellText: "#F9FAFB",
1617
+ cellMutedText: "#9CA3AF",
1618
+ borderColor: themeKey === "forma" ? "#2A2A2A" : "#1F2937",
1619
+ todayBg: "#172554",
1620
+ todayBorder: base.primaryColor,
1621
+ todayNumberBg: base.primaryColor,
1622
+ todayNumberText: "#FFFFFF",
1623
+ badgeBg: "#1E293B",
1624
+ badgeText: "#E2E8F0",
1625
+ badgeBorder: "#334155"
1626
+ };
1627
+ }
1628
+ function selectPaletteColors(palette, isDark) {
1629
+ if (isDark) {
1630
+ return {
1631
+ primary: palette.darkPrimary,
1632
+ headerBg: palette.darkHeaderBg,
1633
+ headerText: palette.darkHeaderText,
1634
+ todayBg: palette.darkTodayBg,
1635
+ badgeBg: palette.darkBadgeBg,
1636
+ badgeText: palette.darkBadgeText,
1637
+ badgeBorder: palette.darkBadgeBorder
1638
+ };
1639
+ }
1640
+ return {
1641
+ primary: palette.lightPrimary,
1642
+ headerBg: palette.lightHeaderBg,
1643
+ headerText: palette.lightHeaderText,
1644
+ todayBg: palette.lightTodayBg,
1645
+ badgeBg: palette.lightBadgeBg,
1646
+ badgeText: palette.lightBadgeText,
1647
+ badgeBorder: palette.lightBadgeBorder
1648
+ };
1649
+ }
1650
+ function applyPaletteOverride(resolved, paletteKey, isDark) {
1651
+ const palette = CALENDAR_COLOR_PALETTES[paletteKey];
1652
+ if (!palette) return resolved;
1653
+ const c = selectPaletteColors(palette, isDark);
1654
+ return {
1655
+ ...resolved,
1656
+ primaryColor: c.primary,
1657
+ headerBg: c.headerBg,
1658
+ headerText: c.headerText,
1659
+ todayBg: c.todayBg,
1660
+ todayBorder: c.primary,
1661
+ todayNumberBg: c.primary,
1662
+ todayNumberText: isDark && paletteKey === "purple" ? "#381E72" : "#FFFFFF",
1663
+ badgeBg: c.badgeBg,
1664
+ badgeText: c.badgeText,
1665
+ badgeBorder: c.badgeBorder
1666
+ };
1667
+ }
1668
+ function resolveCalendarTheme(themeKey = "breeze", colorScheme = "auto", paletteKey = "default", strapiNeutral0) {
1669
+ const base = CALENDAR_THEME_PRESETS[themeKey] ?? CALENDAR_THEME_PRESETS.breeze;
1670
+ const effectiveScheme = detectEffectiveColorScheme(
1671
+ colorScheme,
1672
+ strapiNeutral0
1673
+ );
1674
+ const isDark = effectiveScheme === "dark" || themeKey === "midnight";
1675
+ const surfaced = isDark && themeKey !== "midnight" ? applyDarkSurfaceOverride(base, themeKey) : { ...base };
1676
+ return paletteKey !== "default" ? applyPaletteOverride(surfaced, paletteKey, isDark) : surfaced;
1677
+ }
1678
+ function buildEventStyleCss(scope, activeTheme, evtRadius) {
1679
+ if (activeTheme.eventStyle === "tinted") {
1680
+ return `
1681
+ ${scope} .fc-daygrid-event,
1682
+ ${scope} .fc-timegrid-event {
1683
+ border-radius: ${evtRadius} !important;
1684
+ border-width: 1px !important;
1685
+ border-style: solid !important;
1686
+ background: color-mix(in oklab, var(--fc-event-bg-color, ${activeTheme.primaryColor}) 20%, ${activeTheme.cellBg}) !important;
1687
+ border-color: color-mix(in oklab, var(--fc-event-bg-color, ${activeTheme.primaryColor}) 45%, ${activeTheme.borderColor}) !important;
1688
+ color: color-mix(in oklab, var(--fc-event-bg-color, ${activeTheme.primaryColor}) 72%, ${activeTheme.cellText}) !important;
1689
+ box-shadow: none !important;
1690
+ }
1691
+ ${scope} .fc-daygrid-event .fc-event-main,
1692
+ ${scope} .fc-timegrid-event .fc-event-main,
1693
+ ${scope} .fc-daygrid-event .vicacha-fc-event-inner,
1694
+ ${scope} .fc-timegrid-event .vicacha-fc-event-inner {
1695
+ color: color-mix(in oklab, var(--fc-event-bg-color, ${activeTheme.primaryColor}) 75%, ${activeTheme.cellText}) !important;
1696
+ }
1697
+ `;
1698
+ }
1699
+ if (activeTheme.eventStyle === "accent-bar") {
1700
+ return `
1701
+ ${scope} .fc-daygrid-event,
1702
+ ${scope} .fc-timegrid-event {
1703
+ border-radius: ${evtRadius} !important;
1704
+ border-top: 1px solid color-mix(in oklab, var(--fc-event-bg-color, ${activeTheme.primaryColor}) 32%, transparent) !important;
1705
+ border-bottom: 1px solid color-mix(in oklab, var(--fc-event-bg-color, ${activeTheme.primaryColor}) 32%, transparent) !important;
1706
+ border-inline-end: 1px solid color-mix(in oklab, var(--fc-event-bg-color, ${activeTheme.primaryColor}) 32%, transparent) !important;
1707
+ border-inline-start: 6px solid var(--fc-event-bg-color, ${activeTheme.primaryColor}) !important;
1708
+ background: color-mix(in oklab, var(--fc-event-bg-color, ${activeTheme.primaryColor}) 24%, ${activeTheme.cellBg}) !important;
1709
+ color: ${activeTheme.cellText} !important;
1710
+ box-shadow: none !important;
1711
+ }
1712
+ ${scope} .fc-daygrid-event .fc-event-main,
1713
+ ${scope} .fc-timegrid-event .fc-event-main,
1714
+ ${scope} .fc-daygrid-event .vicacha-fc-event-inner,
1715
+ ${scope} .fc-timegrid-event .vicacha-fc-event-inner {
1716
+ color: ${activeTheme.cellText} !important;
1717
+ }
1718
+ `;
1719
+ }
1720
+ return `
1721
+ ${scope} .fc-daygrid-event,
1722
+ ${scope} .fc-timegrid-event {
1723
+ border-radius: ${evtRadius} !important;
1724
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08) !important;
1725
+ }
1726
+ `;
1727
+ }
1728
+ function buildTodayCellCss(scope, activeTheme) {
1729
+ if (activeTheme.todayHeaderAccent === "top-bar") {
1730
+ return `
1731
+ ${scope} .fc-daygrid-day.fc-day-today {
1732
+ background: ${activeTheme.todayBg} !important;
1733
+ box-shadow: inset 0 4px 0 0 ${activeTheme.todayBorder} !important;
1734
+ }
1735
+ ${scope} .fc-col-header-cell.fc-day-today {
1736
+ box-shadow: inset 0 4px 0 0 ${activeTheme.todayBorder} !important;
1737
+ }
1738
+ `;
1739
+ }
1740
+ if (activeTheme.id === "monarch" || activeTheme.id === "pulse") {
1741
+ return `
1742
+ ${scope} .fc-daygrid-day.fc-day-today {
1743
+ background: ${activeTheme.todayBg} !important;
1744
+ }
1745
+ `;
1746
+ }
1747
+ return `
1748
+ ${scope} .fc-daygrid-day.fc-day-today {
1749
+ background: ${activeTheme.todayBg} !important;
1750
+ box-shadow: inset 0 0 0 2px ${activeTheme.todayBorder} !important;
1751
+ }
1752
+ `;
1753
+ }
1754
+ function buildInteractiveCursorCss(scope, allowCreate) {
1755
+ if (!allowCreate) return "";
1756
+ return `
1757
+ ${scope} .fc-daygrid-day,
1758
+ ${scope} .fc-timegrid-slot-lane {
1759
+ cursor: pointer;
1760
+ }
1761
+ ${scope} .vicacha-fc-day-quick-add {
1762
+ opacity: 0;
1763
+ transition: opacity 0.15s ease, transform 0.15s ease;
1764
+ }
1765
+ ${scope} .fc-daygrid-day:hover .vicacha-fc-day-quick-add {
1766
+ opacity: 1;
1767
+ }
1768
+ `;
1769
+ }
1770
+ function resolveThemeDefaults(activeTheme) {
1771
+ return {
1772
+ radius: activeTheme.containerRadius ?? "10px",
1773
+ shadow: activeTheme.containerShadow ?? "none",
1774
+ evtRadius: activeTheme.eventRadius ?? "6px",
1775
+ fontFamily: activeTheme.fontFamily ?? "'Inter Variable', Inter, system-ui, sans-serif"
1776
+ };
1777
+ }
1778
+ function resolveThemeVariantTokens(activeTheme) {
1779
+ const isForma = activeTheme.id === "forma";
1780
+ return {
1781
+ nowColor: activeTheme.id === "pulse" ? "#EF4444" : activeTheme.primaryColor,
1782
+ headerPad: activeTheme.id === "monarch" ? "12px 4px" : "10px 4px",
1783
+ headerWeight: activeTheme.id === "pulse" ? 600 : 700,
1784
+ headerTransform: isForma ? "none" : "uppercase",
1785
+ headerTracking: isForma ? "0" : "0.04em",
1786
+ listDayBorder: isForma ? `border-inline-start: 4px solid ${activeTheme.primaryColor} !important;` : "",
1787
+ weekNumRadius: isForma ? "2px" : "5px"
1788
+ };
1789
+ }
1790
+ function buildCalendarThemeCss(rawTheme, allowCreate = true) {
1791
+ const safePrimary = /^#[0-9a-fA-F]{3,8}$/.test(rawTheme.primaryColor ?? "") ? rawTheme.primaryColor : "#0284C7";
1792
+ const activeTheme = { ...rawTheme, primaryColor: safePrimary };
1793
+ const scope = `.vicacha-fc-container.vicacha-fc-theme-${activeTheme.id}`;
1794
+ const defaults = resolveThemeDefaults(activeTheme);
1795
+ const variants = resolveThemeVariantTokens(activeTheme);
1796
+ const eventRules = buildEventStyleCss(scope, activeTheme, defaults.evtRadius);
1797
+ const todayCellRule = buildTodayCellCss(scope, activeTheme);
1798
+ const interactiveCursorRule = buildInteractiveCursorCss(scope, allowCreate);
1799
+ return `
1800
+ ${scope} {
1801
+ --fc-border-color: ${activeTheme.borderColor};
1802
+ --fc-page-bg-color: ${activeTheme.cellBg};
1803
+ --fc-neutral-bg-color: ${activeTheme.cellAltBg};
1804
+ --fc-today-bg-color: ${activeTheme.todayBg};
1805
+ --fc-highlight-color: ${activeTheme.cellHoverBg};
1806
+ --fc-now-indicator-color: ${variants.nowColor};
1807
+ font-family: ${defaults.fontFamily};
1808
+ background: ${activeTheme.cellBg};
1809
+ color: ${activeTheme.cellText};
1810
+ border-radius: ${defaults.radius};
1811
+ border: 1px solid ${activeTheme.borderColor};
1812
+ box-shadow: ${defaults.shadow};
1813
+ overflow: hidden;
1814
+ }
1815
+ ${scope} .fc-theme-standard th,
1816
+ ${scope} .fc-col-header-cell {
1817
+ background: ${activeTheme.headerBg} !important;
1818
+ color: ${activeTheme.headerText} !important;
1819
+ border-color: ${activeTheme.borderColor} !important;
1820
+ padding: ${variants.headerPad} !important;
1821
+ }
1822
+ ${scope} .fc-col-header-cell-cushion {
1823
+ color: ${activeTheme.headerText} !important;
1824
+ font-weight: ${variants.headerWeight} !important;
1825
+ font-size: 12px !important;
1826
+ text-transform: ${variants.headerTransform} !important;
1827
+ letter-spacing: ${variants.headerTracking} !important;
1828
+ text-decoration: none !important;
1829
+ }
1830
+ ${scope} .fc-daygrid-day {
1831
+ background: ${activeTheme.cellBg} !important;
1832
+ color: ${activeTheme.cellText} !important;
1833
+ border-color: ${activeTheme.borderColor} !important;
1834
+ transition: background-color 0.15s ease;
1835
+ }
1836
+ ${scope} .fc-daygrid-day:hover {
1837
+ background: ${activeTheme.cellHoverBg} !important;
1838
+ }
1839
+ ${scope} .fc-daygrid-day.fc-day-other {
1840
+ background: ${activeTheme.cellAltBg} !important;
1841
+ opacity: 0.72;
1842
+ }
1843
+ ${todayCellRule}
1844
+ ${eventRules}
1845
+ ${interactiveCursorRule}
1846
+ ${scope} .fc-daygrid-day-top {
1847
+ flex-direction: row !important;
1848
+ width: 100% !important;
1849
+ padding: 4px 4px 2px !important;
1850
+ box-sizing: border-box !important;
1851
+ }
1852
+ ${scope} .fc-daygrid-day-number {
1853
+ width: 100% !important;
1854
+ min-width: 0 !important;
1855
+ padding: 0 !important;
1856
+ color: ${activeTheme.cellText} !important;
1857
+ text-decoration: none !important;
1858
+ }
1859
+ ${scope} .fc-daygrid-day-frame {
1860
+ min-height: 92px !important;
1861
+ padding: 2px !important;
1862
+ box-sizing: border-box !important;
1863
+ }
1864
+ ${scope} .fc-daygrid-more-link {
1865
+ font-size: 11px !important;
1866
+ font-weight: 700 !important;
1867
+ color: ${activeTheme.primaryColor} !important;
1868
+ padding: 1px 4px !important;
1869
+ text-decoration: none !important;
1870
+ }
1871
+ ${scope} .fc-timegrid-slot,
1872
+ ${scope} .fc-timegrid-axis,
1873
+ ${scope} .fc-scrollgrid {
1874
+ border-color: ${activeTheme.borderColor} !important;
1875
+ color: ${activeTheme.cellText} !important;
1876
+ background: ${activeTheme.cellBg} !important;
1877
+ }
1878
+ ${scope} .fc-timegrid-slot-minor {
1879
+ border-top-style: dotted !important;
1880
+ }
1881
+ ${scope} .fc-timegrid-slot-label-cushion,
1882
+ ${scope} .fc-list-day-text,
1883
+ ${scope} .fc-list-day-side-text {
1884
+ color: ${activeTheme.headerText} !important;
1885
+ font-weight: 600 !important;
1886
+ text-decoration: none !important;
1887
+ }
1888
+ ${scope} .fc-list-day-cushion {
1889
+ background: ${activeTheme.headerBg} !important;
1890
+ ${variants.listDayBorder}
1891
+ }
1892
+ ${scope} .fc-list-event td {
1893
+ background: ${activeTheme.cellBg} !important;
1894
+ color: ${activeTheme.cellText} !important;
1895
+ border-color: ${activeTheme.borderColor} !important;
1896
+ }
1897
+ ${scope} .fc-list-empty {
1898
+ background: ${activeTheme.cellAltBg} !important;
1899
+ color: ${activeTheme.cellMutedText} !important;
1900
+ }
1901
+ ${scope} .fc-multimonth {
1902
+ border: none !important;
1903
+ padding: 12px !important;
1904
+ gap: 14px !important;
1905
+ box-sizing: border-box !important;
1906
+ background: ${activeTheme.cellAltBg} !important;
1907
+ }
1908
+ ${scope} .fc-multimonth-month {
1909
+ background: ${activeTheme.cellBg} !important;
1910
+ color: ${activeTheme.cellText} !important;
1911
+ border: 1px solid ${activeTheme.borderColor} !important;
1912
+ border-radius: ${defaults.radius} !important;
1913
+ box-sizing: border-box !important;
1914
+ overflow: hidden !important;
1915
+ padding: 0 !important;
1916
+ }
1917
+ ${scope} .fc-multimonth-month[style*="33.3"] {
1918
+ width: calc((100% - 28px) / 3) !important;
1919
+ flex: 0 0 calc((100% - 28px) / 3) !important;
1920
+ }
1921
+ ${scope} .fc-multimonth-month[style*="50%"] {
1922
+ width: calc((100% - 14px) / 2) !important;
1923
+ flex: 0 0 calc((100% - 14px) / 2) !important;
1924
+ }
1925
+ ${scope} .fc-multimonth-month[style*="25%"] {
1926
+ width: calc((100% - 42px) / 4) !important;
1927
+ flex: 0 0 calc((100% - 42px) / 4) !important;
1928
+ }
1929
+ ${scope} .fc-multimonth-month[style*="100%"] {
1930
+ width: 100% !important;
1931
+ flex: 0 0 100% !important;
1932
+ }
1933
+ ${scope} .fc-multimonth-header-table th,
1934
+ ${scope} .fc-multimonth-month .fc-col-header-cell {
1935
+ padding: 6px 2px !important;
1936
+ }
1937
+ ${scope} .fc-multimonth-month .fc-col-header-cell-cushion {
1938
+ font-size: 10.5px !important;
1939
+ letter-spacing: 0.02em !important;
1940
+ }
1941
+ ${scope} .fc-multimonth-month .fc-daygrid-day-frame {
1942
+ min-height: 46px !important;
1943
+ padding: 1px !important;
1944
+ }
1945
+ ${scope} .fc-multimonth-month .fc-daygrid-day-top {
1946
+ padding: 2px 2px 1px !important;
1947
+ }
1948
+ ${scope} .fc-multimonth-title {
1949
+ color: ${activeTheme.headerText} !important;
1950
+ background: ${activeTheme.headerBg} !important;
1951
+ border-bottom: 1px solid ${activeTheme.borderColor} !important;
1952
+ font-size: 13px !important;
1953
+ font-weight: 700 !important;
1954
+ padding: 10px 12px !important;
1955
+ text-transform: uppercase !important;
1956
+ letter-spacing: 0.06em !important;
1957
+ }
1958
+ ${scope} .fc-daygrid-week-number {
1959
+ background: ${activeTheme.badgeBg} !important;
1960
+ color: ${activeTheme.badgeText} !important;
1961
+ border-radius: ${variants.weekNumRadius} !important;
1962
+ font-size: 10px !important;
1963
+ font-weight: 600 !important;
1964
+ padding: 1px 5px !important;
1965
+ margin: 2px 4px !important;
1966
+ opacity: 0.85;
1967
+ }
1968
+ @media (max-width: 1100px) {
1969
+ ${scope} .vicacha-fc-day-quick-add {
1970
+ display: none !important;
1971
+ }
1972
+ ${scope} .vicacha-fc-secondary-badge {
1973
+ font-size: 9.5px !important;
1974
+ padding: 0 5px !important;
1975
+ }
1976
+ ${scope} .fc-daygrid-day-frame {
1977
+ min-height: 76px !important;
1978
+ }
1979
+ }
1980
+ @media (max-width: 768px) {
1981
+ ${scope} .vicacha-fc-secondary-badge {
1982
+ display: none !important;
1983
+ }
1984
+ ${scope} .fc-daygrid-day-frame {
1985
+ min-height: 58px !important;
1986
+ }
1987
+ ${scope} .fc-multimonth {
1988
+ padding: 6px !important;
1989
+ gap: 10px !important;
1990
+ }
1991
+ }
1992
+ `;
1993
+ }
1994
+ function computeSecondaryBadges(dateKey, effectiveSecondaryLang, localeCode, activeSecondaryCalendars) {
1995
+ if (effectiveSecondaryLang === "off") return [];
1996
+ const targetLoc = effectiveSecondaryLang === "ar" ? "ar-SA" : localeCode;
1997
+ return activeSecondaryCalendars.map((sys) => {
1998
+ const sysLocale = effectiveSecondaryLang === "ar" ? "ar-SA" : sys.locale || targetLoc;
1999
+ return formatDateInCalendarSystem(
2000
+ dateKey,
2001
+ { ...sys, locale: sysLocale },
2002
+ targetLoc,
2003
+ {
2004
+ compact: true
2005
+ }
2006
+ );
2007
+ }).filter(Boolean);
2008
+ }
2009
+ function buildDayHeaderStyle(activeTheme, isCompactYear, hasSecondaryBadges, allowCreate) {
2010
+ const isEndAligned = activeTheme.dayNumberAlign === "end";
2011
+ const isCenterAligned = activeTheme.dayNumberAlign === "center";
2012
+ const justifyCenter = isCompactYear || isCenterAligned && !hasSecondaryBadges;
2013
+ return {
2014
+ display: "flex",
2015
+ flexDirection: isEndAligned ? "row-reverse" : "row",
2016
+ flexWrap: isCompactYear ? "nowrap" : "wrap",
2017
+ justifyContent: justifyCenter ? "center" : "space-between",
2018
+ alignItems: "center",
2019
+ width: "100%",
2020
+ minWidth: 0,
2021
+ overflow: "hidden",
2022
+ gap: isCompactYear ? 2 : 4,
2023
+ padding: isCompactYear ? "1px 2px" : "2px 4px",
2024
+ cursor: allowCreate ? "pointer" : "default"
2025
+ };
2026
+ }
2027
+ function buildDayNumberBadgeStyle(activeTheme, isCompactYear, isToday) {
2028
+ const fontSize = isCompactYear ? 12 : activeTheme.id === "monarch" ? 13.5 : 13;
2029
+ const size = isCompactYear ? 22 : 26;
2030
+ const borderRadius = activeTheme.id === "forma" && !isToday ? "3px" : "999px";
2031
+ return {
2032
+ fontWeight: 700,
2033
+ fontSize,
2034
+ lineHeight: 1.2,
2035
+ display: "inline-flex",
2036
+ alignItems: "center",
2037
+ justifyContent: "center",
2038
+ minWidth: size,
2039
+ height: size,
2040
+ flexShrink: 0,
2041
+ borderRadius,
2042
+ padding: isCompactYear ? "0 4px" : "0 6px",
2043
+ background: isToday ? activeTheme.todayNumberBg : "transparent",
2044
+ color: isToday ? activeTheme.todayNumberText : activeTheme.cellText
2045
+ };
2046
+ }
2047
+ const DayCellMetaBadges = ({
2048
+ secondarySummary,
2049
+ effectiveSecondaryLang,
2050
+ activeTheme,
2051
+ allowCreate
2052
+ }) => /* @__PURE__ */ jsxRuntime.jsxs(
2053
+ "span",
2054
+ {
2055
+ className: "vicacha-fc-day-meta",
2056
+ style: {
2057
+ display: "inline-flex",
2058
+ alignItems: "center",
2059
+ gap: 4,
2060
+ minWidth: 0,
2061
+ maxWidth: "100%"
2062
+ },
2063
+ children: [
2064
+ secondarySummary && /* @__PURE__ */ jsxRuntime.jsx(
2065
+ "span",
2066
+ {
2067
+ className: "vicacha-fc-secondary-badge",
2068
+ dir: effectiveSecondaryLang === "ar" ? "rtl" : "auto",
2069
+ style: {
2070
+ fontSize: 10.5,
2071
+ background: activeTheme.badgeBg,
2072
+ color: activeTheme.badgeText,
2073
+ border: `1px solid ${activeTheme.badgeBorder}`,
2074
+ borderRadius: activeTheme.id === "forma" ? "4px" : "999px",
2075
+ padding: "1px 6px",
2076
+ fontWeight: 600,
2077
+ whiteSpace: "nowrap",
2078
+ overflow: "hidden",
2079
+ textOverflow: "ellipsis",
2080
+ maxWidth: "100%",
2081
+ lineHeight: 1.3
2082
+ },
2083
+ children: secondarySummary
2084
+ }
2085
+ ),
2086
+ allowCreate && /* @__PURE__ */ jsxRuntime.jsx(
2087
+ "span",
2088
+ {
2089
+ className: "vicacha-fc-day-quick-add",
2090
+ style: {
2091
+ display: "inline-flex",
2092
+ alignItems: "center",
2093
+ justifyContent: "center",
2094
+ width: 18,
2095
+ height: 18,
2096
+ flexShrink: 0,
2097
+ borderRadius: "999px",
2098
+ background: activeTheme.primaryColor,
2099
+ color: "#FFFFFF",
2100
+ fontSize: 12,
2101
+ fontWeight: 700,
2102
+ lineHeight: 1
2103
+ },
2104
+ children: "+"
2105
+ }
2106
+ )
2107
+ ]
2108
+ }
2109
+ );
2110
+ const FULLCALENDAR_PLUGINS = [
2111
+ dayGridPlugin__default.default,
2112
+ timeGridPlugin__default.default,
2113
+ listPlugin__default.default,
2114
+ multiMonthPlugin__default.default,
2115
+ interactionPlugin__default.default
2116
+ ];
2117
+ const VIEW_SPECIFIC_OPTIONS = {
2118
+ dayGridMonth: { dayMaxEventRows: 3 },
2119
+ listMonth: { type: "list", duration: { years: 1 } },
2120
+ multiMonthYear: {
2121
+ type: "multiMonth",
2122
+ duration: { years: 1 },
2123
+ multiMonthMaxColumns: 3,
2124
+ multiMonthMinWidth: 300,
2125
+ dayMaxEventRows: 2,
2126
+ weekNumbers: false
2127
+ },
2128
+ multiMonthYearStacked: {
2129
+ type: "multiMonth",
2130
+ duration: { years: 1 },
2131
+ multiMonthMaxColumns: 1,
2132
+ dayMaxEventRows: 3
2133
+ },
2134
+ dayGridYear: {
2135
+ type: "dayGrid",
2136
+ duration: { years: 1 },
2137
+ dayMaxEventRows: 3
2138
+ }
2139
+ };
2140
+ const ALL_CALENDAR_LOCALES = (() => {
2141
+ const builtInMap = /* @__PURE__ */ new Map();
2142
+ if (Array.isArray(allLocales__default.default)) {
2143
+ for (const loc of allLocales__default.default) {
2144
+ if (loc && loc.code) {
2145
+ builtInMap.set(loc.code.toLowerCase(), loc);
2146
+ }
2147
+ }
2148
+ }
2149
+ for (const customLoc of SUPPORTED_CALENDAR_LOCALES) {
2150
+ builtInMap.set(customLoc.code.toLowerCase(), customLoc);
2151
+ }
2152
+ return Array.from(builtInMap.values());
2153
+ })();
2154
+ function renderEventContent(eventArg) {
2155
+ const occ = eventArg.event.extendedProps?.occurrence;
2156
+ const isCompactYear = eventArg.view?.type === "multiMonthYear";
2157
+ const tooltip = [eventArg.timeText, eventArg.event.title].filter(Boolean).join(" • ");
2158
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2159
+ "div",
2160
+ {
2161
+ className: "vicacha-fc-event-inner",
2162
+ title: tooltip,
2163
+ style: {
2164
+ overflow: "hidden",
2165
+ textOverflow: "ellipsis",
2166
+ whiteSpace: "nowrap",
2167
+ padding: isCompactYear ? "1px 4px" : "2px 6px",
2168
+ fontSize: isCompactYear ? 11 : 12,
2169
+ fontWeight: 600,
2170
+ lineHeight: 1.3
2171
+ },
2172
+ children: [
2173
+ occ?.isRecurring ? "↻ " : "",
2174
+ !isCompactYear && eventArg.timeText ? /* @__PURE__ */ jsxRuntime.jsxs("b", { className: "vicacha-fc-event-time", children: [
2175
+ eventArg.timeText,
2176
+ " "
2177
+ ] }) : null,
2178
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "vicacha-fc-event-title", children: eventArg.event.title })
2179
+ ]
2180
+ }
2181
+ );
2182
+ }
2183
+ function resolveVisualKeys(props) {
2184
+ const { settings } = props;
2185
+ return {
2186
+ activeThemeKey: props.theme ?? settings?.theme ?? "breeze",
2187
+ configuredColorScheme: props.colorScheme ?? settings?.colorScheme ?? "auto",
2188
+ activePaletteKey: props.themePalette ?? settings?.themePalette ?? "default",
2189
+ effectiveSecondaryLang: props.secondaryCalendarLanguage ?? settings?.secondaryCalendarLanguage ?? "local",
2190
+ allowCreate: settings?.showCreateButton !== false
2191
+ };
2192
+ }
2193
+ function buildCalendarGridConfig(settings) {
2194
+ return {
2195
+ initialView: settings?.defaultView || "dayGridMonth",
2196
+ weekNumbers: settings?.weekNumbers ?? false,
2197
+ weekNumberCalculation: settings?.weekNumberCalculation ?? "ISO",
2198
+ weekends: settings?.showWeekends ?? true,
2199
+ slotMinTime: settings?.startHour || "07:00:00",
2200
+ slotMaxTime: settings?.endHour || "21:00:00",
2201
+ slotDuration: settings?.slotDuration || "00:30:00",
2202
+ businessHours: settings?.businessHours || true
2203
+ };
2204
+ }
2205
+ const FullCalendarCanvas = (props) => {
2206
+ const {
2207
+ calendarRef,
2208
+ locale,
2209
+ direction,
2210
+ settings,
2211
+ events,
2212
+ activeSecondaryCalendars,
2213
+ onDatesSet,
2214
+ onSelectSlot,
2215
+ onSelectOccurrence,
2216
+ onDropOrResizeOccurrence
2217
+ } = props;
2218
+ const styledTheme = styledComponents.useTheme();
2219
+ const strapiNeutral0 = styledTheme?.colors?.neutral0;
2220
+ const localeBundle = react.useMemo(
2221
+ () => getLocalizedCalendarBundle(locale),
2222
+ [locale]
2223
+ );
2224
+ const {
2225
+ activeThemeKey,
2226
+ configuredColorScheme,
2227
+ activePaletteKey,
2228
+ effectiveSecondaryLang,
2229
+ allowCreate
2230
+ } = resolveVisualKeys(props);
2231
+ const activeColorScheme = detectEffectiveColorScheme(
2232
+ configuredColorScheme,
2233
+ strapiNeutral0
2234
+ );
2235
+ const activeTheme = react.useMemo(
2236
+ () => resolveCalendarTheme(
2237
+ activeThemeKey,
2238
+ activeColorScheme,
2239
+ activePaletteKey,
2240
+ strapiNeutral0
2241
+ ),
2242
+ [activeThemeKey, activeColorScheme, activePaletteKey, strapiNeutral0]
2243
+ );
2244
+ const effectiveDirection = react.useMemo(
2245
+ () => direction ?? resolveCalendarDirection(locale, settings?.direction),
2246
+ [direction, locale, settings?.direction]
2247
+ );
2248
+ const effectiveFirstDay = react.useMemo(
2249
+ () => resolveLocaleFirstDay(locale, settings?.firstDayOfWeek),
2250
+ [locale, settings?.firstDayOfWeek]
2251
+ );
2252
+ const timeFormat = react.useMemo(
2253
+ () => buildFullCalendarTimeFormat(settings?.timeFormat24h ?? true),
2254
+ [settings?.timeFormat24h]
2255
+ );
2256
+ const gridConfig = react.useMemo(
2257
+ () => buildCalendarGridConfig(settings),
2258
+ [settings]
2259
+ );
2260
+ const handleDatesSet = react.useCallback(
2261
+ (dateInfo) => {
2262
+ onDatesSet(
2263
+ dateInfo.view.currentStart,
2264
+ dateInfo.start.toISOString(),
2265
+ dateInfo.end.toISOString()
2266
+ );
2267
+ },
2268
+ [onDatesSet]
2269
+ );
2270
+ const handleDateClick = react.useCallback(
2271
+ (clickInfo) => {
2272
+ if (allowCreate) onSelectSlot(clickInfo.dateStr, "");
2273
+ },
2274
+ [allowCreate, onSelectSlot]
2275
+ );
2276
+ const handleSelect = react.useCallback(
2277
+ (selectInfo) => {
2278
+ if (allowCreate) onSelectSlot(selectInfo.startStr, selectInfo.endStr);
2279
+ },
2280
+ [allowCreate, onSelectSlot]
2281
+ );
2282
+ const handleEventClick = react.useCallback(
2283
+ (clickInfo) => {
2284
+ const occ = clickInfo.event.extendedProps?.occurrence;
2285
+ if (occ) onSelectOccurrence(occ);
2286
+ },
2287
+ [onSelectOccurrence]
2288
+ );
2289
+ const handleEventDropOrResize = react.useCallback(
2290
+ (arg) => {
2291
+ const occ = arg.event.extendedProps?.occurrence;
2292
+ if (!occ || !arg.event.start) return;
2293
+ const startIso = arg.event.allDay ? `${arg.event.start.toISOString().slice(0, 10)}T00:00:00.000Z` : arg.event.start.toISOString();
2294
+ let endIso = arg.event.end?.toISOString();
2295
+ if (arg.event.allDay && arg.event.end) {
2296
+ const inclusiveEnd = new Date(arg.event.end.getTime() - 864e5);
2297
+ endIso = `${inclusiveEnd.toISOString().slice(0, 10)}T23:59:59.000Z`;
2298
+ }
2299
+ onDropOrResizeOccurrence(occ, startIso, endIso);
2300
+ },
2301
+ [onDropOrResizeOccurrence]
2302
+ );
2303
+ const renderDayCellContent = react.useCallback(
2304
+ (cellArg) => {
2305
+ const dateKey = cellArg.date.toISOString().slice(0, 10);
2306
+ const secondaryBadges = computeSecondaryBadges(
2307
+ dateKey,
2308
+ effectiveSecondaryLang,
2309
+ localeBundle.code,
2310
+ activeSecondaryCalendars
2311
+ );
2312
+ const cleanDayNumber = cellArg.dayNumberText.replace(/[^\d٠-٩]/g, "") || cellArg.dayNumberText;
2313
+ const isCompactYear = cellArg.view?.type === "multiMonthYear";
2314
+ const secondarySummary = secondaryBadges.join(" · ");
2315
+ const tooltipParts = [
2316
+ dateKey,
2317
+ secondarySummary,
2318
+ allowCreate ? "+" : ""
2319
+ ].filter(Boolean);
2320
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2321
+ "div",
2322
+ {
2323
+ className: "vicacha-fc-day-header",
2324
+ onClick: (e) => {
2325
+ if (allowCreate) {
2326
+ e.stopPropagation();
2327
+ onSelectSlot(dateKey, "");
2328
+ }
2329
+ },
2330
+ title: tooltipParts.join(" • "),
2331
+ style: buildDayHeaderStyle(
2332
+ activeTheme,
2333
+ isCompactYear,
2334
+ secondaryBadges.length > 0,
2335
+ allowCreate
2336
+ ),
2337
+ children: [
2338
+ /* @__PURE__ */ jsxRuntime.jsx(
2339
+ "span",
2340
+ {
2341
+ style: buildDayNumberBadgeStyle(
2342
+ activeTheme,
2343
+ isCompactYear,
2344
+ Boolean(cellArg.isToday)
2345
+ ),
2346
+ children: cleanDayNumber
2347
+ }
2348
+ ),
2349
+ !isCompactYear && /* @__PURE__ */ jsxRuntime.jsx(
2350
+ DayCellMetaBadges,
2351
+ {
2352
+ secondarySummary,
2353
+ effectiveSecondaryLang,
2354
+ activeTheme,
2355
+ allowCreate
2356
+ }
2357
+ )
2358
+ ]
2359
+ }
2360
+ );
2361
+ },
2362
+ [
2363
+ activeSecondaryCalendars,
2364
+ activeTheme,
2365
+ allowCreate,
2366
+ effectiveSecondaryLang,
2367
+ localeBundle.code,
2368
+ onSelectSlot
2369
+ ]
2370
+ );
2371
+ const themeCss = react.useMemo(
2372
+ () => buildCalendarThemeCss(activeTheme, allowCreate),
2373
+ [activeTheme, allowCreate]
2374
+ );
2375
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2376
+ designSystem.Box,
2377
+ {
2378
+ background: "neutral0",
2379
+ hasRadius: true,
2380
+ shadow: "tableShadow",
2381
+ padding: 4,
2382
+ style: { direction: effectiveDirection },
2383
+ children: [
2384
+ /* @__PURE__ */ jsxRuntime.jsx("style", { children: themeCss }),
2385
+ /* @__PURE__ */ jsxRuntime.jsx(
2386
+ "div",
2387
+ {
2388
+ className: `vicacha-fc-container vicacha-fc-theme-${activeTheme.id}`,
2389
+ "data-color-scheme": activeColorScheme,
2390
+ children: /* @__PURE__ */ jsxRuntime.jsx(
2391
+ FullCalendar__default.default,
2392
+ {
2393
+ ref: calendarRef,
2394
+ plugins: FULLCALENDAR_PLUGINS,
2395
+ views: VIEW_SPECIFIC_OPTIONS,
2396
+ initialView: gridConfig.initialView,
2397
+ headerToolbar: false,
2398
+ locales: ALL_CALENDAR_LOCALES,
2399
+ locale: localeBundle.code,
2400
+ direction: effectiveDirection,
2401
+ firstDay: effectiveFirstDay,
2402
+ weekNumbers: gridConfig.weekNumbers,
2403
+ weekNumberCalculation: gridConfig.weekNumberCalculation,
2404
+ weekText: localeBundle.weekText,
2405
+ allDayText: localeBundle.allDayText,
2406
+ moreLinkText: localeBundle.moreLinkText,
2407
+ noEventsText: localeBundle.noEventsText,
2408
+ buttonText: localeBundle.buttonText,
2409
+ eventTimeFormat: timeFormat,
2410
+ slotLabelFormat: timeFormat,
2411
+ weekends: gridConfig.weekends,
2412
+ slotMinTime: gridConfig.slotMinTime,
2413
+ slotMaxTime: gridConfig.slotMaxTime,
2414
+ slotDuration: gridConfig.slotDuration,
2415
+ businessHours: gridConfig.businessHours,
2416
+ nowIndicator: true,
2417
+ editable: true,
2418
+ selectable: allowCreate,
2419
+ selectMirror: true,
2420
+ dayMaxEvents: true,
2421
+ height: "auto",
2422
+ events,
2423
+ datesSet: handleDatesSet,
2424
+ dateClick: handleDateClick,
2425
+ select: handleSelect,
2426
+ eventClick: handleEventClick,
2427
+ eventDrop: handleEventDropOrResize,
2428
+ eventResize: handleEventDropOrResize,
2429
+ dayCellContent: renderDayCellContent,
2430
+ eventContent: renderEventContent
2431
+ }
2432
+ )
2433
+ }
2434
+ )
2435
+ ]
2436
+ }
2437
+ );
2438
+ };
2439
+ function resolveSidePanelStartDate(docRecord, mappedStartField) {
2440
+ const candidates = [
2441
+ ...mappedStartField ? [mappedStartField] : [],
2442
+ "startDate",
2443
+ "preferredDate",
2444
+ "date",
2445
+ "checkIn"
2446
+ ];
2447
+ for (const key of candidates) {
2448
+ const val = docRecord[key];
2449
+ if (typeof val === "string" && val) return val;
2450
+ }
2451
+ return "";
2452
+ }
2453
+ const CalendarSidePanel = (props) => {
2454
+ const { formatMessage, locale } = reactIntl.useIntl();
2455
+ const { get } = admin.useFetchClient();
2456
+ const [settings, setSettings] = react.useState(null);
2457
+ const model = props.model ?? "";
2458
+ const docRecord = props.document ?? {};
2459
+ const documentId = props.documentId ?? (typeof docRecord.documentId === "string" ? docRecord.documentId : "");
2460
+ const matchedSource = react.useMemo(
2461
+ () => settings?.sources?.find((s) => s.uid === model && s.enabled),
2462
+ [settings?.sources, model]
2463
+ );
2464
+ const rawStartDate = resolveSidePanelStartDate(
2465
+ docRecord,
2466
+ matchedSource?.fields.startDate
2467
+ );
2468
+ const activeLocale = resolveActiveContentLocale(locale);
2469
+ const rruleField = matchedSource?.fields.rrule || "recurrence";
2470
+ const hasScheduleFields = Boolean(rawStartDate || docRecord[rruleField]);
2471
+ react.useEffect(() => {
2472
+ if (!hasScheduleFields || !documentId) return;
2473
+ let mounted = true;
2474
+ get(`/${PLUGIN_ID}/settings`).then((res) => {
2475
+ if (mounted && res?.data?.data) {
2476
+ setSettings(res.data.data);
2477
+ }
2478
+ }).catch(() => {
2479
+ });
2480
+ return () => {
2481
+ mounted = false;
2482
+ };
2483
+ }, [model, get, hasScheduleFields, documentId]);
2484
+ const computedAlternateDates = react.useMemo(() => {
2485
+ if (!rawStartDate || !settings?.secondaryCalendars) {
2486
+ return [];
2487
+ }
2488
+ return settings.secondaryCalendars.filter((sys) => sys.enabled).map((sys) => {
2489
+ const rawFieldVal = sys.overrideField ? docRecord[sys.overrideField] : void 0;
2490
+ const overrideVal = typeof rawFieldVal === "string" ? rawFieldVal.trim() : "";
2491
+ const formatted = overrideVal || formatDateInCalendarSystem(rawStartDate, sys, activeLocale, {
2492
+ compact: false
2493
+ });
2494
+ return {
2495
+ id: sys.id,
2496
+ label: sys.label,
2497
+ formatted,
2498
+ isOverridden: Boolean(overrideVal)
2499
+ };
2500
+ }).filter((item) => Boolean(item.formatted));
2501
+ }, [rawStartDate, settings?.secondaryCalendars, docRecord, activeLocale]);
2502
+ if (!hasScheduleFields || !documentId) {
2503
+ return null;
2504
+ }
2505
+ const t = (id, defaultMessage) => formatPluginMessageWithUrlLocale(
2506
+ PLUGIN_ID,
2507
+ id,
2508
+ defaultMessage,
2509
+ formatMessage
2510
+ );
2511
+ const icsUrl = `/api/calendar/${encodeURIComponent(documentId)}/invite.ics`;
2512
+ return {
2513
+ title: t("sidePanel.title", "Calendar & Schedule"),
2514
+ content: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { paddingTop: 2, paddingBottom: 2, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 3, children: [
2515
+ computedAlternateDates.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { padding: 3, background: "neutral100", hasRadius: true, children: [
2516
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", textColor: "neutral600", children: t(
2517
+ "sidePanel.secondaryCalendars",
2518
+ "Alternative Calendar Dates"
2519
+ ) }),
2520
+ /* @__PURE__ */ jsxRuntime.jsx(
2521
+ designSystem.Flex,
2522
+ {
2523
+ direction: "column",
2524
+ alignItems: "stretch",
2525
+ gap: 1,
2526
+ paddingTop: 2,
2527
+ children: computedAlternateDates.map((alt) => /* @__PURE__ */ jsxRuntime.jsxs(
2528
+ designSystem.Flex,
2529
+ {
2530
+ justifyContent: "space-between",
2531
+ alignItems: "center",
2532
+ children: [
2533
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: [
2534
+ alt.label,
2535
+ ":"
2536
+ ] }),
2537
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "bold", children: alt.formatted })
2538
+ ]
2539
+ },
2540
+ alt.id
2541
+ ))
2542
+ }
2543
+ )
2544
+ ] }),
2545
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 2, children: [
2546
+ /* @__PURE__ */ jsxRuntime.jsx(
2547
+ designSystem.Button,
2548
+ {
2549
+ variant: "secondary",
2550
+ size: "S",
2551
+ tag: "a",
2552
+ href: icsUrl,
2553
+ target: "_blank",
2554
+ rel: "noopener noreferrer",
2555
+ children: t("sidePanel.downloadIcs", "Download .ics Invite")
2556
+ }
2557
+ ),
2558
+ /* @__PURE__ */ jsxRuntime.jsx(
2559
+ designSystem.Button,
2560
+ {
2561
+ variant: "tertiary",
2562
+ size: "S",
2563
+ tag: reactRouterDom.NavLink,
2564
+ to: `/plugins/${PLUGIN_ID}?documentId=${encodeURIComponent(documentId)}`,
2565
+ children: t("sidePanel.openCalendar", "Open in Calendar View")
2566
+ }
2567
+ )
2568
+ ] })
2569
+ ] }) })
2570
+ };
2571
+ };
2572
+ const Initializer = ({ setPlugin }) => {
2573
+ const ref = react.useRef(setPlugin);
2574
+ react.useEffect(() => {
2575
+ ref.current?.(PLUGIN_ID);
2576
+ }, []);
2577
+ return null;
2578
+ };
2579
+ const PluginIcon = () => /* @__PURE__ */ jsxRuntime.jsx(icons.Calendar, {});
2580
+ const VALID_FREQUENCIES = [
2581
+ "NONE",
2582
+ "DAILY",
2583
+ "WEEKLY",
2584
+ "MONTHLY",
2585
+ "YEARLY"
2586
+ ];
2587
+ const JS_DAY_TO_RRULE_DAY = [
2588
+ "SU",
2589
+ "MO",
2590
+ "TU",
2591
+ "WE",
2592
+ "TH",
2593
+ "FR",
2594
+ "SA"
2595
+ ];
2596
+ const DAY_LABELS = {
2597
+ MO: "Mon",
2598
+ TU: "Tue",
2599
+ WE: "Wed",
2600
+ TH: "Thu",
2601
+ FR: "Fri",
2602
+ SA: "Sat",
2603
+ SU: "Sun"
2604
+ };
2605
+ const FREQ_UNITS = {
2606
+ NONE: ["", "days"],
2607
+ DAILY: ["Every day", "days"],
2608
+ WEEKLY: ["Every week", "weeks"],
2609
+ MONTHLY: ["Every month", "months"],
2610
+ YEARLY: ["Every year", "years"]
2611
+ };
2612
+ function parseBoundedInt(val, min, max) {
2613
+ const n = Number.parseInt(val, 10);
2614
+ return Number.isFinite(n) && n >= min && n <= max ? n : void 0;
2615
+ }
2616
+ function parseUntilDate(val) {
2617
+ if (/^\d{8}(?:T\d{6}Z?)?$/.test(val)) {
2618
+ return `${val.slice(0, 4)}-${val.slice(4, 6)}-${val.slice(6, 8)}`;
2619
+ }
2620
+ return /^\d{4}-\d{2}-\d{2}/.test(val) ? val.slice(0, 10) : void 0;
2621
+ }
2622
+ function parseByDayList(val) {
2623
+ return val.split(",").map((d) => d.trim().slice(-2)).filter((d) => JS_DAY_TO_RRULE_DAY.includes(d));
2624
+ }
2625
+ const intAssigner = (key, min, max) => (p, v) => {
2626
+ const n = parseBoundedInt(v, min, max);
2627
+ if (n !== void 0) p[key] = n;
2628
+ };
2629
+ const RRULE_PART_ASSIGNERS = {
2630
+ RSCALE: (p, v) => {
2631
+ if (v) p.rscale = v.toLowerCase();
2632
+ },
2633
+ FREQ: (p, v) => {
2634
+ if (VALID_FREQUENCIES.includes(v)) {
2635
+ p.freq = v;
2636
+ }
2637
+ },
2638
+ INTERVAL: intAssigner("interval", 1, 365),
2639
+ BYDAY: (p, v) => {
2640
+ p.byDay = parseByDayList(v);
2641
+ },
2642
+ BYMONTH: intAssigner("byMonth", 1, 13),
2643
+ BYMONTHDAY: intAssigner("byMonthDay", 1, 31),
2644
+ COUNT: intAssigner("count", 1, 1e3),
2645
+ UNTIL: (p, v) => {
2646
+ const u = parseUntilDate(v);
2647
+ if (u !== void 0) p.until = u;
2648
+ }
2649
+ };
2650
+ function parseRRule(rawRule) {
2651
+ const defaultParsed = { freq: "NONE", interval: 1, byDay: [] };
2652
+ if (!rawRule || typeof rawRule !== "string") return defaultParsed;
2653
+ const cleaned = rawRule.trim().replace(/^RRULE:/i, "");
2654
+ if (!cleaned) return defaultParsed;
2655
+ const parsed = { ...defaultParsed };
2656
+ for (const part of cleaned.split(";")) {
2657
+ const [rawKey, rawVal] = part.split("=");
2658
+ if (!rawKey || !rawVal) continue;
2659
+ RRULE_PART_ASSIGNERS[rawKey.trim().toUpperCase()]?.(
2660
+ parsed,
2661
+ rawVal.trim().toUpperCase()
2662
+ );
2663
+ }
2664
+ return parsed;
2665
+ }
2666
+ function buildRRule(parsed) {
2667
+ if (!parsed || parsed.freq === "NONE") return "";
2668
+ const rscale = parsed.rscale;
2669
+ const segments = [];
2670
+ if (rscale && rscale !== "gregory" && rscale !== "iso8601") {
2671
+ segments.push(`RSCALE=${rscale.toUpperCase()}`);
2672
+ }
2673
+ segments.push(
2674
+ `FREQ=${parsed.freq}`,
2675
+ `INTERVAL=${Math.max(1, Math.floor(parsed.interval || 1))}`
2676
+ );
2677
+ if (parsed.freq === "WEEKLY" && Array.isArray(parsed.byDay)) {
2678
+ const validDays = parsed.byDay.filter(
2679
+ (d) => JS_DAY_TO_RRULE_DAY.includes(d)
2680
+ );
2681
+ if (validDays.length > 0) segments.push(`BYDAY=${validDays.join(",")}`);
2682
+ }
2683
+ if (parsed.freq === "YEARLY" && typeof parsed.byMonth === "number" && parsed.byMonth >= 1 && parsed.byMonth <= 13) {
2684
+ segments.push(`BYMONTH=${Math.floor(parsed.byMonth)}`);
2685
+ }
2686
+ if ((parsed.freq === "MONTHLY" || parsed.freq === "YEARLY") && typeof parsed.byMonthDay === "number" && parsed.byMonthDay >= 1 && parsed.byMonthDay <= 31) {
2687
+ segments.push(`BYMONTHDAY=${Math.floor(parsed.byMonthDay)}`);
2688
+ }
2689
+ if (typeof parsed.count === "number" && parsed.count >= 1) {
2690
+ segments.push(`COUNT=${Math.floor(parsed.count)}`);
2691
+ } else if (parsed.until && /^\d{4}-\d{2}-\d{2}$/.test(parsed.until)) {
2692
+ segments.push(`UNTIL=${parsed.until.replace(/-/g, "")}T235959Z`);
2693
+ }
2694
+ return segments.join(";");
2695
+ }
2696
+ function formatRRuleSuffix(parsed) {
2697
+ if (parsed.freq === "WEEKLY" && parsed.byDay.length > 0) {
2698
+ return ` on ${parsed.byDay.map((d) => DAY_LABELS[d]).join(", ")}`;
2699
+ }
2700
+ if (parsed.freq === "YEARLY" && parsed.byMonth && parsed.byMonthDay) {
2701
+ return ` on month ${parsed.byMonth}, day ${parsed.byMonthDay}`;
2702
+ }
2703
+ if (parsed.freq === "MONTHLY" && parsed.byMonthDay) {
2704
+ return ` on day ${parsed.byMonthDay}`;
2705
+ }
2706
+ return "";
2707
+ }
2708
+ function describeRRule(rawRule) {
2709
+ const parsed = parseRRule(rawRule);
2710
+ if (parsed.freq === "NONE") return "Does not repeat (Single occurrence)";
2711
+ const rscalePrefix = parsed.rscale && parsed.rscale !== "gregory" ? `[${parsed.rscale.toUpperCase()}] ` : "";
2712
+ const [singular, plural] = FREQ_UNITS[parsed.freq] || ["Every year", "years"];
2713
+ const intervalText = parsed.interval === 1 ? singular : `Every ${parsed.interval} ${plural}`;
2714
+ const limit = parsed.count ? `, ${parsed.count} times` : parsed.until ? `, until ${parsed.until}` : "";
2715
+ return `${rscalePrefix}${intervalText}${formatRRuleSuffix(parsed)}${limit}`;
2716
+ }
2717
+ function normalizeRRuleObject(obj, defaultTimezone) {
2718
+ const rrule = typeof obj.rrule === "string" ? obj.rrule : "";
2719
+ const parsed = parseRRule(rrule);
2720
+ const timezone = typeof obj.timezone === "string" && obj.timezone ? obj.timezone : defaultTimezone;
2721
+ const rscale = typeof obj.rscale === "string" ? obj.rscale : parsed.rscale;
2722
+ const exdates = Array.isArray(obj.exdates) ? obj.exdates.filter((d) => typeof d === "string") : [];
2723
+ const overrides = obj.overrides && typeof obj.overrides === "object" ? obj.overrides : {};
2724
+ return {
2725
+ rrule,
2726
+ timezone,
2727
+ rscale,
2728
+ allDay: Boolean(obj.allDay),
2729
+ exdates,
2730
+ overrides
2731
+ };
2732
+ }
2733
+ function parseRRuleFieldValue(raw, defaultTimezone = "UTC") {
2734
+ const fallback = normalizeRRuleObject({}, defaultTimezone);
2735
+ if (!raw) return fallback;
2736
+ if (typeof raw === "string") {
2737
+ const trimmed = raw.trim();
2738
+ if (trimmed.startsWith("{")) {
2739
+ try {
2740
+ const obj = JSON.parse(trimmed);
2741
+ return normalizeRRuleObject(obj, defaultTimezone);
2742
+ } catch {
2743
+ return fallback;
2744
+ }
2745
+ }
2746
+ const parsed = parseRRule(trimmed);
2747
+ return { ...fallback, rrule: trimmed, rscale: parsed.rscale };
2748
+ }
2749
+ return typeof raw === "object" ? normalizeRRuleObject(raw, defaultTimezone) : fallback;
2750
+ }
2751
+ const WEEKDAYS = [
2752
+ { code: "MO", label: "Mon" },
2753
+ { code: "TU", label: "Tue" },
2754
+ { code: "WE", label: "Wed" },
2755
+ { code: "TH", label: "Thu" },
2756
+ { code: "FR", label: "Fri" },
2757
+ { code: "SA", label: "Sat" },
2758
+ { code: "SU", label: "Sun" }
2759
+ ];
2760
+ const COMMON_TIMEZONES = "UTC,America/Bogota,America/New_York,America/Mexico_City,America/Sao_Paulo,Europe/Madrid,Europe/London,Europe/Paris,Europe/Berlin,Europe/Amsterdam,Europe/Istanbul,Asia/Riyadh,Asia/Dubai".split(
2761
+ ","
2762
+ );
2763
+ const FREQ_OPTIONS = [
2764
+ ["NONE", "rrule.freq.none", "Does not repeat (Single Entry)"],
2765
+ ["DAILY", "rrule.freq.daily", "Daily"],
2766
+ ["WEEKLY", "rrule.freq.weekly", "Weekly"],
2767
+ ["MONTHLY", "rrule.freq.monthly", "Monthly"],
2768
+ ["YEARLY", "rrule.freq.yearly", "Yearly"]
2769
+ ];
2770
+ const END_OPTIONS = [
2771
+ ["never", "rrule.ends.never", "Never (Ongoing)"],
2772
+ ["until", "rrule.ends.until", "On Specific Date (UNTIL)"],
2773
+ ["count", "rrule.ends.count", "After N Occurrences (COUNT)"]
2774
+ ];
2775
+ const REMOVE_BTN_STYLE = {
2776
+ border: "none",
2777
+ background: "transparent",
2778
+ cursor: "pointer"
2779
+ };
2780
+ function applyEndModeChange(mode, updateRule) {
2781
+ if (mode === "never") {
2782
+ updateRule({ count: void 0, until: void 0 });
2783
+ } else if (mode === "count") {
2784
+ updateRule({ count: 10, until: void 0 });
2785
+ } else if (mode === "until") {
2786
+ const nextYear = /* @__PURE__ */ new Date();
2787
+ nextYear.setUTCFullYear(nextYear.getUTCFullYear() + 1);
2788
+ updateRule({
2789
+ until: nextYear.toISOString().slice(0, 10),
2790
+ count: void 0
2791
+ });
2792
+ }
2793
+ }
2794
+ const RRuleInput = ({
2795
+ name,
2796
+ value,
2797
+ onChange,
2798
+ disabled = false,
2799
+ error,
2800
+ label,
2801
+ required = false,
2802
+ hint
2803
+ }) => {
2804
+ const { formatMessage } = reactIntl.useIntl();
2805
+ const t = (id, defaultMessage) => formatPluginMessageWithUrlLocale(
2806
+ PLUGIN_ID,
2807
+ id,
2808
+ defaultMessage,
2809
+ formatMessage
2810
+ );
2811
+ const fieldValue = react.useMemo(() => parseRRuleFieldValue(value, "UTC"), [value]);
2812
+ const parsedRule = react.useMemo(
2813
+ () => parseRRule(fieldValue.rrule),
2814
+ [fieldValue.rrule]
2815
+ );
2816
+ const [newExdate, setNewExdate] = react.useState("");
2817
+ const isRepeating = parsedRule.freq !== "NONE";
2818
+ const endMode = parsedRule.until ? "until" : parsedRule.count ? "count" : "never";
2819
+ const emitChange = (next) => {
2820
+ onChange({ target: { name, value: next, type: "json" } });
2821
+ };
2822
+ const updateRule = (patch) => {
2823
+ const merged = { ...parsedRule, ...patch };
2824
+ emitChange({
2825
+ ...fieldValue,
2826
+ rrule: buildRRule(merged),
2827
+ rscale: merged.rscale
2828
+ });
2829
+ };
2830
+ const toggleWeekday = (day) => {
2831
+ const nextDays = parsedRule.byDay.includes(day) ? parsedRule.byDay.filter((d) => d !== day) : [...parsedRule.byDay, day];
2832
+ updateRule({ byDay: nextDays });
2833
+ };
2834
+ const handleAddExdate = () => {
2835
+ const clean = newExdate.trim();
2836
+ if (!/^\d{4}-\d{2}-\d{2}$/.test(clean)) return;
2837
+ const current = fieldValue.exdates ?? [];
2838
+ if (!current.includes(clean)) {
2839
+ emitChange({ ...fieldValue, exdates: [...current, clean].sort() });
2840
+ }
2841
+ setNewExdate("");
2842
+ };
2843
+ const handleRemoveExdate = (dateToRemove) => {
2844
+ emitChange({
2845
+ ...fieldValue,
2846
+ exdates: (fieldValue.exdates ?? []).filter((d) => d !== dateToRemove)
2847
+ });
2848
+ };
2849
+ const renderSelect = (style, labelText, placeholderText, val, options, onSelect) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
2850
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: labelText }),
2851
+ /* @__PURE__ */ jsxRuntime.jsx(
2852
+ designSystem.SingleSelect,
2853
+ {
2854
+ disabled,
2855
+ placeholder: placeholderText,
2856
+ value: val,
2857
+ onChange: onSelect,
2858
+ children: options.map(([optVal, optLabel]) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: optVal, children: optLabel }, optVal))
2859
+ }
2860
+ )
2861
+ ] }) });
2862
+ const renderNumber = (width, labelText, placeholderText, val, onValChange) => /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { width }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
2863
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: labelText }),
2864
+ /* @__PURE__ */ jsxRuntime.jsx(
2865
+ designSystem.NumberInput,
2866
+ {
2867
+ disabled,
2868
+ placeholder: placeholderText,
2869
+ value: val,
2870
+ onValueChange: onValChange
2871
+ }
2872
+ )
2873
+ ] }) });
2874
+ const exdatesList = fieldValue.exdates ?? [];
2875
+ const summaryText = !isRepeating ? t("rrule.freq.none", "Does not repeat (Single Entry)") : describeRRule(fieldValue.rrule);
2876
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Root, { name, error, hint, required, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 3, children: [
2877
+ label && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: label }),
2878
+ /* @__PURE__ */ jsxRuntime.jsx(
2879
+ designSystem.Box,
2880
+ {
2881
+ padding: 4,
2882
+ background: "neutral100",
2883
+ borderColor: "neutral200",
2884
+ hasRadius: true,
2885
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 4, children: [
2886
+ /* @__PURE__ */ jsxRuntime.jsxs(
2887
+ designSystem.Flex,
2888
+ {
2889
+ justifyContent: "space-between",
2890
+ alignItems: "center",
2891
+ wrap: "wrap",
2892
+ gap: 2,
2893
+ children: [
2894
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", textColor: "neutral600", children: t("rrule.summaryLabel", "Recurrence Summary") }),
2895
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { backgroundColor: "primary100", textColor: "primary700", children: summaryText })
2896
+ ]
2897
+ }
2898
+ ),
2899
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 3, wrap: "wrap", children: [
2900
+ renderSelect(
2901
+ { flex: 1, minWidth: 170 },
2902
+ t("rrule.frequency", "Repeat Frequency"),
2903
+ t(
2904
+ "placeholder.select_repeat_frequency",
2905
+ "Select repeat frequency"
2906
+ ),
2907
+ parsedRule.freq,
2908
+ FREQ_OPTIONS.map(([v, id, def]) => [v, t(id, def)]),
2909
+ (val) => updateRule({
2910
+ freq: val,
2911
+ byDay: val === "WEEKLY" ? ["FR"] : []
2912
+ })
2913
+ ),
2914
+ isRepeating && renderNumber(
2915
+ 130,
2916
+ t("rrule.interval", "Every (Interval)"),
2917
+ t("placeholder.1", "1"),
2918
+ parsedRule.interval,
2919
+ (val) => updateRule({ interval: val && val >= 1 ? val : 1 })
2920
+ ),
2921
+ isRepeating && renderSelect(
2922
+ { flex: 1, minWidth: 190 },
2923
+ t("rrule.rscale", "Calendar Scale (RSCALE)"),
2924
+ t(
2925
+ "placeholder.select_calendar_scale",
2926
+ "Select calendar scale"
2927
+ ),
2928
+ parsedRule.rscale || "gregory",
2929
+ SUPPORTED_INTL_CALENDARS.map((c) => [
2930
+ c.intlCalendar,
2931
+ c.defaultLabel
2932
+ ]),
2933
+ (val) => updateRule({ rscale: val === "gregory" ? void 0 : val })
2934
+ ),
2935
+ renderSelect(
2936
+ { flex: 1, minWidth: 180 },
2937
+ t("rrule.timezone", "Timezone"),
2938
+ t("placeholder.select_timezone", "Select timezone"),
2939
+ fieldValue.timezone || "UTC",
2940
+ COMMON_TIMEZONES.map((tz) => [tz, tz]),
2941
+ (tz) => emitChange({ ...fieldValue, timezone: String(tz) })
2942
+ )
2943
+ ] }),
2944
+ /* @__PURE__ */ jsxRuntime.jsx(
2945
+ designSystem.Checkbox,
2946
+ {
2947
+ disabled,
2948
+ checked: Boolean(fieldValue.allDay),
2949
+ onCheckedChange: (checked) => emitChange({ ...fieldValue, allDay: Boolean(checked) }),
2950
+ children: t("rrule.allDay", "All-day entry")
2951
+ }
2952
+ ),
2953
+ parsedRule.freq === "WEEKLY" && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
2954
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", fontWeight: "bold", children: t("rrule.byDay", "Repeat on Weekdays") }),
2955
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { gap: 2, paddingTop: 2, wrap: "wrap", children: WEEKDAYS.map((day) => {
2956
+ const active = parsedRule.byDay.includes(day.code);
2957
+ return /* @__PURE__ */ jsxRuntime.jsx(
2958
+ designSystem.Button,
2959
+ {
2960
+ size: "S",
2961
+ variant: active ? "default" : "tertiary",
2962
+ disabled,
2963
+ onClick: () => toggleWeekday(day.code),
2964
+ children: t(`weekday.${day.code.toLowerCase()}`, day.label)
2965
+ },
2966
+ day.code
2967
+ );
2968
+ }) })
2969
+ ] }),
2970
+ (parsedRule.freq === "MONTHLY" || parsedRule.freq === "YEARLY") && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 3, wrap: "wrap", children: [
2971
+ parsedRule.freq === "YEARLY" && renderNumber(
2972
+ 160,
2973
+ t("rrule.byMonth", "Month (1-12)"),
2974
+ t("placeholder.1", "1"),
2975
+ parsedRule.byMonth ?? 1,
2976
+ (val) => {
2977
+ const maxMonth = parsedRule.rscale && parsedRule.rscale !== "gregory" && parsedRule.rscale !== "iso8601" ? 13 : 12;
2978
+ updateRule({
2979
+ byMonth: val && val >= 1 && val <= maxMonth ? Math.floor(val) : void 0
2980
+ });
2981
+ }
2982
+ ),
2983
+ renderNumber(
2984
+ 160,
2985
+ t("rrule.byMonthDay", "Day of Month (1-31)"),
2986
+ t("placeholder.1", "1"),
2987
+ parsedRule.byMonthDay ?? 1,
2988
+ (val) => updateRule({
2989
+ byMonthDay: val && val >= 1 && val <= 31 ? Math.floor(val) : void 0
2990
+ })
2991
+ )
2992
+ ] }),
2993
+ isRepeating && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 3, wrap: "wrap", alignItems: "flex-end", children: [
2994
+ renderSelect(
2995
+ { width: 180 },
2996
+ t("rrule.ends", "Series Ends"),
2997
+ t(
2998
+ "placeholder.select_series_end_mode",
2999
+ "Select series end mode"
3000
+ ),
3001
+ endMode,
3002
+ END_OPTIONS.map(([v, id, def]) => [v, t(id, def)]),
3003
+ (mode) => applyEndModeChange(mode, updateRule)
3004
+ ),
3005
+ endMode === "until" && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { width: 200 }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
3006
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: t("rrule.untilDate", "Until Date (YYYY-MM-DD)") }),
3007
+ /* @__PURE__ */ jsxRuntime.jsx(
3008
+ designSystem.DatePicker,
3009
+ {
3010
+ disabled,
3011
+ value: parseDateTimePickerValue(parsedRule.until),
3012
+ clearLabel: t("common.clear", "Clear"),
3013
+ onChange: (d) => updateRule({
3014
+ until: d ? formatLocalYmd(d) : void 0
3015
+ }),
3016
+ onClear: () => updateRule({ until: void 0 })
3017
+ }
3018
+ )
3019
+ ] }) }),
3020
+ endMode === "count" && renderNumber(
3021
+ 180,
3022
+ t("rrule.occurrenceCount", "Max Occurrences"),
3023
+ t("placeholder.10", "10"),
3024
+ parsedRule.count ?? 10,
3025
+ (val) => updateRule({
3026
+ count: val && val >= 1 ? Math.floor(val) : 1
3027
+ })
3028
+ )
3029
+ ] }),
3030
+ isRepeating && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
3031
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, paddingTop: 1, wrap: "wrap", alignItems: "flex-end", children: [
3032
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { width: 240 }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
3033
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: t(
3034
+ "rrule.exdates",
3035
+ "Excluded Dates (Holiday / Exception Dates)"
3036
+ ) }),
3037
+ /* @__PURE__ */ jsxRuntime.jsx(
3038
+ designSystem.DatePicker,
3039
+ {
3040
+ disabled,
3041
+ value: parseDateTimePickerValue(newExdate),
3042
+ clearLabel: t("common.clear", "Clear"),
3043
+ onChange: (d) => setNewExdate(d ? formatLocalYmd(d) : ""),
3044
+ onClear: () => setNewExdate("")
3045
+ }
3046
+ )
3047
+ ] }) }),
3048
+ /* @__PURE__ */ jsxRuntime.jsx(
3049
+ designSystem.Button,
3050
+ {
3051
+ variant: "secondary",
3052
+ size: "S",
3053
+ disabled,
3054
+ onClick: handleAddExdate,
3055
+ children: t("rrule.addExdate", "Add Excluded Date")
3056
+ }
3057
+ )
3058
+ ] }),
3059
+ exdatesList.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { gap: 2, paddingTop: 2, wrap: "wrap", children: exdatesList.map((dt) => /* @__PURE__ */ jsxRuntime.jsxs(
3060
+ designSystem.Badge,
3061
+ {
3062
+ backgroundColor: "neutral200",
3063
+ textColor: "neutral800",
3064
+ children: [
3065
+ dt,
3066
+ " ",
3067
+ /* @__PURE__ */ jsxRuntime.jsx(
3068
+ "button",
3069
+ {
3070
+ type: "button",
3071
+ onClick: () => handleRemoveExdate(dt),
3072
+ style: REMOVE_BTN_STYLE,
3073
+ "aria-label": `Remove ${dt}`,
3074
+ children: "×"
3075
+ }
3076
+ )
3077
+ ]
3078
+ },
3079
+ dt
3080
+ )) })
3081
+ ] })
3082
+ ] })
3083
+ }
3084
+ ),
3085
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Error, {}),
3086
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Hint, {})
3087
+ ] }) });
3088
+ };
3089
+ const RRuleInput$1 = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
3090
+ __proto__: null,
3091
+ RRuleInput
3092
+ }, Symbol.toStringTag, { value: "Module" }));
3093
+ const rruleCustomField = {
3094
+ name: "rrule",
3095
+ pluginId: PLUGIN_ID,
3096
+ type: "json",
3097
+ intlLabel: {
3098
+ id: `${PLUGIN_ID}.rrule.field.label`,
3099
+ defaultMessage: "Recurrence Schedule (RRULE / RSCALE)"
3100
+ },
3101
+ intlDescription: {
3102
+ id: `${PLUGIN_ID}.rrule.field.description`,
3103
+ defaultMessage: "Configure RFC 5545 & RFC 7529 recurring schedules, non-Gregorian calendar recurrence, excluded dates, and per-occurrence overrides."
3104
+ },
3105
+ icon: PluginIcon,
3106
+ components: {
3107
+ Input: async () => Promise.resolve().then(() => RRuleInput$1).then((module2) => ({
3108
+ default: module2.RRuleInput
3109
+ }))
3110
+ }
3111
+ };
3112
+ const SUPPORTED_LOCALES = [
3113
+ "en",
3114
+ "es",
3115
+ "fr",
3116
+ "de",
3117
+ "pt",
3118
+ "it",
3119
+ "nl",
3120
+ "pl",
3121
+ "tr",
3122
+ "ar"
3123
+ ];
3124
+ const index = {
3125
+ register(app) {
3126
+ app.customFields.register(rruleCustomField);
3127
+ app.addMenuLink({
3128
+ to: `plugins/${PLUGIN_ID}`,
3129
+ icon: PluginIcon,
3130
+ intlLabel: {
3131
+ id: `${PLUGIN_ID}.plugin.name`,
3132
+ defaultMessage: "Calendar"
3133
+ },
3134
+ permissions: [],
3135
+ Component: () => Promise.resolve().then(() => require("./CalendarPage-CVqowKst.cjs")).then((mod) => ({
3136
+ default: mod.CalendarPage
3137
+ }))
3138
+ });
3139
+ const calendarSettingsSection = {
3140
+ id: PLUGIN_ID,
3141
+ intlLabel: {
3142
+ id: `${PLUGIN_ID}.settings.section`,
3143
+ defaultMessage: "Calendar, Events & Bookings"
3144
+ }
3145
+ };
3146
+ const calendarSettingsLink = {
3147
+ intlLabel: {
3148
+ id: `${PLUGIN_ID}.settings.general`,
3149
+ defaultMessage: "Calendar & Scheduler"
3150
+ },
3151
+ id: `${PLUGIN_ID}-settings`,
3152
+ to: `${PLUGIN_ID}`,
3153
+ Component: () => Promise.resolve().then(() => require("./SettingsPage-D0aCDb9w.cjs")).then((mod) => ({
3154
+ default: mod.SettingsPage
3155
+ })),
3156
+ permissions: []
3157
+ };
3158
+ try {
3159
+ if (typeof app.addSettingsLink === "function") {
3160
+ app.addSettingsLink(calendarSettingsSection.id, calendarSettingsLink);
3161
+ } else {
3162
+ app.createSettingSection(calendarSettingsSection, [
3163
+ calendarSettingsLink
3164
+ ]);
3165
+ }
3166
+ } catch {
3167
+ app.createSettingSection(calendarSettingsSection, [calendarSettingsLink]);
3168
+ }
3169
+ app.registerPlugin({
3170
+ id: PLUGIN_ID,
3171
+ initializer: Initializer,
3172
+ isReady: false,
3173
+ name: PLUGIN_ID
3174
+ });
3175
+ },
3176
+ bootstrap(app) {
3177
+ const cmPlugin = app.getPlugin("content-manager");
3178
+ if (cmPlugin?.apis?.addEditViewSidePanel) {
3179
+ cmPlugin.apis.addEditViewSidePanel([CalendarSidePanel]);
3180
+ }
3181
+ },
3182
+ registerTrads: createRegisterTrads({
3183
+ pluginId: PLUGIN_ID,
3184
+ defaultLocales: SUPPORTED_LOCALES,
3185
+ loadTranslation: (locale) => __variableDynamicImportRuntimeHelper(/* @__PURE__ */ Object.assign({ "./translations/ar.json": () => Promise.resolve().then(() => require("./ar-yKsEpX_y.cjs")), "./translations/de.json": () => Promise.resolve().then(() => require("./de-ryVtwWOz.cjs")), "./translations/en.json": () => Promise.resolve().then(() => require("./en-BHYSUqXv.cjs")), "./translations/es.json": () => Promise.resolve().then(() => require("./es-T5v93hXW.cjs")), "./translations/fr.json": () => Promise.resolve().then(() => require("./fr-DTdTwx7e.cjs")), "./translations/it.json": () => Promise.resolve().then(() => require("./it-BkJNzy27.cjs")), "./translations/nl.json": () => Promise.resolve().then(() => require("./nl-CjnlbeUM.cjs")), "./translations/pl.json": () => Promise.resolve().then(() => require("./pl-BJArPgCQ.cjs")), "./translations/pt.json": () => Promise.resolve().then(() => require("./pt-CSYqSGnz.cjs")), "./translations/tr.json": () => Promise.resolve().then(() => require("./tr-zUVxuw9X.cjs")) }), `./translations/${locale}.json`, 3)
3186
+ })
3187
+ };
3188
+ exports.CALENDAR_COLOR_PALETTES = CALENDAR_COLOR_PALETTES;
3189
+ exports.CALENDAR_THEME_PRESETS = CALENDAR_THEME_PRESETS;
3190
+ exports.CalendarSidePanel = CalendarSidePanel;
3191
+ exports.CalendarToolbar = CalendarToolbar;
3192
+ exports.FALLBACK_RECURRENCE_PRESETS = FALLBACK_RECURRENCE_PRESETS;
3193
+ exports.FullCalendarCanvas = FullCalendarCanvas;
3194
+ exports.PLUGIN_ID = PLUGIN_ID;
3195
+ exports.RRuleInput = RRuleInput;
3196
+ exports.SUPPORTED_CALENDAR_LOCALES = SUPPORTED_CALENDAR_LOCALES;
3197
+ exports.SUPPORTED_INTL_CALENDARS = SUPPORTED_INTL_CALENDARS;
3198
+ exports.SUPPORTED_LOCALES = SUPPORTED_LOCALES;
3199
+ exports.WEEKDAYS = WEEKDAYS$1;
3200
+ exports.alignStartDateToWeekday = alignStartDateToWeekday;
3201
+ exports.buildEnabledCalendarViews = buildEnabledCalendarViews;
3202
+ exports.buildInitialDateWindow = buildInitialDateWindow;
3203
+ exports.buildOrderedRecurrencePresets = buildOrderedRecurrencePresets;
3204
+ exports.buildQuickCreateRequestPayload = buildQuickCreateRequestPayload;
3205
+ exports.buildSecondaryHeaderLabel = buildSecondaryHeaderLabel;
3206
+ exports.collectAvailableCategories = collectAvailableCategories;
3207
+ exports.computeSourceCounts = computeSourceCounts;
3208
+ exports.describeRRule = describeRRule;
3209
+ exports.detectEffectiveColorScheme = detectEffectiveColorScheme;
3210
+ exports.expandDateWindowToYear = expandDateWindowToYear;
3211
+ exports.extractByDays = extractByDays;
3212
+ exports.filterVisibleOccurrences = filterVisibleOccurrences;
3213
+ exports.formatCategoryLabel = formatCategoryLabel;
3214
+ exports.formatLocalYmd = formatLocalYmd;
3215
+ exports.index = index;
3216
+ exports.mapOccurrencesToFullCalendarEvents = mapOccurrencesToFullCalendarEvents;
3217
+ exports.matchLoadedSourceId = matchLoadedSourceId;
3218
+ exports.normalizeSelectedEndSlot = normalizeSelectedEndSlot;
3219
+ exports.parseDateTimePickerValue = parseDateTimePickerValue;
3220
+ exports.resolveActiveSecondaryCalendars = resolveActiveSecondaryCalendars;
3221
+ exports.resolveCalendarDirection = resolveCalendarDirection;
3222
+ exports.resolveCalendarTheme = resolveCalendarTheme;
3223
+ exports.resolveFullCalendarLocale = resolveFullCalendarLocale;