@janus-scheduler/core 1.0.3 → 2.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.
- package/LICENSE +15 -0
- package/README.md +94 -0
- package/dist/api/EventBus.d.ts +20 -0
- package/dist/api/EventBus.d.ts.map +1 -0
- package/dist/api/api.init.d.ts +3 -0
- package/dist/api/api.init.d.ts.map +1 -0
- package/dist/api/api.types.d.ts +309 -0
- package/dist/api/api.types.d.ts.map +1 -0
- package/dist/index.d.ts +16 -464
- package/dist/index.d.ts.map +1 -0
- package/dist/interactions/interactions.init.d.ts +1 -0
- package/dist/interactions/interactions.init.d.ts.map +1 -0
- package/dist/janus-scheduler.cjs +8 -0
- package/dist/janus-scheduler.cjs.map +1 -0
- package/dist/janus-scheduler.es.js +2426 -208
- package/dist/janus-scheduler.es.js.map +1 -1
- package/dist/models/assignments/assignments.d.ts +15 -0
- package/dist/models/assignments/assignments.d.ts.map +1 -0
- package/dist/models/events/event.d.ts +19 -0
- package/dist/models/events/event.d.ts.map +1 -0
- package/dist/models/resources/resources.d.ts +19 -0
- package/dist/models/resources/resources.d.ts.map +1 -0
- package/dist/rendering/rendering.init.d.ts +1 -0
- package/dist/rendering/rendering.init.d.ts.map +1 -0
- package/dist/services/SchedulerManager.service.d.ts +284 -0
- package/dist/services/SchedulerManager.service.d.ts.map +1 -0
- package/dist/store/index.d.ts +2 -0
- package/dist/store/index.d.ts.map +1 -0
- package/dist/store/scheduler.store.d.ts +45 -0
- package/dist/store/scheduler.store.d.ts.map +1 -0
- package/dist/types/event.types.d.ts +15 -0
- package/dist/types/event.types.d.ts.map +1 -0
- package/dist/utils/conflict.d.ts +54 -0
- package/dist/utils/conflict.d.ts.map +1 -0
- package/dist/utils/recurrence.d.ts +9 -0
- package/dist/utils/recurrence.d.ts.map +1 -0
- package/dist/utils/time.d.ts +42 -0
- package/dist/utils/time.d.ts.map +1 -0
- package/package.json +45 -14
- package/dist/janus-scheduler.cjs.js +0 -2
- package/dist/janus-scheduler.cjs.js.map +0 -1
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const v=o=>{let e;const t=new Set,n=(a,c)=>{const h=typeof a=="function"?a(e):a;if(!Object.is(h,e)){const g=e;e=c??(typeof h!="object"||h===null)?h:Object.assign({},e,h),t.forEach(f=>f(e,g))}},s=()=>e,d={setState:n,getState:s,getInitialState:()=>l,subscribe:a=>(t.add(a),()=>t.delete(a))},l=e=o(n,s,d);return d},k=(o=>o?v(o):v);class y{id;title;startTime;endTime;resourceId;status;description;constructor(e){this.id=e.id,this.title=e.title,this.startTime=e.startTime,this.endTime=e.endTime,this.resourceId=e.resourceId,this.status=e.status||"draft",this.description=e.description||""}isValid(){if(!this.id||!this.title||!this.resourceId)return!1;const e=new Date(this.startTime),t=new Date(this.endTime);return!(e>=t)}getDuration(){const e=new Date(this.startTime).getTime();return(new Date(this.endTime).getTime()-e)/(1e3*60)}}class p{id;name;type;email;constructor(e){this.id=e.id,this.name=e.name,this.type=e.type||"person",this.email=e.email||""}isValid(){return!!(this.id&&this.name)}}class E{id;eventId;resourceId;role;constructor(e){this.id=e.id,this.eventId=e.eventId,this.resourceId=e.resourceId,this.role=e.role||"attendee"}isValid(){return!!(this.id&&this.eventId&&this.resourceId)}}function T(){return Intl.DateTimeFormat().resolvedOptions().timeZone}function R(o,e){const t=new Date(o),n=new Intl.DateTimeFormat("en-CA",{timeZone:e,year:"numeric",month:"2-digit",day:"2-digit"}).formatToParts(t),s=n.find(c=>c.type==="year")?.value??"",r=n.find(c=>c.type==="month")?.value??"",u=n.find(c=>c.type==="day")?.value??"",d=new Intl.DateTimeFormat("en-US",{timeZone:e,hour:"2-digit",minute:"2-digit",hour12:!1}).formatToParts(t);let l=d.find(c=>c.type==="hour")?.value??"00";const a=d.find(c=>c.type==="minute")?.value??"00";return l==="24"&&(l="00"),{date:`${s}-${r}-${u}`,time:`${l}:${a}`}}function A(o,e,t){const[n,s,r]=o.split("-").map(Number),[u,d]=e.split(":").map(Number),l=Date.UTC(n,s-1,r,u,d,0),a=new Intl.DateTimeFormat("en-US",{timeZone:t,year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit",hour12:!1}).formatToParts(new Date(l)),c=Number(a.find(m=>m.type==="year")?.value),h=Number(a.find(m=>m.type==="month")?.value),g=Number(a.find(m=>m.type==="day")?.value);let f=Number(a.find(m=>m.type==="hour")?.value);const w=Number(a.find(m=>m.type==="minute")?.value),D=Number(a.find(m=>m.type==="second")?.value);f===24&&(f=0);const b=Date.UTC(c,h-1,g,f,w,D)-l;return l-b}function _(o,e,t){return new Date(A(o,e,t)).toISOString()}function O(o,e){const t=new Intl.DateTimeFormat("en-US",{timeZone:e,year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",second:"2-digit",hour12:!1}).formatToParts(new Date(o)),n=Number(t.find(a=>a.type==="year")?.value),s=Number(t.find(a=>a.type==="month")?.value)-1,r=Number(t.find(a=>a.type==="day")?.value);let u=Number(t.find(a=>a.type==="hour")?.value);const d=Number(t.find(a=>a.type==="minute")?.value),l=Number(t.find(a=>a.type==="second")?.value);return u===24&&(u=0),new Date(n,s,r,u,d,l)}function N(o){try{const e=new Intl.DateTimeFormat("en",{timeZone:o,timeZoneName:"shortOffset"}).formatToParts(new Date).find(t=>t.type==="timeZoneName")?.value??"";return`${o} (${e})`}catch{return o}}function z(){try{return Intl.supportedValuesOf("timeZone")??S}catch{return S}}const S=["UTC","America/New_York","America/Chicago","America/Denver","America/Los_Angeles","America/Toronto","America/Sao_Paulo","Europe/London","Europe/Paris","Europe/Berlin","Europe/Moscow","Asia/Dubai","Asia/Kolkata","Asia/Colombo","Asia/Singapore","Asia/Tokyo","Asia/Shanghai","Australia/Sydney","Pacific/Auckland"],i=k((o,e)=>({events:{},resources:{},assignments:{},timezone:T(),addEvent:t=>{const n=new y(t);if(!n.isValid())throw new Error("Invalid event data");return o(s=>({events:{...s.events,[n.id]:n}})),n},updateEvent:(t,n)=>{const r=e().events[t];if(!r)throw new Error(`Event ${t} not found`);return n.title&&(r.title=n.title),n.startTime&&(r.startTime=n.startTime),n.endTime&&(r.endTime=n.endTime),n.resourceId&&(r.resourceId=n.resourceId),n.status&&(r.status=n.status),n.description!==void 0&&(r.description=n.description),o(u=>({events:{...u.events,[t]:r}})),r},deleteEvent:t=>{let n=!1;return o(s=>{if(s.events[t]){const r={...s.events};return delete r[t],n=!0,{events:r}}return s}),n},addResource:t=>{const n=new p(t);if(!n.isValid())throw new Error("Invalid resource data");return o(s=>({resources:{...s.resources,[n.id]:n}})),n},updateResource:(t,n)=>{const r=e().resources[t];if(!r)throw new Error(`Resource ${t} not found`);return n.name&&(r.name=n.name),n.type&&(r.type=n.type),n.email!==void 0&&(r.email=n.email),o(u=>({resources:{...u.resources,[t]:r}})),r},deleteResource:t=>{const n=e();if(Object.values(n.events).some(u=>u.resourceId===t))throw new Error(`Cannot delete resource ${t} - has assigned events`);let r=!1;return o(u=>{if(u.resources[t]){const d={...u.resources};return delete d[t],r=!0,{resources:d}}return u}),r},addAssignment:t=>{const n=e(),s=new E(t);if(!s.isValid())throw new Error("Invalid assignment data");if(!n.events[s.eventId])throw new Error(`Event ${s.eventId} not found`);if(!n.resources[s.resourceId])throw new Error(`Resource ${s.resourceId} not found`);return o(r=>({assignments:{...r.assignments,[s.id]:s}})),s},deleteAssignment:t=>{let n=!1;return o(s=>{if(s.assignments[t]){const r={...s.assignments};return delete r[t],n=!0,{assignments:r}}return s}),n},clearAll:()=>{o({events:{},resources:{},assignments:{}})},setTimezone:t=>{o({timezone:t})}}));class Z{_hooks;constructor(e={}){this._hooks=e}_fireHook(e,t,n){let s;try{s=t()}catch(r){n(),this._hooks.onError?.({operation:e,error:r,reverted:!0});return}s?.catch(r=>{n(),this._hooks.onError?.({operation:e,error:r,reverted:!0})})}addEvent(e){const t=i.getState().addEvent(e);return this._fireHook("addEvent",()=>this._hooks.onEventAdd?.(t),()=>i.getState().deleteEvent(t.id)),t}getEvent(e){return i.getState().events[e]}getAllEvents(){return Object.values(i.getState().events)}updateEvent(e,t){const n=i.getState().events[e],s=n?{...n}:null,r=i.getState().updateEvent(e,t);return this._fireHook("updateEvent",()=>this._hooks.onEventUpdate?.(r),()=>{s&&i.getState().updateEvent(e,{title:s.title,startTime:s.startTime,endTime:s.endTime,status:s.status,description:s.description,resourceId:s.resourceId})}),r}deleteEvent(e){const t=i.getState().events[e],n=i.getState().deleteEvent(e);return n&&this._fireHook("deleteEvent",()=>this._hooks.onEventDelete?.(e),()=>{t&&i.getState().addEvent(t)}),n}getEventsByResource(e){return this.getAllEvents().filter(t=>t.resourceId===e)}getEventsByDate(e){const t=new Date(e).toISOString().split("T")[0];return this.getAllEvents().filter(n=>new Date(n.startTime).toISOString().split("T")[0]===t)}addResource(e){const t=i.getState().addResource(e);return this._fireHook("addResource",()=>this._hooks.onResourceAdd?.(t),()=>i.getState().deleteResource(t.id)),t}getResource(e){return i.getState().resources[e]}getAllResources(){return Object.values(i.getState().resources)}updateResource(e,t){const n=i.getState().resources[e],s=n?{...n}:null,r=i.getState().updateResource(e,t);return this._fireHook("updateResource",()=>this._hooks.onResourceUpdate?.(r),()=>{s&&i.getState().updateResource(e,{name:s.name,type:s.type,email:s.email})}),r}deleteResource(e){const t=i.getState().resources[e],n=i.getState().deleteResource(e);return n&&this._fireHook("deleteResource",()=>this._hooks.onResourceDelete?.(e),()=>{t&&i.getState().addResource(t)}),n}addAssignment(e){const t=i.getState().addAssignment(e);return this._fireHook("addAssignment",()=>this._hooks.onAssignmentAdd?.(t),()=>i.getState().deleteAssignment(t.id)),t}getAssignmentsByEvent(e){return Object.values(i.getState().assignments).filter(t=>t.eventId===e)}getAssignmentsByResource(e){return Object.values(i.getState().assignments).filter(t=>t.resourceId===e)}deleteAssignment(e){const t=i.getState().assignments[e],n=i.getState().deleteAssignment(e);return n&&this._fireHook("deleteAssignment",()=>this._hooks.onAssignmentDelete?.(e),()=>{t&&i.getState().addAssignment(t)}),n}getEventCount(){return Object.keys(i.getState().events).length}getResourceCount(){return Object.keys(i.getState().resources).length}clearAll(){i.getState().clearAll()}}class I{listeners={};on(e,t){this.listeners[e]||(this.listeners[e]=[]),this.listeners[e].push(t)}off(e,t){if(!this.listeners[e])return;const n=this.listeners[e];this.listeners[e]=n.filter(s=>s!==t)}emit(e,t){this.listeners[e]&&this.listeners[e].forEach(n=>{try{n(t)}catch(s){console.error(`Error in event handler for ${e}:`,s)}})}}const j=new I;exports.Assignment=E;exports.Event=y;exports.JanusEventBus=I;exports.Resource=p;exports.SchedulerManager=Z;exports.formatTimezoneLabel=N;exports.getAllTimezones=z;exports.getBrowserTimezone=T;exports.getUtcMsForZonedDatetime=A;exports.isoToZonedDisplay=R;exports.janusEventBus=j;exports.schedulerStore=i;exports.toFakeLocalDate=O;exports.zonedInputToISO=_;
|
|
2
|
-
//# sourceMappingURL=janus-scheduler.cjs.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"janus-scheduler.cjs.js","sources":["../../../node_modules/zustand/esm/vanilla.mjs","../src/models/events/event.ts","../src/models/resources/resources.ts","../src/models/assignments/assignments.ts","../src/utils/time.ts","../src/store/scheduler.store.ts","../src/services/SchedulerManager.service.ts","../src/api/EventBus.ts"],"sourcesContent":["const createStoreImpl = (createState) => {\n let state;\n const listeners = /* @__PURE__ */ new Set();\n const setState = (partial, replace) => {\n const nextState = typeof partial === \"function\" ? partial(state) : partial;\n if (!Object.is(nextState, state)) {\n const previousState = state;\n state = (replace != null ? replace : typeof nextState !== \"object\" || nextState === null) ? nextState : Object.assign({}, state, nextState);\n listeners.forEach((listener) => listener(state, previousState));\n }\n };\n const getState = () => state;\n const getInitialState = () => initialState;\n const subscribe = (listener) => {\n listeners.add(listener);\n return () => listeners.delete(listener);\n };\n const api = { setState, getState, getInitialState, subscribe };\n const initialState = state = createState(setState, getState, api);\n return api;\n};\nconst createStore = ((createState) => createState ? createStoreImpl(createState) : createStoreImpl);\n\nexport { createStore };\n","import { EventData } from \"../../types/event.types\";\n\nexport class Event {\n id: string;\n title: string;\n startTime: string;\n endTime: string;\n resourceId: string;\n status: \"draft\" | \"confirmed\" | \"cancelled\";\n description: string;\n\n constructor(data: EventData) {\n this.id = data.id;\n this.title = data.title;\n this.startTime = data.startTime;\n this.endTime = data.endTime;\n this.resourceId = data.resourceId;\n this.status = data.status || \"draft\";\n this.description = data.description || \"\";\n }\n\n // Check if event data is valid\n isValid(): boolean {\n if (!this.id || !this.title || !this.resourceId) return false;\n const start = new Date(this.startTime);\n const end = new Date(this.endTime);\n if (start >= end) return false;\n return true;\n }\n\n // Get duration in minutes\n getDuration(): number {\n const start = new Date(this.startTime).getTime();\n const end = new Date(this.endTime).getTime();\n return (end - start) / (1000 * 60);\n }\n}\n","export interface ResourceData {\n id: string;\n name: string;\n type?: \"person\" | \"room\" | \"equipment\";\n email?: string;\n}\n\nexport class Resource {\n id: string;\n name: string;\n type: \"person\" | \"room\" | \"equipment\";\n email: string;\n\n constructor(data: ResourceData) {\n this.id = data.id;\n this.name = data.name;\n this.type = data.type || \"person\";\n this.email = data.email || \"\";\n }\n\n // Check if resource data is valid\n isValid(): boolean {\n return !!(this.id && this.name);\n }\n}\n","export interface AssignmentData {\n id: string;\n eventId: string;\n resourceId: string;\n role?: \"organizer\" | \"attendee\";\n}\n\nexport class Assignment {\n id: string;\n eventId: string;\n resourceId: string;\n role: \"organizer\" | \"attendee\";\n\n constructor(data: AssignmentData) {\n this.id = data.id;\n this.eventId = data.eventId;\n this.resourceId = data.resourceId;\n this.role = data.role || \"attendee\";\n }\n\n // Check if assignment data is valid\n isValid(): boolean {\n return !!(this.id && this.eventId && this.resourceId);\n }\n}\n\n// chathura test\n","/**\n * time.ts\n *\n * Timezone-aware date/time utilities for the Janus Scheduler.\n * All functions use the native Intl API — no external dependencies.\n */\n\n/** Get the browser's IANA timezone string (e.g. \"Asia/Kolkata\"). */\nexport function getBrowserTimezone(): string {\n return Intl.DateTimeFormat().resolvedOptions().timeZone;\n}\n\n/**\n * Convert a UTC ISO string → { date, time } displayed in the given IANA timezone.\n * date: \"YYYY-MM-DD\" | time: \"HH:MM\" (24-hour)\n */\nexport function isoToZonedDisplay(\n iso: string,\n timezone: string,\n): { date: string; time: string } {\n const d = new Date(iso);\n\n const dateParts = new Intl.DateTimeFormat(\"en-CA\", {\n timeZone: timezone,\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n }).formatToParts(d);\n\n const year = dateParts.find((p) => p.type === \"year\")?.value ?? \"\";\n const month = dateParts.find((p) => p.type === \"month\")?.value ?? \"\";\n const day = dateParts.find((p) => p.type === \"day\")?.value ?? \"\";\n\n const timeParts = new Intl.DateTimeFormat(\"en-US\", {\n timeZone: timezone,\n hour: \"2-digit\",\n minute: \"2-digit\",\n hour12: false,\n }).formatToParts(d);\n\n let hour = timeParts.find((p) => p.type === \"hour\")?.value ?? \"00\";\n const minute = timeParts.find((p) => p.type === \"minute\")?.value ?? \"00\";\n if (hour === \"24\") hour = \"00\"; // midnight edge case\n\n return { date: `${year}-${month}-${day}`, time: `${hour}:${minute}` };\n}\n\n/**\n * Get the UTC milliseconds for a date+time string interpreted in the given IANA timezone.\n * date: \"YYYY-MM-DD\" | time: \"HH:MM\"\n */\nexport function getUtcMsForZonedDatetime(\n date: string,\n time: string,\n timezone: string,\n): number {\n const [y, mo, d] = date.split(\"-\").map(Number);\n const [h, m] = time.split(\":\").map(Number);\n\n // Treat the input as UTC first (nominal) then measure the TZ offset at that moment\n const nominalUtc = Date.UTC(y, mo - 1, d, h, m, 0);\n\n const parts = new Intl.DateTimeFormat(\"en-US\", {\n timeZone: timezone,\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\",\n hour12: false,\n }).formatToParts(new Date(nominalUtc));\n\n const zonedY = Number(parts.find((p) => p.type === \"year\")?.value);\n const zonedMo = Number(parts.find((p) => p.type === \"month\")?.value);\n const zonedD = Number(parts.find((p) => p.type === \"day\")?.value);\n let zonedH = Number(parts.find((p) => p.type === \"hour\")?.value);\n const zonedM = Number(parts.find((p) => p.type === \"minute\")?.value);\n const zonedS = Number(parts.find((p) => p.type === \"second\")?.value);\n if (zonedH === 24) zonedH = 0;\n\n // tzOffset = how far ahead the timezone is from UTC (ms)\n const zonedAsUtc = Date.UTC(\n zonedY,\n zonedMo - 1,\n zonedD,\n zonedH,\n zonedM,\n zonedS,\n );\n const tzOffset = zonedAsUtc - nominalUtc;\n\n // actual UTC = input (treated as TZ local) - tzOffset\n return nominalUtc - tzOffset;\n}\n\n/**\n * Convert a user-entered date+time (in the given timezone) → UTC ISO string.\n * date: \"YYYY-MM-DD\" | time: \"HH:MM\"\n */\nexport function zonedInputToISO(\n date: string,\n time: string,\n timezone: string,\n): string {\n return new Date(getUtcMsForZonedDatetime(date, time, timezone)).toISOString();\n}\n\n/**\n * Return a \"fake\" local Date whose local-time getters reflect the given timezone.\n * Used to pass to existing formatter functions that call getHours(), getDay(), etc.\n */\nexport function toFakeLocalDate(tsMs: number, timezone: string): Date {\n const parts = new Intl.DateTimeFormat(\"en-US\", {\n timeZone: timezone,\n year: \"numeric\",\n month: \"2-digit\",\n day: \"2-digit\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n second: \"2-digit\",\n hour12: false,\n }).formatToParts(new Date(tsMs));\n\n const y = Number(parts.find((p) => p.type === \"year\")?.value);\n const mo = Number(parts.find((p) => p.type === \"month\")?.value) - 1;\n const d = Number(parts.find((p) => p.type === \"day\")?.value);\n let h = Number(parts.find((p) => p.type === \"hour\")?.value);\n const min = Number(parts.find((p) => p.type === \"minute\")?.value);\n const s = Number(parts.find((p) => p.type === \"second\")?.value);\n if (h === 24) h = 0;\n\n // Constructed with local Date constructor → getHours() etc. return correct values\n return new Date(y, mo, d, h, min, s);\n}\n\n/**\n * Human-readable label for a timezone including its UTC offset.\n * e.g. \"Asia/Kolkata (UTC+5:30)\"\n */\nexport function formatTimezoneLabel(tz: string): string {\n try {\n const offset =\n new Intl.DateTimeFormat(\"en\", {\n timeZone: tz,\n timeZoneName: \"shortOffset\",\n })\n .formatToParts(new Date())\n .find((p) => p.type === \"timeZoneName\")?.value ?? \"\";\n return `${tz} (${offset})`;\n } catch {\n return tz;\n }\n}\n\n/**\n * All IANA timezone strings supported by the browser.\n * Falls back to a curated list when Intl.supportedValuesOf is unavailable.\n */\nexport function getAllTimezones(): string[] {\n try {\n const list = Intl.supportedValuesOf(\"timeZone\") as string[];\n return list ?? FALLBACK_TIMEZONES;\n } catch {\n return FALLBACK_TIMEZONES;\n }\n}\n\nconst FALLBACK_TIMEZONES = [\n \"UTC\",\n \"America/New_York\",\n \"America/Chicago\",\n \"America/Denver\",\n \"America/Los_Angeles\",\n \"America/Toronto\",\n \"America/Sao_Paulo\",\n \"Europe/London\",\n \"Europe/Paris\",\n \"Europe/Berlin\",\n \"Europe/Moscow\",\n \"Asia/Dubai\",\n \"Asia/Kolkata\",\n \"Asia/Colombo\",\n \"Asia/Singapore\",\n \"Asia/Tokyo\",\n \"Asia/Shanghai\",\n \"Australia/Sydney\",\n \"Pacific/Auckland\",\n];\n","import { createStore } from \"zustand/vanilla\";\nimport { Event } from \"../models/events/event\";\nimport { Resource, type ResourceData } from \"../models/resources/resources\";\nimport {\n Assignment,\n type AssignmentData,\n} from \"../models/assignments/assignments\";\nimport type { EventData } from \"../types/event.types\";\nimport { getBrowserTimezone } from \"../utils/time\";\n\nexport interface SchedulerState {\n events: Record<string, Event>;\n resources: Record<string, Resource>;\n assignments: Record<string, Assignment>;\n /** IANA timezone string used for all display conversions. Defaults to the browser's local timezone. */\n timezone: string;\n}\n\nexport interface SchedulerActions {\n addEvent: (data: EventData) => Event;\n updateEvent: (id: string, updates: Partial<EventData>) => Event;\n deleteEvent: (id: string) => boolean;\n addResource: (data: ResourceData) => Resource;\n updateResource: (id: string, updates: Partial<ResourceData>) => Resource;\n deleteResource: (id: string) => boolean;\n addAssignment: (data: AssignmentData) => Assignment;\n deleteAssignment: (id: string) => boolean;\n clearAll: () => void;\n /** Change the display timezone for all scheduler components. */\n setTimezone: (timezone: string) => void;\n}\n\nexport type SchedulerStore = SchedulerState & SchedulerActions;\n\nexport const schedulerStore = createStore<SchedulerStore>((set, get) => ({\n events: {},\n resources: {},\n assignments: {},\n timezone: getBrowserTimezone(),\n\n addEvent: (data) => {\n const event = new Event(data);\n if (!event.isValid()) throw new Error(\"Invalid event data\");\n\n set((state) => ({\n events: { ...state.events, [event.id]: event },\n }));\n return event;\n },\n\n updateEvent: (id, updates) => {\n const state = get();\n const event = state.events[id];\n if (!event) throw new Error(`Event ${id} not found`);\n\n if (updates.title) event.title = updates.title;\n if (updates.startTime) event.startTime = updates.startTime;\n if (updates.endTime) event.endTime = updates.endTime;\n if (updates.resourceId) event.resourceId = updates.resourceId;\n if (updates.status) event.status = updates.status;\n if (updates.description !== undefined)\n event.description = updates.description;\n\n // To ensure reactivity in zustand, we create a new object reference for the events record.\n set((state) => ({\n events: { ...state.events, [id]: event },\n }));\n return event;\n },\n\n deleteEvent: (id) => {\n let deleted = false;\n set((state) => {\n if (state.events[id]) {\n const newEvents = { ...state.events };\n delete newEvents[id];\n deleted = true;\n return { events: newEvents };\n }\n return state;\n });\n return deleted;\n },\n\n addResource: (data) => {\n const resource = new Resource(data);\n if (!resource.isValid()) throw new Error(\"Invalid resource data\");\n\n set((state) => ({\n resources: { ...state.resources, [resource.id]: resource },\n }));\n return resource;\n },\n\n updateResource: (id, updates) => {\n const state = get();\n const resource = state.resources[id];\n if (!resource) throw new Error(`Resource ${id} not found`);\n\n if (updates.name) resource.name = updates.name;\n if (updates.type) resource.type = updates.type;\n if (updates.email !== undefined) resource.email = updates.email;\n\n set((state) => ({\n resources: { ...state.resources, [id]: resource },\n }));\n return resource;\n },\n\n deleteResource: (id) => {\n const state = get();\n // Check if resource has events\n const hasEvents = Object.values(state.events).some(\n (e) => e.resourceId === id,\n );\n if (hasEvents) {\n throw new Error(`Cannot delete resource ${id} - has assigned events`);\n }\n\n let deleted = false;\n set((state) => {\n if (state.resources[id]) {\n const newResources = { ...state.resources };\n delete newResources[id];\n deleted = true;\n return { resources: newResources };\n }\n return state;\n });\n return deleted;\n },\n\n addAssignment: (data) => {\n const state = get();\n const assignment = new Assignment(data);\n if (!assignment.isValid()) throw new Error(\"Invalid assignment data\");\n\n if (!state.events[assignment.eventId]) {\n throw new Error(`Event ${assignment.eventId} not found`);\n }\n if (!state.resources[assignment.resourceId]) {\n throw new Error(`Resource ${assignment.resourceId} not found`);\n }\n\n set((state) => ({\n assignments: { ...state.assignments, [assignment.id]: assignment },\n }));\n return assignment;\n },\n\n deleteAssignment: (id) => {\n let deleted = false;\n set((state) => {\n if (state.assignments[id]) {\n const newAssignments = { ...state.assignments };\n delete newAssignments[id];\n deleted = true;\n return { assignments: newAssignments };\n }\n return state;\n });\n return deleted;\n },\n\n clearAll: () => {\n set({\n events: {},\n resources: {},\n assignments: {},\n });\n },\n\n setTimezone: (timezone) => {\n set({ timezone });\n },\n}));\n","import { Event } from \"../models/events/event\";\nimport { Resource, ResourceData } from \"../models/resources/resources\";\nimport { Assignment, AssignmentData } from \"../models/assignments/assignments\";\nimport { EventData } from \"../types/event.types\";\nimport { schedulerStore } from \"../store/scheduler.store\";\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Lifecycle hooks — provided by the host application.\n//\n// Pattern: Optimistic Update + Automatic Rollback (Option C)\n//\n// 1. The store is updated immediately → UI re-renders with no perceived lag.\n// 2. The async hook fires (e.g. your fetch() to a backend API).\n// 3a. Hook resolves → done, nothing more to do.\n// 3b. Hook rejects → the store change is automatically rolled back and\n// `onError` is called so you can display an error to the user.\n//\n// All hooks are optional. Omit any you do not need.\n// ─────────────────────────────────────────────────────────────────────────────\nexport interface SchedulerHooks {\n // ── Event lifecycle ────────────────────────────────────────────────────────\n onEventAdd?: (event: Event) => void | Promise<void>;\n onEventUpdate?: (event: Event) => void | Promise<void>;\n onEventDelete?: (id: string) => void | Promise<void>;\n\n // ── Resource lifecycle ─────────────────────────────────────────────────────\n onResourceAdd?: (resource: Resource) => void | Promise<void>;\n onResourceUpdate?: (resource: Resource) => void | Promise<void>;\n onResourceDelete?: (id: string) => void | Promise<void>;\n\n // ── Assignment lifecycle ───────────────────────────────────────────────────\n onAssignmentAdd?: (assignment: Assignment) => void | Promise<void>;\n onAssignmentDelete?: (id: string) => void | Promise<void>;\n\n // ── Error handler ──────────────────────────────────────────────────────────\n /**\n * Called whenever any hook fails — either throws synchronously or returns a\n * rejected Promise (e.g. a failed fetch()). The store change that triggered\n * the hook has already been rolled back by the time this fires, so the UI\n * has reverted to the previous state.\n *\n * Use this hook to show an error notification to your user, e.g.:\n * onError: ({ operation, error }) => toast.error(`${operation} failed: ${error.message}`)\n *\n * @param ctx.operation - Name of the method that failed, e.g. \"addEvent\"\n * @param ctx.error - The raw thrown/rejected value from your hook\n * @param ctx.reverted - Always true — the store was rolled back automatically\n */\n onError?: (ctx: {\n operation: string;\n error: unknown;\n reverted: boolean;\n }) => void;\n}\n\n/**\n * SchedulerManager\n *\n * The single public API surface for reading and writing scheduler data.\n * Internally it writes to the Zustand vanilla store (which drives UI reactivity)\n * and then fires the matching lifecycle hook so the host application can persist\n * the change to its own backend.\n *\n * If the async hook rejects (e.g. a network error), the store is automatically\n * rolled back and `onError` is called — the host only needs to display a message.\n *\n * @example\n * ```ts\n * const manager = new SchedulerManager({\n * onEventAdd: async (event) => {\n * await fetch('/api/events', { method: 'POST', body: JSON.stringify(event) });\n * },\n * onEventUpdate: async (event) => {\n * await fetch(`/api/events/${event.id}`, { method: 'PATCH', body: JSON.stringify(event) });\n * },\n * onEventDelete: async (id) => {\n * await fetch(`/api/events/${id}`, { method: 'DELETE' });\n * },\n * onError: ({ operation, error }) => {\n * // The UI has already reverted — just show a notification\n * toast.error(`\"${operation}\" failed. Your change has been undone.`);\n * console.error(error);\n * },\n * });\n * ```\n */\nexport class SchedulerManager {\n private readonly _hooks: SchedulerHooks;\n\n constructor(hooks: SchedulerHooks = {}) {\n this._hooks = hooks;\n }\n\n // ── Private helper ─────────────────────────────────────────────────────────\n\n /**\n * Invokes an optional lifecycle hook inside a try/catch and, if it fails\n * (synchronous throw OR async rejection), runs the rollback then calls onError.\n *\n * The hook is passed as a **thunk** (`() => hook?.()`) so that invocation\n * happens inside the try block. Without this, a synchronous throw would\n * escape at the call site before _fireHook was entered, bypassing rollback.\n *\n * @param operation - Method name shown in the error context, e.g. \"addEvent\"\n * @param invokeHook - Zero-arg function that calls the hook and returns its result\n * @param rollback - Undoes the optimistic store write\n */\n private _fireHook(\n operation: string,\n invokeHook: () => void | Promise<void>,\n rollback: () => void,\n ): void {\n let result: void | Promise<void>;\n try {\n result = invokeHook();\n } catch (error: unknown) {\n // Synchronous throw — treat identically to an async rejection.\n rollback();\n this._hooks.onError?.({ operation, error, reverted: true });\n return;\n }\n // Async rejection path.\n result?.catch((error: unknown) => {\n rollback();\n this._hooks.onError?.({ operation, error, reverted: true });\n });\n }\n\n // ============ EVENT FUNCTIONS ============\n\n /**\n * Optimistically adds an event to the store, then fires `onEventAdd`.\n * If the hook rejects, the event is removed from the store and `onError` fires.\n *\n * @param data - Raw event data\n * @returns The created Event instance (already visible in the UI)\n */\n addEvent(data: EventData): Event {\n const event = schedulerStore.getState().addEvent(data);\n\n this._fireHook(\n \"addEvent\",\n () => this._hooks.onEventAdd?.(event),\n () => schedulerStore.getState().deleteEvent(event.id),\n );\n\n return event;\n }\n\n /**\n * Get a single event by ID.\n * @param id - Event ID\n * @returns Event if found, undefined otherwise\n */\n getEvent(id: string): Event | undefined {\n return schedulerStore.getState().events[id];\n }\n\n /**\n * Get all events currently in the store.\n * @returns Array of all Event instances\n */\n getAllEvents(): Event[] {\n return Object.values(schedulerStore.getState().events);\n }\n\n /**\n * Optimistically updates an event in the store, then fires `onEventUpdate`.\n * If the hook rejects, the event is restored to its previous state and `onError` fires.\n *\n * @param id - Event ID\n * @param updates - Partial event fields to update\n * @returns The updated Event instance (already visible in the UI)\n */\n updateEvent(id: string, updates: Partial<EventData>): Event {\n // Shallow-clone the primitive fields BEFORE the store writes.\n // Without the spread, `snapshot` would be a reference to the same\n // object that updateEvent() mutates in-place — meaning the rollback\n // would read already-updated values and restore nothing.\n const existing = schedulerStore.getState().events[id];\n const snapshot = existing ? { ...existing } : null;\n\n const event = schedulerStore.getState().updateEvent(id, updates);\n\n this._fireHook(\n \"updateEvent\",\n () => this._hooks.onEventUpdate?.(event),\n () => {\n if (snapshot) {\n // All fields are primitive strings — snapshot holds the original values.\n schedulerStore.getState().updateEvent(id, {\n title: snapshot.title,\n startTime: snapshot.startTime,\n endTime: snapshot.endTime,\n status: snapshot.status,\n description: snapshot.description,\n resourceId: snapshot.resourceId,\n });\n }\n },\n );\n\n return event;\n }\n\n /**\n * Optimistically deletes an event from the store, then fires `onEventDelete`.\n * If the hook rejects, the event is re-added to the store and `onError` fires.\n *\n * @param id - Event ID\n * @returns true if the event was found and deleted, false otherwise\n */\n deleteEvent(id: string): boolean {\n // Snapshot before deletion so we can restore it\n const snapshot = schedulerStore.getState().events[id];\n const deleted = schedulerStore.getState().deleteEvent(id);\n\n if (deleted) {\n this._fireHook(\n \"deleteEvent\",\n () => this._hooks.onEventDelete?.(id),\n () => {\n if (snapshot) schedulerStore.getState().addEvent(snapshot);\n },\n );\n }\n\n return deleted;\n }\n\n /**\n * Get all events assigned to a specific resource.\n * @param resourceId - Resource ID\n * @returns Filtered array of Event instances\n */\n getEventsByResource(resourceId: string): Event[] {\n return this.getAllEvents().filter((e) => e.resourceId === resourceId);\n }\n\n /**\n * Get all events that start on a specific calendar date.\n * @param date - Date string in YYYY-MM-DD format\n * @returns Filtered array of Event instances\n */\n getEventsByDate(date: string): Event[] {\n const targetDate = new Date(date).toISOString().split(\"T\")[0];\n return this.getAllEvents().filter((event) => {\n const eventDate = new Date(event.startTime).toISOString().split(\"T\")[0];\n return eventDate === targetDate;\n });\n }\n\n // ============ RESOURCE FUNCTIONS ============\n\n /**\n * Optimistically adds a resource to the store, then fires `onResourceAdd`.\n * If the hook rejects, the resource is removed from the store and `onError` fires.\n *\n * @param data - Raw resource data\n * @returns The created Resource instance (already visible in the UI)\n */\n addResource(data: ResourceData): Resource {\n const resource = schedulerStore.getState().addResource(data);\n\n this._fireHook(\n \"addResource\",\n () => this._hooks.onResourceAdd?.(resource),\n () => schedulerStore.getState().deleteResource(resource.id),\n );\n\n return resource;\n }\n\n /**\n * Get a single resource by ID.\n * @param id - Resource ID\n * @returns Resource if found, undefined otherwise\n */\n getResource(id: string): Resource | undefined {\n return schedulerStore.getState().resources[id];\n }\n\n /**\n * Get all resources currently in the store.\n * @returns Array of all Resource instances\n */\n getAllResources(): Resource[] {\n return Object.values(schedulerStore.getState().resources);\n }\n\n /**\n * Optimistically updates a resource in the store, then fires `onResourceUpdate`.\n * If the hook rejects, the resource is restored to its previous state and `onError` fires.\n *\n * @param id - Resource ID\n * @param updates - Partial resource fields to update\n * @returns The updated Resource instance (already visible in the UI)\n */\n updateResource(id: string, updates: Partial<ResourceData>): Resource {\n // Shallow-clone before the store write — same reason as updateEvent.\n // Without the spread, `snapshot` is a reference that the store may\n // mutate in-place, causing the rollback to restore the new values.\n const existing = schedulerStore.getState().resources[id];\n const snapshot = existing ? { ...existing } : null;\n\n const resource = schedulerStore.getState().updateResource(id, updates);\n\n this._fireHook(\n \"updateResource\",\n () => this._hooks.onResourceUpdate?.(resource),\n () => {\n if (snapshot) {\n schedulerStore.getState().updateResource(id, {\n name: snapshot.name,\n type: snapshot.type,\n email: snapshot.email,\n });\n }\n },\n );\n\n return resource;\n }\n\n /**\n * Optimistically deletes a resource from the store, then fires `onResourceDelete`.\n * If the hook rejects, the resource is re-added to the store and `onError` fires.\n * Will throw synchronously if the resource has assigned events (store-level guard).\n *\n * @param id - Resource ID\n * @returns true if the resource was found and deleted\n */\n deleteResource(id: string): boolean {\n const snapshot = schedulerStore.getState().resources[id];\n const deleted = schedulerStore.getState().deleteResource(id);\n\n if (deleted) {\n this._fireHook(\n \"deleteResource\",\n () => this._hooks.onResourceDelete?.(id),\n () => {\n if (snapshot) schedulerStore.getState().addResource(snapshot);\n },\n );\n }\n\n return deleted;\n }\n\n // ============ ASSIGNMENT FUNCTIONS ============\n\n /**\n * Optimistically creates an assignment, then fires `onAssignmentAdd`.\n * If the hook rejects, the assignment is deleted from the store and `onError` fires.\n *\n * @param data - Assignment data\n * @returns The created Assignment instance (already visible in the UI)\n */\n addAssignment(data: AssignmentData): Assignment {\n const assignment = schedulerStore.getState().addAssignment(data);\n\n this._fireHook(\n \"addAssignment\",\n () => this._hooks.onAssignmentAdd?.(assignment),\n () => schedulerStore.getState().deleteAssignment(assignment.id),\n );\n\n return assignment;\n }\n\n /**\n * Get all assignments for a specific event.\n * @param eventId - Event ID\n * @returns Array of Assignment instances\n */\n getAssignmentsByEvent(eventId: string): Assignment[] {\n return Object.values(schedulerStore.getState().assignments).filter(\n (a) => a.eventId === eventId,\n );\n }\n\n /**\n * Get all assignments for a specific resource.\n * @param resourceId - Resource ID\n * @returns Array of Assignment instances\n */\n getAssignmentsByResource(resourceId: string): Assignment[] {\n return Object.values(schedulerStore.getState().assignments).filter(\n (a) => a.resourceId === resourceId,\n );\n }\n\n /**\n * Optimistically deletes an assignment, then fires `onAssignmentDelete`.\n * If the hook rejects, the assignment is re-added to the store and `onError` fires.\n *\n * @param id - Assignment ID\n * @returns true if deleted, false if not found\n */\n deleteAssignment(id: string): boolean {\n const snapshot = schedulerStore.getState().assignments[id];\n const deleted = schedulerStore.getState().deleteAssignment(id);\n\n if (deleted) {\n this._fireHook(\n \"deleteAssignment\",\n () => this._hooks.onAssignmentDelete?.(id),\n () => {\n if (snapshot) schedulerStore.getState().addAssignment(snapshot);\n },\n );\n }\n\n return deleted;\n }\n\n // ============ UTILITY FUNCTIONS ============\n\n /**\n * Total number of events currently in the store.\n */\n getEventCount(): number {\n return Object.keys(schedulerStore.getState().events).length;\n }\n\n /**\n * Total number of resources currently in the store.\n */\n getResourceCount(): number {\n return Object.keys(schedulerStore.getState().resources).length;\n }\n\n /**\n * Remove all events, resources, and assignments from the store.\n * Does NOT fire individual delete hooks — use this only for full resets\n * (e.g. on logout or when switching tenants).\n */\n clearAll(): void {\n schedulerStore.getState().clearAll();\n }\n}\n\nexport default SchedulerManager;\n","import { JanusEventMap } from \"./api.types\";\n\ntype EventHandler<K extends keyof JanusEventMap> = (\n payload: JanusEventMap[K],\n) => void;\n\nexport class JanusEventBus {\n private listeners: {\n [K in keyof JanusEventMap]?: Array<EventHandler<K>>;\n } = {};\n\n /**\n * Subscribe to a Janus Scheduler event.\n */\n on<K extends keyof JanusEventMap>(event: K, handler: EventHandler<K>): void {\n if (!this.listeners[event]) {\n this.listeners[event] = [];\n }\n this.listeners[event]!.push(handler);\n }\n\n /**\n * Unsubscribe from a Janus Scheduler event.\n */\n off<K extends keyof JanusEventMap>(event: K, handler: EventHandler<K>): void {\n if (!this.listeners[event]) return;\n const currentListeners = this.listeners[event] as Array<EventHandler<K>>;\n (this.listeners as any)[event] = currentListeners.filter(\n (h) => h !== handler,\n );\n }\n\n /**\n * Emit an event to all subscribers.\n */\n emit<K extends keyof JanusEventMap>(\n event: K,\n payload: JanusEventMap[K],\n ): void {\n if (!this.listeners[event]) return;\n this.listeners[event]!.forEach((handler) => {\n try {\n handler(payload);\n } catch (e) {\n console.error(`Error in event handler for ${event}:`, e);\n }\n });\n }\n}\n\nexport const janusEventBus = new JanusEventBus();\n"],"names":["createStoreImpl","createState","state","listeners","setState","partial","replace","nextState","previousState","listener","getState","api","initialState","createStore","Event","data","start","end","Resource","Assignment","getBrowserTimezone","isoToZonedDisplay","iso","timezone","d","dateParts","year","p","month","day","timeParts","hour","minute","getUtcMsForZonedDatetime","date","time","y","mo","h","m","nominalUtc","parts","zonedY","zonedMo","zonedD","zonedH","zonedM","zonedS","tzOffset","zonedInputToISO","toFakeLocalDate","tsMs","min","s","formatTimezoneLabel","tz","offset","getAllTimezones","FALLBACK_TIMEZONES","schedulerStore","set","get","event","id","updates","deleted","newEvents","resource","e","newResources","assignment","newAssignments","SchedulerManager","hooks","operation","invokeHook","rollback","result","error","existing","snapshot","resourceId","targetDate","eventId","a","JanusEventBus","handler","currentListeners","payload","janusEventBus"],"mappings":"gFAAA,MAAMA,EAAmBC,GAAgB,CACvC,IAAIC,EACJ,MAAMC,EAA4B,IAAI,IAChCC,EAAW,CAACC,EAASC,IAAY,CACrC,MAAMC,EAAY,OAAOF,GAAY,WAAaA,EAAQH,CAAK,EAAIG,EACnE,GAAI,CAAC,OAAO,GAAGE,EAAWL,CAAK,EAAG,CAChC,MAAMM,EAAgBN,EACtBA,EAASI,IAA4B,OAAOC,GAAc,UAAYA,IAAc,MAAQA,EAAY,OAAO,OAAO,CAAA,EAAIL,EAAOK,CAAS,EAC1IJ,EAAU,QAASM,GAAaA,EAASP,EAAOM,CAAa,CAAC,CAChE,CACF,EACME,EAAW,IAAMR,EAMjBS,EAAM,CAAE,SAAAP,EAAU,SAAAM,EAAU,gBALV,IAAME,EAKqB,UAJhCH,IACjBN,EAAU,IAAIM,CAAQ,EACf,IAAMN,EAAU,OAAOM,CAAQ,EAEoB,EACtDG,EAAeV,EAAQD,EAAYG,EAAUM,EAAUC,CAAG,EAChE,OAAOA,CACT,EACME,GAAgBZ,GAAgBA,EAAcD,EAAgBC,CAAW,EAAID,GCnB5E,MAAMc,CAAM,CACjB,GACA,MACA,UACA,QACA,WACA,OACA,YAEA,YAAYC,EAAiB,CAC3B,KAAK,GAAKA,EAAK,GACf,KAAK,MAAQA,EAAK,MAClB,KAAK,UAAYA,EAAK,UACtB,KAAK,QAAUA,EAAK,QACpB,KAAK,WAAaA,EAAK,WACvB,KAAK,OAASA,EAAK,QAAU,QAC7B,KAAK,YAAcA,EAAK,aAAe,EACzC,CAGA,SAAmB,CACjB,GAAI,CAAC,KAAK,IAAM,CAAC,KAAK,OAAS,CAAC,KAAK,WAAY,MAAO,GACxD,MAAMC,EAAQ,IAAI,KAAK,KAAK,SAAS,EAC/BC,EAAM,IAAI,KAAK,KAAK,OAAO,EACjC,MAAI,EAAAD,GAASC,EAEf,CAGA,aAAsB,CACpB,MAAMD,EAAQ,IAAI,KAAK,KAAK,SAAS,EAAE,QAAA,EAEvC,OADY,IAAI,KAAK,KAAK,OAAO,EAAE,QAAA,EACrBA,IAAU,IAAO,GACjC,CACF,CC7BO,MAAME,CAAS,CACpB,GACA,KACA,KACA,MAEA,YAAYH,EAAoB,CAC9B,KAAK,GAAKA,EAAK,GACf,KAAK,KAAOA,EAAK,KACjB,KAAK,KAAOA,EAAK,MAAQ,SACzB,KAAK,MAAQA,EAAK,OAAS,EAC7B,CAGA,SAAmB,CACjB,MAAO,CAAC,EAAE,KAAK,IAAM,KAAK,KAC5B,CACF,CCjBO,MAAMI,CAAW,CACtB,GACA,QACA,WACA,KAEA,YAAYJ,EAAsB,CAChC,KAAK,GAAKA,EAAK,GACf,KAAK,QAAUA,EAAK,QACpB,KAAK,WAAaA,EAAK,WACvB,KAAK,KAAOA,EAAK,MAAQ,UAC3B,CAGA,SAAmB,CACjB,MAAO,CAAC,EAAE,KAAK,IAAM,KAAK,SAAW,KAAK,WAC5C,CACF,CChBO,SAASK,GAA6B,CAC3C,OAAO,KAAK,iBAAiB,gBAAA,EAAkB,QACjD,CAMO,SAASC,EACdC,EACAC,EACgC,CAChC,MAAMC,EAAI,IAAI,KAAKF,CAAG,EAEhBG,EAAY,IAAI,KAAK,eAAe,QAAS,CACjD,SAAUF,EACV,KAAM,UACN,MAAO,UACP,IAAK,SAAA,CACN,EAAE,cAAcC,CAAC,EAEZE,EAAOD,EAAU,KAAME,GAAMA,EAAE,OAAS,MAAM,GAAG,OAAS,GAC1DC,EAAQH,EAAU,KAAME,GAAMA,EAAE,OAAS,OAAO,GAAG,OAAS,GAC5DE,EAAMJ,EAAU,KAAME,GAAMA,EAAE,OAAS,KAAK,GAAG,OAAS,GAExDG,EAAY,IAAI,KAAK,eAAe,QAAS,CACjD,SAAUP,EACV,KAAM,UACN,OAAQ,UACR,OAAQ,EAAA,CACT,EAAE,cAAcC,CAAC,EAElB,IAAIO,EAAOD,EAAU,KAAMH,GAAMA,EAAE,OAAS,MAAM,GAAG,OAAS,KAC9D,MAAMK,EAASF,EAAU,KAAMH,GAAMA,EAAE,OAAS,QAAQ,GAAG,OAAS,KACpE,OAAII,IAAS,OAAMA,EAAO,MAEnB,CAAE,KAAM,GAAGL,CAAI,IAAIE,CAAK,IAAIC,CAAG,GAAI,KAAM,GAAGE,CAAI,IAAIC,CAAM,EAAA,CACnE,CAMO,SAASC,EACdC,EACAC,EACAZ,EACQ,CACR,KAAM,CAACa,EAAGC,EAAIb,CAAC,EAAIU,EAAK,MAAM,GAAG,EAAE,IAAI,MAAM,EACvC,CAACI,EAAGC,CAAC,EAAIJ,EAAK,MAAM,GAAG,EAAE,IAAI,MAAM,EAGnCK,EAAa,KAAK,IAAIJ,EAAGC,EAAK,EAAGb,EAAGc,EAAGC,EAAG,CAAC,EAE3CE,EAAQ,IAAI,KAAK,eAAe,QAAS,CAC7C,SAAUlB,EACV,KAAM,UACN,MAAO,UACP,IAAK,UACL,KAAM,UACN,OAAQ,UACR,OAAQ,UACR,OAAQ,EAAA,CACT,EAAE,cAAc,IAAI,KAAKiB,CAAU,CAAC,EAE/BE,EAAS,OAAOD,EAAM,KAAMd,GAAMA,EAAE,OAAS,MAAM,GAAG,KAAK,EAC3DgB,EAAU,OAAOF,EAAM,KAAMd,GAAMA,EAAE,OAAS,OAAO,GAAG,KAAK,EAC7DiB,EAAS,OAAOH,EAAM,KAAMd,GAAMA,EAAE,OAAS,KAAK,GAAG,KAAK,EAChE,IAAIkB,EAAS,OAAOJ,EAAM,KAAMd,GAAMA,EAAE,OAAS,MAAM,GAAG,KAAK,EAC/D,MAAMmB,EAAS,OAAOL,EAAM,KAAMd,GAAMA,EAAE,OAAS,QAAQ,GAAG,KAAK,EAC7DoB,EAAS,OAAON,EAAM,KAAMd,GAAMA,EAAE,OAAS,QAAQ,GAAG,KAAK,EAC/DkB,IAAW,KAAIA,EAAS,GAW5B,MAAMG,EARa,KAAK,IACtBN,EACAC,EAAU,EACVC,EACAC,EACAC,EACAC,CAAA,EAE4BP,EAG9B,OAAOA,EAAaQ,CACtB,CAMO,SAASC,EACdf,EACAC,EACAZ,EACQ,CACR,OAAO,IAAI,KAAKU,EAAyBC,EAAMC,EAAMZ,CAAQ,CAAC,EAAE,YAAA,CAClE,CAMO,SAAS2B,EAAgBC,EAAc5B,EAAwB,CACpE,MAAMkB,EAAQ,IAAI,KAAK,eAAe,QAAS,CAC7C,SAAUlB,EACV,KAAM,UACN,MAAO,UACP,IAAK,UACL,KAAM,UACN,OAAQ,UACR,OAAQ,UACR,OAAQ,EAAA,CACT,EAAE,cAAc,IAAI,KAAK4B,CAAI,CAAC,EAEzBf,EAAI,OAAOK,EAAM,KAAMd,GAAMA,EAAE,OAAS,MAAM,GAAG,KAAK,EACtDU,EAAK,OAAOI,EAAM,KAAMd,GAAMA,EAAE,OAAS,OAAO,GAAG,KAAK,EAAI,EAC5DH,EAAI,OAAOiB,EAAM,KAAMd,GAAMA,EAAE,OAAS,KAAK,GAAG,KAAK,EAC3D,IAAIW,EAAI,OAAOG,EAAM,KAAMd,GAAMA,EAAE,OAAS,MAAM,GAAG,KAAK,EAC1D,MAAMyB,EAAM,OAAOX,EAAM,KAAMd,GAAMA,EAAE,OAAS,QAAQ,GAAG,KAAK,EAC1D0B,EAAI,OAAOZ,EAAM,KAAMd,GAAMA,EAAE,OAAS,QAAQ,GAAG,KAAK,EAC9D,OAAIW,IAAM,KAAIA,EAAI,GAGX,IAAI,KAAKF,EAAGC,EAAIb,EAAGc,EAAGc,EAAKC,CAAC,CACrC,CAMO,SAASC,EAAoBC,EAAoB,CACtD,GAAI,CACF,MAAMC,EACJ,IAAI,KAAK,eAAe,KAAM,CAC5B,SAAUD,EACV,aAAc,aAAA,CACf,EACE,cAAc,IAAI,IAAM,EACxB,KAAM5B,GAAMA,EAAE,OAAS,cAAc,GAAG,OAAS,GACtD,MAAO,GAAG4B,CAAE,KAAKC,CAAM,GACzB,MAAQ,CACN,OAAOD,CACT,CACF,CAMO,SAASE,GAA4B,CAC1C,GAAI,CAEF,OADa,KAAK,kBAAkB,UAAU,GAC/BC,CACjB,MAAQ,CACN,OAAOA,CACT,CACF,CAEA,MAAMA,EAAqB,CACzB,MACA,mBACA,kBACA,iBACA,sBACA,kBACA,oBACA,gBACA,eACA,gBACA,gBACA,aACA,eACA,eACA,iBACA,aACA,gBACA,mBACA,kBACF,EC1JaC,EAAiB9C,EAA4B,CAAC+C,EAAKC,KAAS,CACvE,OAAQ,CAAA,EACR,UAAW,CAAA,EACX,YAAa,CAAA,EACb,SAAUzC,EAAA,EAEV,SAAWL,GAAS,CAClB,MAAM+C,EAAQ,IAAIhD,EAAMC,CAAI,EAC5B,GAAI,CAAC+C,EAAM,QAAA,EAAW,MAAM,IAAI,MAAM,oBAAoB,EAE1D,OAAAF,EAAK1D,IAAW,CACd,OAAQ,CAAE,GAAGA,EAAM,OAAQ,CAAC4D,EAAM,EAAE,EAAGA,CAAA,CAAM,EAC7C,EACKA,CACT,EAEA,YAAa,CAACC,EAAIC,IAAY,CAE5B,MAAMF,EADQD,EAAA,EACM,OAAOE,CAAE,EAC7B,GAAI,CAACD,EAAO,MAAM,IAAI,MAAM,SAASC,CAAE,YAAY,EAEnD,OAAIC,EAAQ,QAAOF,EAAM,MAAQE,EAAQ,OACrCA,EAAQ,YAAWF,EAAM,UAAYE,EAAQ,WAC7CA,EAAQ,UAASF,EAAM,QAAUE,EAAQ,SACzCA,EAAQ,aAAYF,EAAM,WAAaE,EAAQ,YAC/CA,EAAQ,SAAQF,EAAM,OAASE,EAAQ,QACvCA,EAAQ,cAAgB,SAC1BF,EAAM,YAAcE,EAAQ,aAG9BJ,EAAK1D,IAAW,CACd,OAAQ,CAAE,GAAGA,EAAM,OAAQ,CAAC6D,CAAE,EAAGD,CAAA,CAAM,EACvC,EACKA,CACT,EAEA,YAAcC,GAAO,CACnB,IAAIE,EAAU,GACd,OAAAL,EAAK1D,GAAU,CACb,GAAIA,EAAM,OAAO6D,CAAE,EAAG,CACpB,MAAMG,EAAY,CAAE,GAAGhE,EAAM,MAAA,EAC7B,cAAOgE,EAAUH,CAAE,EACnBE,EAAU,GACH,CAAE,OAAQC,CAAA,CACnB,CACA,OAAOhE,CACT,CAAC,EACM+D,CACT,EAEA,YAAclD,GAAS,CACrB,MAAMoD,EAAW,IAAIjD,EAASH,CAAI,EAClC,GAAI,CAACoD,EAAS,QAAA,EAAW,MAAM,IAAI,MAAM,uBAAuB,EAEhE,OAAAP,EAAK1D,IAAW,CACd,UAAW,CAAE,GAAGA,EAAM,UAAW,CAACiE,EAAS,EAAE,EAAGA,CAAA,CAAS,EACzD,EACKA,CACT,EAEA,eAAgB,CAACJ,EAAIC,IAAY,CAE/B,MAAMG,EADQN,EAAA,EACS,UAAUE,CAAE,EACnC,GAAI,CAACI,EAAU,MAAM,IAAI,MAAM,YAAYJ,CAAE,YAAY,EAEzD,OAAIC,EAAQ,OAAMG,EAAS,KAAOH,EAAQ,MACtCA,EAAQ,OAAMG,EAAS,KAAOH,EAAQ,MACtCA,EAAQ,QAAU,SAAWG,EAAS,MAAQH,EAAQ,OAE1DJ,EAAK1D,IAAW,CACd,UAAW,CAAE,GAAGA,EAAM,UAAW,CAAC6D,CAAE,EAAGI,CAAA,CAAS,EAChD,EACKA,CACT,EAEA,eAAiBJ,GAAO,CACtB,MAAM7D,EAAQ2D,EAAA,EAKd,GAHkB,OAAO,OAAO3D,EAAM,MAAM,EAAE,KAC3CkE,GAAMA,EAAE,aAAeL,CAAA,EAGxB,MAAM,IAAI,MAAM,0BAA0BA,CAAE,wBAAwB,EAGtE,IAAIE,EAAU,GACd,OAAAL,EAAK1D,GAAU,CACb,GAAIA,EAAM,UAAU6D,CAAE,EAAG,CACvB,MAAMM,EAAe,CAAE,GAAGnE,EAAM,SAAA,EAChC,cAAOmE,EAAaN,CAAE,EACtBE,EAAU,GACH,CAAE,UAAWI,CAAA,CACtB,CACA,OAAOnE,CACT,CAAC,EACM+D,CACT,EAEA,cAAgBlD,GAAS,CACvB,MAAMb,EAAQ2D,EAAA,EACRS,EAAa,IAAInD,EAAWJ,CAAI,EACtC,GAAI,CAACuD,EAAW,QAAA,EAAW,MAAM,IAAI,MAAM,yBAAyB,EAEpE,GAAI,CAACpE,EAAM,OAAOoE,EAAW,OAAO,EAClC,MAAM,IAAI,MAAM,SAASA,EAAW,OAAO,YAAY,EAEzD,GAAI,CAACpE,EAAM,UAAUoE,EAAW,UAAU,EACxC,MAAM,IAAI,MAAM,YAAYA,EAAW,UAAU,YAAY,EAG/D,OAAAV,EAAK1D,IAAW,CACd,YAAa,CAAE,GAAGA,EAAM,YAAa,CAACoE,EAAW,EAAE,EAAGA,CAAA,CAAW,EACjE,EACKA,CACT,EAEA,iBAAmBP,GAAO,CACxB,IAAIE,EAAU,GACd,OAAAL,EAAK1D,GAAU,CACb,GAAIA,EAAM,YAAY6D,CAAE,EAAG,CACzB,MAAMQ,EAAiB,CAAE,GAAGrE,EAAM,WAAA,EAClC,cAAOqE,EAAeR,CAAE,EACxBE,EAAU,GACH,CAAE,YAAaM,CAAA,CACxB,CACA,OAAOrE,CACT,CAAC,EACM+D,CACT,EAEA,SAAU,IAAM,CACdL,EAAI,CACF,OAAQ,CAAA,EACR,UAAW,CAAA,EACX,YAAa,CAAA,CAAC,CACf,CACH,EAEA,YAAcrC,GAAa,CACzBqC,EAAI,CAAE,SAAArC,EAAU,CAClB,CACF,EAAE,ECzFK,MAAMiD,CAAiB,CACX,OAEjB,YAAYC,EAAwB,GAAI,CACtC,KAAK,OAASA,CAChB,CAgBQ,UACNC,EACAC,EACAC,EACM,CACN,IAAIC,EACJ,GAAI,CACFA,EAASF,EAAA,CACX,OAASG,EAAgB,CAEvBF,EAAA,EACA,KAAK,OAAO,UAAU,CAAE,UAAAF,EAAW,MAAAI,EAAO,SAAU,GAAM,EAC1D,MACF,CAEAD,GAAQ,MAAOC,GAAmB,CAChCF,EAAA,EACA,KAAK,OAAO,UAAU,CAAE,UAAAF,EAAW,MAAAI,EAAO,SAAU,GAAM,CAC5D,CAAC,CACH,CAWA,SAAS/D,EAAwB,CAC/B,MAAM+C,EAAQH,EAAe,SAAA,EAAW,SAAS5C,CAAI,EAErD,YAAK,UACH,WACA,IAAM,KAAK,OAAO,aAAa+C,CAAK,EACpC,IAAMH,EAAe,SAAA,EAAW,YAAYG,EAAM,EAAE,CAAA,EAG/CA,CACT,CAOA,SAASC,EAA+B,CACtC,OAAOJ,EAAe,WAAW,OAAOI,CAAE,CAC5C,CAMA,cAAwB,CACtB,OAAO,OAAO,OAAOJ,EAAe,SAAA,EAAW,MAAM,CACvD,CAUA,YAAYI,EAAYC,EAAoC,CAK1D,MAAMe,EAAWpB,EAAe,SAAA,EAAW,OAAOI,CAAE,EAC9CiB,EAAWD,EAAW,CAAE,GAAGA,GAAa,KAExCjB,EAAQH,EAAe,SAAA,EAAW,YAAYI,EAAIC,CAAO,EAE/D,YAAK,UACH,cACA,IAAM,KAAK,OAAO,gBAAgBF,CAAK,EACvC,IAAM,CACAkB,GAEFrB,EAAe,SAAA,EAAW,YAAYI,EAAI,CACxC,MAAOiB,EAAS,MAChB,UAAWA,EAAS,UACpB,QAASA,EAAS,QAClB,OAAQA,EAAS,OACjB,YAAaA,EAAS,YACtB,WAAYA,EAAS,UAAA,CACtB,CAEL,CAAA,EAGKlB,CACT,CASA,YAAYC,EAAqB,CAE/B,MAAMiB,EAAWrB,EAAe,SAAA,EAAW,OAAOI,CAAE,EAC9CE,EAAUN,EAAe,SAAA,EAAW,YAAYI,CAAE,EAExD,OAAIE,GACF,KAAK,UACH,cACA,IAAM,KAAK,OAAO,gBAAgBF,CAAE,EACpC,IAAM,CACAiB,GAAUrB,EAAe,SAAA,EAAW,SAASqB,CAAQ,CAC3D,CAAA,EAIGf,CACT,CAOA,oBAAoBgB,EAA6B,CAC/C,OAAO,KAAK,eAAe,OAAQb,GAAMA,EAAE,aAAea,CAAU,CACtE,CAOA,gBAAgB/C,EAAuB,CACrC,MAAMgD,EAAa,IAAI,KAAKhD,CAAI,EAAE,cAAc,MAAM,GAAG,EAAE,CAAC,EAC5D,OAAO,KAAK,aAAA,EAAe,OAAQ4B,GACf,IAAI,KAAKA,EAAM,SAAS,EAAE,YAAA,EAAc,MAAM,GAAG,EAAE,CAAC,IACjDoB,CACtB,CACH,CAWA,YAAYnE,EAA8B,CACxC,MAAMoD,EAAWR,EAAe,SAAA,EAAW,YAAY5C,CAAI,EAE3D,YAAK,UACH,cACA,IAAM,KAAK,OAAO,gBAAgBoD,CAAQ,EAC1C,IAAMR,EAAe,SAAA,EAAW,eAAeQ,EAAS,EAAE,CAAA,EAGrDA,CACT,CAOA,YAAYJ,EAAkC,CAC5C,OAAOJ,EAAe,WAAW,UAAUI,CAAE,CAC/C,CAMA,iBAA8B,CAC5B,OAAO,OAAO,OAAOJ,EAAe,SAAA,EAAW,SAAS,CAC1D,CAUA,eAAeI,EAAYC,EAA0C,CAInE,MAAMe,EAAWpB,EAAe,SAAA,EAAW,UAAUI,CAAE,EACjDiB,EAAWD,EAAW,CAAE,GAAGA,GAAa,KAExCZ,EAAWR,EAAe,SAAA,EAAW,eAAeI,EAAIC,CAAO,EAErE,YAAK,UACH,iBACA,IAAM,KAAK,OAAO,mBAAmBG,CAAQ,EAC7C,IAAM,CACAa,GACFrB,EAAe,SAAA,EAAW,eAAeI,EAAI,CAC3C,KAAMiB,EAAS,KACf,KAAMA,EAAS,KACf,MAAOA,EAAS,KAAA,CACjB,CAEL,CAAA,EAGKb,CACT,CAUA,eAAeJ,EAAqB,CAClC,MAAMiB,EAAWrB,EAAe,SAAA,EAAW,UAAUI,CAAE,EACjDE,EAAUN,EAAe,SAAA,EAAW,eAAeI,CAAE,EAE3D,OAAIE,GACF,KAAK,UACH,iBACA,IAAM,KAAK,OAAO,mBAAmBF,CAAE,EACvC,IAAM,CACAiB,GAAUrB,EAAe,SAAA,EAAW,YAAYqB,CAAQ,CAC9D,CAAA,EAIGf,CACT,CAWA,cAAclD,EAAkC,CAC9C,MAAMuD,EAAaX,EAAe,SAAA,EAAW,cAAc5C,CAAI,EAE/D,YAAK,UACH,gBACA,IAAM,KAAK,OAAO,kBAAkBuD,CAAU,EAC9C,IAAMX,EAAe,SAAA,EAAW,iBAAiBW,EAAW,EAAE,CAAA,EAGzDA,CACT,CAOA,sBAAsBa,EAA+B,CACnD,OAAO,OAAO,OAAOxB,EAAe,SAAA,EAAW,WAAW,EAAE,OACzDyB,GAAMA,EAAE,UAAYD,CAAA,CAEzB,CAOA,yBAAyBF,EAAkC,CACzD,OAAO,OAAO,OAAOtB,EAAe,SAAA,EAAW,WAAW,EAAE,OACzDyB,GAAMA,EAAE,aAAeH,CAAA,CAE5B,CASA,iBAAiBlB,EAAqB,CACpC,MAAMiB,EAAWrB,EAAe,SAAA,EAAW,YAAYI,CAAE,EACnDE,EAAUN,EAAe,SAAA,EAAW,iBAAiBI,CAAE,EAE7D,OAAIE,GACF,KAAK,UACH,mBACA,IAAM,KAAK,OAAO,qBAAqBF,CAAE,EACzC,IAAM,CACAiB,GAAUrB,EAAe,SAAA,EAAW,cAAcqB,CAAQ,CAChE,CAAA,EAIGf,CACT,CAOA,eAAwB,CACtB,OAAO,OAAO,KAAKN,EAAe,SAAA,EAAW,MAAM,EAAE,MACvD,CAKA,kBAA2B,CACzB,OAAO,OAAO,KAAKA,EAAe,SAAA,EAAW,SAAS,EAAE,MAC1D,CAOA,UAAiB,CACfA,EAAe,SAAA,EAAW,SAAA,CAC5B,CACF,CClbO,MAAM0B,CAAc,CACjB,UAEJ,CAAA,EAKJ,GAAkCvB,EAAUwB,EAAgC,CACrE,KAAK,UAAUxB,CAAK,IACvB,KAAK,UAAUA,CAAK,EAAI,CAAA,GAE1B,KAAK,UAAUA,CAAK,EAAG,KAAKwB,CAAO,CACrC,CAKA,IAAmCxB,EAAUwB,EAAgC,CAC3E,GAAI,CAAC,KAAK,UAAUxB,CAAK,EAAG,OAC5B,MAAMyB,EAAmB,KAAK,UAAUzB,CAAK,EAC5C,KAAK,UAAkBA,CAAK,EAAIyB,EAAiB,OAC/CjD,GAAMA,IAAMgD,CAAA,CAEjB,CAKA,KACExB,EACA0B,EACM,CACD,KAAK,UAAU1B,CAAK,GACzB,KAAK,UAAUA,CAAK,EAAG,QAASwB,GAAY,CAC1C,GAAI,CACFA,EAAQE,CAAO,CACjB,OAASpB,EAAG,CACV,QAAQ,MAAM,8BAA8BN,CAAK,IAAKM,CAAC,CACzD,CACF,CAAC,CACH,CACF,CAEO,MAAMqB,EAAgB,IAAIJ","x_google_ignoreList":[0]}
|